diff --git a/.CBDL_graphics.js.swp b/.CBDL_graphics.js.swp new file mode 100644 index 0000000..3f7f008 Binary files /dev/null and b/.CBDL_graphics.js.swp differ diff --git a/.CBDL_net.js.swp b/.CBDL_net.js.swp new file mode 100644 index 0000000..31b52ad Binary files /dev/null and b/.CBDL_net.js.swp differ diff --git a/CBDL.js b/CBDL.js index d3cf25d..3879060 100644 --- a/CBDL.js +++ b/CBDL.js @@ -254,6 +254,20 @@ CBDL.Loop = function(scope, callback, interval) { } // TODO: Add CallbackLoop - an alternative object that loops only on some event +CBDL.EventLoop = function(scope, callback, events) { + this.scope = scope; + this.time_start = 0; + this.time_end = 0; + + this.running = false; + + this.start = function() { + + }; + this.stop = function() { + + }; +} /* ================ @@ -453,6 +467,7 @@ associative array comparison. e.g.: CBDL.Event = CBDL.Event || function(event_types, context) { context = (typeof context !== 'undefined' ? context : window); this.event_pool = new CBDL.Event.EventPool; + this.callbacks = []; for (event_type in event_types) { CBDL.addEvent(context, event_types[event_type], (function(scope) { return function(event) { @@ -525,6 +540,13 @@ CBDL.Event.prototype.addEvent = function(event_types, context) { CBDL.Event.prototype.handleEvent = function(event) { this.event_pool.push(event); + // TODO: add a standard JS "event" event here for an Event-based loop -- or just use a callback function for the same purpose + if (this.callbacks.length > 0) { + for (callback in this.callbacks) { + this.callbacks[callback].callback.call(this.callbacks[callback].scope, event); + } + this.event_pool.pop(event); + } }; CBDL.Event.prototype.pollEvent = function() { @@ -540,6 +562,19 @@ CBDL.Event.prototype.getMouse = function(event, display) { return ( {x: (event.clientX-display.element.offsetLeft), y: (event.clientY-display.element.offsetTop)} ); }; +CBDL.Event.prototype.addCallback = function(scope, callback) { + this.callbacks.push({scope: scope, callback: callback}); + return 0; +}; + +CBDL.Event.prototype.remCallback = function(callback) { + for (callbacks in this.callbacks) { + if (callbacks.callback == callback) { + callbacks = null; + } + } +}; + CBDL.Event.EventPool = CBDL.Event.EventPool || function() {}; CBDL.Event.EventPool.prototype.events = []; @@ -562,6 +597,10 @@ CBDL.Event.EventPool.prototype.push = function(event) { this.events.push(event); }; +CBDL.Event.EventPool.prototype.pop = function(event) { + this.events.pop(event); +}; + // TODO: create a CBDL.Loader object that allows for fancier file loading with // progress, current states, etc. @@ -678,17 +717,55 @@ CBDL.Console.prototype.print = (typeof console.log !== 'undefined' ? // CROSS-BROWSER GLUE CBDL.addEvent = (typeof window.attachEvent !== 'undefined' ? + // TODO: don't use tagName, find out the base Element type or array of types and use those function(target, event_type, callback, bubble) { var bubble = (typeof bubble === 'undefined' ? true : bubble); - target.attachEvent('on'+event_type, callback); + if (target.attachEvent == undefined) { // not an Object with built-in Event handling, add a bogus element for events + target.__event = (target.__event ? target.__event : document.createElement("div")); + target.__event.parent = target; + target.__event.attachEvent('on'+event_type, callback); + } else { + target.attachEvent('on'+event_type, callback); + } } : function(target, event_type, callback, bubble) { var bubble = (typeof bubble === 'undefined' ? true : bubble); - target.addEventListener(event_type, callback, bubble); + if (target.addEventListener == undefined) { // not an Object with built-in Event handling, add a bogus element for events + target.__event = (target.__event ? target.__event : document.createElement("div")); + target.__event.parent = target; + target.__event.addEventListener(event_type, callback, bubble); + } else { + target.addEventListener(event_type, callback, bubble); + } } ); +CBDL.fireEvent = function(target, event_type, obj) { + // TODO: we really want to return target as the event target here, rather than target.__event. This could be solved in a few ways -- 1. copy over all necessary methods/properties to the target Event when a custom event is added via CBDL.addEvent, thereby removing the need for target.__event. 2. Figure out a way to properly dispatchEvent from target.__event with target being passed in the end. + var call_target = (target.__event ? target.__event : target); + if (document.createEventObject) { // IE + var evt = document.createEventObject(); + // NOTE: we do a shallow copy of obj to evt - should this be done differently? :S + for (attr in obj) { + evt[attr] = obj[attr]; + } + return call_target.fireEvent('on'+event_type, evt); + } else { // Godly Browsers that are not Horrendous + var evt = document.createEvent("HTMLEvents"); + for (attr in obj) { + evt[attr] = obj[attr]; + } + evt.initEvent(event_type, true, true); + return !call_target.dispatchEvent(evt); + //return !(call_target.dispatchEvent.call(target, evt)); + //return !(call_target.dispatchEvent(evt)).call(target, evt); + /*return !(function(evt) { + this.dispatchEvent(evt); + }).call(target);*/ + } +}; + CBDL.test_div = document.createElement('div'); if ('WebkitTransform' in CBDL.test_div.style) { diff --git a/CBDL_graphics.js b/CBDL_graphics.js index 1b386c8..6a64069 100644 --- a/CBDL_graphics.js +++ b/CBDL_graphics.js @@ -94,6 +94,18 @@ CBDL.Graphics.BaseDisplay.prototype._Box_.prototype.setColor = function(color) { this.color = color; }; +CBDL.Graphics.BaseDisplay.prototype._Line_ = function(display, coords, size, scale, rotation) { + this.display = display; + this.coords = coords; + this.size = size; + this.scale = scale; + this.rotation = rotation; +}; CBDL.extend(CBDL.Graphics.BaseDisplay.prototype._Drawable_, CBDL.Graphics.BaseDisplay.prototype._Line_); + +CBDL.Graphics.BaseDisplay.prototype._Line_.prototype.setColor = function(color) { + this.color = color; +}; + CBDL.Graphics.BaseDisplay.prototype._Font_ = function(family) { this.family = family; }; @@ -309,6 +321,8 @@ CBDL.Graphics.Canvas2dDisplay.prototype.clear = function() { CBDL.Graphics.Canvas2dDisplay.prototype.draw = function(source, s_position, t_position) { var width = source.size.width || 1; var height = source.size.height || 1; + var s_position = s_position || {x: 0, y: 0}; + var t_position = t_position || {x: 0, y: 0}; // How this works: element_context, being the overall canvas, handles sprite rotation // and scaling. offscreen_context handles setting opacity, and is written to // element_context as an Image when complete. @@ -374,7 +388,38 @@ CBDL.Graphics.Canvas2dDisplay.prototype.draw = function(source, s_position, t_po } // Copy manipulated image back over to main canvas. this.element_context.drawImage(this.offscreen, -(width/2), -(height/2)); - + } else if (source instanceof CBDL.Graphics.BaseDisplay.prototype._Line_) { + this.offscreen.width = width; + this.offscreen.height = height; + this.element_context.translate(width/2, height/2); + if (typeof source.scale !== 'undefined') { + this.element_context.scale(source.scale.x, source.scale.y); + } + if (typeof source.rotation !== 'undefined') { + this.element_context.rotate(source.rotation * CBDL.Graphics.RADIAN); + } + this.element_context.fillStyle = source.color; + this.element_context.beginPath(); + this.element_context.moveTo(source.coords.ax, source.coords.ay); + this.element_context.lineTo(source.coords.bx, source.coords.by); + this.element_context.lineWidth = width; + this.element_context.stroke(); + if (typeof source.opacity !== 'undefined') { + // NOTE: getImageData & putImageData are painfully slow! :( + var image = this.offscreen_context.getImageData(0, 0, width, height); + var image_data = image.data; + var length = image_data.length; + for (var i=3;i < length;i +=4) { + if (image_data[i] > 0) { // if not transparent + image_data[i] = image_data[i]*source.opacity; + } + } + image.data = image_data; + this.offscreen_context.putImageData(image, 0, 0); + } + // Copy manipulated image back over to main canvas. + this.element_context.drawImage(this.offscreen, -(width/2), -(height/2)); + } else if (source instanceof CBDL.Graphics.BaseDisplay.prototype._String_) { this.element_context.font = source.size+"px "+source.font.family; this.element_context.fillStyle = source.color; @@ -402,6 +447,10 @@ CBDL.Graphics.Canvas2dDisplay.prototype.Box = function(size, scale, rotation) { return (new CBDL.Graphics.BaseDisplay.prototype._Box_(this, size, scale, rotation)); }; +CBDL.Graphics.Canvas2dDisplay.prototype.Line = function(coords, size, scale, rotation) { + return (new CBDL.Graphics.BaseDisplay.prototype._Line_(this, coords, size, scale, rotation)); +}; + CBDL.Graphics.Canvas2dDisplay.prototype.Font = function(family) { return (new CBDL.Graphics.BaseDisplay.prototype._Font_(family)); }; @@ -477,6 +526,8 @@ CBDL.Graphics.DivDisplay.prototype.Fill = function(red, green, blue) { }; CBDL.Graphics.DivDisplay.prototype.draw = function(source, s_position, t_position) { + var s_position = s_position || {x: 0, y: 0}; + var t_position = t_position || {x: 0, y: 0}; this.current_z++; source.data.style.visibility = 'hidden'; if (!source.isDrawn) { @@ -488,8 +539,27 @@ CBDL.Graphics.DivDisplay.prototype.draw = function(source, s_position, t_positio var transform = ''; + if (source instanceof CBDL.Graphics.BaseDisplay.prototype._Line_) { + delta_x = source.coords.bx - source.coords.ax; + delta_y = source.coords.by - source.coords.ay; + source.data.style.height = (Math.abs(delta_x) + Math.abs(delta_y))+"px"; + rads = Math.atan2(delta_y, delta_x); + angle = (rads * 180 / Math.PI) + 90; + var rot_x = (source.coords.ax < source.coords.bx ? source.coords.ax : source.coords.bx); + var rot_y = (source.coords.ay < source.coords.by ? source.coords.ay : source.coords.by); + /*t_position.x += rot_x * Math.cos(rads) + rot_y * Math.sin(rads); + t_position.y += -rot_x * Math.sin(rads) + rot_y * Math.cos(rads); + //t_position.x += rot_x - (rot_x/2); + //t_position.y += rot_y - (rot_y/2);*/ + t_position.x += rot_x; + t_position.y += rot_y; + transform += "rotate("+angle+"deg)"; + } else { + source.data.style.height = source.size.height; + } + if (typeof source.rotation !== 'undefined') { - //transform += "rotate("+source.rotation+"deg)" + transform += "rotate("+source.rotation+"deg)" } if (typeof source.scale !== 'undefined') { transform += "scale("+source.scale.x+", "+source.scale.y+")" @@ -497,9 +567,7 @@ CBDL.Graphics.DivDisplay.prototype.draw = function(source, s_position, t_positio if (typeof source.opacity !== 'undefined') { source.data.style.opacity = source.opacity; } - - source.data.style.width = source.size.width; - source.data.style.height = source.size.height; + source.data.style.width = source.size.width+"px"; source.data.style.left = t_position.x+"px"; source.data.style.top = t_position.y+"px"; source.data.style.zIndex = this.current_z; @@ -561,6 +629,34 @@ CBDL.Graphics.DivDisplay.prototype._Box_.prototype.setColor = function(color) { this.data.style.backgroundColor = this.color; }; +CBDL.Graphics.DivDisplay.prototype.Line = function(coords, size, scale, rotation) { + return (new CBDL.Graphics.DivDisplay.prototype._Line_(this, coords, size, scale, rotation)); +}; + +CBDL.Graphics.DivDisplay.prototype._Line_ = function(display, coords, size, scale, rotation) { + this.isDrawn = false; + this.display = display; + this.color = "black"; + this.data = document.createElement("div"); + this.data.style.width = Math.abs(coords.ax - coords.bx) + Math.abs(coords.ay - coords.by); + this.data.style.height = size.height; + this.data.style.display = "block"; + this.data.style.backgroundColor = this.color; + this.coords = coords; + this.size = size; + this.scale = scale; + this.rotation = rotation; +}; CBDL.extend(CBDL.Graphics.BaseDisplay.prototype._Line_, CBDL.Graphics.DivDisplay.prototype._Line_); +CBDL.Graphics.DivDisplay.prototype._Line_.prototype.destroy = function() { + if (this.data.parentNode) { + this.data.parentNode.removeChild(this.data); + } +}; +CBDL.Graphics.DivDisplay.prototype._Line_.prototype.setColor = function(color) { + this.color = color; + this.data.style.backgroundColor = this.color; +}; + CBDL.Graphics.DivDisplay.prototype.Font = function(family) { return (new CBDL.Graphics.BaseDisplay.prototype._Font_(family)); }; @@ -576,7 +672,8 @@ CBDL.Graphics.DivDisplay.prototype._String_ = function(display, text, font, size this.font = font; this.size = size; this.color = "black"; - this.data = document.createElement("div"); + this.data = document.createElement("xmp"); // xmp is universally supported and displays contained content raw, so we use it + this.data.style.padding = this.data.style.margin = 0; this.data.style.display = "block"; this.data.style.fontSize = size+"px"; this.data.style.fontFamily = font.family; @@ -665,6 +762,7 @@ CBDL.Graphics.VideoMode = function(width, height, flags) { this.width = width; this.height = height; this.flags = flags; + return this; }; /* diff --git a/CBDL_net.js b/CBDL_net.js index 4938294..3c24322 100644 --- a/CBDL_net.js +++ b/CBDL_net.js @@ -1,6 +1,9 @@ -/* **** CBDL_net - Optional layer which adds network support +/* **** CBDL_net - Optional layer that adds network support * * * */ -CBDL.Net = CBDL.Net || {}; -CBDL.Net.version = 0.1; +CBDL.Net = CBDL.Net || { + version: 0.1 +}; + +CBDL.Net.BaseSocket.prototype.Send = function(send) {}; diff --git a/CBDL_netwine.js b/CBDL_netwine.js new file mode 100644 index 0000000..08b2c9f --- /dev/null +++ b/CBDL_netwine.js @@ -0,0 +1,124 @@ +/* CBDL_netwine - netwine (bi-directional proxy) network library + +This library is designed to interface with the netwine server - a proxy server that seemlessly connects the networking methods of browser-based JavaScript to a standard TCP bidirectional stream. To understand more of this technology, see http://kettek.exoss.net/netwine/ + +Provided is a single Socket "class" that does its best to emulate a normal bi-direction TCP stream. This is accomplished through one dedicated inbound stream from the server (non-closing socket) and multiple dynamically created outbound push requests to the server. + +In future versions, netwine may also provide a WebSockets interface for more efficient bandwidth usage and less latency. + +*/ + +CBDL.netwine = CBDL.netwine || { + version: 0.1, + FLAG: { + SYNC: 0x01 // syncronous socket behavior + }, + UNOPENED: 0x00, + PROXY_CONNECTING: 0x01, + PROXY_CONNECTED: 0x02, + PROXY_FAILED: 0x03, + SERVER_CONNECTING: 0x04, + SERVER_CONNECTED: 0x05, + SERVER_FAILED: 0x06 +}; + +// TODO: Socket should produce Event(s) in the standard browser method - I hope custom events are possible. +CBDL.netwine.Socket = function(proxy_address, proxy_port, target_address, target_port, flags) { + this.proxy_address = proxy_address; + this.proxy_port = proxy_port; + this.target_address = target_address; + this.target_port = target_port; + + this.key = null; + + this.flags = flags; + + this.in_socket = null; // our in socket - outbound is dynamically created + this.inbound = []; // inbound sockets, uses an array due to socket refresh + this.outbound = []; // outbound sockets + + this.is_connected = false; + this.status = 0; + this.poll_interval = 50; // poll input every 50 ms + + this.buffer = ""; // our buffer of bytes received + this.bytes = 0; // the amount of bytes read thus far + + // +}; + +CBDL.netwine.Socket.prototype.Connect = function() { + // first we open up our INIT request socket and wait for the ack and our new key + // if successful, we establish INIT socket as our input and poll it for new data according to poll_interval. Due to the input being cached, it may be wise to open a new input connection (live), switch to it, then close the original seemlessly, so as to free memory + // + this.status = CBDL.netwine.PROXY_CONNECTING; + var init_socket = new XMLHttpRequest(); + init_socket.open("INIT", "/"+this.target_address+":"+this.target_port, (this.flags & CBDL.netwine.FLAG.SYNC ? false : true)); + init_socket.onreadystatechange = (function(socket) {return function() { + switch(this.readyState) { + case 1: // open + break; + case 2: // headers received + if (this.status == 200) { // we're good! + console.log(this.status+': '+this.statusText); + // report connection ok + socket.key = this.statusText; + socket.in_socket = this; + socket.status = CBDL.netwine.PROXY_CONNECTED; + socket.bytes = this.responseText.length; + } + CBDL.fireEvent(socket, "open", {status: this.status}); + break; + case 3: // loading + // FIXME: loading triggers even when responseText is not populated with new bytes! :S + // "read in" the new bytes + var read = this.responseText.substr(socket.bytes); + // increment our bytes read + socket.bytes += read.length; + // append to the socket's buffer (remove?) + socket.buffer += read; + // send off the event to all our listeners out there! + CBDL.fireEvent(socket, "recv", {data: read, length: read.length}); + break; + case 4: // done + CBDL.fireEvent(socket, "close", {}); + break; + } + }})(this); + init_socket.send(""); + return 0; +}; + +//this function sends the provided data to the netwine server we are connected to. +CBDL.netwine.Socket.prototype.Send = function(bytes, size) { + if (this.status != CBDL.netwine.PROXY_CONNECTED) { + return 1; + } + if (!this.key) { + return 2; + } + var socket = new XMLHttpRequest(); + socket.open("PUSH", this.key, (this.flags & CBDL.netwine.FLAG.SYNC ? false : true)); + socket.onreadystatechange = (function(socket) {return function() { + switch(this.readyState) { + case 1: // open + break; + case 2: // headers received + if (this.response == 200) { // we're good! + } + break; + case 3: // loading + break; + case 4: // done + break; + } + }})(this); + socket.send(bytes); + + return 0; // success +}; + +// this function is called by the parent program to see if any new data has come on. +CBDL.netwine.Socket.Poll = function() { + +}; diff --git a/tests/line.html b/tests/line.html new file mode 100644 index 0000000..fb09874 --- /dev/null +++ b/tests/line.html @@ -0,0 +1,83 @@ + +
+ + + +