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 cc.RESOLUTION_POLICY = {
 28     // The entire application is visible in the specified area without trying to preserve the original aspect ratio.
 29     // Distortion can occur, and the application may appear stretched or compressed.
 30     EXACT_FIT: 0,
 31     // The entire application fills the specified area, without distortion but possibly with some cropping,
 32     // while maintaining the original aspect ratio of the application.
 33     NO_BORDER: 1,
 34     // The entire application is visible in the specified area without distortion while maintaining the original
 35     // aspect ratio of the application. Borders can appear on two sides of the application.
 36     SHOW_ALL: 2,
 37     // The application takes the height of the design resolution size and modifies the width of the internal
 38     // canvas so that it fits the aspect ratio of the device
 39     // no distortion will occur however you must make sure your application works on different
 40     // aspect ratios
 41     FIXED_HEIGHT: 3,
 42     // The application takes the width of the design resolution size and modifies the height of the internal
 43     // canvas so that it fits the aspect ratio of the device
 44     // no distortion will occur however you must make sure your application works on different
 45     // aspect ratios
 46     FIXED_WIDTH: 4,
 47 
 48     UNKNOWN: 5
 49 };
 50 
 51 cc.Touches = [];
 52 cc.TouchesIntergerDict = {};
 53 
 54 /**
 55  * @class
 56  * @extends cc.Class
 57  */
 58 cc.EGLView = cc.Class.extend(/** @lends cc.EGLView# */{
 59     _delegate: null,
 60     // Size of parent node that contains cc.container and cc.canvas
 61     _frameSize: null,
 62     // resolution size, it is the size appropriate for the app resources.
 63     _designResolutionSize: null,
 64     _originalDesignResolutionSize: null,
 65     // Viewport is the container's rect related to content's coordinates in pixel
 66     _viewPortRect: null,
 67     // The visible rect in content's coordinate in point
 68     _visibleRect: null,
 69     // the view name
 70     _viewName: "",
 71     // Custom callback for resize event
 72     _resizeCallback: null,
 73     _scaleX: 1,
 74     _originalScaleX: 1,
 75     _scaleY: 1,
 76     _originalScaleY: 1,
 77     _indexBitsUsed: 0,
 78     _maxTouches: 5,
 79     _resolutionPolicy: null,
 80     _rpExactFit: null,
 81     _rpShowAll: null,
 82     _rpNoBorder: null,
 83     _rpFixedHeight: null,
 84     _rpFixedWidth: null,
 85     _initialized: false,
 86 
 87     _captured: false,
 88     _wnd: null,
 89     _hDC: null,
 90     _hRC: null,
 91     _accelerometerKeyHook: null,
 92     _supportTouch: false,
 93     _contentTranslateLeftTop: null,
 94 
 95     _menu: null,
 96     // Parent node that contains cc.container and cc.canvas
 97     _frame: null,
 98     _frameZoomFactor: 1.0,
 99     __resizeWithBrowserSize: false,
100     _isAdjustViewPort: false,
101 
102     ctor: function () {
103         this._frame = (cc.container.parentNode === document.body) ? document.documentElement : cc.container.parentNode;
104         this._frameSize = cc.size(0, 0);
105         this._initFrameSize();
106 
107         var w = cc.canvas.width, h = cc.canvas.height;
108         this._designResolutionSize = cc.size(w, h);
109         this._originalDesignResolutionSize = cc.size(w, h);
110         this._viewPortRect = cc.rect(0, 0, w, h);
111         this._visibleRect = cc.rect(0, 0, w, h);
112         this._delegate = cc.Director.getInstance().getTouchDispatcher();
113         this._contentTranslateLeftTop = {left: 0, top: 0};
114         this._viewName = "Cocos2dHTML5";
115 
116         cc.VisibleRect.init(this._designResolutionSize);
117 
118         // Setup system default resolution policies
119         this._rpExactFit = new cc.ResolutionPolicy(cc.ContainerStrategy.EQUAL_TO_FRAME, cc.ContentStrategy.EXACT_FIT);
120         this._rpShowAll = new cc.ResolutionPolicy(cc.ContainerStrategy.PROPORTION_TO_FRAME, cc.ContentStrategy.SHOW_ALL);
121         this._rpNoBorder = new cc.ResolutionPolicy(cc.ContainerStrategy.EQUAL_TO_FRAME, cc.ContentStrategy.NO_BORDER);
122         this._rpFixedHeight = new cc.ResolutionPolicy(cc.ContainerStrategy.EQUAL_TO_FRAME, cc.ContentStrategy.FIXED_HEIGHT);
123         this._rpFixedWidth = new cc.ResolutionPolicy(cc.ContainerStrategy.EQUAL_TO_FRAME, cc.ContentStrategy.FIXED_WIDTH);
124 
125         this._hDC = cc.canvas;
126         this._hRC = cc.renderContext;
127     },
128 
129     // Resize helper functions
130     _resizeEvent: function () {
131         var width = this._originalDesignResolutionSize.width;
132         var height = this._originalDesignResolutionSize.height;
133         if (this._resizeCallback) {
134             this._initFrameSize();
135             this._resizeCallback.call();
136         }
137         if (width > 0)
138             this.setDesignResolutionSize(width, height, this._resolutionPolicy);
139     },
140 
141     resizeWithBrowserSize: function (enabled) {
142         var adjustSize;
143         if (enabled) {
144             //enable
145             if (!this.__resizeWithBrowserSize) {
146                 this.__resizeWithBrowserSize = true;
147                 adjustSize = this._resizeEvent.bind(this);
148                 window.addEventListener('resize', adjustSize, false);
149             }
150         } else {
151             //disable
152             if (this.__resizeWithBrowserSize) {
153                 this.__resizeWithBrowserSize = true;
154                 adjustSize = this._resizeEvent.bind(this);
155                 window.removeEventListener('resize', adjustSize, false);
156             }
157         }
158     },
159 
160     setResizeCallback: function (callback) {
161         if (typeof callback == "function" || callback == null) {
162             this._resizeCallback = callback;
163         }
164     },
165 
166     _initFrameSize: function () {
167         var locFrameSize = this._frameSize;
168         locFrameSize.width = this._frame.clientWidth;
169         locFrameSize.height = this._frame.clientHeight;
170     },
171 
172     // hack
173     _adjustSizeKeepCanvasSize: function (width, height) {
174         var designWidth = this._originalDesignResolutionSize.width;
175         var designHeight = this._originalDesignResolutionSize.height;
176         if (designWidth > 0)
177             this.setDesignResolutionSize(designWidth, designHeight, this._resolutionPolicy);
178     },
179 
180     _setViewPortMeta: function (width, height) {
181         if (this._isAdjustViewPort) {
182             var viewportMetas = {"user-scalable": "no", "maximum-scale": "1.0", "initial-scale": "1.0"}, elems = document.getElementsByName("viewport"), vp, content;
183             if (elems.length == 0) {
184                 vp = document.createElement("meta");
185                 vp.name = "viewport";
186                 vp.content = "";
187                 document.head.appendChild(vp);
188             }
189             else vp = elems[0];
190             content = vp.content;
191             for (var key in viewportMetas) {
192                 var pattern = new RegExp(key);
193                 if (!pattern.test(content)) {
194                     content += (content == "" ? "" : ",") + key + "=" + viewportMetas[key];
195                 }
196             }
197             /*
198              if(width<=320){
199              width = 321;
200              }
201              if(height)
202              content ="height="+height+","+content;
203              if(width)
204              content ="width="+width+","+content;
205              */
206             vp.content = content;
207         }
208     },
209 
210     // RenderTexture hacker
211     _setScaleXYForRenderTexture: function () {
212         //hack for RenderTexture on canvas mode when adapting multiple resolution resources
213         var scaleFactor = cc.CONTENT_SCALE_FACTOR();
214         this._scaleX = scaleFactor;
215         this._scaleY = scaleFactor;
216     },
217 
218     // Other helper functions
219     _resetScale: function () {
220         this._scaleX = this._originalScaleX;
221         this._scaleY = this._originalScaleY;
222     },
223 
224     _getUnUsedIndex: function () {
225         var i;
226         var temp = this._indexBitsUsed;
227 
228         for (i = 0; i < this._maxTouches; i++) {
229             if (!(temp & 0x00000001)) {
230                 this._indexBitsUsed |= (1 << i);
231                 return i;
232             }
233 
234             temp >>= 1;
235         }
236 
237         // all bits are used
238         return -1;
239     },
240 
241     _removeUsedIndexBit: function (index) {
242         if (index < 0 || index >= this._maxTouches) {
243             return;
244         }
245 
246         var temp = 1 << index;
247         temp = ~temp;
248         this._indexBitsUsed &= temp;
249     },
250 
251     // Useless, just make sure the compatibility temporarily, should be removed
252     _adjustSizeToBrowser: function () {
253     },
254 
255     /**
256      * init
257      */
258     initialize: function () {
259         this._initialized = true;
260     },
261 
262     adjustViewPort: function (enabled) {
263         this._isAdjustViewPort = enabled;
264     },
265 
266     /**
267      * Force destroying EGL view, subclass must implement this method.
268      */
269     end: function () {
270     },
271 
272     /**
273      * Get whether render system is ready(no matter opengl or canvas),
274      * this name is for the compatibility with cocos2d-x, subclass must implement this method.
275      * @return {Boolean}
276      */
277     isOpenGLReady: function () {
278         return (this._hDC != null && this._hRC != null);
279     },
280 
281     /*
282      * Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop.
283      * @param {Number} zoomFactor
284      */
285     setFrameZoomFactor: function (zoomFactor) {
286         this._frameZoomFactor = zoomFactor;
287         this.centerWindow();
288         cc.Director.getInstance().setProjection(cc.Director.getInstance().getProjection());
289     },
290 
291     /**
292      * Exchanges the front and back buffers, subclass must implement this method.
293      */
294     swapBuffers: function () {
295     },
296 
297     /**
298      * Open or close IME keyboard , subclass must implement this method.
299      */
300     setIMEKeyboardState: function (isOpen) {
301         if (isOpen) {
302             // [EAGLView sharedEGLView] becomeFirstResponder
303         } else {
304             //  [EAGLView sharedEGLView] resignFirstResponder
305         }
306     },
307 
308     /**
309      * <p>
310      *   The resolution translate on EGLView
311      * </p>
312      * @param {Number} offsetLeft
313      * @param {Number} offsetTop
314      */
315     setContentTranslateLeftTop: function (offsetLeft, offsetTop) {
316         this._contentTranslateLeftTop = {left: offsetLeft, top: offsetTop};
317     },
318 
319     /**
320      * <p>
321      *   get the resolution translate on EGLView
322      * </p>
323      * @return {cc.Size|Object}
324      */
325     getContentTranslateLeftTop: function () {
326         return this._contentTranslateLeftTop;
327     },
328 
329     /**
330      * Get the frame size of EGL view.
331      * In general, it returns the screen size since the EGL view is a fullscreen view.
332      * @return {cc.Size}
333      */
334     getFrameSize: function () {
335         return cc.size(this._frameSize.width, this._frameSize.height);
336     },
337 
338     /**
339      * Set the frame size of EGL view.
340      * @param {Number} width
341      * @param {Number} height
342      */
343     setFrameSize: function (width, height) {
344         this._frameSize.width = width;
345         this._frameSize.height = height;
346         this._frame.style.width = width + "px";
347         this._frame.style.height = height + "px";
348         //this.centerWindow();
349         this._resizeEvent();
350         cc.Director.getInstance().setProjection(cc.Director.getInstance().getProjection());
351     },
352 
353     centerWindow: function () {
354     },
355 
356     setAccelerometerKeyHook: function (accelerometerKeyHook) {
357         this._accelerometerKeyHook = accelerometerKeyHook;
358     },
359 
360     /**
361      * Get the visible area size of opengl viewport.
362      * @return {cc.Size}
363      */
364     getVisibleSize: function () {
365         return this._visibleRect._size;
366     },
367 
368     /**
369      * Get the visible origin povar of opengl viewport.
370      * @return {cc.Point}
371      */
372     getVisibleOrigin: function () {
373         return this._visibleRect._origin;
374     },
375 
376     canSetContentScaleFactor: function () {
377         return true;
378     },
379 
380     /**
381      * Get the current resolution policy
382      * @return {cc.ResolutionPolicy}
383      */
384     getResolutionPolicy: function () {
385         return this._resolutionPolicy;
386     },
387 
388     /**
389      * Set the current resolution policy
390      * @param {cc.ResolutionPolicy|Number} resolutionPolicy
391      */
392     setResolutionPolicy: function (resolutionPolicy) {
393         if (resolutionPolicy instanceof cc.ResolutionPolicy) {
394             this._resolutionPolicy = resolutionPolicy;
395         }
396         // Ensure compatibility with JSB
397         else {
398             switch (resolutionPolicy) {
399                 case cc.RESOLUTION_POLICY.EXACT_FIT:
400                     this._resolutionPolicy = this._rpExactFit;
401                     break;
402                 case cc.RESOLUTION_POLICY.SHOW_ALL:
403                     this._resolutionPolicy = this._rpShowAll;
404                     break;
405                 case cc.RESOLUTION_POLICY.NO_BORDER:
406                     this._resolutionPolicy = this._rpNoBorder;
407                     break;
408                 case cc.RESOLUTION_POLICY.FIXED_HEIGHT:
409                     this._resolutionPolicy = this._rpFixedHeight;
410                     break;
411                 case cc.RESOLUTION_POLICY.FIXED_WIDTH:
412                     this._resolutionPolicy = this._rpFixedWidth;
413                     break;
414             }
415         }
416     },
417 
418     /**
419      * Set the design resolution size.
420      * @param {Number} width Design resolution width.
421      * @param {Number} height Design resolution height.
422      * @param {cc.ResolutionPolicy|Number} resolutionPolicy The resolution policy desired, you may choose:
423      * [1] ResolutionExactFit       Fill screen by stretch-to-fit: if the design resolution ratio of width to height is different from the screen resolution ratio, your game view will be stretched.
424      * [2] ResolutionNoBorder       Full screen without black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two areas of your game view will be cut.
425      * [3] ResolutionShowAll        Full screen with black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two black borders will be shown.
426      * [4] ResolutionFixedHeight    Scale the content's height to screen's height and proportionally scale its width
427      * [5] ResolutionFixedWidth     Scale the content's width to screen's width and proportionally scale its height
428      * [cc.ResolutionPolicy]        Custom resolution policy, constructed by cc.ResolutionPolicy
429      */
430     setDesignResolutionSize: function (width, height, resolutionPolicy) {
431         // Defensive code
432         if (isNaN(width) || width == 0 || isNaN(height) || height == 0) {
433             cc.log("Resolution not valid");
434             return;
435         }
436         this.setResolutionPolicy(resolutionPolicy);
437         var policy;
438         if (policy = this._resolutionPolicy)
439             policy.preApply(this);
440         else {
441             cc.log("should set resolutionPolicy");
442             return;
443         }
444 
445         // Reinit frame size
446         var frameW = this._frameSize.width, frameH = this._frameSize.height;
447         if (cc.Browser.isMobile)
448             this._setViewPortMeta(this._frameSize.width, this._frameSize.height);
449         this._initFrameSize();
450         // No change
451         if (resolutionPolicy == this._resolutionPolicy
452             && width == this._originalDesignResolutionSize.width && height == this._originalDesignResolutionSize.height
453             && frameW == this._frameSize.width && frameH == this._frameSize.height)
454             return;
455         this._designResolutionSize = cc.size(width, height);
456         this._originalDesignResolutionSize = cc.size(width, height);
457 
458         var result = policy.apply(this, this._designResolutionSize);
459         if (result.scale && result.scale.length == 2) {
460             this._scaleX = result.scale[0];
461             this._scaleY = result.scale[1];
462         }
463         if (result.viewport instanceof cc.Rect) {
464             var vp = this._viewPortRect = result.viewport, visible = this._visibleRect;
465             visible._size.width = cc.canvas.width / this._scaleX;
466             visible._size.height = cc.canvas.height / this._scaleY;
467             visible._origin.x = -vp.x / this._scaleX;
468             visible._origin.y = -vp.y / this._scaleY;
469         }
470 
471         // reset director's member variables to fit visible rect
472         var director = cc.Director.getInstance();
473         director._winSizeInPoints = this.getDesignResolutionSize();
474 
475         if (cc.renderContextType == cc.WEBGL) {
476             // reset director's member variables to fit visible rect
477             director._createStatsLabel();
478             director.setGLDefaultValues();
479         }
480 
481         this._originalScaleX = this._scaleX;
482         this._originalScaleY = this._scaleY;
483         // For editbox
484         if (cc.DOM) {
485             cc.DOM._resetEGLViewDiv();
486         }
487 
488         cc.VisibleRect.init(this.getVisibleSize());
489 
490         policy.postApply(this);
491     },
492 
493     /**
494      * Get design resolution size.
495      * Default resolution size is the same as 'getFrameSize'.
496      * @return {cc.Size}
497      */
498     getDesignResolutionSize: function () {
499         return cc.size(this._designResolutionSize.width, this._designResolutionSize.height);
500     },
501 
502     /**
503      * set touch delegate
504      * @param {cc.TouchDispatcher} delegate
505      */
506     setTouchDelegate: function (delegate) {
507         this._delegate = delegate;
508     },
509 
510     /**
511      * Set opengl view port rectangle with points.
512      * @param {Number} x
513      * @param {Number} y
514      * @param {Number} w width
515      * @param {Number} h height
516      */
517     setViewPortInPoints: function (x, y, w, h) {
518         var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY;
519         cc.renderContext.viewport((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor),
520             (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor),
521             (w * locScaleX * locFrameZoomFactor),
522             (h * locScaleY * locFrameZoomFactor));
523     },
524 
525     /**
526      * Set Scissor rectangle with points.
527      * @param {Number} x
528      * @param {Number} y
529      * @param {Number} w
530      * @param {Number} h
531      */
532     setScissorInPoints: function (x, y, w, h) {
533         var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY;
534         cc.renderContext.scissor((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor),
535             (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor),
536             (w * locScaleX * locFrameZoomFactor),
537             (h * locScaleY * locFrameZoomFactor));
538     },
539 
540     /**
541      * Get whether GL_SCISSOR_TEST is enable
542      */
543     isScissorEnabled: function () {
544         var gl = cc.renderContext;
545         return gl.isEnabled(gl.SCISSOR_TEST);
546     },
547 
548     /**
549      * Get the current scissor rectangle
550      * @return {cc.Rect}
551      */
552     getScissorRect: function () {
553         var gl = cc.renderContext, scaleX = this._scaleX, scaleY = this._scaleY;
554         var boxArr = gl.getParameter(gl.SCISSOR_BOX);
555         return cc.rect((boxArr[0] - this._viewPortRect.x) / scaleX, (boxArr[1] - this._viewPortRect.y) / scaleY,
556             boxArr[2] / scaleX, boxArr[3] / scaleY);
557     },
558 
559     /**
560      * @param {String} viewName
561      */
562     setViewName: function (viewName) {
563         if (viewName != null && viewName.length > 0) {
564             this._viewName = viewName;
565         }
566     },
567 
568     /**
569      * get view name
570      * @return {String}
571      */
572     getViewName: function () {
573         return this._viewName;
574     },
575 
576     /**
577      * Get the opengl view port rectangle.
578      */
579     getViewPortRect: function () {
580         return this._viewPortRect;
581     },
582 
583     /**
584      * Get scale factor of the horizontal direction.
585      */
586     getScaleX: function () {
587         return this._scaleX;
588     },
589 
590     /**
591      * Get scale factor of the vertical direction.
592      */
593     getScaleY: function () {
594         return this._scaleY;
595     },
596 
597     /**
598      * Get the real location in view
599      */
600     convertToLocationInView: function (tx, ty, relatedPos) {
601         return {x: tx - relatedPos.left, y: relatedPos.top + relatedPos.height - ty};
602     },
603 
604     /**
605      * Touch events are handled by default; if you want to customize your handlers, please override these functions:
606      * @param {Number} num
607      * @param {Array} ids
608      * @param {Array} xs
609      * @param {Array} ys
610      */
611     handleTouchesBegin: function (num, ids, xs, ys) {
612         var arr = [], locViewPortRect = this._viewPortRect, locScaleX = this._scaleX, locScaleY = this._scaleY;
613         for (var i = 0; i < num; ++i) {
614             var id = ids[i];
615             var x = xs[i];
616             var y = ys[i];
617 
618             var index = cc.TouchesIntergerDict[id];
619             var unusedIndex = 0;
620 
621             // it is a new touch
622             if (index == null) {
623                 unusedIndex = this._getUnUsedIndex();
624 
625                 // The touches is more than MAX_TOUCHES ?
626                 if (unusedIndex == -1) {
627                     cc.log("The touches is more than MAX_TOUCHES, nUnusedIndex = " + unusedIndex);
628                     continue;
629                 }
630 
631                 var touch = cc.Touches[unusedIndex] = new cc.Touch();
632                 touch.setTouchInfo(unusedIndex, (x - locViewPortRect.x) / locScaleX,
633                     (y - locViewPortRect.y) / locScaleY);
634 
635                 cc.TouchesIntergerDict[id] = 0 | unusedIndex;
636                 arr.push(touch);
637             }
638         }
639 
640         if (arr.length !== 0)
641             this._delegate.touchesBegan(arr, null);
642     },
643 
644     /**
645      * @param {Number} num
646      * @param {Number} ids
647      * @param {Number} xs
648      * @param {Number} ys
649      */
650     handleTouchesMove: function (num, ids, xs, ys) {
651         var arr = [];
652         var locScaleX = this._scaleX, locScaleY = this._scaleY, locViewPortX = this._viewPortRect.x, locViewPortY = this._viewPortRect.y;
653         for (var i = 0; i < num; ++i) {
654             var id = ids[i];
655             var x = xs[i];
656             var y = ys[i];
657 
658             var index = cc.TouchesIntergerDict[id];
659             if (index == null) {
660                 //cc.log("if the index doesn't exist, it is an error");
661                 continue;
662             }
663 
664             var touch = cc.Touches[index];
665             if (touch) {
666                 touch.setTouchInfo(index, (x - locViewPortX) / locScaleX,
667                     (y - locViewPortY) / locScaleY);
668                 arr.push(touch);
669             }
670             else {
671                 // It is error, should return.
672                 //cc.log("Moving touches with id: " + id + " error");
673                 return;
674             }
675         }
676 
677         if (arr.length == 0) {
678             //cc.log("touchesMoved: count = 0");
679             return;
680         }
681 
682         this._delegate.touchesMoved(arr, null);
683     },
684 
685     /**
686      * @param {Number} num
687      * @param {Number} ids
688      * @param {Number} xs
689      * @param {Number} ys
690      */
691     handleTouchesEnd: function (num, ids, xs, ys) {
692         var arr = [];
693         this.getSetOfTouchesEndOrCancel(arr, num, ids, xs, ys);
694         this._delegate.touchesEnded(arr, null);
695     },
696 
697     /**
698      * @param {Number} num
699      * @param {Number} ids
700      * @param {Number} xs
701      * @param {Number} ys
702      */
703     handleTouchesCancel: function (num, ids, xs, ys) {
704         var arr = [];
705         this.getSetOfTouchesEndOrCancel(arr, num, ids, xs, ys);
706         this._delegate.touchesCancelled(arr, null);
707     },
708 
709     /**
710      * @param {Array} arr
711      * @param {Number} num
712      * @param {Number} ids
713      * @param {Number} xs
714      * @param {Number} ys
715      */
716     getSetOfTouchesEndOrCancel: function (arr, num, ids, xs, ys) {
717         var locScaleX = this._scaleX, locScaleY = this._scaleY, locViewPortRect = this._viewPortRect;
718         for (var i = 0; i < num; ++i) {
719             var id = ids[i];
720             var x = xs[i];
721             var y = ys[i];
722 
723             var index = cc.TouchesIntergerDict[id];
724             if (index == null) {
725                 //cc.log("if the index doesn't exist, it is an error");
726                 continue;
727             }
728             /* Add to the set to send to the director */
729             var touch = cc.Touches[index];
730             if (touch) {
731                 //cc.log("Ending touches with id: " + id + ", x=" + x + ", y=" + y);
732                 touch.setTouchInfo(index, (x - locViewPortRect.x) / locScaleX,
733                     (y - locViewPortRect.y) / locScaleY);
734 
735                 arr.push(touch);
736 
737                 // release the object
738                 cc.Touches[index] = null;
739                 this._removeUsedIndexBit(index);
740 
741                 delete cc.TouchesIntergerDict[id];
742             } else {
743                 //cc.log("Ending touches with id: " + id + " error");
744                 return;
745             }
746         }
747     },
748 
749     // Pass the touches to the superview
750     touchesBegan: function (touches, event) {
751         var ids = [];
752         var xs = [];
753         var ys = [];
754 
755         var i = 0;
756         var touch;
757         for (var j = 0; j < touches.length; j++) {
758             touch = touches[j];
759             ids[i] = touch.getId() || j;
760             xs[i] = touch.getLocation().x;
761             ys[i] = touch.getLocation().y;
762             ++i;
763         }
764         this.handleTouchesBegin(i, ids, xs, ys);
765     },
766 
767     touchesMoved: function (touches, event) {
768         var ids = [];
769         var xs = [];
770         var ys = [];
771 
772         var i = 0;
773         var touch;
774         for (var j = 0; j < touches.length; j++) {
775             touch = touches[j];
776             ids[i] = touch.getId() || j;
777             xs[i] = touch.getLocation().x;
778             ys[i] = touch.getLocation().y;
779             ++i;
780         }
781         this.handleTouchesMove(i, ids, xs, ys);
782     },
783 
784     touchesEnded: function (touches, event) {
785         var ids = [];
786         var xs = [];
787         var ys = [];
788 
789         var i = 0;
790         var touch;
791         for (var j = 0; j < touches.length; j++) {
792             touch = touches[j];
793             ids[i] = touch.getId() || j;
794             xs[i] = touch.getLocation().x;
795             ys[i] = touch.getLocation().y;
796             ++i;
797         }
798         this.handleTouchesEnd(i, ids, xs, ys);
799     },
800 
801     touchesCancelled: function (touches, event) {
802         var ids = [];
803         var xs = [];
804         var ys = [];
805 
806         var i = 0;
807         var touch;
808         for (var j = 0; j < touches.length; j++) {
809             touch = touches[j];
810             ids[i] = touch.getId() || j;
811             xs[i] = touch.getLocation().x;
812             ys[i] = touch.getLocation().y;
813             ++i;
814         }
815         this.handleTouchesCancel(i, ids, xs, ys);
816     }
817 });
818 
819 
820 cc.EGLView.getInstance = function () {
821     if (!this._instance) {
822         this._instance = new cc.EGLView();
823         this._instance.initialize();
824     }
825     return this._instance;
826 };
827 
828 
829 /**
830  * <p>cc.ContainerStrategy class is the root strategy class of container's scale strategy,
831  * it controls the behavior of how to scale the cc.container and cc.canvas object</p>
832  *
833  * @class
834  * @extends cc.Class
835  */
836 
837 cc.ContainerStrategy = cc.Class.extend({
838     /**
839      * Manipulation before applying the strategy
840      * @param {cc.EGLView} view The target view
841      */
842     preApply: function (view) {
843     },
844 
845     /**
846      * Function to apply this strategy
847      * @param {cc.EGLView} view
848      * @param {cc.Size} designedResolution
849      */
850     apply: function (view, designedResolution) {
851     },
852 
853     /**
854      * Manipulation after applying the strategy
855      * @param {cc.EGLView} view The target view
856      */
857     postApply: function (view) {
858     },
859 
860     _setupContainer: function (frame, w, h) {
861         if (cc.Browser.isMobile && frame == document.documentElement) {
862             // Automatically full screen when user touches on mobile version
863             cc.Screen.getInstance().autoFullScreen(cc.canvas);
864         }
865 
866         var locCanvasElement = cc.canvas, locContainer = cc.container;
867         // Setup canvas
868         locCanvasElement.width = w;
869         locCanvasElement.height = h;
870         // Setup container
871         locContainer.style.width = w + "px";
872         locContainer.style.height = h + "px";
873 
874         var body = document.body, style;
875         if (body && (style = body.style)) {
876             style.paddingTop = style.paddingTop || "0px";
877             style.paddingRight = style.paddingRight || "0px";
878             style.paddingBottom = style.paddingBottom || "0px";
879             style.paddingLeft = style.paddingLeft || "0px";
880             style.borderTop = style.borderTop || "0px";
881             style.borderRight = style.borderRight || "0px";
882             style.borderBottom = style.borderBottom || "0px";
883             style.borderLeft = style.borderLeft || "0px";
884             style.marginTop = style.marginTop || "0px";
885             style.marginRight = style.marginRight || "0px";
886             style.marginBottom = style.marginBottom || "0px";
887             style.marginLeft = style.marginLeft || "0px";
888         }
889     },
890 
891     _fixContainer: function () {
892         // Add container to document body
893         document.body.insertBefore(cc.container, document.body.firstChild);
894         // Set body's width height to window's size, and forbid overflow, so that game will be centered
895         var bs = document.body.style;
896         bs.width = window.innerWidth + "px";
897         bs.height = window.innerHeight + "px";
898         bs.overflow = "hidden";
899         // Body size solution doesn't work on all mobile browser so this is the aleternative: fixed container
900         var contStyle = cc.container.style;
901         contStyle.position = "fixed";
902         contStyle.left = contStyle.top = "0px";
903         // Reposition body
904         document.body.scrollTop = 0;
905     }
906 });
907 
908 /**
909  * <p>cc.ContentStrategy class is the root strategy class of content's scale strategy,
910  * it controls the behavior of how to scale the scene and setup the viewport for the game</p>
911  *
912  * @class
913  * @extends cc.Class
914  */
915 
916 cc.ContentStrategy = cc.Class.extend({
917 
918     _result: {
919         scale: [1, 1],
920         viewport: null
921     },
922 
923     _buildResult: function (containerW, containerH, contentW, contentH, scaleX, scaleY) {
924         var viewport = cc.rect(Math.round((containerW - contentW) / 2),
925                                Math.round((containerH - contentH) / 2),
926                                contentW, contentH);
927 
928         // Translate the content
929         if (cc.renderContextType == cc.CANVAS)
930             cc.renderContext.translate(viewport.x, viewport.y + contentH);
931 
932         this._result.scale = [scaleX, scaleY];
933         this._result.viewport = viewport;
934         return this._result;
935     },
936 
937     /**
938      * Manipulation before appling the strategy
939      * @param {cc.EGLView} The target view
940      */
941     preApply: function (view) {
942     },
943 
944     /**
945      * Function to apply this strategy
946      * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}},
947      * The target view can then apply these value to itself, it's prefered not to modify directly its private variables
948      * @param {cc.EGLView} view
949      * @param {cc.Size} designedResolution
950      * @return {object} scaleAndViewportRect
951      */
952     apply: function (view, designedResolution) {
953         return {"scale": [1, 1]};
954     },
955 
956     /**
957      * Manipulation after appling the strategy
958      * @param {cc.EGLView} The target view
959      */
960     postApply: function (view) {
961     }
962 });
963 
964 (function () {
965 
966 // Container scale strategys
967     var EqualToFrame = cc.ContainerStrategy.extend({
968         apply: function (view) {
969             this._setupContainer(view._frame, view._frameSize.width, view._frameSize.height);
970         }
971     });
972 
973     var ProportionalToFrame = cc.ContainerStrategy.extend({
974         apply: function (view, designedResolution) {
975             var frameW = view._frameSize.width, frameH = view._frameSize.height, containerStyle = cc.container.style,
976                 designW = designedResolution.width, designH = designedResolution.height,
977                 scaleX = frameW / designW, scaleY = frameH / designH,
978                 containerW, containerH;
979 
980             scaleX < scaleY ? (containerW = frameW, containerH = designH * scaleX) : (containerW = designW * scaleY, containerH = frameH);
981 
982             // Adjust container size with integer value
983             var offx = Math.round((frameW - containerW) / 2);
984             var offy = Math.round((frameH - containerH) / 2);
985             containerW = frameW - 2 * offx;
986             containerH = frameH - 2 * offy;
987 
988             this._setupContainer(view._frame, containerW, containerH);
989             // Setup container's margin
990             containerStyle.marginLeft = offx + "px";
991             containerStyle.marginRight = offx + "px";
992             containerStyle.marginTop = offy + "px";
993             containerStyle.marginBottom = offy + "px";
994         }
995     });
996 
997     var EqualToWindow = EqualToFrame.extend({
998         preApply: function (view) {
999             view._frame = document.documentElement;
1000         },
1001 
1002         apply: function (view) {
1003             this._super(view);
1004 
1005             this._fixContainer();
1006         }
1007     });
1008 
1009     var ProportionalToWindow = ProportionalToFrame.extend({
1010         preApply: function (view) {
1011             view._frame = document.documentElement;
1012         },
1013 
1014         apply: function (view, designedResolution) {
1015             this._super(view, designedResolution);
1016 
1017             this._fixContainer();
1018         }
1019     });
1020 
1021     var OriginalContainer = cc.ContainerStrategy.extend({});
1022 
1023 // #NOT STABLE on Android# Alias: Strategy that makes the container's size equals to the window's size
1024 //    cc.ContainerStrategy.EQUAL_TO_WINDOW = new EqualToWindow();
1025 // #NOT STABLE on Android# Alias: Strategy that scale proportionally the container's size to window's size
1026 //    cc.ContainerStrategy.PROPORTION_TO_WINDOW = new ProportionalToWindow();
1027 // Alias: Strategy that makes the container's size equals to the frame's size
1028     cc.ContainerStrategy.EQUAL_TO_FRAME = new EqualToFrame();
1029 // Alias: Strategy that scale proportionally the container's size to frame's size
1030     cc.ContainerStrategy.PROPORTION_TO_FRAME = new ProportionalToFrame();
1031 // Alias: Strategy that keeps the original container's size
1032     cc.ContainerStrategy.ORIGINAL_CONTAINER = new OriginalContainer();
1033 
1034 // Content scale strategys
1035     var ExactFit = cc.ContentStrategy.extend({
1036         apply: function (view, designedResolution) {
1037             var containerW = cc.canvas.width, containerH = cc.canvas.height,
1038                 scaleX = containerW / designedResolution.width, scaleY = containerH / designedResolution.height;
1039 
1040             return this._buildResult(containerW, containerH, containerW, containerH, scaleX, scaleY);
1041         }
1042     });
1043 
1044     var ShowAll = cc.ContentStrategy.extend({
1045         apply: function (view, designedResolution) {
1046             var containerW = cc.canvas.width, containerH = cc.canvas.height,
1047                 designW = designedResolution.width, designH = designedResolution.height,
1048                 scaleX = containerW / designW, scaleY = containerH / designH, scale,
1049                 contentW, contentH;
1050 
1051             scaleX < scaleY ? (scale = scaleX, contentW = containerW, contentH = designH * scale)
1052                 : (scale = scaleY, contentW = designW * scale, contentH = containerH);
1053 
1054             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
1055         }
1056     });
1057 
1058     var NoBorder = cc.ContentStrategy.extend({
1059         apply: function (view, designedResolution) {
1060             var containerW = cc.canvas.width, containerH = cc.canvas.height,
1061                 designW = designedResolution.width, designH = designedResolution.height,
1062                 scaleX = containerW / designW, scaleY = containerH / designH, scale,
1063                 contentW, contentH;
1064 
1065             scaleX < scaleY ? (scale = scaleY, contentW = designW * scale, contentH = containerH)
1066                 : (scale = scaleX, contentW = containerW, contentH = designH * scale);
1067 
1068             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
1069         }
1070     });
1071 
1072     var FixedHeight = cc.ContentStrategy.extend({
1073         apply: function (view, designedResolution) {
1074             var containerW = cc.canvas.width, containerH = cc.canvas.height,
1075                 designH = designedResolution.height, scale = containerH / designH,
1076                 contentW = containerW, contentH = containerH;
1077 
1078             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
1079         },
1080 
1081         postApply: function (view) {
1082             cc.Director.getInstance()._winSizeInPoints = view.getVisibleSize();
1083         }
1084     });
1085 
1086     var FixedWidth = cc.ContentStrategy.extend({
1087         apply: function (view, designedResolution) {
1088             var containerW = cc.canvas.width, containerH = cc.canvas.height,
1089                 designW = designedResolution.width, scale = containerW / designW,
1090                 contentW = containerW, contentH = containerH;
1091 
1092             return this._buildResult(containerW, containerH, contentW, contentH, scale, scale);
1093         },
1094 
1095         postApply: function (view) {
1096             cc.Director.getInstance()._winSizeInPoints = view.getVisibleSize();
1097         }
1098     });
1099 
1100 // Alias: Strategy to scale the content's size to container's size, non proportional
1101     cc.ContentStrategy.EXACT_FIT = new ExactFit();
1102 // Alias: Strategy to scale the content's size proportionally to maximum size and keeps the whole content area to be visible
1103     cc.ContentStrategy.SHOW_ALL = new ShowAll();
1104 // Alias: Strategy to scale the content's size proportionally to fill the whole container area
1105     cc.ContentStrategy.NO_BORDER = new NoBorder();
1106 // Alias: Strategy to scale the content's height to container's height and proportionally scale its width
1107     cc.ContentStrategy.FIXED_HEIGHT = new FixedHeight();
1108 // Alias: Strategy to scale the content's width to container's width and proportionally scale its height
1109     cc.ContentStrategy.FIXED_WIDTH = new FixedWidth();
1110 
1111 })();
1112 
1113 /**
1114  * <p>cc.ResolutionPolicy class is the root strategy class of scale strategy,
1115  * its main task is to maintain the compatibility with Cocos2d-x</p>
1116  *
1117  * @class
1118  * @extends cc.Class
1119  */
1120 cc.ResolutionPolicy = cc.Class.extend({
1121     _containerStrategy: null,
1122     _contentStrategy: null,
1123 
1124     ctor: function (containerStg, contentStg) {
1125         this.setContainerStrategy(containerStg);
1126         this.setContentStrategy(contentStg);
1127     },
1128 
1129     /**
1130      * Manipulation before appling the resolution policy
1131      * @param {cc.EGLView} The target view
1132      */
1133     preApply: function (view) {
1134         this._containerStrategy.preApply(view);
1135         this._contentStrategy.preApply(view);
1136     },
1137 
1138     /**
1139      * Function to apply this resolution policy
1140      * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}},
1141      * The target view can then apply these value to itself, it's prefered not to modify directly its private variables
1142      * @param {cc.EGLView} The target view
1143      * @param {cc.Size} The user defined design resolution
1144      * @return {object} An object contains the scale X/Y values and the viewport rect
1145      */
1146     apply: function (view, designedResolution) {
1147         this._containerStrategy.apply(view, designedResolution);
1148         return this._contentStrategy.apply(view, designedResolution);
1149     },
1150 
1151     /**
1152      * Manipulation after appling the strategy
1153      * @param {cc.EGLView} The target view
1154      */
1155     postApply: function (view) {
1156         this._containerStrategy.postApply(view);
1157         this._contentStrategy.postApply(view);
1158     },
1159 
1160     /**
1161      * Setup the container's scale strategy
1162      * @param {cc.ContainerStrategy} containerStg
1163      */
1164     setContainerStrategy: function (containerStg) {
1165         if (containerStg instanceof cc.ContainerStrategy)
1166             this._containerStrategy = containerStg;
1167     },
1168 
1169     /**
1170      * Setup the content's scale strategy
1171      * @param {cc.ContentStrategy} contentStg
1172      */
1173     setContentStrategy: function (contentStg) {
1174         if (contentStg instanceof cc.ContentStrategy)
1175             this._contentStrategy = contentStg;
1176     }
1177 });