/* **** CBDL - Cross-browser DirectMedia Layer * * look at: http://en.wikipedia.org/wiki/SFML */ var CBDL = CBDL || {}; CBDL.version = 0.1; // TODO: completely redo requires & library includes. At the moment, it is broken and disallows multiple Apps from starting if they both require libraries. /* =============================================================================== CBDL.App Object that should be the base for all CBDL-based applications. Contains the bare-minimum logic, members, and vars that any CBDL app should hold. Usage: some_app = new CBDL.App(); some_app.Init(); =============================================================================== */ CBDL.App = CBDL.App || function() {}; CBDL.App.prototype.Main = function() {}; CBDL.App.prototype.Go = function() { if (this.requires) { this._includes = new CBDL.Includes(this); for (var require in this.requires) { this._includes.add(this.requires[require]); } } if (this._includes) { if (this._includes.state != CBDL.states.LOADED) { this.includeCallback = function(return_value) { if (return_value < 0) { // fail console.log("CBDL error: includes failed to load!"); return return_value; } this.Main(); }; } else if (this._includes.state == CBDL.states.LOADED) { this.Main(); } } else { this.Main(); } }; CBDL.App.prototype.include = function(some_library) { if (!this._includes) { this._includes = new CBDL.Includes(this); } this._includes.add(some_library); }; /* called when all includes succeed or fail */ CBDL.App.prototype.includeCallback = function(return_value) { if (return_value < 0) { // fail console.log("CBDL error: includes failed to load!"); return return_value; } else { this.Main(); } }; /* =============================================================================== CBDL.Includes Object that manages includes for an App. Attempts to load all includes specified by the App, providing they are provided prior to the App's onExecute member function being called. TODO: fix on mobile, yoo =============================================================================== */ CBDL.Includes = function(app) { _Includes = this; this.app = app; this.state = 0x00; this.current = 0; this.includes = []; this.add = function(some_library) { if (this.state != CBDL.states.LOADED) { this.includes.push(some_library); if (this.state != CBDL.states.LOADING) { this.load(); } } }; this.load = function() { this.state = CBDL.states.LOADING; if (this.current < this.includes.length) { this.netLoad(); } else { this.state = CBDL.states.LOADED; this.app.includeCallback(); } }; this.netLoad = function() { var exists = false; // Compare some_library string to every script.src so as to disallow // multiple loading of the same library. // TODO: implement getElementsByTagName for <=IE8 var script_list = document.head.getElementsByTagName('script'); for(script in script_list) { if (script_list[script].src) { if (script_list[script].src.substring(script_list[script].src.length-_Includes.includes[_Includes.current].length) == _Includes.includes[_Includes.current]) { exists = true; } } } if (exists == false) { var element = document.createElement("script"); element.type = "text/javascript"; element.src = _Includes.includes[_Includes.current]; element.onload = function() { _Includes.state = 0x00; _Includes.current++; _Includes.load(); // load next file } document.getElementsByTagName('head')[0].appendChild(element); } else { _Includes.state = 0x00; _Includes.current++; _Includes.load(); // load next file } // if (!/*@cc_on!@*/false) { /* var request = new XMLHttpRequest(); } else { var request = new ActiveXObject("Microsoft.XMLHTTP"); } request.onreadystatechange = function() { if (this.readyState == 4) { // done loading console.log(this.status); if (this.status == 200) { var element = document.createElement("script"); element.type = "text/javascript"; element.text = this.responseText; document.getElementsByTagName('head')[0].appendChild(element); _Includes.state = 0x00; _Includes.current++; _Includes.load(); // load next file } else { _Includes.state = CBDL.states.FAILED; } } } request.open('GET', _Includes.includes[_Includes.current], true); request.send(null); */ }; } /* ================ CBDL.inherit(baseClass, newClass) Function that inherits the properties from baseClass into newClass ================ */ CBDL.inherit = function(base_class, new_class) { base_class.call(new_class); new_class.constructor = new_class; } CBDL.extend = function(base_class, new_class) { new_class.prototype = new base_class; new_class.prototype.constructor = base_class; // fix constructor } /* =============================================================================== CBDL.Loop(scope, callback, interval) Object that acts as a simple interface to creating and managing loops within the script. Attempts to call callback() after X interval has passed. Interval is modified internally to adjust for the time callback() takes to finish. If the time is beyond the defined interval, then callback() is called with a 0ms delay. If the interval is not passed, or is passed as 0, then the Loop will rely on the callback() to return an interval value. Usage: new_loop = new CBDL.Loop(some_func, 50); new_loop.start(50); // calls some_func() after 50ms and repeats every 50ms. Or: some_func = function() { return(1000); }; new_loop = new CBDL.Loop(some_func); new_loop.start(); // calls some_func() every 1000ms. =============================================================================== */ CBDL.Loop = function(scope, callback, interval) { this.scope = scope; this.callback = callback; this.interval = (interval ? interval : 0); this.time_start = 0; this.time_end = 0; this.time_delay = this.interval; this.running = false; this.start = function(delay) { this.running = true; if (delay) { setTimeout( (function(_Loop) { return function() { _Loop.loop.call(_Loop); } })(this), delay); //setTimeout(_Loop.loop, _Loop.time_delay); } else { this.loop(); } }; this.stop = function() { this.running = false; }; this.loop = function() { if (this.running) { this.time_start = new Date().getTime(); if (this.interval == 0) { // acquire time_delay from callback return this.time_delay = this.callback.call(this.scope, this.time_start - this.time_end); this.time_end = new Date().getTime(); } else { this.callback.call(this.scope, this.time_start-this.time_end); // return delta to callback this.time_end = new Date().getTime(); this.time_delay = this.time_end-this.time_start; this.time_delay = (this.time_delay > this.interval ? 0 : this.interval - this.time_delay); } setTimeout( (function(_Loop) { return function() { _Loop.loop.call(_Loop); } })(this), this.time_delay); //setTimeout(this.callLoop.call, this); } }; this.callLoop = function() { this.loop(); } } // 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() { }; } /* ================ CBDL.useNamespace(object) Sets the window parent to own all of the defined object's members. Functions in a fashion similar to setting the namespace in C++. Usage: CBDL.dump(); // prints CBDL version CBDL.useNamespace(CBDL); dump(); // prints CBDL version ================ */ CBDL.useNamespace = function(object) { var parent = window; for (var member in object) { parent[member] = object[member]; } } /* ================ CBDL.globalize(name, variable) Adds the passed variable/function/object to a new var in the "global" namespace via the passed name. Usage: some_function() { var foo = 'bar'; } console.log(foo); // undefined some_function() { var foo = 'bar'; CBDL.globalize("foo", foo); // window.foo = 'bar', effectively } console.log(foo); // prints 'bar' ================ */ CBDL.globalize = function(name, variable) { var parent = window; parent[name] = parent[name] || variable; } /* =============================================================================== CBDL.Include(filename) Internal object created by calls to CBDL.include(filename). Simply stores the filename and the current state via the 'state' member (bitmask). =============================================================================== */ CBDL.Include = function(filename) { this.filename = filename; this.state = 0x00; } CBDL.current_include = 0; CBDL.includes = []; CBDL.states = {OPEN: 0x00, LOADING:0x01, LOADED:0x02, FAILED:0x03}; /* ================ CBDL.include(filename) Method which should be invoked by the program implementing CBDL. It provides a nice method for including new source files/libraries located within the same directory as the invoking script. Usage: CBDL.include("CBDL_video.js"); // loads CBDL_video.js, making all objects/vars, etc. available to the calling script. ================ */ CBDL.include = function(filename) { if (typeof filename == "string") { CBDL.includes.push(new CBDL.Include(filename)); if (CBDL.includes[CBDL.current_include].state === CBDL.states.OPEN) { CBDL.loadInclude(CBDL.includes[CBDL.current_include]); //CBDL.nextInclude(); } } else { for (var file in filename) { CBDL.include(filename[file]); } } } /* ================ CBDL.nextInclude() Internal method that is called when an Include finishes loading, so as to load the next Include within the includes list. Usage: CBDL.nextInclude(); ================ */ CBDL.nextInclude = function() { if (CBDL.includes[CBDL.current_include+1] instanceof CBDL.Include) { CBDL.current_include++; CBDL.loadInclude(CBDL.includes[CBDL.current_include]); } else { // CBDL.onReady(); // CBDL.onReady = function(){}; } } /* ================ CBDL.loadInclude(Include) Internal method that attempts to use XMLHttpRequest or otherwise to dynamically load the specified Include. Upon success, a new