1 /*
  2  *Copyright (c) 2009, TellurianRing.com
  3  *All rights reserved.
  4  *
  5  *Redistribution and use in source and binary forms, with or without modification,
  6  *are permitted provided that the following conditions are met:
  7  *
  8  *   Redistributions of source code must retain the above copyright notice, this
  9  *   list of conditions and the following disclaimer.
 10  *   Redistributions in binary form must reproduce the above copyright notice,
 11  *   this list of conditions and the following disclaimer in the documentation
 12  *   and/or other materials provided with the distribution.
 13  *   Neither the name of the Organization (TellurianRing.com) nor the names of
 14  *   its contributors may be used to endorse or promote products derived from
 15  *   this software without specific prior written permission.
 16  *
 17  *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 18  *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 19  *WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 20  *DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 21  *ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 22  *(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 23  *LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 24  *ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 25  *(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 26  *SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27  */
 28 
 29 /**
 30  * @class SceneContent represents any object which can be placed in the
 31  * "content" field of any group node of a Scene. This object defines the methods
 32  * and attributes, which are expected to be implemented by any scene content.
 33  * @author <a href="mailto:elberry@tellurianring.com">Eric Berry</a>
 34  *
 35  * @param _details All SceneContent takes a JSON object in it's factory
 36  *    function with the following properties:
 37  *    Expected but not required properties:
 38  * <table cellpadding="0" cellspacing="1" border="0" class="constructor_details">
 39  *    <tr><th>Property</th> <th>Required</th> <th>Default</th></tr>
 40  *    <tr><td>active</td>   <td>no</td>       <td>true</td></tr>
 41  *    <tr><td>h</td>        <td>no</td>       <td>0</td></tr>
 42  *    <tr><td>visible</td>  <td>no</td>       <td>true</td></tr>
 43  *    <tr><td>w</td>        <td>no</td>       <td>0</td></tr>
 44  *    <tr><td>x</td>        <td>no</td>       <td>0</td></tr>
 45  *    <tr><td>y</td>        <td>no</td>       <td>0</td></tr>
 46  *    <tr><td>name</td>     <td>no</td>       <td></td></tr>
 47  * </table>
 48  * This is a base set of properties. Any object that extends SceneContent should
 49  * be expected to handle this set of properties. Some exceptions include the
 50  * Graphic object which sets the width and height properties after the image has
 51  * been loaded. The Sound object, which doesn't have any use of coordinates or
 52  * size. And Groups, their width and height attributes are based on their
 53  * content.
 54  * @example
 55  * // Creates a new SceneContent object.
 56  * var myContent = SceneContent({x: 10, y: 10, w: 100, h: 100, name: "my_content"});
 57  * @example
 58  * // Your content can be retrieved from the scene using its name.
 59  * myScene["my content"]
 60  * @constructs
 61  */
 62 function SceneContent(_details) { // {{{
 63    /** @constructs */
 64    function _SceneContent(_details) { // {{{
 65       // private variables {{{
 66       var active = check(_details.active, true);
 67       var h = check(_details.h, 0);
 68       var name = _details.name;
 69       var parent = null;
 70       var scene = null;
 71       var stage = null;
 72       var visible = check(_details.visible, true);
 73       var w = check(_details.w, 0);
 74       var x = check(_details.x, 0);
 75       var y = check(_details.y, 0);
 76 
 77       // used as a prototype for the clone function below.
 78       function Clone() {}
 79       // }}}
 80 
 81       // public variables {{{
 82       /**
 83        * The public details field should refer to the original object passed in to the
 84        * creation function. Users should always be able to attach their own details to
 85        * any scene content object and retrieve it again using the public details
 86        * field.
 87        */
 88       this.details = _details;
 89       // }}}
 90 
 91       // public functions {{{
 92       /**
 93        * The draw function is responsible for drawing this content to the given
 94        * canvas context. It is called after the update function.
 95        * @param _context The canvas context this content should be drawn to.
 96        */
 97       this.draw = function(_context) {}
 98       /**
 99        * This extends the provided child function with this instance.
100        * @param _child The child function to be extended.
101        */
102       this.extend = function(_child) { // {{{
103          _child.prototype = this;
104       } // }}} extend
105       /**
106        * The getBounds function should return the location and size of this
107        * content relative to it's parent.
108        * @example
109        * var myGraphic = Graphic({ x: 13, y: 13, url: "./images/ball.png" });
110        * var myGrp = Group({ x: 10, y: 10, content: [myGraphic] });
111        * alert(myGraphic.getBounds().x); // alerts 13
112        * <script type="text/javascript">
113        *    function scenecontent_bounds() {
114        *       var myGraphic = Graphic({ x: 13, y: 13, url: "./images/ball.png" });
115        *       var myGrp = Group({ x: 10, y: 10, content: [myGraphic] });
116        *       alert(myGraphic.getBounds().x); // alerts 13
117        *    }
118        * </script>
119        * <button onclick="scenecontent_bounds()">Prove It!</button>
120        * @return {Bounds} with 4 fields, x, y, w, and h.
121        */
122       this.getBounds = function() { // {{{
123          return Bounds({x: x, y: y, w: w, h: h});
124       } // }}} getBounds
125       /**
126        * Gets the name of this SceneContent as it was passed in the original
127        * _details object.
128        * @return The name of this SceneContent
129        */
130       this.getName = function() { // {{{
131          return name;
132       } // }}} getName
133       /**
134        * The getParent function returns the parent of this content. This usually
135        * returns another SceneContent object, except when this object is the
136        * first child of the Scene object. In which case, the Scene itself is
137        * returned.
138        * @return {SceneContent|Scene} The parent of this content.
139        */
140       this.getParent = function() { // {{{
141          return parent;
142       } // }}} getParent
143       /**
144        * The getScene function returns the scene this content belongs to. The scene
145        * is passed in to the load function.
146        * @return {Scene} The Scene Object this content belongs to.
147        */
148       this.getScene = function() { // {{{
149          return scene;
150       } // }}} getScene
151       /**
152        * The getStage function returns the stage this content belongs to. The stage
153        * can originally be retrieved by the Scene object which is passed in to the
154        * load function.
155        * @return {Stage} The Stage Object this content belongs to.
156        */
157       this.getStage = function() { // {{{
158          return stage;
159       } // }}} getStage
160       /**
161        * isActive Is this content currently active. This field tells the Scene
162        * that this content needs to be updated and drawn. If content is not
163        * active then it will not be updated or drawn.
164        * @function
165        * @returns true if this content is active, false otherwise.
166        */
167       this.isActive = function() { // {{{
168          return active;
169       } // }}} isActive
170       /**
171        * isVisible Is this content currently visible. This field tells the Scene
172        * that this content needs to be drawn. If the content is not visible then
173        * it will not be drawn.
174        * @function
175        * @returns true if this content is visible, false otherwise.
176        */
177       this.isVisible = function() { // {{{
178          return visible;
179       } // }}}
180       /**
181        * The load function gets called after all the stage objects and content have
182        * been initialized. It's only called once when the Scene's load method is
183        * called.
184        * @param {Scene} _scene The Scene this content belongs to.
185        * @param {SceneContent|Scene} _parent The parent of this content.
186        */
187       this.load = function(_scene, _parent) { // {{{
188          this.loadBase(_scene, _parent);
189       } // }}} load
190       /**
191        * Sets the scene, parent and stage for this content. Also adds this object
192        * to the scene by name.
193        */
194       this.loadBase = function(_scene, _parent) { // {{{
195          scene = _scene;
196          parent = _parent;
197          stage = scene.getStage();
198          scene[name] = this;
199       } // }}} loadBase
200       /**
201        * The moveBy function moves this content's relative x, y position by the
202        * the given distances. This is equivalent to an instant translation, vs.
203        * the use of a Translation Transform node. The function should work on
204        * partial data, and a lack of data should signify non-action.
205        * @example
206        * var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" });
207        * alert(myImage.getBounds().x); // alerts 10
208        * myImage.moveBy({x: 5});
209        * alert(myImage.getBounds().x); // alerts 15
210        * <script type="text/javascript">
211        *    function scenecontent_moveby() {
212        *       var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" });
213        *       alert(myImage.getBounds().x); // alerts 10
214        *       myImage.moveBy({x: 5});
215        *       alert(myImage.getBounds().x); // alerts 15
216        *    }
217        * </script>
218        * <button onclick="scenecontent_moveby()">Prove It!</button>
219        * @param {Number} _distance A Distance object with two fields, x, and y.
220        */
221       this.moveBy = function(_distance) { // {{{
222             if(exists(_distance.x)) {
223                x += _distance.x;
224             }
225             if(exists(_distance.y)) {
226                y += _distance.y;
227             }
228       } // }}} moveBy
229       /**
230        * The moveTo function moves this content's relative x, y position to the
231        * values provided in the point object. The function should work on partial
232        * data, and a lack of data should signify non-action.
233        * @example
234        * var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" });
235        * alert(myImage.getBounds().x); // alerts 10
236        * myImage.moveTo({x: 15});
237        * alert(myImage.getBounds().x); // alerts 15
238        * <script type="text/javascript">
239        *    function scenecontent_moveto() {
240        *       var myImage = Graphic({ x: 10, y: 10, url: "./images/ball.png" });
241        *       alert(myImage.getBounds().x); // alerts 10
242        *       myImage.moveTo({x: 15});
243        *       alert(myImage.getBounds().x); // alerts 15
244        *    }
245        * </script>
246        * <button onclick="scenecontent_moveto()">Prove It!</button>
247        * @param {Point} _point Point object with two fields, x, and y.
248        */
249       this.moveTo = function(_point) { // {{{
250          if(exists(_point.x)) {
251             x = _point.x;
252          }
253          if(exists(_point.y)) {
254             y = _point.y;
255          }
256       } // }}} moveTo
257       /**
258        * setActive Sets this content's active status flag.
259        * @function
260        * @param {Boolean} _active true or false
261        */
262       this.setActive = function(_active) { // {{{
263          active = _active;
264       } // }}} setActive
265       /**
266        * The setSize function sets this content's width and height. The function
267        * should work on partial data, and a lack of data should signify
268        * non-action.
269        * @example // Only updates the width of the SceneContent.
270        * var rect = Rectangle({ x: 10, y: 10, w: 10, h: 10 });
271        * rect.setSize({w: 15 });
272        * alert(rect.getBounds().w == 15) // alerts true.
273        * <script type="text/javascript">
274        *    function scenecontent_setsize() {
275        *       var rect = Rectangle({ x: 10, y: 10, w: 10, h: 10 });
276        *       rect.setSize({w: 15 });
277        *       alert(rect.getBounds().w == 15) // alerts true.
278        *    }
279        * </script>
280        * <button onclick="scenecontent_setsize()">Prove It!</button>
281        * @param {Bounds} _dimension A Dimension object with two fields, w, and h.
282        */
283       this.setSize = function(_dimension) { // {{{
284          if(exists(_dimension.w)) {
285             w = _dimension.w;
286          }
287          if(exists(_dimension.h)) {
288             h = _dimension.h;
289          }
290       } // }}} setSize
291       /**
292        * setVisible Sets this content's visible status flag.
293        * @function
294        * @param {Boolean} _visible true or false
295        */
296       this.setVisible = function(_visible) { // {{{
297          visible = _visible;
298       } // }}} setVisible
299       /**
300        * The update function is called after the load function has been called
301        * and before the draw function is called. It's called repeatedly with the
302        * total time passed since the Scene's load time.
303        * @param {Number} _runtime The total time passed since the Scene's load time.
304        */
305       this.update = function(_runtime) {
306          this.updateBase(_runtime);
307       }
308       /**
309        * The updateBase function should be called by subclasses overriding the
310        * update function. This function automatically hides SceneContent if it
311        * goes outside the boundaries of the stage. This method can be extended
312        * by supplying an update function in the initialization data.
313        * @example // Supplying your own update function
314        * Graphic({
315        *    x: 10, y: 10, url: "./images/ball.png",
316        *    update: function(_runtime) {
317        *       // do something
318        *    }
319        * })
320        * @param {Number} _runtime The total time passed since the Scene's load time.
321        */
322       this.updateBase = function(_runtime) {
323          var bounds = this.getBounds();
324          var stage_size = this.getStage().getSize();
325          if(stage_size.contains(bounds)) {
326             this.setVisible(true);
327          } else {
328             this.setVisible(false);
329          }
330          if(exists(this.details.update)) {
331             this.details.update.apply(this, arguments);
332          }
333       }
334       // }}} public functions
335    } // }}} _SceneContent
336    var theSceneContent = new _SceneContent(_details);
337    return theSceneContent;
338 } // }}} SceneContent
339 // These properties are for jEdit - Programmer's Text Editor.
340 // Load this file in jEdit to see what they do.
341 // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4::
342