1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 /**
 27  * the DOM object
 28  * @class
 29  * @type {Object}
 30  */
 31 cc.DOM = {};
 32 
 33 /**
 34  * @function
 35  * @private
 36  * @param x
 37  */
 38 cc.DOM.addMethods = function (x) {
 39     for (var funcs in cc.DOM.methods) {
 40         x[funcs] = cc.DOM.methods[funcs];
 41     }
 42 };
 43 cc.DOM.methods = /** @lends cc.DOM# */{
 44     /**
 45      * Replace the set position of ccNode
 46      * @param {object|Number} x
 47      * @param {Number} y
 48      */
 49     setPosition:function (x, y) {
 50         if (arguments.length == 2) {
 51             this._position.x = x;
 52             this._position.y = y;
 53             //this._position = cc.p(newPosOrxValue,yValue);
 54         } else {
 55             this._position = x;
 56         }
 57         this.setNodeDirty();
 58         this.dom.translates(this._position.x, -this._position.y);
 59     },
 60     /**
 61      * replace set Position Y of ccNode
 62      * @param {Number} y
 63      */
 64     setPositionY:function (y) {
 65         this._position.y = y;
 66         this.setNodeDirty();
 67         this.dom.translates(this._position.x, -this._position.y);
 68     },
 69 
 70     /**
 71      * replace set Position X of ccNode
 72      * @param {Number} x
 73      */
 74     setPositionX:function (x) {
 75         this._position.x = x;
 76         this.setNodeDirty();
 77         this.dom.translates(this._position.x, -this._position.y);
 78     },
 79 
 80     /**
 81      * replace set Scale of ccNode
 82      * @param {object|Number} scale
 83      * @param {Number} scaleY
 84      */
 85     setScale:function (scale, scaleY) {
 86         //save dirty region when before change
 87         //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
 88 
 89         this._scaleX = scale;
 90         this._scaleY = scaleY || scale;
 91 
 92         //save dirty region when after changed
 93         //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
 94         this.setNodeDirty();
 95         this.dom.resize(this._scaleX, this._scaleY);
 96     },
 97 
 98     /**
 99      * replace set Scale X of ccNode
100      * @param {Number} x
101      */
102     setScaleX:function (x) {
103         this._scaleX = x;
104         this.setNodeDirty();
105         this.dom.resize(this._scaleX, this._scaleY);
106     },
107 
108     /**
109      * replace set Scale Y of ccNode
110      * @param {Number} y
111      */
112     setScaleY:function (y) {
113         this._scaleY = y;
114         this.setNodeDirty();
115         this.dom.resize(this._scaleX, this._scaleY);
116     },
117 
118     /**
119      * replace set anchorpoint of ccNode
120      * @param {object} point
121      */
122     setAnchorPoint:function (point) {
123         this._anchorPoint = point;
124         this._anchorPointInPoints = cc.p(this._contentSize.width * this._anchorPoint.x,
125             this._contentSize.height * this._anchorPoint.y);
126         this.dom.style[cc.$.pfx + 'TransformOrigin'] = '' + this._anchorPointInPoints.x + 'px ' + -this._anchorPointInPoints.y + 'px';
127         if (this.isIgnoreAnchorPointForPosition()) {
128             this.dom.style.marginLeft = 0;
129             this.dom.style.marginBottom = 0;
130         }
131         else {
132             this.dom.style.marginLeft = (this.isToggler) ? 0 : -this._anchorPointInPoints.x + 'px';
133             this.dom.style.marginBottom = -this._anchorPointInPoints.y + 'px';
134         }
135         this.setNodeDirty();
136     },
137 
138     /**
139      * replace set ContentSize of ccNode
140      * @param {cc.Size} size
141      */
142     setContentSize:function (size) {
143         if (!cc.sizeEqualToSize(size, this._contentSize)) {
144             this._contentSize = size;
145             this._anchorPointInPoints = cc.p(this._contentSize.width * this._anchorPoint.x,
146                 this._contentSize.height * this._anchorPoint.y);
147             this.dom.width = size.width;
148             this.dom.height = size.height;
149             this.setAnchorPoint(this.getAnchorPoint());
150         }
151         if (this.canvas) {
152             this.canvas.width = this._contentSize.width;
153             this.canvas.height = this._contentSize.height;
154         }
155         this.setNodeDirty();
156         this.redraw();
157     },
158 
159     /**
160      * replace set Rotation of ccNode
161      * @param {Number} newRotation
162      */
163     setRotation:function (newRotation) {
164         if (this._rotation == newRotation)
165             return;
166         //save dirty region when before change
167         //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
168 
169         this._rotationX = this._rotationY = newRotation;
170         this._rotationRadiansX = this._rotationX * (Math.PI / 180);
171         this._rotationRadiansY = this._rotationY * (Math.PI / 180);
172 
173         this.setNodeDirty();
174         this.dom.rotate(newRotation);
175     },
176 
177     /**
178      * replace set SkewX of ccNode
179      * @param {Number} x
180      */
181     setSkewX:function (x) {
182         this._skewX = x;
183         this.setNodeDirty();
184         this.dom.setSkew(this._skewX, this._skewY);
185     },
186 
187     /**
188      * replace set SkewY of ccNode
189      * @param {Number} y
190      */
191     setSkewY:function (y) {
192         this._skewY = y;
193         this.setNodeDirty();
194         this.dom.setSkew(this._skewX, this._skewY);
195     },
196 
197     /**
198      * replace set Visible of ccNode
199      * @param {Boolean} x
200      */
201     setVisible:function (x) {
202         this._visible = x;
203         this.setNodeDirty();
204         if (this.dom)
205             this.dom.style.display = (x) ? 'block' : 'none';
206     },
207     _setZOrder:function (z) {
208         this._zOrder = z
209         this.setNodeDirty();
210         if (this.dom)
211             this.dom.zIndex = z;
212     },
213 
214     /**
215      * replace set Parent of ccNode
216      * @param {cc.Node} p
217      */
218     setParent:function (p) {
219         this._parent = p;
220 
221         if (p !== null) {
222             p.setAnchorPoint(p.getAnchorPoint());
223             this.setNodeDirty();
224             cc.DOM.parentDOM(this);
225         }
226     },
227 
228     /**
229      * replace resume Schedule and actions of ccNode
230      */
231     resumeSchedulerAndActions:function () {
232         this.getScheduler().resumeTarget(this);
233         this.getActionManager().resumeTarget(this);
234         //if dom does not have parent, but node has no parent and its running
235         if (this.dom && !this.dom.parentNode) {
236             if (!this.getParent()) {
237                 this.dom.appendTo(cc.container);
238             }
239             else {
240                 cc.DOM.parentDOM(this);
241             }
242         }
243         if (this.dom)
244             this.dom.style.visibility = "visible";
245     },
246 
247     /**
248      * replace pause Schedule and Actions of ccNode
249      */
250     pauseSchedulerAndActions:function () {
251         this.getScheduler().pauseTarget(this);
252         this.getActionManager().pauseTarget(this);
253         if (this.dom) {
254             this.dom.style.visibility = 'hidden';
255         }
256     },
257 
258     /**
259      * replace clean up of ccNode
260      */
261     cleanup:function () {
262         // actions
263         this.stopAllActions();
264         this.unscheduleAllCallbacks();
265 
266         // timers
267         this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.cleanup);
268         if (this.dom) {
269             this.dom.remove();
270         }
271     },
272     /**
273      * replace remove from parent and clean up of ccNode
274      */
275     removeFromParentAndCleanup:function () {
276         this.dom.remove();
277     },
278     setOpacity:function (o) {
279         this._opacity = o;
280         this.dom.style.opacity = o / 255;
281     },
282     /**
283      * refresh/updates the DOM element
284      */
285     redraw:function () {
286         if (this.isSprite) {
287             var tmp = this._children;
288             this._children = [];
289             cc.Sprite.prototype.visit.call(this, this.ctx);
290             this._children = tmp;
291         }
292         else {
293             cc.Sprite.prototype.visit.call(this, this.ctx);
294         }
295     }
296 };
297 
298 cc.DOM._resetEGLViewDiv = function(){
299     var eglViewDiv = cc.$("#EGLViewDiv");
300     if(eglViewDiv){
301         var eglViewer = cc.EGLView.getInstance();
302         var designSize = eglViewer.getDesignResolutionSize();
303         var viewPortRect = eglViewer.getViewPortRect();
304         var screenSize = eglViewer.getFrameSize();
305         var designSizeWidth = designSize.width, designSizeHeight = designSize.height;
306         if((designSize.width === 0) && (designSize.height === 0)){
307             designSizeWidth = screenSize.width;
308             designSizeHeight = screenSize.height;
309         }
310 
311         var viewPortWidth = viewPortRect.size.width, viewPortHeight = viewPortRect.size.height;
312         if((viewPortRect.size.width === 0) && (viewPortRect.size.height === 0)){
313             viewPortWidth = screenSize.width;
314             viewPortHeight = screenSize.height;
315         }
316 
317         eglViewDiv.style.position = 'absolute';
318         //x.dom.style.display='block';
319         eglViewDiv.style.width = designSizeWidth + "px";
320         eglViewDiv.style.maxHeight = designSizeHeight + "px";
321         eglViewDiv.style.margin = 0;
322 
323         eglViewDiv.resize(eglViewer.getScaleX(), eglViewer.getScaleY());
324 
325         if (viewPortWidth < screenSize.width) {
326             eglViewDiv.style.left = ((viewPortWidth - designSizeWidth) / 2
327                 + (screenSize.width - viewPortWidth ) / 2) + "px";
328         } else {
329             eglViewDiv.style.left = (viewPortWidth - designSizeWidth) / 2 + "px";
330         }
331 
332         if (viewPortHeight < screenSize.height) {
333             eglViewDiv.style.bottom = ((screenSize.height - viewPortHeight ) / 2) + "px";
334         } else {
335             eglViewDiv.style.bottom = "0px";
336         }
337     }
338 };
339 
340 /**
341  * @function
342  * @private
343  * @param x
344  * @return {Boolean}
345  */
346 cc.DOM.parentDOM = function (x) {
347     var p = x.getParent();
348     //if has parent, parent need to have dom too
349     if (!p || !x.dom)
350         return false;
351     if (!p.dom) {
352         cc.DOM.placeHolder(p);
353         p.setParent = cc.DOM.methods.setParent;
354     }
355     //if parent have dom, attach self to parent
356     x.dom.appendTo(p.dom);
357     p.setAnchorPoint(p.getAnchorPoint());
358 
359     if (p.getParent()) {
360         cc.DOM.parentDOM(p);
361     } else {
362         //parent has no more parent, if its running, then add it to the container
363         if (p.isRunning()) {
364             //find EGLView div
365             var eglViewDiv = cc.$("#EGLViewDiv");
366             if (eglViewDiv) {
367                 p.dom.appendTo(eglViewDiv);
368             } else {
369                 eglViewDiv = cc.$new("div");
370                 eglViewDiv.id = "EGLViewDiv";
371 
372                 var eglViewer = cc.EGLView.getInstance();
373                 var designSize = eglViewer.getDesignResolutionSize();
374                 var viewPortRect = eglViewer.getViewPortRect();
375                 var screenSize = eglViewer.getFrameSize();
376                 var designSizeWidth = designSize.width, designSizeHeight = designSize.height;
377                 if ((designSize.width === 0) && (designSize.height === 0)) {
378                     designSizeWidth = screenSize.width;
379                     designSizeHeight = screenSize.height;
380                 }
381 
382                 var viewPortWidth = viewPortRect.size.width, viewPortHeight = viewPortRect.size.height;
383                 if ((viewPortRect.size.width === 0) && (viewPortRect.size.height === 0)) {
384                     viewPortWidth = screenSize.width;
385                     viewPortHeight = screenSize.height;
386                 }
387 
388                 eglViewDiv.style.position = 'absolute';
389                 //x.dom.style.display='block';
390                 eglViewDiv.style.width = designSizeWidth + "px";
391                 eglViewDiv.style.maxHeight = designSizeHeight + "px";
392                 eglViewDiv.style.margin = 0;
393 
394                 eglViewDiv.resize(eglViewer.getScaleX(), eglViewer.getScaleY());
395 
396                 if (viewPortWidth < screenSize.width) {
397                     eglViewDiv.style.left = ((viewPortWidth - designSizeWidth) / 2
398                         + (screenSize.width - viewPortWidth ) / 2) + "px";
399                 } else {
400                     eglViewDiv.style.left = (viewPortWidth - designSizeWidth) / 2 + "px";
401                 }
402 
403                 if (viewPortHeight < screenSize.height) {
404                     eglViewDiv.style.bottom = ((screenSize.height - viewPortHeight ) / 2) + "px";
405                 } else {
406                     eglViewDiv.style.bottom = "0px";
407                 }
408 
409                 p.dom.appendTo(eglViewDiv);
410                 eglViewDiv.appendTo(cc.container);
411             }
412         }
413     }
414     return true;
415 };
416 
417 /**
418  * @function
419  * @private
420  * @param x
421  */
422 cc.DOM.setTransform = function (x) {
423     if (x.ctx) {
424         /*        x.ctx.save();
425          x.ctx.setTransform(1,0,0,1,0,0);
426          x.ctx.clearRect(0,0,x.canvas.width, x.canvas.height);
427          x.ctx.restore();*/
428         x.ctx.translate(x.getAnchorPointInPoints().x, x.getAnchorPointInPoints().y);
429         if (x.isSprite) {
430             var tmp = x._children;
431             x._children = [];
432             cc.Sprite.prototype.visit.call(x, x.ctx);
433             x._children = tmp;
434         }
435         else {
436             cc.Sprite.prototype.visit.call(x, x.ctx);
437         }
438     }
439     if (x.dom) {
440         x.dom.position.x = x.getPosition().x;
441         x.dom.position.y = -x.getPosition().y;
442         x.dom.rotation = x.getRotation();
443         x.dom.scale = {x:x.getScaleX(), y:x.getScaleY()};
444         x.dom.skew = {x:x.getSkewX(), y:x.getSkewY()};
445         if (x.setAnchorPoint)
446             x.setAnchorPoint(x.getAnchorPoint());
447         x.dom.transforms();
448         x.dom.position.y = -x.getPosition().y;
449         x.dom.rotation = x.getRotation();
450         x.dom.scale = {x:x.getScaleX(), y:x.getScaleY()};
451         x.dom.skew = {x:x.getSkewX(), y:x.getSkewY()};
452         if (x.setAnchorPoint)
453             x.setAnchorPoint(x.getAnchorPoint());
454         x.dom.transforms();
455     }
456 
457 };
458 
459 /**
460  * @function
461  * @private
462  * @param x
463  */
464 cc.DOM.forSprite = function (x) {
465     x.dom = cc.$new('div');
466     x.canvas = cc.$new('canvas');
467     x.canvas.width = x.getContentSize().width;
468     x.canvas.height = x.getContentSize().height;
469     x.dom.style.position = 'absolute';
470     x.dom.style.bottom = 0;
471     x.ctx = x.canvas.getContext('2d');
472     x.dom.appendChild(x.canvas);
473     if (x.getParent()) {
474         cc.DOM.parentDOM(x);
475     }
476     x.isSprite = true;
477 };
478 
479 /**
480  * This creates divs for parent Nodes that are related to the current node
481  * @function
482  * @private
483  * @param x
484  */
485 cc.DOM.placeHolder = function (x) {
486     //creating a placeholder dom to simulate other ccNode in the hierachy
487     x.dom = cc.$new('div');
488     x.placeholder = true;
489     x.dom.style.position = 'absolute';
490     x.dom.style.bottom = 0;
491     //x.dom.style.display='block';
492     x.dom.style.width = (x.getContentSize().width || cc.Director.getInstance().getWinSize().width) + "px";
493     x.dom.style.maxHeight = (x.getContentSize().height || cc.Director.getInstance().getWinSize().height) + "px";
494     x.dom.style.margin = 0;
495     cc.DOM.setTransform(x);
496     x.dom.transforms();
497     cc.DOM.addMethods(x);
498     //x.dom.style.border = 'red 1px dotted';
499 };
500 
501 /**
502  * Converts cc.Sprite or cc.MenuItem to DOM elements <br/>
503  * It currently only supports cc.Sprite and cc.MenuItem
504  * @function
505  * @param {cc.Sprite|cc.MenuItem|Array} nodeObject
506     * * @example
507  * // example
508  * cc.DOM.convert(Sprite1, Sprite2, Menuitem);
509  *
510  * var myDOMElements = [Sprite1, Sprite2, MenuItem];
511  * cc.DOM.convert(myDOMElements);
512  */
513 cc.DOM.convert = function (nodeObject) {
514     //if passing by list, make it an array
515     if (arguments.length > 1) {
516         cc.DOM.convert(arguments);
517         return;
518     } else if (arguments.length == 1 && !arguments[0].length) {
519         cc.DOM.convert([arguments[0]]);
520         return;
521     }
522     var args = arguments[0];
523     for (var i = 0; i < args.length; i++) {
524         //first check if its sprite
525         if (args[i] instanceof cc.Sprite) {
526             // create a canvas
527             if (!args[i].dom)
528                 cc.DOM.forSprite(args[i]);
529         } else {
530             cc.log('DOM converter only supports sprite and menuitems yet');
531         }
532         cc.DOM.addMethods(args[i]);
533         args[i].visit = function () {
534         };
535         args[i].transform = function () {
536         };
537         cc.DOM.setTransform(args[i]);
538         args[i].setVisible(args[i].isVisible());
539     }
540 };