Added callback system to CBDL.Event, allowing for a scope-aware function to be triggered when any Event comes in. This can be used as an alternative to a standard poll-based Loop. Added additional code related to adding and firing custom Events, even to objects that are not DOM elements - this needs work, however. Added the Line object to CBDL.Graphics. This is fully implemented in the Canvas renderer, but needs to be properly implemented in the Div renderer. Fixed Div renderer's String object to use the xmp element type rather than div for rendering text.

master
kts 2014-09-04 16:05:53 -04:00
parent 8d1a4f86f5
commit 3fb2511b53
7 changed files with 396 additions and 11 deletions

Binary file not shown.

BIN
.CBDL_net.js.swp 100644

Binary file not shown.

81
CBDL.js
View File

@ -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) {

View File

@ -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;
};
/*

View File

@ -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) {};

124
CBDL_netwine.js 100644
View File

@ -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() {
};

83
tests/line.html 100644
View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html> <head>
<script type="text/javascript" src="../CBDL.js"></script>
<script type="text/javascript" src="../CBDL_graphics.js"></script>
<script type="text/javascript">
lineTest = function() {
this.name = "lineTest";
//this.requires = ["../CBDL_graphics.js"];
this.Main = function() {
// set up our display
if (!(this.display = new CBDL.Graphics.Display(document.body, new CBDL.Graphics.VideoMode(320, 240, 0), CBDL.Graphics.BACKEND.CANVAS_2D))) {
console.log(" Error while creating display backend!");
return 1;
}
// initialize the display
this.display.Init();
// create our Line object
this.line = new this.display.Line({ax: 64, ay: 64, bx: 128, by:196}, {width:1, height: 0});
this.which = false; // bool for A/B line points
// create our information strings
var font = new this.display.Font("courier");
this.string = new this.display.String("<space> - toggle between A/B points, <up/down> - increase/decrease Y of point, <left/right> - increase/decrease X of point", font, 10);
this.info_a = new this.display.String("A("+this.line.coords.ax+","+this.line.coords.ay+")", font, 10);
this.info_a.setColor("#00F");
this.info_b = new this.display.String("B("+this.line.coords.bx+","+this.line.coords.by+")", font, 10);
// initial drawing operations
this.reDraw();
// set up our events
this.events = new CBDL.Event(["keydown", "keyup"], window);
// connect to Event handler
this.events.addCallback(this, onEvent);
};
function onEvent(event) {
switch (event.type) {
case "keydown":
switch(event.keyCode) {
case 32: // toggle A or B
if (this.which) {
this.info_a.setColor("#00F");
this.info_b.setColor("#000");
this.which = false;
} else {
this.info_b.setColor("#00F");
this.info_a.setColor("#000");
this.which = true;
}
break;
case 37: // decrease point x
(this.which ? this.line.coords.bx-- : this.line.coords.ax--);
break;
case 39: // increase point x
(this.which ? this.line.coords.bx++ : this.line.coords.ax++);
break;
case 38: // decrease point y
(this.which ? this.line.coords.by-- : this.line.coords.ay--);
break;
case 40: // increase point y
(this.which ? this.line.coords.by++ : this.line.coords.ay++);
break;
}
break;
}
this.reDraw();
}
this.reDraw = function() {
this.display.Fill(0xFF, 0xFF, 0xFF);
this.display.draw(this.line, null, null);
this.display.draw(this.string, null, null);
// update and draw coordinate strings
var ax = this.line.coords.ax;
var ay = this.line.coords.ay;
var bx = this.line.coords.bx;
var by = this.line.coords.by;
this.info_a.setText("A("+ax+","+ay+")");
this.info_b.setText("B("+bx+","+by+")");
this.display.draw(this.info_a, null, {x: ax, y: ay});
this.display.draw(this.info_b, null, {x: bx, y: by});
};
}; CBDL.extend(CBDL.App, lineTest);
CBDL.addEvent(window, 'load', function() { (new lineTest()).Go();});
</script>
</head> <body> </body> </html>