/* *Copyright (c) 2009, TellurianRing.com *All rights reserved. * *Redistribution and use in source and binary forms, with or without modification, *are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the Organization (TellurianRing.com) nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR *ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @extends Point * @class Represents a Rectangular area with x,y coordinates as well as width, * and height. * @author Eric Berry * @example * // Creates a new Bounds object using standard JSON. * var myBounds = Bounds({x: 10, y: 10, w: 100, h: 100}); * @example Alerts true * alert(myBounds.contains(Bounds({x: 15, y: 15}))); * @example Alerts true * alert(myBounds.contains({x: 20, y: 20, w: 20, h: 20})); * @example Alerts false * alert(myBounds.contains({x: 20, y: 20, w: 100, h: 100})); * This example alerts false, because when width and height are supplied, the * given bounds must be completely within the 'myBounds' area. * @example // Using a Bounds object. * var myBounds = Bounds({x: 10, y: 10, w: 100, h: 100}); * // alerts 10 * alert(myBounds.x); * // alerts 200 * alerts(myBounds.w * myBounds.h); * @param {Bounds,Point,Object} _bounds Another Bounds object, or Point object. Can be created * using this function or created with a JSON Object containing the * following attributes: * * * * * * *
Property Required Default
x yes
y yes
w yes
h yes
*/ function Bounds(_details) { // {{{ /** @constructs */ function _Bounds(_details) { // {{{ /** * Height * @field */ this.h = _details.h; /** * Width * @field */ this.w = _details.w; /** * Checks to see if the given Bounds object is within this * bounds object. This function works with Point objects as well. * Meaning that if you pass it an object with only x, and y * coordinates, it only checks to see if they are within this bounds. * @function */ this.contains = function(_bounds) { // my maximum x and y coords. var mx = this.x + this.w; var my = this.y + this.h; // other bounds x and y coords. var ox = _bounds.x; var oy = _bounds.y; // initial checks to make sure other x, and y coords are within these // bounds. if(ox < this.x || ox > mx || oy < this.y || oy > my) { return false; } // if there's a width and height in the given bounds, then test to see // if entire bounds fits within this bounds. if(exists(_bounds.w, _bounds.h)) { // we only need to check that the max x, and y coords of the other // bounds is less than our max x and y coords because the other // x and y values have already been checked, and wouldn't have // reached here if they weren't inside this bounds. ox = ox + _bounds.w; oy = oy + _bounds.h; if(ox > mx) { return false; } if(oy > my) { return false; } } // if we've gotten this far, then the other bounds is within these // bounds return true; } } // }}} _Bounds Point(_details).extend(_Bounds); var theBounds = new _Bounds(_details); return theBounds; } // }}} Bounds // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Represents a CSS Font. * @author Eric Berry * @example * // Creates a new Font object * var myFont = Font({style: "bold", size: "12px", family: "Tahoma"}); * @example * // Creates a new Font with multiple families. * var myFont = Font({style: "bold", size: "12px", family: ["Tahoma", "Arial"]}); * @example * // Creates a new Font using convienence method. * var myFont = font("Tahoma", "12px", "bold"); * @param _details An JSON object containing the following properties: * * * * * * * *
Property Required Default
family no Tahoma
size no 12px
style no Font.Styles.normal
variant no normal
weight no Font.Weights.normal
* @see * Font for more details on the CSS font property. */ function Font(_details) { // {{{ /** @constructs */ function _Font(_details) { // {{{ this.family = check(_details.family, "Tahoma"); this.size = check(_details.size, "12px"); this.style = check(_details.style, "normal"); this.variant = check(_details.variant, "normal"); this.weight = check(_details.weight, "normal"); // toCss Creates a CSS font string from the given details. /** * Creates a CSS string from this Font object. * @function * @return A String in the format of: "[style] [variant] [weight] [size] [family(ies)]" */ this.toCss = function() { var cssFont = this.style + " " + this.variant + " " + this.weight + " " + this.size if(typeof(this.family) == 'array') { for(var fi in this.family) { cssFont += " " + this.family[fi]; } } else { cssFont += " " + this.family; } return cssFont; } } // }}} _Font var theFont = new _Font(_details); return theFont; } // }}} Font /** *@namespace Some Font style constants */ Font.Styles = { /** @property */ italic: "italic", /** @property */ normal: "normal", /** @property */ oblique: "oblique" }; /** *@namespace Some Font weight constants */ Font.Weights = { /** @property */ bold: "bold", /** @property */ bolder: "bolder", /** @property */ lighter: "lighter", /** @property */ normal: "normal" }; // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Groups some content together. Any action taken on a group is applied to all * of it's children. * @extends SceneContent * @example // New Group where the content starts at (10,10). * Group({ * x: 10, y: 10, * content: [ * Graphic({ url: "./images/ball.png" }), * Graphic({ url: "./images/beetleship.png", y: 20, x: 30 }) * ] * }) *
* * // In this example, the ball will display at (10,10), and the beetleship * // will display at (50,10). * @example // Moving the group. * myGroup.moveBy({ y: 10 }); * // In this example, the ball will now display at (10, 20), and the beetleship * // will display at (50, 20). *
* * @param {Object} _details A JSON object with extra content. * * * *
Property Required Default
content no new Array()
*/ function Group(_details) { // {{{ /** @constructs */ function _Group(_details) { // {{{ // Private Members {{{ var content = check(_details.content, new Array()); var name = _details.name; // }}} // Public Members {{{ /** * Adds content to this group. Content added is first loaded then added to * the the other content of this group at which point it will join the * draw cycle. * @param {SceneContent} _content The SceneContent to add to this group. */ this.addContent = function(_content) { _content.load(scene, this); content.push(_content); } this.draw = function(_context) { // draw the groups contents to a different canvas. var bounds = this.getBounds(); _context.save(); // translate the context to where the group will begin. if(bounds.x > 0 || bounds.y > 0) { _context.translate(bounds.x, bounds.y); } for(var index in content) { var component = content[index]; if(component.isActive() && component.isVisible()) { component.draw(_context); } } _context.restore(); } /** * Gets the content of this group. This implementation returns the actual * content array, but should not be used to add content to this group. * To add content to this group use the addContent function, which loads * the content before adding it to the content array. Using this function * to add content to this group will result in that content not being * loaded, but being added to the draw cycle. * @return The Content array. */ this.getContent = function() { // {{{ return content; } // }}} getContent this.load = function(_scene, _parent) { // {{{ this.loadBase(_scene, _parent); for(var index in content) { var component = content[index]; component.load(_scene, this); } } // }}} load this.update = function(_run_time) { // {{{ this.updateGroup(_run_time); } // }}} update /** * Updates each of this groups content, and then calculates the groups * cumulative bounds. This is mainly used by sub classes like Rotate and * Translate groups. */ this.updateGroup = function(_run_time) { // {{{ var bounds = this.getBounds(); for(var index in content) { var component = content[index]; if(component.isActive()) { component.update(_run_time); var comp_bounds = component.getBounds(); bounds.w = Math.max(bounds.w, comp_bounds.x + comp_bounds.w); bounds.h = Math.max(bounds.h, comp_bounds.y + comp_bounds.h); } } this.setSize(bounds); this.updateBase(); } // }}} updateGroup // }}} Public Members } // }}} _Group SceneContent(_details).extend(_Group); var theGroup = new _Group(_details); return theGroup; } // }}} Group // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Describes an x and y coordinate. * @example // Creating a point from a JSON object * var myPoint = Point({ x: 10, y: 10 }); * @example // creating a point from another Point object, or a Bounds object. * var myPoint2 = Point(myPoint); * var bounds = Bounds(myPoint2); * var myPoint3 = Point(bounds); * @param {Point, Bounds, Object} _details Another Point object, or Bounds * object. Can also be created with a JSON object with these attributes: * * * * *
Property Required Default
x yes
y yes
*/ function Point(_details) { // {{{ /** @constructs */ function _Point(_details) { // {{{ var that = this; /** @field Public details field. Contains user's original details object. */ this.details = _details; /** @field X coordinate */ this.x = check(_details.x, 0); /** @field Y coordinate */ this.y = check(_details.y, 0); /** * This extends the provided child function with this instance. */ this.extend = function(_child) { // {{{ _child.prototype = that; } // }}} extend } // }}} _Point var thePoint = new _Point(_details); return thePoint; } // }}} Point // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class A Scene represents the root level of content within a Stage. * @param {Object} _details A JSON object with the following properties: * * * * *
Property Required Default
content no new Array()
name no ""
* Properties: * name: The name of this Scene. * content: The content of this Scene. * @example * var myScene = Scene({ * name: "MyScene", * content: [ * Graphic({url: "./images/ball.png"}) * ] * }); *
* */ function Scene(_details) { // {{{ /** @constructs */ function _Scene(_details) { // {{{ // Private Members {{{ var content = check(_details.content, new Array()); var name = check(_details.name, ""); var stage = null; // }}} // Public Members {{{ this.details = _details; /** * Adds the given SceneContent to this Scene. This function * calls the load function on the given SceneContent before adding it to * the Scene's content list, so that it will be loaded before getting * picked up by the Stage's fun loop. * @function * @param SceneContent _content The SceneContent to be added to this * Scene. */ this.addContent = function(_content) { _content.load(this, this); content.push(_content); } /** * Calls draw on each child content with the given canvas context. * @function * @param _context The drawing context from the Stage's canvas element. */ this.draw = function(_context) { // {{{ for(var index in content) { var component = content[index]; if(component.isActive() && component.isVisible()) { component.draw(_context); } } } // }}} draw /** * This extends the provided child function with this instance. * @function * @param _child The child function to be extended. */ this.extend = function(_child) { // {{{ _child.prototype = this; } // }}} extend /** * Uses Stage.getSize() to retrieve the size of the stage, and * returns a Bounds({x: 0, y: 0, w: stage_size.w, h: stage_size.h}). * @function * @return Bounds({x: 0, y: 0, w: stage_size.w, h: stage_size.h}) */ this.getBounds = function() { // {{{ var stage_size = stage.getSize(); return Bounds({x: 0, y: 0, w: stage_size.w, h: stage_size.h}); } // }}} /** * Always returns null. The Scene always represents the root * level of content within a Stage. * @function * @return null */ this.getParent = function() { // {{{ return null; } // }}} /** * Gets the Stage this scene belongs to. * @function * @return Stage object this scene belongs to. */ this.getStage = function() { // {{{ return stage; } // }}} getStage /** * Returns a reference to itself. * @function * @return A reference to this Scene. */ this.getScene = function() { // {{{ return this; } // }}} /** * Loads this Scene in the given Stage. This function loops * through the Scene content and calls load on each component, passing a * reference to this Scene. * @function * @param Stage _stage The Stage this scene belongs to. */ this.load = function(_stage) { // {{{ this.loadContent(_stage); } // }}} load /** * Loads this Scene's content in the given Stage. This function * loops through the Scene content and calls load on each component, * passing a reference to this Scene. * @function * @param Stage _stage The Stage this scene belongs to. */ this.loadContent = function(_stage) { // {{{ stage = _stage; stage[name] = this; for(var index in content) { var component = content[index]; component.load(this, this); } } // }}} load /** * Scene's can't be moved. This method does nothing. * @function */ this.moveBy = function(_distance) { // {{{ } // }}} /** * Scene's can't be moved. This method does nothing. * @function */ this.moveTo = function(_point) { // {{{ } // }}} /** * Updates this Scene with the time since the Stage loaded. * This function loops through the Scene content and calls the update * method on each component. * @function * @param _runtime The time since the Stage was loaded. */ this.update = function(_runtime) { // {{{ this.updateContent(_runtime); } // }}} update /** * Updates this Scene's content with the time since the Stage * loaded. This function loops through the Scene content and calls the * update method on each component. * @function * @param _runtime The time since the Stage was loaded. */ this.updateContent = function(_runtime) { // {{{ for(var index in content) { var component = content[index]; if(component.isActive()) { component.update(_runtime); } } } // }}} update // }}} Public Members } // }}} _Scene var theScene = new _Scene(_details); return theScene; } // }}} Scene // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class SceneContent represents any object which can be placed in the * "content" field of any group node of a Scene. This object defines the methods * and attributes, which are expected to be implemented by any scene content. * @author Eric Berry * * @param _details All SceneContent takes a JSON object in it's factory * function with the following properties: * Expected but not required properties: * * * * * * * * * *
Property Required Default
active no true
h no 0
visible no true
w no 0
x no 0
y no 0
name no
* This is a base set of properties. Any object that extends SceneContent should * be expected to handle this set of properties. Some exceptions include the * Graphic object which sets the width and height properties after the image has * been loaded. The Sound object, which doesn't have any use of coordinates or * size. And Groups, their width and height attributes are based on their * content. * @example * // Creates a new SceneContent object. * var myContent = SceneContent({x: 10, y: 10, w: 100, h: 100, name: "my_content"}); * @example * // Your content can be retrieved from the scene using its name. * myScene["my content"] * @constructs */ function SceneContent(_details) { // {{{ /** @constructs */ function _SceneContent(_details) { // {{{ // private variables {{{ var active = check(_details.active, true); var h = check(_details.h, 0); var name = _details.name; var parent = null; var scene = null; var stage = null; var visible = check(_details.visible, true); var w = check(_details.w, 0); var x = check(_details.x, 0); var y = check(_details.y, 0); // used as a prototype for the clone function below. function Clone() {} // }}} // public variables {{{ /** * The public details field should refer to the original object passed in to the * creation function. Users should always be able to attach their own details to * any scene content object and retrieve it again using the public details * field. */ this.details = _details; // }}} // public functions {{{ /** * The draw function is responsible for drawing this content to the given * canvas context. It is called after the update function. * @param _context The canvas context this content should be drawn to. */ this.draw = function(_context) {} /** * This extends the provided child function with this instance. * @param _child The child function to be extended. */ this.extend = function(_child) { // {{{ _child.prototype = this; } // }}} extend /** * The getBounds function should return the location and size of this * content relative to it's parent. * @example * var myGraphic = Graphic({ x: 13, y: 13, url: "./images/ball.png" }); * var myGrp = Group({ x: 10, y: 10, content: [myGraphic] }); * alert(myGraphic.getBounds().x); // alerts 13 * * * @return {Bounds} with 4 fields, x, y, w, and h. */ this.getBounds = function() { // {{{ return Bounds({x: x, y: y, w: w, h: h}); } // }}} getBounds /** * Gets the name of this SceneContent as it was passed in the original * _details object. * @return The name of this SceneContent */ this.getName = function() { // {{{ return name; } // }}} getName /** * The getParent function returns the parent of this content. This usually * returns another SceneContent object, except when this object is the * first child of the Scene object. In which case, the Scene itself is * returned. * @return {SceneContent|Scene} The parent of this content. */ this.getParent = function() { // {{{ return parent; } // }}} getParent /** * The getScene function returns the scene this content belongs to. The scene * is passed in to the load function. * @return {Scene} The Scene Object this content belongs to. */ this.getScene = function() { // {{{ return scene; } // }}} getScene /** * The getStage function returns the stage this content belongs to. The stage * can originally be retrieved by the Scene object which is passed in to the * load function. * @return {Stage} The Stage Object this content belongs to. */ this.getStage = function() { // {{{ return stage; } // }}} getStage /** * isActive Is this content currently active. This field tells the Scene * that this content needs to be updated and drawn. If content is not * active then it will not be updated or drawn. * @function * @returns true if this content is active, false otherwise. */ this.isActive = function() { // {{{ return active; } // }}} isActive /** * isVisible Is this content currently visible. This field tells the Scene * that this content needs to be drawn. If the content is not visible then * it will not be drawn. * @function * @returns true if this content is visible, false otherwise. */ this.isVisible = function() { // {{{ return visible; } // }}} /** * The load function gets called after all the stage objects and content have * been initialized. It's only called once when the Scene's load method is * called. * @param {Scene} _scene The Scene this content belongs to. * @param {SceneContent|Scene} _parent The parent of this content. */ this.load = function(_scene, _parent) { // {{{ this.loadBase(_scene, _parent); } // }}} load /** * Sets the scene, parent and stage for this content. Also adds this object * to the scene by name. */ this.loadBase = function(_scene, _parent) { // {{{ scene = _scene; parent = _parent; stage = scene.getStage(); scene[name] = this; } // }}} loadBase /** * The moveBy function moves this content's relative x, y position by the * the given distances. This is equivalent to an instant translation, vs. * the use of a Translation Transform node. The function should work on * partial data, and a lack of data should signify non-action. * @example * var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" }); * alert(myImage.getBounds().x); // alerts 10 * myImage.moveBy({x: 5}); * alert(myImage.getBounds().x); // alerts 15 * * * @param {Number} _distance A Distance object with two fields, x, and y. */ this.moveBy = function(_distance) { // {{{ if(exists(_distance.x)) { x += _distance.x; } if(exists(_distance.y)) { y += _distance.y; } } // }}} moveBy /** * The moveTo function moves this content's relative x, y position to the * values provided in the point object. The function should work on partial * data, and a lack of data should signify non-action. * @example * var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" }); * alert(myImage.getBounds().x); // alerts 10 * myImage.moveTo({x: 15}); * alert(myImage.getBounds().x); // alerts 15 * * * @param {Point} _point Point object with two fields, x, and y. */ this.moveTo = function(_point) { // {{{ if(exists(_point.x)) { x = _point.x; } if(exists(_point.y)) { y = _point.y; } } // }}} moveTo /** * setActive Sets this content's active status flag. * @function * @param {Boolean} _active true or false */ this.setActive = function(_active) { // {{{ active = _active; } // }}} setActive /** * The setSize function sets this content's width and height. The function * should work on partial data, and a lack of data should signify * non-action. * @example // Only updates the width of the SceneContent. * var rect = Rectangle({ x: 10, y: 10, w: 10, h: 10 }); * rect.setSize({w: 15 }); * alert(rect.getBounds().w == 15) // alerts true. * * * @param {Bounds} _dimension A Dimension object with two fields, w, and h. */ this.setSize = function(_dimension) { // {{{ if(exists(_dimension.w)) { w = _dimension.w; } if(exists(_dimension.h)) { h = _dimension.h; } } // }}} setSize /** * setVisible Sets this content's visible status flag. * @function * @param {Boolean} _visible true or false */ this.setVisible = function(_visible) { // {{{ visible = _visible; } // }}} setVisible /** * The update function is called after the load function has been called * and before the draw function is called. It's called repeatedly with the * total time passed since the Scene's load time. * @param {Number} _runtime The total time passed since the Scene's load time. */ this.update = function(_runtime) { this.updateBase(_runtime); } /** * The updateBase function should be called by subclasses overriding the * update function. This function automatically hides SceneContent if it * goes outside the boundaries of the stage. This method can be extended * by supplying an update function in the initialization data. * @example // Supplying your own update function * Graphic({ * x: 10, y: 10, url: "./images/ball.png", * update: function(_runtime) { * // do something * } * }) * @param {Number} _runtime The total time passed since the Scene's load time. */ this.updateBase = function(_runtime) { var bounds = this.getBounds(); var stage_size = this.getStage().getSize(); if(stage_size.contains(bounds)) { this.setVisible(true); } else { this.setVisible(false); } if(exists(this.details.update)) { this.details.update.apply(this, arguments); } } // }}} public functions } // }}} _SceneContent var theSceneContent = new _SceneContent(_details); return theSceneContent; } // }}} SceneContent // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class The Stage function creates a new Stage and returns it. * @param {Object} _details A JSON object with the following properties: * * * * * * * * * *
Property Required Default
bg_color no #EFEFEF
container_id yes
height yes
name no ""
scene no Scene({name: "empty", content: []})
update_time no 1
width yes
* @example // creates an empty stage. * var stage1 = Stage({ * width: 100, height: 100, * container_id: "stage_example_1" * }); *
* * @example // Loading an additional Scene. * var scene1 = Scene({ * content: [ Graphic({ x: 24, y: 24, url: "./images/ball.png" }) ] * }); * * var scene2 = Scene({ * content: [ Graphic({ x: 24, y: 20, url: "./images/beetleship.png" }) ] * }); * * var stage2 = Stage({ * width: 100, height: 100, * container_id: "stage_example_2", * scene: scene1 * }); * stage2.unload(); * stage2.setScene(scene2); * stage2.load(); *
* * */ function Stage(_details) { // {{{ /** * @constructs A private constructor that creates a Stage object. The use of a private * function here acts as a further abstraction to the underlying properties * of a stage. Any modifications to the Stage function's prototype aren't * passed down to actual Stage objects. That would require modifying the * prototype of the _Stage function, which is inaccessable from outside. This * doesn't prevent modifications to Stage object instances, however those * modifications will be limited to the scope of that instance alone. */ function _Stage(_details) { // {{{ // Private Members {{{ // These are all private members. They have been made private so that the // Stage isn't abnormally mutable. If properties of this Stage need to be // modified, mutators should be created and used. var bg_color = check(_details.bg_color, "#EFEFEF"); var canvas = null; var container_id = _details.container_id; var context = null; var height = _details.height; var load_time = null; var name = check(_details.name, ""); var running = false; var scene = exists(_details.scene) ? _details.scene : Scene({name: "empty", content: []}); var update_time = check(_details.update_time, 1); var width = _details.width; /** * The funLoop is essentially an animation loop. It passes the time passed * since the load time of the Stage down to the current scene, which then * updates all of it's content. * @private */ function funLoop() { // {{{ var current_time = new Date().getTime(); var run_time = current_time - load_time; context.fillStyle = bg_color; context.fillRect (0, 0, width, height); if(exists(scene)) { scene.update(run_time); scene.draw(context); } if(running) { setTimeout(funLoop, update_time); } } // }}} funLoop // }}} Private Members. // Public Members {{{ this.details = _details; /** * This function creates the canvas element under the container element. * It uses the findContainer function to find the container, and uses * document.creatElement to create the canvas element. */ this.createCanvas = function() { // {{{ var container = this.findContainer(); if(typeof container != 'undefined') { canvas = createCanvas(container_id + "_canvas", width, height); context = canvas.getContext("2d"); context.fillStyle = bg_color; context.fillRect (0, 0, width, height); container.appendChild(canvas); } } // }}} createCanvas /** * This function finds the container element from the Document. This * implementation uses document.getElementById to find the container * element. */ this.findContainer = function() { // {{{ return document.getElementById(container_id); } // }}} findContainer /** * This function returns the name of this Stage. */ this.getName = function() { // {{{ return name; } // }}} getName /** * Returns the size of this Stage. */ this.getSize = function() { // {{{ return Bounds({ x: 0, y: 0, w: width, h: height}); } // }}} /** * This function returns the current scene of the Stage. */ this.getScene = function() { // {{{ return scene; } // }}} getScene /** * Checks to see if the scene is running. * @return true if the scene is running. */ this.isRunning = function() { // {{{ return running; } // }}} isRunning /** * This function loads this Stage, creating it's canvas and then calling * loadScene with the current Scene if there is one. */ this.load = function() { // {{{ if(canvas == null) { this.createCanvas(); } if(exists(scene)) { this.loadScene(scene); } load_time = new Date().getTime(); running = true; funLoop(); } // }}} load /** * This function loads the given Scene into the Stage. It calls the load * function of the given scene with an instance of this Stage. * @param _scene - The Scene to load into this Stage. */ this.loadScene = function(_scene) { // {{{ scene = _scene; scene.load(this); } // }}} /** * Sets the scene for this stage. */ this.setScene = function(_scene) { // {{{ scene = _scene; } // }}} setScene /** * Stops the funLoop from running and clears the FPS update. */ this.unload = function() { running = false; } // }}} Public Members } // }}} _Stage // Create a Stage object with the given scene data. var theStage = new _Stage(_details); // if load wasn't specified in the scene definition, or it was specified as // true, then we load the scene. Otherwise, we assume load will be called // explicitly by the user. if(!exists(_details.load) || _details.load) { theStage.load(); } return theStage; } // }}} Stage // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: var Units = { MILLISECONDS: 1, MINUTES: 60000, SECONDS: 1000, milliseconds: 1, minutes: 60000, seconds: 1000 } // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * A utility function that checks to see if a given set of objects all are * defined. This does not check against null. * @param An undefined number of arguments. */ function exists() { // {{{ var exists = true; // This loops through each argument and checks to see if it's defined. // It shortcuts the first time it finds one that doesn't. for(var index = 0; index < arguments.length && exists; index++) { exists = exists && (typeof arguments[index] != 'undefined') } return exists; }// }}} exists /** * A utility function for create a canvas element with the given ID, width, and * height. It is not added to the document, that is left up to the caller. * @param _id - The ID of the canvas object that will be created. * @param _w - The width of the canvas object. * @param _h - The height of the canvas object. * @param _def - The default content of the canvas object. This content is shown * in browsers that don't support the HTML 5 Canvas element. */ function createCanvas(_id, _w, _h, _def) { // {{{ var default_content = '

Ooops. Your browser does not support the HTML 5 Canvas element.

' + '

Please install a compatible browser.

' + '

Such As: ' + 'Firefox, ' + 'Safari, ' + 'Google Chrome, ' + 'or Opera' + '

'; // create the element. var canvas = document.createElement("canvas"); // set the attributes for id, width, and height canvas.setAttribute("id", _id); canvas.setAttribute("width", _w); canvas.setAttribute("height", _h); // also add the width and height style attributes. canvas.style.width = _w + "px"; canvas.style.height = _h + "px"; canvas.innerHTML = check(_def, default_content); return canvas; }// }}} createCanvas /** * A utility function which checks if the given _value exists. If it does, it * returns that, otherwise it returns the given _default. * @param _value The value to check for. * @param _default The value to return if the given _value doesn't exist. */ function check(_value, _default) { if(exists(_value)) { return _value; } return _default; } /** * A convenience function which creates a new Font object using nameless parameters. * @function * @param family * @param size * @param style * @param varient * @param weight */ function font(family, size, style, varient, weight) { var obj = {family: family, size: size, style: style, varient: varient, weight: weight}; return Font(obj); } /** * Utility function to convert degrees into radians. * @function * @param {Number} _degrees The degrees to convert. */ function toRadians(_degrees) { return _degrees * Math.PI / 180.0; } // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class This Content simply prints out the current frames per second. * @extends Text * @param {Object} _details A JSON object. This content has the same properties as the Text * object. * * * *
Property Required Default
text no "FPS: "
* "text" is prepended to the actual FPS value: "[text][FPS]" * @see Text for other properties, and styling information. * @example // Creates a new FPS object * FPS({ x: 10, y: 16 }) *
* * @requires Firefox 3.5.x, Safari, or Google Chrome */ function FPS(_details) { // {{{ function _FPS(_details) { // {{{ // Private Members {{{ var text = check(_details.text, "FPS: "); var updates = 0; var that = this; // }}} Private Members // Public Members {{{ this.update = function(_run_time) { this.setText(text + (updates++ / (_run_time / Units.seconds))); if(exists(_details.update)) { _details.update.apply(this, arguments); } } // }}} Public Members } // }}} _FPS Text(_details).extend(_FPS); var theFps = new _FPS(_details); return theFps; } // }}} FPS // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Draws an image into the Scene. The name "Graphic" was chosen over "Image" due * to the native Javascript Image object. It had to be named something else, to * avoid browser confusion as to which Image object we're actually creating. * To create a Graphic, you need to supply it's location and url. * @example // Using a remote image. * Graphic({ * x: 10, y: 40, * url: "http://www.tellurianring.com/images/logo-wordmark-version_100.png" * }) *
* * @example // Relative URL's work as well: * Graphic({ * x: 10, y: 10, * url: "./images/ball.png" * }) *
* * @example // Scaling the image. * Graphic({ * x: 10, y: 50, * scale_w: 100, scale_h: 26, * url: "http://www.tellurianring.com/images/logo-wordmark-version.png" * }) *
* * @extends SceneContent * @param {Object} _details A JSON object with the following properties: * * * * * *
Property Required Default
scale_h no
scale_w no
url yes
*/ function Graphic(_details) { // {{{ /** @constructs */ function _Graphic(_details) { // {{{ // Private Members {{{ var image = new Image(); var loaded = false; var scale_h = _details.scale_h; var scale_w = _details.scale_w; var url = _details.url; // tries to preload the image at initialization. image.loader = this; image.onload = function() { this.loader.doLoad(); } // }}} // Public Members {{{ this.doLoad = function() { loaded = true; if(exists(scale_w, scale_h)) { this.setSize({ w: scale_w, h: scale_h }); } else { this.setSize({w: image.width, h: image.height}); } if(exists(_details.onLoad)) { _details.onLoad(); } } this.draw = function(_context) { // {{{ var bounds = this.getBounds(); if(loaded) { if(exists(scale_w, scale_h)) { _context.drawImage(image, bounds.x, bounds.y, scale_w, scale_h); } else { _context.drawImage(image, bounds.x, bounds.y); } } } // }}} draw // Overriden so the image is actually loaded during the load event from // the scene. this.load = function(_scene, _parent) { this.loadBase(_scene, _parent); image.src = url; } // }}} Public Members }// }}} _Graphic SceneContent(_details).extend(_Graphic); var theGraphic = new _Graphic(_details); return theGraphic; } // }}} Graphic // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class LoadingScene Loads a given set of assets. Assets include Graphics and Sounds. * @example // Displays a single message as assets are loading. * LoadingScene({ * steps: { * "Assets": [ * Graphic({ url: "./images/ball.png" }), * Sound({ url: "./sounds/bang.ogg" }), * ... * ] * } * }) * // This example will show the following message: * // "Loading - Assets [percent]% complete, overall [percent]% complete" * @example // Having two steps for images and sounds. * LoadingScene({ * steps: { * "Graphics": [ * Graphic({ url: "./images/ball.png" }), * ... * ], * "Sounds": [ * Sound({ url: "./sounds/bang.ogg" }), * ... * ] * } * }) * @example // You can also supply your own content. * // Say you want to display your own background image as the assets are loading * LoadingScene({ * steps: { * "Assets": [ * Graphic({ url: "./images/ball.png" }), * Sound({ url: "./sounds/bang.ogg" }), * ... * ] * }, * content: [ * Graphic({ url: "./images/loading_bg.png" }) * ] * }) * @param {Object} _details A JSON object with the following properties. * * * * *
Property Required Default
content no
steps no
*/ function LoadingScene(_details) { // {{{ function _LoadingScene(_details) { // {{{ // Private Members {{{ var content = _details.content; var current_step = 0; var finished = false; var step_loaded = 0; var step_loading = 0; var step_names = new Array(); var step_totals = {}; var steps = check(_details.steps, {}); var that = this; var total = 0; var total_loaded = 0; var total_loading = 0; // }}} Private Members // Public Members {{{ /** * Creates the default content for this LoadingScene. The default content * is a Text object which displays the following message in the middle of * the scene: * "Loading - [step] [percent]% complete, overall [percent]% complete" * @function */ this.createDefaultContent = function() { // {{{ var scene_bounds = this.getScene().getBounds(); var defaultContent = [ Text({ x: scene_bounds.w / 2, y: scene_bounds.h / 2, align: Text.Align.center, font: Font({ size: "16px", weight: Font.Weights.bold }), update: function(_run_time, _step, _step_percent, _total_percent, _finished) { var text = "Loading - " + _step + " " + Math.round(_step_percent * 100) + "% complete, overall " + Math.round(_total_percent * 100) + "% complete"; if(_finished) { text = "Loaded!"; } this.setText(text); } }) ]; return defaultContent; } // }}} createDefaultContent this.draw = function(_context) { // {{{ for(var ci in content) { var component = content[ci]; component.draw(_context); } } // }}} draw /** * @function called when all the assets have been loaded. Calls finish * function of details if one is provided. This function can be used to * set the scene on the stage after the assets have been loaded. * @example * var stage = Stage({ * scene: Scene({ * content: [ * LoadingScene({ * steps: { ... }, * finished: function() { * stage.unload(); stage.setScene(nextScene); stage.load(); * } * }) * ] * }) * }); */ this.finish = function() { // {{{ finished = true; if(exists(this.details.finish)) { details.finish(); } } // }}} finish /** * Loads any content provided then starts the loader thread to preload * any assets provided in the steps. * @function */ this.load = function(_scene, _parent) { // {{{ this.loadBase(_scene, _parent); if(!exists(content)) { content = this.createDefaultContent(); } // Load content. for(var ci in content) { var component = content[ci]; component.load(_scene, that); } // set up the info. var stepCount = 0; for(var si in steps) { var step = steps[si]; step_names[stepCount] = si; var assetCount = 0; for(var ai in step) { var asset = step[ai]; assetCount++; total++; } step_totals[si] = assetCount; stepCount++; } // start the loading thread setTimeout(this.loadNextAsset, 30); } // }}} load /** * Loads the next asset in the current step. * @function */ this.loadNextAsset = function() { // {{{ var step_name = step_names[current_step]; var step = steps[step_name]; // get next current asset and increment loading variables. var asset = step[step_loading++]; total_loading++; asset.details.onload = function() { // increment loaded variables. total_loaded++; step_loaded++; if(step_loading < step_totals[step_name]) { // if loading is less than total number of assets for current step // we load the next one. setTimeout(that.loadNextAsset, 30); } else if (++current_step < step_names.length) { // else if the next current_step is less than total number of steps // start on the next step. step_loaded = 0; step_loading = 0; setTimeout(that.loadNextAsset, 30); } else { // we're done. that.finish(); } }; asset.load(that.getScene(), that); } // }}} loadNextAsset /** * Updates all content within this LoadingScene. This update function * differs from the Scene object because it passes extra information to * each content. It calls each component's update function with the * following: * * * * * * * *
Property Type Description
runtime Integer Current time in milliseconds since the scene was loaded.
step String Current step name as defined in the _details object.
step_percent Decimal Percent complete within the current step.
total_percent Decimal Percent complete overall.
finished Boolean Have all the assets been loaded.
* @function */ this.update = function(_run_time) { // {{{ var current_step_name = step_names[current_step]; var step_total = step_totals[current_step_name]; for(var ci in content) { var component = content[ci]; component.update(_run_time, current_step_name, step_loaded / step_total, total_loaded / total, finished); } } // }}} update // }}} Public Members } // }}} _LoadingScene SceneContent(_details).extend(_LoadingScene); var theLoadingScene = new _LoadingScene(_details); return theLoadingScene; } // }}} LoadingScene // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class A Path builder which can be used to draw pretty much any shape. Due to * current Canvas path restrictions, a Path's width and height are always equal * to the width and height passed into the Path factory function. This is unlike * other Groups in where the Groups bounds is calculated based on it's sub content. * @extends Group * @example // A Paths bounds is equal to the bounds passed in. * var path = Path({ x: 10, y: 10, w: 25, h: 30 }); * alert(path.getBounds().x == 10 && path.getBounds().w == 25); // alerts true * path.moveBy({ x: 10 }); * alert(path.getBounds().x == 20); // alerts true * * * @example * // Creating a circle. * Path({ fill: "#CCCCCC" }).jumpTo(75, 50) * .arc(50, 50, 25, 0, 360 * (Math.PI / 180), true) *
* * @example * // Creating a box. * Path({x: 10, y: 10, stroke: "#333333"}) * .lineTo(50, 50) * .lineTo(50, 90) * .lineTo(10, 90) * .lineTo(10, 10) *
* * @example * // Splitting the builder across multiple lines. * var path = Path({x: 10, y: 10, stroke: "#333333"}); * path.lineTo(50, 50); * path.lineTo(50, 90); * path.lineTo(10, 90); * path.lineTo(10, 10); *
* * @extends Group * @param {Object} _details A JSON Object with these properties. * * * * * *
Property Required Default
fill no transparent
stroke no #000000
stroke_width no 1
*/ function Path(_details) { // {{{ /** @constructs */ function _Path(_details) { // {{{ // Private members {{{ var fill = check(_details.fill, "transparent"); var stroke = check(_details.stroke, "#000000"); var stroke_width = check(_details.stroke_width, 1); // }}} // Public Members {{{ /** * Dreates an arc (based on a circle) in this path. * @function * @param _x The center x coord. of the circle. * @param _y The center y coord. of the circle. * @param _start The starting angle of this arc in radians. * @param _end The ending angle of this arc in radians. * @param _clockwise Should this arc go clockwise. */ this.arc = function(_x, _y, _radius, _start, _end, _clockwise) { this.getContent().push(Arc({x: _x, y: _y, radius: _radius, start: _start, end: _end, clockwise: _clockwise})); return this; } /** * Draws a bezier curve from the current position of the path to the * given coordinates. * @function * @param _cpx1 The control point x1. * @param _cpy1 The control point y1. * @param _cpx2 The control point x2. * @param _cpy2 The control point y2. * @param _x The x coord. to draw to. * @param _y The y coord. to draw to. */ this.beziTo = function(_cpx1, _cpy1, _cpx2, _cpy2, _x, _y) { // {{{ this.getContent().push(BezierTo({cpx1: _cpx1, cpy1: _cpy1, cpx2: _cpx2, cpy2: _cpy2, x: _x, y: _y})); return this; } // }}} lineTo // Overridden to draw this path. this.draw = function(_context) { var bounds = this.getBounds(); var content = this.getContent(); _context.save(); _context.beginPath(); _context.moveTo(bounds.x, bounds.y); for(var index in content) { var component = content[index]; component.draw(_context); } if(exists(fill)) { _context.fillStyle = fill; _context.fill(); } if(exists(stroke)) { _context.strokeStyle = stroke; _context.lineWidth = stroke_width; _context.stroke(); } _context.closePath(); _context.restore(); } /** * Moves the next part of this path to the given coordinates. * This function does not draw anything. * @function * @param _x The x coord. to jump to. * @param _y The y coord. to jump to. */ this.jumpTo = function(_x, _y) { // {{{ this.getContent().push(JumpTo({x: _x, y: _y})); return this; } // }}} jumpTo /** * Draws a line from the current position of the path to the given coordinates. * @function * @param _x The x coord. to draw to. * @param _y The y coord. to draw to. */ this.lineTo = function(_x, _y) { // {{{ this.getContent().push(LineTo({x: _x, y: _y})); return this; } // }}} lineTo /** * Draws a quadratic curve from the current position of the path to the * given coordinates. * @function * @param _cpx The control point x. * @param _cpy The control point x. * @param _x The x coord. to draw to. * @param _y The y coord. to draw to. */ this.quadTo = function(_cpx, _cpy, _x, _y) { // {{{ this.getContent().push(QuadraticTo({cpx: _cpx, cpy: _cpy, x: _x, y: _y})); return this; } // }}} quadTo // Overriden because Group's update method calculates bounds based on // sub content. this.update = function(_runtime) { this.updateBase(_runtime); } // }}} } // }}} _Path /** * Draws an arc from the current location to a new one. * @private */ function Arc(_details) { // {{{ /** @constructs */ function _Arc(_details) { // {{{ // Private Members {{{ var radius = check(_details.radius, 0); var start = check(_details.start, 0); var end = check(_details.end, 0); var clockwise = check(_details.clockwise, true); // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); // arc function asks for anti-clockwise parameters. So we just // negate the clockwise value. _context.arc(bounds.x, bounds.y, radius, start, end, !clockwise); } // }}} draw // }}} Public Members } // }}} _Arc SceneContent(_details).extend(_Arc); var theArc = new _Arc(_details); return theArc; } // }}} Arc /** * Draws a Bezier curve from the current location to a new one. * @private */ function BezierTo(_details) { // {{{ /** @constructs */ function _BezierTo(_details) { // {{{ // Private Members {{{ var cpx1 = check(_details.cpx1, 0); var cpy1 = check(_details.cpy1, 0); var cpx2 = check(_details.cpx2, 0); var cpy2 = check(_details.cpy2, 0); // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); _context.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, bounds.x, bounds.y); } // }}} draw // }}} Public Members } // }}} _BezierTo SceneContent(_details).extend(_BezierTo); var theBezierTo = new _BezierTo(_details); return theBezierTo; } // }}} BezierTo /** * Moves the context to a new location. * @private */ function JumpTo(_details) { // {{{ /** @constructs */ function _JumpTo(_details) { // {{{ // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); _context.moveTo(bounds.x, bounds.y); } // }}} draw // }}} Public Members } // }}} _JumpTo SceneContent(_details).extend(_JumpTo); var theJumpTo = new _JumpTo(_details); return theJumpTo; } // }}} JumpTo /** * Draws a line from the current position to a new one. * @private */ function LineTo(_details) { // {{{ /** @constructs */ function _LineTo(_details) { // {{{ // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); _context.lineTo(bounds.x, bounds.y); } // }}} draw // }}} Public Members } // }}} _LineTo SceneContent(_details).extend(_LineTo); var theLineTo = new _LineTo(_details); return theLineTo; } // }}} LineTo /** * Draws a Quadratic curve from the current location to a new one. * @private */ function QuadraticTo(_details) { // {{{ /** @constructs */ function _QuadraticTo(_details) { // {{{ // Private Members {{{ var cpx = check(_details.cpx, 0); var cpy = check(_details.cpy, 0); // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); _context.quadraticCurveTo(cpx, cpy, bounds.x, bounds.y); } // }}} draw // }}} Public Members } // }}} _QuadTo SceneContent(_details).extend(_QuadraticTo); var theQuadTo = new _QuadraticTo(_details); return theQuadTo; } // }}} QuadTo Group(_details).extend(_Path); var thePath = new _Path(_details); return thePath; } // }}} Path // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Draws a rectangle into the Scene. To create a Rectangle, you need only supply * it's location and size, along with at least 1 of 3 rendering instructions. * The 3 rendering instructions are "fill", "stroke", and "clear". * The fill rendering instruction tells this rectangle to fill the rectangle * with the given color. * The stroke rendering instruction tells this rectangle to stroke (outline) the * rectangle with the given color. You can also specify a stroke_width, which * is used to define the line thickness used while outlining the rectangle. * The clear rendering instruction actually is set to "true" or "false", and it * tells the rectangle to clear/remove the area behind it. * * @example * To draw a red rectangle with a blue outline, you might define it as such: * Rectangle({ * x: 25, y: 25, w: 50, h: 50, * fill: "red", stroke: "blue" * }) * This creates a red square at (0,0) with width and height of 50, and a blue * outline. *
* * @extends SceneContent * @param {Object} _details A JSON Object with the following properties: * * * * * * *
Property Required Default
clear no false
fill no transparent
stroke no #000000
stroke_width no 1
*/ function Rectangle(_details) { // {{{ /** @constructs */ function _Rectangle(_details) { // {{{ // Private Members {{{ var clear = check(_details.clear, false); var fill = check(_details.fill, "transparent"); var stroke = check(_details.stroke, "#000000"); var stroke_width = check(_details.stroke_width, 1); // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); if(exists(fill)) { _context.fillStyle = fill; _context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h); } if(exists(stroke)) { _context.strokeStyle = stroke; if(typeof stroke_width != 'undefined') { _context.lineWidth = stroke_width; } _context.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h); } if(clear) { _context.clearRect(bounds.x, bounds.y, bounds.w, bounds.h); } } // }}} draw // }}} Public Members } // }}} _Rectangle SceneContent(_details).extend(_Rectangle); var theRectangle = new _Rectangle(_details); return theRectangle; } // }}} // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Loads some audio or sound into the Scene. This implementation uses the * HTML 5 audio tag and therefore requires a compatible browser. * @example * var sound1 = Audio({ * url: "http://www.tellurianring.com/sounds/laugh.ogg" * }); * sound1.play(); *
* * * @example * Relative URL's work as well: * var sound2 = Audio({ url: "./sounds/ohhh.ogg" }); * sound2.play(); *
* * * @extends SceneContent * @param {Object} _details A JSON object with the following properties: * * * * * * *
Property Required Default
loop no false
start no false
url yes
volume no 1
*/ function Sound(_details) { // {{{ /** @constructs */ function _Sound(_details) { // {{{ // Private Members {{{ var audio = document.createElement("audio"); var loaded = false; var loop = check(_details.loop, false); var start = check(_details.start, false); var url = _details.url; var volume = check(_details.volume, 1); // tries to preload the image at initialization. audio.autoplay = start; audio.loader = this; audio.loop = loop; audio.onload = function() { this.loader.doLoad(); }; audio.volume = volume; // }}} Private Members // Public Members {{{ /** * Executed when the audio is actually loaded. */ this.doLoad = function() { loaded = true; if(exists(_details.onLoad)) { _details.onLoad(); } } // Overridden to load audio at load time instead of creation time. this.load = function(_scene, _parent) { this.loadBase(_scene, _parent); audio.src = url; audio.load() } /** * Sets the volume to the initial volume, and starts the sound. * @function */ this.play = function() { audio.play(); } /** * Pauses the sound. * @function */ this.pause = function() { audio.pause(); } /** * Sets the looping of this Sound. * @function * @param {Boolean} _loop Turns looping on for this sound - true or false. */ this.setLoop = function(_loop) { loop = _loop; audo.loop = _loop; } /** * Sets the volume on this Sound. * @function * @param {Decimal} _volume Volume of sound between 0.0 and 1.0 */ this.setVolume = function(_volume) { volume = _volume; audo.volume = _volume; } /** * Stops the sound. This implementation pauses the sound and resets it. */ this.stop = function() { this.pause(); audio.currentTime = 0; } // Overridden to react to stage unloads - which cause the sound to stop. this.update = function(_runtime) { if(this.getStage().isRunning() == false) { this.pause(); } } // }}} Public Members }// }}} _Sound SceneContent(_details).extend(_Sound); var theSound = new _Sound(_details); return theSound; } // }}} Sound // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Text Draws some text on the Stage. This implementation uses the WHATWG * specifications and is only available in certain browsers. * @extends SceneContent * @see WHATWG Canvas and Text * @param {Object} _details An JSON object containing the following properties: * * * * * * * * * *
Property Required Default
align no start
baseline no alphabetic
fill no #000000
font no Font({family: ["Tahoma", "Ariel"]})
stroke no transparent
stroke_width no 1
text no ""
* @example // Simple example * Text({ x: 10, y: 50, text: "HTML 5 Rocks!" }) *
* * @example // Centering text. * Text({ x: 50, y: 50, text: "HTML 5 Rocks!", align: Text.Align.center }) *
* * @example // Making it cooler. * Text({ * x: 50, y: 50, text: "~CooL~", align: Text.Align.center, * fill: "#DFDFFF", stroke: "#413FFF", stroke_width: 2, * font: Font({ size: "18px", weight: Font.Weights.bold }) * }) *
* * @requires Firefox 3.5.x, Safari, or Google Chrome */ function Text(_details) { // {{{ /** @private @constructs */ function _Text(_details) { // {{{ // Private Members {{{ var align = check(_details.align, Text.Align.start); var baseline = check(_details.baseline, Text.Baseline.alpha); var fill = check(_details.fill, "#000000"); var font = check(_details.font, Font({family: ["Tahoma", "Ariel"]})); var stroke = check(_details.stroke, "transparent"); var stroke_width = check(_details.stroke_width, 1); var text = check(_details.text, ""); // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ var bounds = this.getBounds(); var x = bounds.x; var y = bounds.y; _context.textAlign = align; _context.textBaseline = baseline; _context.font = font.toCss(); if(exists(fill) && exists(_context.fillText)) { _context.fillStyle = fill; _context.fillText(text, x, y) } if(exists(stroke) && exists(_context.strokeText)) { _context.lineWidth = stroke_width; _context.strokeStyle = stroke; _context.strokeText(text, x, y); } } // }}} draw /** * Sets the text of this Text object. * @function * @param {String} _text The text to display. */ this.setText = function(_text) { // {{{ text = _text; }// }}} setText // }}} Public Members } // }}} _Text SceneContent(_details).extend(_Text); var theText = new _Text(_details); return theText; } // }}} Text /** * @namespace Some Text Alignment Constants. */ Text.Align = { /** @property */ center: "center", /** @property */ end: "end", /** @property */ left: "left", /** @property */ right: "right", /** @property */ start: "start" }; /** * @namespace Some Text Baseline Constants. */ Text.Baseline = { /** @property */ alpha: "alphabetic", /** @property */ alphabetic: "alphabetic", /** @property */ bot: "bottom", /** @property */ bottom: "bottom", /** @property */ hang: "hanging", /** @property */ hanging: "hanging", /** @property */ ideo: "ideographic", /** @property */ ideographic: "ideographic", /** @property */ mid: "middle", /** @property */ middle: "middle", /** @property */ top: "top" }; // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: /** * @class Rotate's a group around a given pivot point. * @extends Group * @param {Object} _details A JSON Object describing the rotation of a group * of content. The object may have these properties: * * * * *
Property Required Default
angle no 0 (Radians)
pivot no The middle of this group, based on cumulative bounds.
* @example // Simple Example - rotating an image 45 deg. * Rotate({ x: 25, y: 20.5, angle: toRadians(45), * content: [ * Graphic({ url: "./images/beetleship.png" }) * ] * }) *
* */ function Rotate(_details) { // {{{ /** @constructs */ function _Rotate(_details) { // {{{ // Private Members {{{ var angle = check(_details.angle, 0); var pivot = _details.pivot; // }}} Private Members // Public Members {{{ this.draw = function(_context) { // {{{ // draw the groups contents to a different canvas. var bounds = this.getBounds(); var content = this.getContent(); _context.save(); // translate the context to the center of where the group would be // drawn. var pivot_point = {x: (bounds.w / 2), y: (bounds.h / 2)}; if(exists(pivot)) { pivot_point = pivot; } _context.translate(bounds.x + pivot_point.x, bounds.y + pivot_point.y); // then rotate it. _context.rotate(angle); // then translate it back to where the group will begin to be drawn. _context.translate( -pivot_point.x, -pivot_point.y); for(var index in content) { var component = content[index]; component.draw(_context); } _context.restore(); } // }}} draw /** * Sets this rotation's angle to the given amount. * @param _angle The angle this Rotation group starts at. */ this.rotate = function(_angle) { // {{{ angle = _angle; } // }}} rotate /** * Rotates this group by the given amount. This method simply adds the * given delta angle to the starting angle. * @param _dangle The amount to rotate this group by. */ this.rotateBy = function(_dangle) { // {{{ angle += _dangle; } // }}} rotateBy // }}} public Members } // }}} _Rotate Group(_details).extend(_Rotate); var theRotation = new _Rotate(_details); return theRotation; } // }}} Rotate // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: // These properties are for jEdit - Programmer's Text Editor. // Load this file in jEdit to see what they do. // ::folding=explicit:mode=javascript:noTabs=false:collapseFolds=4::