diff --git a/AUTHORS.txt b/AUTHORS.txt index e61d18e5cb..5a20dc7535 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,22 +1,20 @@ Cocos2d-html5 authors -(ordered by the join in time) +(Ordered by join time) Core Developers: Shun Lin (Sean Lin) - Hao Wu (WuHao) - - Dingping Lv (David Lv) - Ricardo Quesada Huabin LING (@pandamicro) Sijie Wang (@VisualSJ) - Jialong Zhai (@JoshuaAstray) + Long Jiang (@jianglong0156) + + Menghe Zhang (@ZhangMenghe) Contributors: Name GithubID Main contribution @@ -165,6 +163,7 @@ erykwalder @erykwalder Function.prototype.bind bug fix ZippoLag @ZippoLag cc.Application.getCurrentLanguage bug fix typo fix + Fixed `cc.TMXObjectGroup#objectNamed` not returning the result bug Asano @LaercioAsano cc.Node bug fix @@ -178,6 +177,8 @@ Mykyta Usikov @musikov cc.ClippingNode bugs fix cc.Scale9Sprite bugs fix cc.RenderTexture bug fix cc.ParticleSystem bug fix + Made CCProgressTimerCanvasRenderCmd to properly show colorized sprites + cc.ScrollView and cc.TableView: added check for parent visibility in onTouchBegan method Han XiaoLong @kpkhxlgy0 cc.ParticleSytem bug fix @@ -243,6 +244,8 @@ Dany Ellement @DEllement cc.FontDefinition & ccui.RichText improvemen IShm @IShm cc.Screen bug fix cc.ParticleSystem bug fix ccui.PageView bug fix + Fixed crash when character not found into BMP font + Fixed restoring of sprite's color issue Thomas Jablonski @thomas-jablonski cc.audioEngine bug fix Cocostudio typo fix @@ -255,11 +258,28 @@ feijing566 @feijing566 cc.Audio bug fix RackovychV @RackovychV Fixed a bug of `cc.Scheduler`'s `pauseAllTargetsWithMinPriority` +giuseppelt @giuseppelt Fixed TransitionSlideX callback sequence issue + +YShumov @pixmaster Fixed issue in music end event + +SPACELAN @spacelan Fixed `inverse` function bug of `cc.math.Matrix4` + +patriciog @patriciog Allowed timeline animations with only one frame + +Ningxin Hu @huningxin SIMD.js optimization for kazmath functions + +Zachary Lester @ZLester Fix typo in AUTHORS.txt + +Juan Carlos @Ruluk Fixed a bug where not resetting cc.Audio._ignoreEnded when replaying a sound caused it to stay in a "playing" state +Maxim Litvinov @metalim Throw new Error object instead of error message string Retired Core Developers: Shengxiang Chen (Nero Chan) Xingsen Ma + Jialong Zhai (@JoshuaAstray) + Hao Wu (WuHao) + Dingping Lv (David Lv) Cocos2d-x and cocos2d-html5 can not grow so fast without the active community. diff --git a/CCBoot.js b/CCBoot.js index 1be2d4ff3b..22c50494b6 100644 --- a/CCBoot.js +++ b/CCBoot.js @@ -1,6 +1,6 @@ /**************************************************************************** Copyright (c) 2011-2012 cocos2d-x.org - Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2013-2015 Chukong Technologies Inc. http://www.cocos2d-x.org @@ -32,9 +32,7 @@ var cc = cc || {}; cc._tmp = cc._tmp || {}; cc._LogInfos = {}; -/** @expose */ -window._p; -_p = window; +var _p = window; /** @expose */ _p.gl; /** @expose */ @@ -45,8 +43,10 @@ _p.DeviceOrientationEvent; _p.DeviceMotionEvent; /** @expose */ _p.AudioContext; -/** @expose */ -_p.webkitAudioContext; +if (!_p.AudioContext) { + /** @expose */ + _p.webkitAudioContext; +} /** @expose */ _p.mozAudioContext; _p = Object.prototype; @@ -54,18 +54,35 @@ _p = Object.prototype; _p._super; /** @expose */ _p.ctor; -delete window._p; +_p = null; -cc.newElement = function (x) { - return document.createElement(x); -}; +/** + * drawing primitive of game engine + * @type {cc.DrawingPrimitive} + */ +cc._drawingUtil = null; -cc._addEventListener = function (element, type, listener, useCapture) { - element.addEventListener(type, listener, useCapture); -}; +/** + * main Canvas 2D/3D Context of game engine + * @type {CanvasRenderingContext2D|WebGLRenderingContext} + */ +cc._renderContext = null; +cc._supportRender = false; + +/** + * Main canvas of game engine + * @type {HTMLCanvasElement} + */ +cc._canvas = null; + +/** + * The element contains the game canvas + * @type {HTMLDivElement} + */ +cc.container = null; +cc._gameDiv = null; -//is nodejs ? Used to support node-webkit. -cc._isNodeJs = typeof require !== 'undefined' && require("fs"); +window.ENABLE_IMAEG_POOL = true; /** * Iterate over an object or an array, executing a function for each matched element. @@ -95,11 +112,11 @@ cc.each = function (obj, iterator, context) { * @param {object} *sources * @returns {object} */ -cc.extend = function(target) { +cc.extend = function (target) { var sources = arguments.length >= 2 ? Array.prototype.slice.call(arguments, 1) : []; - cc.each(sources, function(src) { - for(var key in src) { + cc.each(sources, function (src) { + for (var key in src) { if (src.hasOwnProperty(key)) { target[key] = src[key]; } @@ -108,12 +125,32 @@ cc.extend = function(target) { return target; }; +/** + * Another way to subclass: Using Google Closure. + * The following code was copied + pasted from goog.base / goog.inherits + * @function + * @param {Function} childCtor + * @param {Function} parentCtor + */ +cc.inherits = function (childCtor, parentCtor) { + function tempCtor() {} + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + childCtor.prototype.constructor = childCtor; + + // Copy "static" method, but doesn't generate subclasses. + // for( var i in parentCtor ) { + // childCtor[ i ] = parentCtor[ i ]; + // } +}; + /** * Check the obj whether is function or not * @param {*} obj * @returns {boolean} */ -cc.isFunction = function(obj) { +cc.isFunction = function (obj) { return typeof obj === 'function'; }; @@ -122,7 +159,7 @@ cc.isFunction = function(obj) { * @param {*} obj * @returns {boolean} */ -cc.isNumber = function(obj) { +cc.isNumber = function (obj) { return typeof obj === 'number' || Object.prototype.toString.call(obj) === '[object Number]'; }; @@ -131,7 +168,7 @@ cc.isNumber = function(obj) { * @param {*} obj * @returns {boolean} */ -cc.isString = function(obj) { +cc.isString = function (obj) { return typeof obj === 'string' || Object.prototype.toString.call(obj) === '[object String]'; }; @@ -140,7 +177,7 @@ cc.isString = function(obj) { * @param {*} obj * @returns {boolean} */ -cc.isArray = function(obj) { +cc.isArray = function (obj) { return Array.isArray(obj) || (typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Array]'); }; @@ -150,7 +187,7 @@ cc.isArray = function(obj) { * @param {*} obj * @returns {boolean} */ -cc.isUndefined = function(obj) { +cc.isUndefined = function (obj) { return typeof obj === 'undefined'; }; @@ -159,7 +196,7 @@ cc.isUndefined = function(obj) { * @param {*} obj * @returns {boolean} */ -cc.isObject = function(obj) { +cc.isObject = function (obj) { return typeof obj === "object" && Object.prototype.toString.call(obj) === '[object Object]'; }; @@ -192,8 +229,9 @@ cc.isCrossOrigin = function (url) { * @param {object} target * @constructor */ -cc.AsyncPool = function(srcObj, limit, iterator, onEnd, target){ +cc.AsyncPool = function (srcObj, limit, iterator, onEnd, target) { var self = this; + self._finished = false; self._srcObj = srcObj; self._limit = limit; self._pool = []; @@ -202,10 +240,10 @@ cc.AsyncPool = function(srcObj, limit, iterator, onEnd, target){ self._onEnd = onEnd; self._onEndTarget = target; self._results = srcObj instanceof Array ? [] : {}; - self._isErr = false; + self._errors = srcObj instanceof Array ? [] : {}; - cc.each(srcObj, function(value, index){ - self._pool.push({index : index, value : value}); + cc.each(srcObj, function (value, index) { + self._pool.push({index: index, value: value}); }); self.size = self._pool.length; @@ -214,43 +252,42 @@ cc.AsyncPool = function(srcObj, limit, iterator, onEnd, target){ self._limit = self._limit || self.size; - self.onIterator = function(iterator, target){ + self.onIterator = function (iterator, target) { self._iterator = iterator; self._iteratorTarget = target; }; - self.onEnd = function(endCb, endCbTarget){ + self.onEnd = function (endCb, endCbTarget) { self._onEnd = endCb; self._onEndTarget = endCbTarget; }; - self._handleItem = function(){ + self._handleItem = function () { var self = this; - if(self._pool.length === 0 || self._workingSize >= self._limit) + if (self._pool.length === 0 || self._workingSize >= self._limit) return; //return directly if the array's length = 0 or the working size great equal limit number var item = self._pool.shift(); var value = item.value, index = item.index; self._workingSize++; self._iterator.call(self._iteratorTarget, value, index, - function(err) { - if (self._isErr) + function (err, result) { + if (self._finished) { return; + } - self.finishedSize++; - self._workingSize--; if (err) { - self._isErr = true; - if (self._onEnd) - self._onEnd.call(self._onEndTarget, err); - return; + self._errors[this.index] = err; + } + else { + self._results[this.index] = result; } - var arr = Array.prototype.slice.call(arguments, 1); - self._results[this.index] = arr[0]; + self.finishedSize++; + self._workingSize--; if (self.finishedSize === self.size) { - if (self._onEnd) - self._onEnd.call(self._onEndTarget, null, self._results); + var errors = self._errors.length === 0 ? null : self._errors; + self.onEnd(errors, self._results); return; } self._handleItem(); @@ -258,16 +295,27 @@ cc.AsyncPool = function(srcObj, limit, iterator, onEnd, target){ self); }; - self.flow = function(){ + self.flow = function () { var self = this; - if(self._pool.length === 0) { - if(self._onEnd) + if (self._pool.length === 0) { + if (self._onEnd) self._onEnd.call(self._onEndTarget, null, []); - return; + return; } - for(var i = 0; i < self._limit; i++) + for (var i = 0; i < self._limit; i++) self._handleItem(); - } + }; + + self.onEnd = function(errors, results) { + self._finished = true; + if (self._onEnd) { + var selector = self._onEnd; + var target = self._onEndTarget; + self._onEnd = null; + self._onEndTarget = null; + selector.call(target, errors, results); + } + }; }; /** @@ -281,8 +329,8 @@ cc.async = /** @lends cc.async# */{ * @param {Object} [target] * @return {cc.AsyncPool} */ - series : function(tasks, cb, target){ - var asyncPool = new cc.AsyncPool(tasks, 1, function(func, index, cb1){ + series: function (tasks, cb, target) { + var asyncPool = new cc.AsyncPool(tasks, 1, function (func, index, cb1) { func.call(target, cb1); }, cb, target); asyncPool.flow(); @@ -296,8 +344,8 @@ cc.async = /** @lends cc.async# */{ * @param {Object} [target] * @return {cc.AsyncPool} */ - parallel : function(tasks, cb, target){ - var asyncPool = new cc.AsyncPool(tasks, 0, function(func, index, cb1){ + parallel: function (tasks, cb, target) { + var asyncPool = new cc.AsyncPool(tasks, 0, function (func, index, cb1) { func.call(target, cb1); }, cb, target); asyncPool.flow(); @@ -311,14 +359,14 @@ cc.async = /** @lends cc.async# */{ * @param {Object} [target] * @return {cc.AsyncPool} */ - waterfall : function(tasks, cb, target){ + waterfall: function (tasks, cb, target) { var args = []; var lastResults = [null];//the array to store the last results var asyncPool = new cc.AsyncPool(tasks, 1, function (func, index, cb1) { args.push(function (err) { args = Array.prototype.slice.call(arguments, 1); - if(tasks.length - 1 === index) lastResults = lastResults.concat(args);//while the last task + if (tasks.length - 1 === index) lastResults = lastResults.concat(args);//while the last task cb1.apply(null, arguments); }); func.apply(target, args); @@ -341,9 +389,9 @@ cc.async = /** @lends cc.async# */{ * @param {Object} [target] * @return {cc.AsyncPool} */ - map : function(tasks, iterator, callback, target){ + map: function (tasks, iterator, callback, target) { var locIterator = iterator; - if(typeof(iterator) === "object"){ + if (typeof(iterator) === "object") { callback = iterator.cb; target = iterator.iteratorTarget; locIterator = iterator.iterator; @@ -361,7 +409,7 @@ cc.async = /** @lends cc.async# */{ * @param {function} cb callback * @param {Object} [target] */ - mapLimit : function(tasks, limit, iterator, cb, target){ + mapLimit: function (tasks, limit, iterator, cb, target) { var asyncPool = new cc.AsyncPool(tasks, limit, iterator, cb, target); asyncPool.flow(); return asyncPool; @@ -374,6 +422,8 @@ cc.async = /** @lends cc.async# */{ * @class */ cc.path = /** @lends cc.path# */{ + normalizeRE: /[^\.\/]+\/\.\.\//, + /** * Join strings to be a path. * @example @@ -413,11 +463,11 @@ cc.path = /** @lends cc.path# */{ * @param {string} fileName * @returns {string} */ - mainFileName: function(fileName){ - if(fileName){ - var idx = fileName.lastIndexOf("."); - if(idx !== -1) - return fileName.substring(0,idx); + mainFileName: function (fileName) { + if (fileName) { + var idx = fileName.lastIndexOf("."); + if (idx !== -1) + return fileName.substring(0, idx); } return fileName; }, @@ -510,526 +560,722 @@ cc.path = /** @lends cc.path# */{ index = pathStr.lastIndexOf("/"); index = index <= 0 ? 0 : index + 1; return pathStr.substring(0, index) + basename + ext + tempStr; + }, + //todo make public after verification + _normalize: function (url) { + var oldUrl = url = String(url); + + //removing all ../ + do { + oldUrl = url; + url = url.replace(this.normalizeRE, ""); + } while (oldUrl.length !== url.length); + return url; } }; //+++++++++++++++++++++++++something about path end++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++something about loader start+++++++++++++++++++++++++++ /** - * Loader for resource loading process. It's a singleton object. + * Resource loading management. Created by in CCBoot.js as a singleton + * cc.loader. + * @name cc.Loader * @class + * @memberof cc + * @see cc.loader */ -cc.loader = /** @lends cc.loader# */{ - _jsCache: {},//cache for js - _register: {},//register of loaders - _langPathCache: {},//cache for lang path - _aliases: {},//aliases for res url - resPath: "",//root path of resource - audioPath: "",//root path of audio - cache: {},//cache for data loaded - - /** - * Get XMLHttpRequest. - * @returns {XMLHttpRequest} - */ - getXMLHttpRequest: function () { - return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP"); +var imagePool = { + _pool: new Array(10), + _MAX: 10, + _smallImg: "data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=", + + count: 0, + get: function () { + if (this.count > 0) { + this.count--; + var result = this._pool[this.count]; + this._pool[this.count] = null; + return result; + } + else { + return new Image(); + } }, + put: function (img) { + var pool = this._pool; + if (img instanceof HTMLImageElement && this.count < this._MAX) { + img.src = this._smallImg; + pool[this.count] = img; + this.count++; + } + } +}; + +/** + * Singleton instance of cc.Loader. + * @name cc.loader + * @member {cc.Loader} + * @memberof cc + */ +cc.loader = (function () { + var _jsCache = {}, //cache for js + _register = {}, //register of loaders + _langPathCache = {}, //cache for lang path + _aliases = {}, //aliases for res url + _queue = {}, // Callback queue for resources already loading + _urlRegExp = new RegExp("^(?:https?|ftp)://\\S*$", "i"); + + return /** @lends cc.Loader# */{ + /** + * Root path of resources. + * @type {String} + */ + resPath: "", + + /** + * Root path of audio resources + * @type {String} + */ + audioPath: "", + + /** + * Cache for data loaded. + * @type {Object} + */ + cache: {}, + + /** + * Get XMLHttpRequest. + * @returns {XMLHttpRequest} + */ + getXMLHttpRequest: function () { + var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP"); + xhr.timeout = 10000; + if (xhr.ontimeout === undefined) { + xhr._timeoutId = -1; + } + return xhr; + }, - //@MODE_BEGIN DEV + //@MODE_BEGIN DEV - _getArgs4Js: function (args) { - var a0 = args[0], a1 = args[1], a2 = args[2], results = ["", null, null]; + _getArgs4Js: function (args) { + var a0 = args[0], a1 = args[1], a2 = args[2], results = ["", null, null]; - if (args.length === 1) { - results[1] = a0 instanceof Array ? a0 : [a0]; - } else if (args.length === 2) { - if (typeof a1 === "function") { + if (args.length === 1) { results[1] = a0 instanceof Array ? a0 : [a0]; - results[2] = a1; - } else { + } else if (args.length === 2) { + if (typeof a1 === "function") { + results[1] = a0 instanceof Array ? a0 : [a0]; + results[2] = a1; + } else { + results[0] = a0 || ""; + results[1] = a1 instanceof Array ? a1 : [a1]; + } + } else if (args.length === 3) { results[0] = a0 || ""; results[1] = a1 instanceof Array ? a1 : [a1]; - } - } else if (args.length === 3) { - results[0] = a0 || ""; - results[1] = a1 instanceof Array ? a1 : [a1]; - results[2] = a2; - } else throw "arguments error to load js!"; - return results; - }, - - /** - * Load js files. - * If the third parameter doesn't exist, then the baseDir turns to be "". - * - * @param {string} [baseDir] The pre path for jsList or the list of js path. - * @param {array} jsList List of js path. - * @param {function} [cb] Callback function - * @returns {*} - */ - loadJs: function (baseDir, jsList, cb) { - var self = this, localJsCache = self._jsCache, - args = self._getArgs4Js(arguments); - - var preDir = args[0], list = args[1], callback = args[2]; - if (navigator.userAgent.indexOf("Trident/5") > -1) { - self._loadJs4Dependency(preDir, list, 0, callback); - } else { - cc.async.map(list, function (item, index, cb1) { - var jsPath = cc.path.join(preDir, item); - if (localJsCache[jsPath]) return cb1(null); - self._createScript(jsPath, false, cb1); - }, callback); - } - }, - /** - * Load js width loading image. - * - * @param {string} [baseDir] - * @param {array} jsList - * @param {function} [cb] - */ - loadJsWithImg: function (baseDir, jsList, cb) { - var self = this, jsLoadingImg = self._loadJsImg(), - args = self._getArgs4Js(arguments); - this.loadJs(args[0], args[1], function (err) { - if (err) throw err; - jsLoadingImg.parentNode.removeChild(jsLoadingImg);//remove loading gif - if (args[2]) args[2](); - }); - }, - _createScript: function (jsPath, isAsync, cb) { - var d = document, self = this, s = cc.newElement('script'); - s.async = isAsync; - self._jsCache[jsPath] = true; - if(cc.game.config["noCache"] && typeof jsPath === "string"){ - if(self._noCacheRex.test(jsPath)) - s.src = jsPath + "&_t=" + (new Date() - 0); - else - s.src = jsPath + "?_t=" + (new Date() - 0); - }else{ - s.src = jsPath; - } - cc._addEventListener(s, 'load', function () { - s.parentNode.removeChild(s); - this.removeEventListener('load', arguments.callee, false); - cb(); - }, false); - cc._addEventListener(s, 'error', function () { - s.parentNode.removeChild(s); - cb("Load " + jsPath + " failed!"); - }, false); - d.body.appendChild(s); - }, - _loadJs4Dependency: function (baseDir, jsList, index, cb) { - if (index >= jsList.length) { - if (cb) cb(); - return; - } - var self = this; - self._createScript(cc.path.join(baseDir, jsList[index]), false, function (err) { - if (err) return cb(err); - self._loadJs4Dependency(baseDir, jsList, index + 1, cb); - }); - }, - _loadJsImg: function () { - var d = document, jsLoadingImg = d.getElementById("cocos2d_loadJsImg"); - if (!jsLoadingImg) { - jsLoadingImg = cc.newElement('img'); - - if (cc._loadingImage) - jsLoadingImg.src = cc._loadingImage; - - var canvasNode = d.getElementById(cc.game.config["id"]); - canvasNode.style.backgroundColor = "black"; - canvasNode.parentNode.appendChild(jsLoadingImg); - - var canvasStyle = getComputedStyle ? getComputedStyle(canvasNode) : canvasNode.currentStyle; - if (!canvasStyle) - canvasStyle = {width: canvasNode.width, height: canvasNode.height}; - jsLoadingImg.style.left = canvasNode.offsetLeft + (parseFloat(canvasStyle.width) - jsLoadingImg.width) / 2 + "px"; - jsLoadingImg.style.top = canvasNode.offsetTop + (parseFloat(canvasStyle.height) - jsLoadingImg.height) / 2 + "px"; - jsLoadingImg.style.position = "absolute"; - } - return jsLoadingImg; - }, - //@MODE_END DEV - - /** - * Load a single resource as txt. - * @param {string} url - * @param {function} [cb] arguments are : err, txt - */ - loadTxt: function (url, cb) { - if (!cc._isNodeJs) { - var xhr = this.getXMLHttpRequest(), - errInfo = "load " + url + " failed!"; - xhr.open("GET", url, true); - if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { - // IE-specific logic here - xhr.setRequestHeader("Accept-Charset", "utf-8"); - xhr.onreadystatechange = function () { - if(xhr.readyState === 4) - xhr.status === 200 ? cb(null, xhr.responseText) : cb(errInfo); - }; + results[2] = a2; + } else throw new Error("arguments error to load js!"); + return results; + }, + + isLoading: function (url) { + return (_queue[url] !== undefined); + }, + + /** + * Load js files. + * If the third parameter doesn't exist, then the baseDir turns to be "". + * + * @param {string} [baseDir] The pre path for jsList or the list of js path. + * @param {array} jsList List of js path. + * @param {function} [cb] Callback function + * @returns {*} + */ + loadJs: function (baseDir, jsList, cb) { + var self = this, + args = self._getArgs4Js(arguments); + + var preDir = args[0], list = args[1], callback = args[2]; + if (navigator.userAgent.indexOf("Trident/5") > -1) { + self._loadJs4Dependency(preDir, list, 0, callback); } else { - if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=utf-8"); - xhr.onload = function () { - if(xhr.readyState === 4) - xhr.status === 200 ? cb(null, xhr.responseText) : cb(errInfo); - }; + cc.async.map(list, function (item, index, cb1) { + var jsPath = cc.path.join(preDir, item); + if (_jsCache[jsPath]) return cb1(null); + self._createScript(jsPath, false, cb1); + }, callback); } - xhr.send(null); - } else { - var fs = require("fs"); - fs.readFile(url, function (err, data) { - err ? cb(err) : cb(null, data.toString()); + }, + /** + * Load js width loading image. + * + * @param {string} [baseDir] + * @param {array} jsList + * @param {function} [cb] + */ + loadJsWithImg: function (baseDir, jsList, cb) { + var self = this, jsLoadingImg = self._loadJsImg(), + args = self._getArgs4Js(arguments); + this.loadJs(args[0], args[1], function (err) { + if (err) throw new Error(err); + jsLoadingImg.parentNode.removeChild(jsLoadingImg);//remove loading gif + if (args[2]) args[2](); }); - } - }, - _loadTxtSync: function (url) { - if (!cc._isNodeJs) { - var xhr = this.getXMLHttpRequest(); - xhr.open("GET", url, false); - if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { - // IE-specific logic here - xhr.setRequestHeader("Accept-Charset", "utf-8"); + }, + _createScript: function (jsPath, isAsync, cb) { + var d = document, self = this, s = document.createElement('script'); + s.async = isAsync; + _jsCache[jsPath] = true; + if (cc.game.config["noCache"] && typeof jsPath === "string") { + if (self._noCacheRex.test(jsPath)) + s.src = jsPath + "&_t=" + (new Date() - 0); + else + s.src = jsPath + "?_t=" + (new Date() - 0); } else { - if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=utf-8"); + s.src = jsPath; } - xhr.send(null); - if (!xhr.readyState === 4 || xhr.status !== 200) { - return null; + s.addEventListener('load', function () { + s.parentNode.removeChild(s); + this.removeEventListener('load', arguments.callee, false); + cb(); + }, false); + s.addEventListener('error', function () { + s.parentNode.removeChild(s); + cb("Load " + jsPath + " failed!"); + }, false); + d.body.appendChild(s); + }, + _loadJs4Dependency: function (baseDir, jsList, index, cb) { + if (index >= jsList.length) { + if (cb) cb(); + return; } - return xhr.responseText; - } else { - var fs = require("fs"); - return fs.readFileSync(url).toString(); - } - }, - - loadCsb: function(url, cb){ - var xhr = new XMLHttpRequest(); - xhr.open("GET", url, true); - xhr.responseType = "arraybuffer"; - - xhr.onload = function () { - var arrayBuffer = xhr.response; // Note: not oReq.responseText - if (arrayBuffer) { - window.msg = arrayBuffer; + var self = this; + self._createScript(cc.path.join(baseDir, jsList[index]), false, function (err) { + if (err) return cb(err); + self._loadJs4Dependency(baseDir, jsList, index + 1, cb); + }); + }, + _loadJsImg: function () { + var d = document, jsLoadingImg = d.getElementById("cocos2d_loadJsImg"); + if (!jsLoadingImg) { + jsLoadingImg = document.createElement('img'); + + if (cc._loadingImage) + jsLoadingImg.src = cc._loadingImage; + + var canvasNode = d.getElementById(cc.game.config["id"]); + canvasNode.style.backgroundColor = "transparent"; + canvasNode.parentNode.appendChild(jsLoadingImg); + + var canvasStyle = getComputedStyle ? getComputedStyle(canvasNode) : canvasNode.currentStyle; + if (!canvasStyle) + canvasStyle = {width: canvasNode.width, height: canvasNode.height}; + jsLoadingImg.style.left = canvasNode.offsetLeft + (parseFloat(canvasStyle.width) - jsLoadingImg.width) / 2 + "px"; + jsLoadingImg.style.top = canvasNode.offsetTop + (parseFloat(canvasStyle.height) - jsLoadingImg.height) / 2 + "px"; + jsLoadingImg.style.position = "absolute"; } - if(xhr.readyState === 4) - xhr.status === 200 ? cb(null, xhr.response) : cb("load " + url + " failed!"); - }; + return jsLoadingImg; + }, + //@MODE_END DEV + + /** + * Load a single resource as txt. + * @param {string} url + * @param {function} [cb] arguments are : err, txt + */ + loadTxt: function (url, cb) { + if (!cc._isNodeJs) { + var xhr = this.getXMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { + // IE-specific logic here + xhr.setRequestHeader("Accept-Charset", "utf-8"); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.responseText) : cb({status:xhr.status, errorMessage:errInfo}, null); + }; + } else { + if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=utf-8"); + var loadCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + if (xhr.readyState === 4) { + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.responseText) : cb({status:xhr.status, errorMessage:errInfo}, null); + } + }; + var errorCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: errInfo}, null); + }; + var timeoutCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: "Request timeout: " + errInfo}, null); + }; + xhr.addEventListener('load', loadCallback); + xhr.addEventListener('error', errorCallback); + if (xhr.ontimeout === undefined) { + xhr._timeoutId = setTimeout(function () { + timeoutCallback(); + }, xhr.timeout); + } + else { + xhr.addEventListener('timeout', timeoutCallback); + } + } + xhr.send(null); + } else { + var fs = require("fs"); + fs.readFile(url, function (err, data) { + err ? cb(err) : cb(null, data.toString()); + }); + } + }, - xhr.send(null); - }, + loadCsb: function(url, cb){ + var xhr = cc.loader.getXMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; - /** - * Load a single resource as json. - * @param {string} url - * @param {function} [cb] arguments are : err, json - */ - loadJson: function (url, cb) { - this.loadTxt(url, function (err, txt) { - if (err) { - cb(err); + var loadCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + var arrayBuffer = xhr.response; // Note: not oReq.responseText + if (arrayBuffer) { + window.msg = arrayBuffer; + } + if (xhr.readyState === 4) { + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.response) : cb({status:xhr.status, errorMessage:errInfo}, null); + } + }; + var errorCallback = function(){ + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status:xhr.status, errorMessage:errInfo}, null); + }; + var timeoutCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: "Request timeout: " + errInfo}, null); + }; + xhr.addEventListener('load', loadCallback); + xhr.addEventListener('error', errorCallback); + if (xhr.ontimeout === undefined) { + xhr._timeoutId = setTimeout(function () { + timeoutCallback(); + }, xhr.timeout); } else { - try { - var result = JSON.parse(txt); + xhr.addEventListener('timeout', timeoutCallback); + } + xhr.send(null); + }, + + /** + * Load a single resource as json. + * @param {string} url + * @param {function} [cb] arguments are : err, json + */ + loadJson: function (url, cb) { + this.loadTxt(url, function (err, txt) { + if (err) { + cb(err); } - catch (e) { - throw "parse json [" + url + "] failed : " + e; - return; + else { + try { + var result = JSON.parse(txt); + } + catch (e) { + throw new Error("parse json [" + url + "] failed : " + e); + return; + } + cb(null, result); } - cb(null, result); + }); + }, + + _checkIsImageURL: function (url) { + var ext = /(\.png)|(\.jpg)|(\.bmp)|(\.jpeg)|(\.gif)/.exec(url); + return (ext != null); + }, + /** + * Load a single image. + * @param {!string} url + * @param {object} [option] + * @param {function} callback + * @returns {Image} + */ + loadImg: function (url, option, callback, img) { + var opt = { + isCrossOrigin: true + }; + if (callback !== undefined) + opt.isCrossOrigin = option.isCrossOrigin === undefined ? opt.isCrossOrigin : option.isCrossOrigin; + else if (option !== undefined) + callback = option; + + var texture = this.getRes(url); + if (texture) { + callback && callback(null, texture); + return null; } - }); - }, - - _checkIsImageURL: function (url) { - var ext = /(\.png)|(\.jpg)|(\.bmp)|(\.jpeg)|(\.gif)/.exec(url); - return (ext != null); - }, - /** - * Load a single image. - * @param {!string} url - * @param {object} [option] - * @param {function} callback - * @returns {Image} - */ - loadImg: function (url, option, callback) { - var opt = { - isCrossOrigin: true - }; - if (callback !== undefined) - opt.isCrossOrigin = option.isCrossOrigin === null ? opt.isCrossOrigin : option.isCrossOrigin; - else if (option !== undefined) - callback = option; - - var img = this.getRes(url); - if (img) { - callback && callback(null, img); - return img; - } - img = new Image(); - if (opt.isCrossOrigin && location.origin !== "file://") - img.crossOrigin = "Anonymous"; - - var loadCallback = function () { - this.removeEventListener('load', loadCallback, false); - this.removeEventListener('error', errorCallback, false); + var queue = _queue[url]; + if (queue) { + queue.callbacks.push(callback); + return queue.img; + } - cc.loader.cache[url] = img; - if (callback) - callback(null, img); - }; + img = img || imagePool.get(); + if (opt.isCrossOrigin && location.origin !== "file://") + img.crossOrigin = "Anonymous"; + else + img.crossOrigin = null; + + var loadCallback = function () { + this.removeEventListener('load', loadCallback, false); + this.removeEventListener('error', errorCallback, false); + + var queue = _queue[url]; + if (queue) { + var callbacks = queue.callbacks; + for (var i = 0; i < callbacks.length; ++i) { + var cb = callbacks[i]; + if (cb) { + cb(null, img); + } + } + queue.img = null; + delete _queue[url]; + } - var self = this; - var errorCallback = function () { - this.removeEventListener('error', errorCallback, false); - - if(img.crossOrigin && img.crossOrigin.toLowerCase() === "anonymous"){ - opt.isCrossOrigin = false; - self.release(url); - cc.loader.loadImg(url, opt, callback); - }else{ - typeof callback === "function" && callback("load image failed"); - } - }; + if (window.ENABLE_IMAEG_POOL && cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + imagePool.put(img); + } + }; + + var self = this; + var errorCallback = function () { + this.removeEventListener('load', loadCallback, false); + this.removeEventListener('error', errorCallback, false); + + if (window.location.protocol !== 'https:' && img.crossOrigin && img.crossOrigin.toLowerCase() === "anonymous") { + opt.isCrossOrigin = false; + self.release(url); + cc.loader.loadImg(url, opt, callback, img); + } else { + var queue = _queue[url]; + if (queue) { + var callbacks = queue.callbacks; + for (var i = 0; i < callbacks.length; ++i) { + var cb = callbacks[i]; + if (cb) { + cb("load image failed"); + } + } + queue.img = null; + delete _queue[url]; + } - cc._addEventListener(img, "load", loadCallback); - cc._addEventListener(img, "error", errorCallback); - img.src = url; - return img; - }, + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + imagePool.put(img); + } + } + }; - /** - * Iterator function to load res - * @param {object} item - * @param {number} index - * @param {function} [cb] - * @returns {*} - * @private - */ - _loadResIterator: function (item, index, cb) { - var self = this, url = null; - var type = item.type; - if (type) { - type = "." + type.toLowerCase(); - url = item.src ? item.src : item.name + type; - } else { - url = item; - type = cc.path.extname(url); - } + _queue[url] = { + img: img, + callbacks: callback ? [callback] : [] + }; - var obj = self.getRes(url); - if (obj) - return cb(null, obj); - var loader = null; - if (type) { - loader = self._register[type.toLowerCase()]; - } - if (!loader) { - cc.error("loader for [" + type + "] not exists!"); - return cb(); - } - var basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; - var realUrl = self.getUrl(basePath, url); - if(cc.game.config["noCache"] && typeof realUrl === "string"){ - if(self._noCacheRex.test(realUrl)) - realUrl += "&_t=" + (new Date() - 0); - else - realUrl += "?_t=" + (new Date() - 0); - } - loader.load(realUrl, url, item, function (err, data) { - if (err) { - cc.log(err); - self.cache[url] = null; - delete self.cache[url]; - cb(); + img.addEventListener("load", loadCallback); + img.addEventListener("error", errorCallback); + img.src = url; + return img; + }, + + /** + * Iterator function to load res + * @param {object} item + * @param {number} index + * @param {function} [cb] + * @returns {*} + * @private + */ + _loadResIterator: function (item, index, cb) { + var self = this, url = null; + var type = item.type; + if (type) { + type = "." + type.toLowerCase(); + url = item.src ? item.src : item.name + type; } else { - self.cache[url] = data; - cb(null, data); + url = item; + type = cc.path.extname(url); } - }); - }, - _noCacheRex: /\?/, - /** - * Get url with basePath. - * @param {string} basePath - * @param {string} [url] - * @returns {*} - */ - getUrl: function (basePath, url) { - var self = this, langPathCache = self._langPathCache, path = cc.path; - if (basePath !== undefined && url === undefined) { - url = basePath; - var type = path.extname(url); - type = type ? type.toLowerCase() : ""; - var loader = self._register[type]; - if (!loader) - basePath = self.resPath; - else - basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; - } - url = cc.path.join(basePath || "", url); - if (url.match(/[\/(\\\\)]lang[\/(\\\\)]/i)) { - if (langPathCache[url]) - return langPathCache[url]; - var extname = path.extname(url) || ""; - url = langPathCache[url] = url.substring(0, url.length - extname.length) + "_" + cc.sys.language + extname; - } - return url; - }, + var obj = self.getRes(url); + if (obj) + return cb(null, obj); + var loader = null; + if (type) { + loader = _register[type.toLowerCase()]; + } + if (!loader) { + cc.error("loader for [" + type + "] doesn't exist!"); + return cb(); + } + var realUrl = url; + if (!_urlRegExp.test(url)) { + var basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; + realUrl = self.getUrl(basePath, url); + } - /** - * Load resources then call the callback. - * @param {string} resources - * @param {function} [option] callback or trigger - * @param {function|Object} [loadCallback] - * @return {cc.AsyncPool} - */ - load : function(resources, option, loadCallback){ - var self = this; - var len = arguments.length; - if(len === 0) - throw "arguments error!"; - - if(len === 3){ - if(typeof option === "function"){ - if(typeof loadCallback === "function") - option = {trigger : option, cb : loadCallback }; + if (cc.game.config["noCache"] && typeof realUrl === "string") { + if (self._noCacheRex.test(realUrl)) + realUrl += "&_t=" + (new Date() - 0); else - option = { cb : option, cbTarget : loadCallback}; + realUrl += "?_t=" + (new Date() - 0); } - }else if(len === 2){ - if(typeof option === "function") - option = {cb : option}; - }else if(len === 1){ - option = {}; - } - - if(!(resources instanceof Array)) - resources = [resources]; - var asyncPool = new cc.AsyncPool( - resources, 0, - function (value, index, AsyncPoolCallback, aPool) { - self._loadResIterator(value, index, function (err) { - if (err) - return AsyncPoolCallback(err); - var arr = Array.prototype.slice.call(arguments, 1); - if (option.trigger) - option.trigger.call(option.triggerTarget, arr[0], aPool.size, aPool.finishedSize); //call trigger - AsyncPoolCallback(null, arr[0]); - }); - }, - option.cb, option.cbTarget); - asyncPool.flow(); - return asyncPool; - }, - - _handleAliases: function (fileNames, cb) { - var self = this, aliases = self._aliases; - var resList = []; - for (var key in fileNames) { - var value = fileNames[key]; - aliases[key] = value; - resList.push(value); - } - this.load(resList, cb); - }, - - /** - *

- * Loads alias map from the contents of a filename.
- *
- * @note The plist file name should follow the format below:
- *
- *
- *
- *
- * filenames
- *
- * sounds/click.wav
- * sounds/click.caf
- * sounds/endgame.wav
- * sounds/endgame.caf
- * sounds/gem-0.wav
- * sounds/gem-0.caf
- *

- * metadata
- *
- * version
- * 1
- *

- *

- *

- *

- * @param {String} url The plist file name. - * @param {Function} [callback] - */ - loadAliases: function (url, callback) { - var self = this, dict = self.getRes(url); - if (!dict) { - self.load(url, function (err, results) { - self._handleAliases(results[0]["filenames"], callback); + loader.load(realUrl, url, item, function (err, data) { + if (err) { + cc.log(err); + self.cache[url] = null; + delete self.cache[url]; + cb({status: 520, errorMessage: err}, null); + } else { + self.cache[url] = data; + cb(null, data); + } }); - } else - self._handleAliases(dict["filenames"], callback); - }, + }, + _noCacheRex: /\?/, + + /** + * Get url with basePath. + * @param {string} basePath + * @param {string} [url] + * @returns {*} + */ + getUrl: function (basePath, url) { + var self = this, path = cc.path; + if (basePath !== undefined && url === undefined) { + url = basePath; + var type = path.extname(url); + type = type ? type.toLowerCase() : ""; + var loader = _register[type]; + if (!loader) + basePath = self.resPath; + else + basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; + } + url = cc.path.join(basePath || "", url); + if (url.match(/[\/(\\\\)]lang[\/(\\\\)]/i)) { + if (_langPathCache[url]) + return _langPathCache[url]; + var extname = path.extname(url) || ""; + url = _langPathCache[url] = url.substring(0, url.length - extname.length) + "_" + cc.sys.language + extname; + } + return url; + }, + + /** + * Load resources then call the callback. + * @param {string} resources + * @param {function} [option] callback or trigger + * @param {function|Object} [loadCallback] + * @return {cc.AsyncPool} + */ + load: function (resources, option, loadCallback) { + var self = this; + var len = arguments.length; + if (len === 0) + throw new Error("arguments error!"); + + if (len === 3) { + if (typeof option === "function") { + if (typeof loadCallback === "function") + option = {trigger: option, cb: loadCallback}; + else + option = {cb: option, cbTarget: loadCallback}; + } + } else if (len === 2) { + if (typeof option === "function") + option = {cb: option}; + } else if (len === 1) { + option = {}; + } - /** - * Register a resource loader into loader. - * @param {string} extNames - * @param {function} loader - */ - register: function (extNames, loader) { - if (!extNames || !loader) return; - var self = this; - if (typeof extNames === "string") - return this._register[extNames.trim().toLowerCase()] = loader; - for (var i = 0, li = extNames.length; i < li; i++) { - self._register["." + extNames[i].trim().toLowerCase()] = loader; + if (!(resources instanceof Array)) + resources = [resources]; + var asyncPool = new cc.AsyncPool( + resources, cc.CONCURRENCY_HTTP_REQUEST_COUNT, + function (value, index, AsyncPoolCallback, aPool) { + self._loadResIterator(value, index, function (err) { + var arr = Array.prototype.slice.call(arguments, 1); + if (option.trigger) + option.trigger.call(option.triggerTarget, arr[0], aPool.size, aPool.finishedSize); //call trigger + AsyncPoolCallback(err, arr[0]); + }); + }, + option.cb, option.cbTarget); + asyncPool.flow(); + return asyncPool; + }, + + _handleAliases: function (fileNames, cb) { + var self = this; + var resList = []; + for (var key in fileNames) { + var value = fileNames[key]; + _aliases[key] = value; + resList.push(value); + } + this.load(resList, cb); + }, + + /** + *

+ * Loads alias map from the contents of a filename.
+ *
+ * @note The plist file name should follow the format below:
+ *
+ *
+ *
+ *
+ * filenames
+ *
+ * sounds/click.wav
+ * sounds/click.caf
+ * sounds/endgame.wav
+ * sounds/endgame.caf
+ * sounds/gem-0.wav
+ * sounds/gem-0.caf
+ *

+ * metadata
+ *
+ * version
+ * 1
+ *

+ *

+ *

+ *

+ * @param {String} url The plist file name. + * @param {Function} [callback] + */ + loadAliases: function (url, callback) { + var self = this, dict = self.getRes(url); + if (!dict) { + self.load(url, function (err, results) { + self._handleAliases(results[0]["filenames"], callback); + }); + } else + self._handleAliases(dict["filenames"], callback); + }, + + /** + * Register a resource loader into loader. + * @param {string} extNames + * @param {function} loader + */ + register: function (extNames, loader) { + if (!extNames || !loader) return; + var self = this; + if (typeof extNames === "string") + return _register[extNames.trim().toLowerCase()] = loader; + for (var i = 0, li = extNames.length; i < li; i++) { + _register["." + extNames[i].trim().toLowerCase()] = loader; + } + }, + + /** + * Get resource data by url. + * @param url + * @returns {*} + */ + getRes: function (url) { + return this.cache[url] || this.cache[_aliases[url]]; + }, + + /** + * Get aliase by url. + * @param url + * @returns {*} + */ + _getAliase: function (url) { + return _aliases[url]; + }, + + /** + * Release the cache of resource by url. + * @param url + */ + release: function (url) { + var cache = this.cache; + var queue = _queue[url]; + if (queue) { + queue.img = null; + delete _queue[url]; + } + delete cache[url]; + delete cache[_aliases[url]]; + delete _aliases[url]; + }, + + /** + * Resource cache of all resources. + */ + releaseAll: function () { + var locCache = this.cache; + for (var key in locCache) + delete locCache[key]; + for (var key in _aliases) + delete _aliases[key]; } - }, - - /** - * Get resource data by url. - * @param url - * @returns {*} - */ - getRes: function (url) { - return this.cache[url] || this.cache[this._aliases[url]]; - }, - - /** - * Release the cache of resource by url. - * @param url - */ - release: function (url) { - var cache = this.cache, aliases = this._aliases; - delete cache[url]; - delete cache[aliases[url]]; - delete aliases[url]; - }, - - /** - * Resource cache of all resources. - */ - releaseAll: function () { - var locCache = this.cache, aliases = this._aliases; - for (var key in locCache) - delete locCache[key]; - for (var key in aliases) - delete aliases[key]; - } -}; + }; +})(); //+++++++++++++++++++++++++something about loader end+++++++++++++++++++++++++++++ /** @@ -1039,143 +1285,65 @@ cc.loader = /** @lends cc.loader# */{ * cc.formatStr(a, b, c); * @returns {String} */ -cc.formatStr = function(){ +cc.formatStr = function () { var args = arguments; var l = args.length; - if(l < 1) + if (l < 1) return ""; var str = args[0]; var needToFormat = true; - if(typeof str === "object"){ + if (typeof str === "object") { needToFormat = false; } - for(var i = 1; i < l; ++i){ + for (var i = 1; i < l; ++i) { var arg = args[i]; - if(needToFormat){ - while(true){ + if (needToFormat) { + while (true) { var result = null; - if(typeof arg === "number"){ + if (typeof arg === "number") { result = str.match(/(%d)|(%s)/); - if(result){ + if (result) { str = str.replace(/(%d)|(%s)/, arg); break; } } result = str.match(/%s/); - if(result) + if (result) str = str.replace(/%s/, arg); else str += " " + arg; break; } - }else + } else str += " " + arg; } return str; }; -//+++++++++++++++++++++++++something about window events begin+++++++++++++++++++++++++++ +//+++++++++++++++++++++++++Engine initialization function begin+++++++++++++++++++++++++++ (function () { - var win = window, hidden, visibilityChange, _undef = "undefined"; - if (!cc.isUndefined(document.hidden)) { - hidden = "hidden"; - visibilityChange = "visibilitychange"; - } else if (!cc.isUndefined(document.mozHidden)) { - hidden = "mozHidden"; - visibilityChange = "mozvisibilitychange"; - } else if (!cc.isUndefined(document.msHidden)) { - hidden = "msHidden"; - visibilityChange = "msvisibilitychange"; - } else if (!cc.isUndefined(document.webkitHidden)) { - hidden = "webkitHidden"; - visibilityChange = "webkitvisibilitychange"; - } - - var onHidden = function () { - if (cc.eventManager && cc.game._eventHide) - cc.eventManager.dispatchEvent(cc.game._eventHide); - }; - var onShow = function () { - if (cc.eventManager && cc.game._eventShow) - cc.eventManager.dispatchEvent(cc.game._eventShow); - if(cc.game._intervalId){ - window.cancelAnimationFrame(cc.game._intervalId); +var _tmpCanvas1 = document.createElement("canvas"), + _tmpCanvas2 = document.createElement("canvas"); - cc.game._runMainLoop(); +cc.create3DContext = function (canvas, opt_attribs) { + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + var context = null; + for (var ii = 0; ii < names.length; ++ii) { + try { + context = canvas.getContext(names[ii], opt_attribs); + } catch (e) { + } + if (context) { + break; } - }; - - if (hidden) { - cc._addEventListener(document, visibilityChange, function () { - if (document[hidden]) onHidden(); - else onShow(); - }, false); - } else { - cc._addEventListener(win, "blur", onHidden, false); - cc._addEventListener(win, "focus", onShow, false); - } - - if(navigator.userAgent.indexOf("MicroMessenger") > -1){ - win.onfocus = function(){ onShow() }; } + return context; +}; - if ("onpageshow" in window && "onpagehide" in window) { - cc._addEventListener(win, "pagehide", onHidden, false); - cc._addEventListener(win, "pageshow", onShow, false); - } - win = null; - visibilityChange = null; -})(); -//+++++++++++++++++++++++++something about window events end+++++++++++++++++++++++++++++ - -//+++++++++++++++++++++++++something about log start++++++++++++++++++++++++++++ - -//to make sure the cc.log, cc.warn, cc.error and cc.assert would not throw error before init by debugger mode. - -cc.log = cc.warn = cc.error = cc.assert = function () { -}; - -//+++++++++++++++++++++++++something about log end+++++++++++++++++++++++++++++ - -/** - * create a webgl context - * @param {HTMLCanvasElement} canvas - * @param {Object} opt_attribs - * @return {WebGLRenderingContext} - */ -cc.create3DContext = function (canvas, opt_attribs) { - var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; - var context = null; - for (var ii = 0; ii < names.length; ++ii) { - try { - context = canvas.getContext(names[ii], opt_attribs); - } catch (e) { - } - if (context) { - break; - } - } - return context; -}; -//+++++++++++++++++++++++++something about sys begin+++++++++++++++++++++++++++++ -cc._initSys = function (config, CONFIG_KEY) { - /** - * Canvas of render type - * @constant - * @type {Number} - */ - cc._RENDER_TYPE_CANVAS = 0; - - /** - * WebGL of render type - * @constant - * @type {Number} - */ - cc._RENDER_TYPE_WEBGL = 1; - +var _initSys = function () { /** * System variables * @namespace @@ -1319,6 +1487,15 @@ cc._initSys = function (config, CONFIG_KEY) { */ sys.LANGUAGE_POLISH = "pl"; + /** + * Unknown language code + * @memberof cc.sys + * @name LANGUAGE_UNKNOWN + * @constant + * @type {Number} + */ + sys.LANGUAGE_UNKNOWN = "unkonwn"; + /** * @memberof cc.sys * @name OS_IOS @@ -1404,55 +1581,55 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.UNKNOWN = 0; + sys.UNKNOWN = -1; /** * @memberof cc.sys - * @name IOS + * @name WIN32 * @constant * @default * @type {Number} */ - sys.IOS = 1; + sys.WIN32 = 0; /** * @memberof cc.sys - * @name ANDROID + * @name LINUX * @constant * @default * @type {Number} */ - sys.ANDROID = 2; + sys.LINUX = 1; /** * @memberof cc.sys - * @name WIN32 + * @name MACOS * @constant * @default * @type {Number} */ - sys.WIN32 = 3; + sys.MACOS = 2; /** * @memberof cc.sys - * @name MARMALADE + * @name ANDROID * @constant * @default * @type {Number} */ - sys.MARMALADE = 4; + sys.ANDROID = 3; /** * @memberof cc.sys - * @name LINUX + * @name IOS * @constant * @default * @type {Number} */ - sys.LINUX = 5; + sys.IPHONE = 4; /** * @memberof cc.sys - * @name BADA + * @name IOS * @constant * @default * @type {Number} */ - sys.BADA = 6; + sys.IPAD = 5; /** * @memberof cc.sys * @name BLACKBERRY @@ -1460,15 +1637,7 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.BLACKBERRY = 7; - /** - * @memberof cc.sys - * @name MACOS - * @constant - * @default - * @type {Number} - */ - sys.MACOS = 8; + sys.BLACKBERRY = 6; /** * @memberof cc.sys * @name NACL @@ -1476,7 +1645,7 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.NACL = 9; + sys.NACL = 7; /** * @memberof cc.sys * @name EMSCRIPTEN @@ -1484,7 +1653,7 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.EMSCRIPTEN = 10; + sys.EMSCRIPTEN = 8; /** * @memberof cc.sys * @name TIZEN @@ -1492,15 +1661,15 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.TIZEN = 11; + sys.TIZEN = 9; /** * @memberof cc.sys - * @name QT5 + * @name WINRT * @constant * @default * @type {Number} */ - sys.QT5 = 12; + sys.WINRT = 10; /** * @memberof cc.sys * @name WP8 @@ -1508,15 +1677,7 @@ cc._initSys = function (config, CONFIG_KEY) { * @default * @type {Number} */ - sys.WP8 = 13; - /** - * @memberof cc.sys - * @name WINRT - * @constant - * @default - * @type {Number} - */ - sys.WINRT = 14; + sys.WP8 = 11; /** * @memberof cc.sys * @name MOBILE_BROWSER @@ -1537,6 +1698,7 @@ cc._initSys = function (config, CONFIG_KEY) { sys.BROWSER_TYPE_WECHAT = "wechat"; sys.BROWSER_TYPE_ANDROID = "androidbrowser"; sys.BROWSER_TYPE_IE = "ie"; + sys.BROWSER_TYPE_QQ_APP = "qq"; // QQ App sys.BROWSER_TYPE_QQ = "qqbrowser"; sys.BROWSER_TYPE_MOBILE_QQ = "mqqbrowser"; sys.BROWSER_TYPE_UC = "ucbrowser"; @@ -1572,7 +1734,7 @@ cc._initSys = function (config, CONFIG_KEY) { * @name isMobile * @type {Boolean} */ - sys.isMobile = ua.indexOf('mobile') !== -1 || ua.indexOf('android') !== -1; + sys.isMobile = /mobile|android|iphone|ipad/.test(ua); /** * Indicate the running platform @@ -1594,31 +1756,26 @@ cc._initSys = function (config, CONFIG_KEY) { */ sys.language = currLanguage; - var browserType = sys.BROWSER_TYPE_UNKNOWN; - var browserTypes = ua.match(/sogou|qzone|liebao|micromessenger|qqbrowser|ucbrowser|360 aphone|360browser|baiduboxapp|baidubrowser|maxthon|trident|oupeng|opera|miuibrowser|firefox/i) - || ua.match(/chrome|safari/i); - if (browserTypes && browserTypes.length > 0) { - browserType = browserTypes[0]; - if (browserType === 'micromessenger') { - browserType = sys.BROWSER_TYPE_WECHAT; - } else if (browserType === "safari" && (ua.match(/android.*applewebkit/))) - browserType = sys.BROWSER_TYPE_ANDROID; - else if (browserType === "trident") browserType = sys.BROWSER_TYPE_IE; - else if (browserType === "360 aphone") browserType = sys.BROWSER_TYPE_360; - }else if(ua.indexOf("iphone") && ua.indexOf("mobile")){ - browserType = "safari"; + // Get the os of system + var isAndroid = false, iOS = false, osVersion = '', osMainVersion = 0; + var uaResult = /android (\d+(?:\.\d+)+)/i.exec(ua) || /android (\d+(?:\.\d+)+)/i.exec(nav.platform); + if (uaResult) { + isAndroid = true; + osVersion = uaResult[1] || ''; + osMainVersion = parseInt(osVersion) || 0; + } + uaResult = /(iPad|iPhone|iPod).*OS ((\d+_?){2,3})/i.exec(ua); + if (uaResult) { + iOS = true; + osVersion = uaResult[2] || ''; + osMainVersion = parseInt(osVersion) || 0; + } + else if (/(iPhone|iPad|iPod)/.exec(nav.platform)) { + iOS = true; + osVersion = ''; + osMainVersion = 0; } - /** - * Indicate the running browser type - * @memberof cc.sys - * @name browserType - * @type {String} - */ - sys.browserType = browserType; - // Get the os of system - var iOS = ( ua.match(/(iPad|iPhone|iPod)/i) ? true : false ); - var isAndroid = ua.match(/android/i) || nav.platform.match(/android/i) ? true : false; var osName = sys.OS_UNKNOWN; if (nav.appVersion.indexOf("Win") !== -1) osName = sys.OS_WINDOWS; else if (iOS) osName = sys.OS_IOS; @@ -1634,78 +1791,116 @@ cc._initSys = function (config, CONFIG_KEY) { * @type {String} */ sys.os = osName; + /** + * Indicate the running os version string + * @memberof cc.sys + * @name osVersion + * @type {String} + */ + sys.osVersion = osVersion; + /** + * Indicate the running os main version number + * @memberof cc.sys + * @name osMainVersion + * @type {Number} + */ + sys.osMainVersion = osMainVersion; - var multipleAudioWhiteList = [ - sys.BROWSER_TYPE_BAIDU, sys.BROWSER_TYPE_OPERA, sys.BROWSER_TYPE_FIREFOX, sys.BROWSER_TYPE_CHROME, sys.BROWSER_TYPE_BAIDU_APP, - sys.BROWSER_TYPE_SAFARI, sys.BROWSER_TYPE_UC, sys.BROWSER_TYPE_QQ, sys.BROWSER_TYPE_MOBILE_QQ, sys.BROWSER_TYPE_IE - ]; + /** + * Indicate the running browser type + * @memberof cc.sys + * @name browserType + * @type {String} + */ + sys.browserType = sys.BROWSER_TYPE_UNKNOWN; + /* Determine the browser type */ + (function(){ + var typeReg1 = /micromessenger|mqqbrowser|sogou|qzone|liebao|ucbrowser|360 aphone|360browser|baiduboxapp|baidubrowser|maxthon|mxbrowser|trident|miuibrowser/i; + var typeReg2 = /qqbrowser|qq|chrome|safari|firefox|opr|oupeng|opera/i; + var browserTypes = typeReg1.exec(ua); + if(!browserTypes) browserTypes = typeReg2.exec(ua); + var browserType = browserTypes ? browserTypes[0] : sys.BROWSER_TYPE_UNKNOWN; + if (browserType === 'micromessenger') + browserType = sys.BROWSER_TYPE_WECHAT; + else if (browserType === "safari" && isAndroid) + browserType = sys.BROWSER_TYPE_ANDROID; + else if (browserType === "trident") + browserType = sys.BROWSER_TYPE_IE; + else if (browserType === "360 aphone") + browserType = sys.BROWSER_TYPE_360; + else if (browserType === "mxbrowser") + browserType = sys.BROWSER_TYPE_MAXTHON; + else if (browserType === "opr") + browserType = sys.BROWSER_TYPE_OPERA; - sys._supportMultipleAudio = multipleAudioWhiteList.indexOf(sys.browserType) > -1; + sys.browserType = browserType; + })(); + /** + * Indicate the running browser version + * @memberof cc.sys + * @name browserVersion + * @type {String} + */ + sys.browserVersion = ""; + /* Determine the browser version number */ + (function(){ + var versionReg1 = /(mqqbrowser|micromessenger|sogou|qzone|liebao|maxthon|mxbrowser|baidu)(mobile)?(browser)?\/?([\d.]+)/i; + var versionReg2 = /(msie |rv:|firefox|chrome|ucbrowser|qq|oupeng|opera|opr|safari|miui)(mobile)?(browser)?\/?([\d.]+)/i; + var tmp = ua.match(versionReg1); + if(!tmp) tmp = ua.match(versionReg2); + sys.browserVersion = tmp ? tmp[4] : ""; + })(); - //++++++++++++++++++something about cc._renderTYpe and cc._supportRender begin++++++++++++++++++++++++++++ + var w = window.innerWidth || document.documentElement.clientWidth; + var h = window.innerHeight || document.documentElement.clientHeight; + var ratio = window.devicePixelRatio || 1; - (function(sys, config){ - var userRenderMode = config[CONFIG_KEY.renderMode] - 0; - if(isNaN(userRenderMode) || userRenderMode > 2 || userRenderMode < 0) - userRenderMode = 0; - var shieldOs = [sys.OS_ANDROID]; - var shieldBrowser = []; - var tmpCanvas = cc.newElement("canvas"); - cc._renderType = cc._RENDER_TYPE_CANVAS; - cc._supportRender = false; + /** + * Indicate the real pixel resolution of the whole game window + * @memberof cc.sys + * @name windowPixelResolution + * @type {Size} + */ + sys.windowPixelResolution = { + width: ratio * w, + height: ratio * h + }; - var supportWebGL = win.WebGLRenderingContext; + sys._checkWebGLRenderMode = function () { + if (cc._renderType !== cc.game.RENDER_TYPE_WEBGL) + throw new Error("This feature supports WebGL render mode only."); + }; - if(userRenderMode === 2 || (userRenderMode === 0 && supportWebGL && shieldOs.indexOf(sys.os) === -1 && shieldBrowser.indexOf(sys.browserType) === -1)) - try{ - var context = cc.create3DContext(tmpCanvas, {'stencil': true, 'preserveDrawingBuffer': true }); - if(context){ - cc._renderType = cc._RENDER_TYPE_WEBGL; - cc._supportRender = true; - } - }catch(e){} - - if(userRenderMode === 1 || (userRenderMode === 0 && cc._supportRender === false)) - try { - tmpCanvas.getContext("2d"); - cc._renderType = cc._RENDER_TYPE_CANVAS; - cc._supportRender = true; - } catch (e) {} - })(sys, config); - - sys._canUseCanvasNewBlendModes = function(){ - var canvas = document.createElement('canvas'); + //Whether or not the Canvas BlendModes are supported. + sys._supportCanvasNewBlendModes = (function(){ + var canvas = _tmpCanvas1; canvas.width = 1; canvas.height = 1; var context = canvas.getContext('2d'); context.fillStyle = '#000'; - context.fillRect(0,0,1,1); + context.fillRect(0, 0, 1, 1); context.globalCompositeOperation = 'multiply'; - var canvas2 = document.createElement('canvas'); + var canvas2 = _tmpCanvas2; canvas2.width = 1; canvas2.height = 1; var context2 = canvas2.getContext('2d'); context2.fillStyle = '#fff'; - context2.fillRect(0,0,1,1); - + context2.fillRect(0, 0, 1, 1); context.drawImage(canvas2, 0, 0, 1, 1); - return context.getImageData(0,0,1,1).data[0] === 0; - }; - - //Whether or not the Canvas BlendModes are supported. - sys._supportCanvasNewBlendModes = sys._canUseCanvasNewBlendModes(); + return context.getImageData(0, 0, 1, 1).data[0] === 0; + })(); - //++++++++++++++++++something about cc._renderType and cc._supportRender end++++++++++++++++++++++++++++++ + // Adjust mobile css settings + if (cc.sys.isMobile) { + var fontStyle = document.createElement("style"); + fontStyle.type = "text/css"; + document.body.appendChild(fontStyle); - // check if browser supports Web Audio - // check Web Audio's context - try { - sys._supportWebAudio = !!(win.AudioContext || win.webkitAudioContext || win.mozAudioContext); - } catch (e) { - sys._supportWebAudio = false; + fontStyle.textContent = "body,canvas,div{ -moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;-khtml-user-select: none;" + + "-webkit-tap-highlight-color:rgba(0,0,0,0);}"; } /** @@ -1720,16 +1915,84 @@ cc._initSys = function (config, CONFIG_KEY) { localStorage.removeItem("storage"); localStorage = null; } catch (e) { - if (e.name === "SECURITY_ERR" || e.name === "QuotaExceededError") { + var warn = function () { cc.warn("Warning: localStorage isn't enabled. Please confirm browser cookie or privacy option"); - } - sys.localStorage = function () { + }; + sys.localStorage = { + getItem : warn, + setItem : warn, + removeItem : warn, + clear : warn }; } - var capabilities = sys.capabilities = {"canvas": true}; - if (cc._renderType === cc._RENDER_TYPE_WEBGL) - capabilities["opengl"] = true; + var _supportCanvas = !!_tmpCanvas1.getContext("2d"); + var _supportWebGL = false; + if (win.WebGLRenderingContext) { + var tmpCanvas = document.createElement("CANVAS"); + try{ + var context = cc.create3DContext(tmpCanvas); + if (context) { + _supportWebGL = true; + } + + if (_supportWebGL && sys.os === sys.OS_IOS && sys.osMainVersion === 9) { + // Not activating WebGL in iOS 9 UIWebView because it may crash when entering background + if (!window.indexedDB) { + _supportWebGL = false; + } + } + + if (_supportWebGL && sys.os === sys.OS_ANDROID) { + var browserVer = parseFloat(sys.browserVersion); + switch (sys.browserType) { + case sys.BROWSER_TYPE_MOBILE_QQ: + case sys.BROWSER_TYPE_BAIDU: + case sys.BROWSER_TYPE_BAIDU_APP: + // QQ & Baidu Brwoser 6.2+ (using blink kernel) + if (browserVer >= 6.2) { + _supportWebGL = true; + } + else { + _supportWebGL = false; + } + break; + case sys.BROWSER_TYPE_CHROME: + // Chrome on android supports WebGL from v.30 + if(browserVer >= 30.0) { + _supportWebGL = true; + } else { + _supportWebGL = false; + } + break; + case sys.BROWSER_TYPE_ANDROID: + // Android 5+ default browser + if (sys.osMainVersion && sys.osMainVersion >= 5) { + _supportWebGL = true; + } + break; + case sys.BROWSER_TYPE_UNKNOWN: + case sys.BROWSER_TYPE_360: + case sys.BROWSER_TYPE_MIUI: + case sys.BROWSER_TYPE_UC: + _supportWebGL = false; + } + } + } + catch (e) {} + tmpCanvas = null; + } + + /** + * The capabilities of the current platform + * @memberof cc.sys + * @name capabilities + * @type {Object} + */ + var capabilities = sys.capabilities = { + "canvas": _supportCanvas, + "opengl": _supportWebGL + }; if (docEle['ontouchstart'] !== undefined || doc['ontouchstart'] !== undefined || nav.msPointerEnabled) capabilities["touches"] = true; if (docEle['onmouseup'] !== undefined) @@ -1807,11 +2070,14 @@ cc._initSys = function (config, CONFIG_KEY) { str += "isMobile : " + self.isMobile + "\r\n"; str += "language : " + self.language + "\r\n"; str += "browserType : " + self.browserType + "\r\n"; + str += "browserVersion : " + self.browserVersion + "\r\n"; str += "capabilities : " + JSON.stringify(self.capabilities) + "\r\n"; str += "os : " + self.os + "\r\n"; + str += "osVersion : " + self.osVersion + "\r\n"; str += "platform : " + self.platform + "\r\n"; + str += "Using " + (cc._renderType === cc.game.RENDER_TYPE_WEBGL ? "WEBGL" : "CANVAS") + " renderer." + "\r\n"; cc.log(str); - } + }; /** * Open a url in browser @@ -1821,264 +2087,307 @@ cc._initSys = function (config, CONFIG_KEY) { */ sys.openURL = function(url){ window.open(url); - } -}; - -//+++++++++++++++++++++++++something about sys end+++++++++++++++++++++++++++++ - -//+++++++++++++++++++++++++something about CCGame begin+++++++++++++++++++++++++++ + }; -/** - * Device oriented vertically, home button on the bottom - * @constant - * @type {Number} - */ -cc.ORIENTATION_PORTRAIT = 0; + /** + * Get the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC. + * @memberof cc.sys + * @name now + * @return {Number} + */ + sys.now = function () { + if (Date.now) { + return Date.now(); + } + else { + return +(new Date); + } + }; +}; +_initSys(); -/** - * Device oriented vertically, home button on the top - * @constant - * @type {Number} - */ -cc.ORIENTATION_PORTRAIT_UPSIDE_DOWN = 1; +_tmpCanvas1 = null; +_tmpCanvas2 = null; -/** - * Device oriented horizontally, home button on the right - * @constant - * @type {Number} - */ -cc.ORIENTATION_LANDSCAPE_LEFT = 2; +//to make sure the cc.log, cc.warn, cc.error and cc.assert would not throw error before init by debugger mode. +cc.log = cc.warn = cc.error = cc.assert = function () { +}; -/** - * Device oriented horizontally, home button on the left - * @constant - * @type {Number} - */ -cc.ORIENTATION_LANDSCAPE_RIGHT = 3; +var _config = null, + //cache for js and module that has added into jsList to be loaded. + _jsAddedCache = {}, + _engineInitCalled = false, + _engineLoadedCallback = null; -/** - * drawing primitive of game engine - * @type {cc.DrawingPrimitive} - */ -cc._drawingUtil = null; +cc._engineLoaded = false; -/** - * main Canvas 2D/3D Context of game engine - * @type {CanvasRenderingContext2D|WebGLRenderingContext} - */ -cc._renderContext = null; +function _determineRenderType(config) { + var CONFIG_KEY = cc.game.CONFIG_KEY, + userRenderMode = parseInt(config[CONFIG_KEY.renderMode]) || 0; -/** - * main Canvas of game engine - * @type {HTMLCanvasElement} - */ -cc._canvas = null; + // Adjust RenderType + if (isNaN(userRenderMode) || userRenderMode > 2 || userRenderMode < 0) + config[CONFIG_KEY.renderMode] = 0; -/** - * This Div element contain all game canvas - * @type {HTMLDivElement} - */ -cc._gameDiv = null; + // Determine RenderType + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = false; -cc._rendererInitialized = false; -/** - *

- * setup game main canvas,renderContext,gameDiv and drawingUtil with argument
- *
- * can receive follow type of arguemnt:
- * - empty: create a canvas append to document's body, and setup other option
- * - string: search the element by document.getElementById(),
- * if this element is HTMLCanvasElement, set this element as main canvas of engine, and set it's ParentNode as cc._gameDiv.
- * if this element is HTMLDivElement, set it's ParentNode to cc._gameDiv, and create a canvas as main canvas of engine.
- *

- * @function - * @example - * //setup with null - * cc._setup(); - * - * // setup with HTMLCanvasElement, gameCanvas is Canvas element - * // declare like this: - * cc._setup("gameCanvas"); - * - * //setup with HTMLDivElement, gameDiv is Div element - * // declare like this:
- * cc._setup("Cocos2dGameContainer"); - */ -cc._setupCalled = false; -cc._setup = function (el, width, height) { - // Avoid setup to be called twice. - if (cc._setupCalled) return; - else cc._setupCalled = true; - var win = window; - var element = cc.$(el) || cc.$('#' + el); - var localCanvas, localContainer, localConStyle; - - cc.game._setAnimFrame(); - - if (element.tagName === "CANVAS") { - width = width || element.width; - height = height || element.height; - - //it is already a canvas, we wrap it around with a div - localContainer = cc.container = cc.newElement("DIV"); - localCanvas = cc._canvas = element; - localCanvas.parentNode.insertBefore(localContainer, localCanvas); - localCanvas.appendTo(localContainer); - localContainer.setAttribute('id', 'Cocos2dGameContainer'); - } else {//we must make a new canvas and place into this element - if (element.tagName !== "DIV") { - cc.log("Warning: target element is not a DIV or CANVAS"); + if (userRenderMode === 0) { + if (cc.sys.capabilities["opengl"]) { + cc._renderType = cc.game.RENDER_TYPE_WEBGL; + cc._supportRender = true; } - width = width || element.clientWidth; - height = height || element.clientHeight; - localContainer = cc.container = element; - localCanvas = cc._canvas = cc.$(cc.newElement("CANVAS")); - element.appendChild(localCanvas); + else if (cc.sys.capabilities["canvas"]) { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = true; + } + } + else if (userRenderMode === 1 && cc.sys.capabilities["canvas"]) { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = true; + } + else if (userRenderMode === 2 && cc.sys.capabilities["opengl"]) { + cc._renderType = cc.game.RENDER_TYPE_WEBGL; + cc._supportRender = true; + } +} + +function _getJsListOfModule(moduleMap, moduleName, dir) { + if (_jsAddedCache[moduleName]) return null; + dir = dir || ""; + var jsList = []; + var tempList = moduleMap[moduleName]; + if (!tempList) throw new Error("can not find module [" + moduleName + "]"); + var ccPath = cc.path; + for (var i = 0, li = tempList.length; i < li; i++) { + var item = tempList[i]; + if (_jsAddedCache[item]) continue; + var extname = ccPath.extname(item); + if (!extname) { + var arr = _getJsListOfModule(moduleMap, item, dir); + if (arr) jsList = jsList.concat(arr); + } else if (extname.toLowerCase() === ".js") jsList.push(ccPath.join(dir, item)); + _jsAddedCache[item] = 1; } + return jsList; +} + +function _afterEngineLoaded(config) { + if (cc._initDebugSetting) + cc._initDebugSetting(config[cc.game.CONFIG_KEY.debugMode]); + cc._engineLoaded = true; + console.log(cc.ENGINE_VERSION); + if (_engineLoadedCallback) _engineLoadedCallback(); +} + +function _load(config) { + var self = this; + var CONFIG_KEY = cc.game.CONFIG_KEY, engineDir = config[CONFIG_KEY.engineDir], loader = cc.loader; - localCanvas.addClass("gameCanvas"); - localCanvas.setAttribute("width", width || 480); - localCanvas.setAttribute("height", height || 320); - localCanvas.setAttribute("tabindex", 99); - localCanvas.style.outline = "none"; - localConStyle = localContainer.style; - localConStyle.width = (width || 480) + "px"; - localConStyle.height = (height || 320) + "px"; - localConStyle.margin = "0 auto"; - - localConStyle.position = 'relative'; - localConStyle.overflow = 'hidden'; - localContainer.top = '100%'; - - if (cc._renderType === cc._RENDER_TYPE_WEBGL) - cc._renderContext = cc.webglContext = cc.create3DContext(localCanvas, { - 'stencil': true, - 'preserveDrawingBuffer': true, - 'antialias': !cc.sys.isMobile, - 'alpha': false - }); - if (cc._renderContext) { - win.gl = cc._renderContext; // global variable declared in CCMacro.js - cc._drawingUtil = new cc.DrawingPrimitiveWebGL(cc._renderContext); - cc._rendererInitialized = true; - cc.textureCache._initializingRenderer(); - cc.shaderCache._init(); + if (cc.Class) { + // Single file loaded + _afterEngineLoaded(config); } else { - cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d")); - cc._drawingUtil = cc.DrawingPrimitiveCanvas ? new cc.DrawingPrimitiveCanvas(cc._renderContext) : null; + // Load cocos modules + var ccModulesPath = cc.path.join(engineDir, "moduleConfig.json"); + loader.loadJson(ccModulesPath, function (err, modulesJson) { + if (err) throw new Error(err); + var modules = config["modules"] || []; + var moduleMap = modulesJson["module"]; + var jsList = []; + if (cc.sys.capabilities["opengl"] && modules.indexOf("base4webgl") < 0) modules.splice(0, 0, "base4webgl"); + else if (modules.indexOf("core") < 0) modules.splice(0, 0, "core"); + for (var i = 0, li = modules.length; i < li; i++) { + var arr = _getJsListOfModule(moduleMap, modules[i], engineDir); + if (arr) jsList = jsList.concat(arr); + } + cc.loader.loadJsWithImg(jsList, function (err) { + if (err) throw err; + _afterEngineLoaded(config); + }); + }); } - - cc._gameDiv = localContainer; - cc.log(cc.ENGINE_VERSION); - cc._setContextMenuEnable(false); - - if (cc.sys.isMobile) { - var fontStyle = cc.newElement("style"); - fontStyle.type = "text/css"; - document.body.appendChild(fontStyle); - - fontStyle.textContent = "body,canvas,div{ -moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;-khtml-user-select: none;" - + "-webkit-tap-highlight-color:rgba(0,0,0,0);}"; +} + +function _windowLoaded() { + this.removeEventListener('load', _windowLoaded, false); + _load(cc.game.config); +} + +cc.initEngine = function (config, cb) { + if (_engineInitCalled) { + var previousCallback = _engineLoadedCallback; + _engineLoadedCallback = function () { + previousCallback && previousCallback(); + cb && cb(); + } + return; } - // Init singletons + _engineLoadedCallback = cb; - /** - * @type {cc.EGLView} - * @name cc.view - * cc.view is the shared view object. - */ - cc.view = cc.EGLView._getInstance(); - // register system events - cc.inputManager.registerSystemEvent(cc._canvas); - - /** - * @type {cc.Director} - * @name cc.director - */ - cc.director = cc.Director._getInstance(); - if (cc.director.setOpenGLView) - cc.director.setOpenGLView(cc.view); - /** - * @type {cc.Size} - * @name cc.winSize - * cc.winSize is the alias object for the size of the current game window. - */ - cc.winSize = cc.director.getWinSize(); + // Config uninitialized and given, initialize with it + if (!cc.game.config && config) { + cc.game.config = config; + } + // No config given and no config set before, load it + else if (!cc.game.config) { + cc.game._loadConfig(); + } + config = cc.game.config; - // Parsers - cc.saxParser = new cc.SAXParser(); - /** - * @type {cc.PlistParser} - * @name cc.plistParser - * A Plist Parser - */ - cc.plistParser = new cc.PlistParser(); -}; + _determineRenderType(config); -cc._checkWebGLRenderMode = function () { - if (cc._renderType !== cc._RENDER_TYPE_WEBGL) - throw "This feature supports WebGL render mode only."; + document.body ? _load(config) : cc._addEventListener(window, 'load', _windowLoaded, false); + _engineInitCalled = true; }; -cc._isContextMenuEnable = false; -/** - * enable/disable contextMenu for Canvas - * @param {Boolean} enabled - */ -cc._setContextMenuEnable = function (enabled) { - cc._isContextMenuEnable = enabled; - cc._canvas.oncontextmenu = function () { - if (!cc._isContextMenuEnable) return false; - }; -}; +})(); +//+++++++++++++++++++++++++Engine initialization function end+++++++++++++++++++++++++++++ +//+++++++++++++++++++++++++something about CCGame begin+++++++++++++++++++++++++++ /** * An object to boot the game. * @class * @name cc.game + * */ cc.game = /** @lends cc.game# */{ + /** + * Debug mode: No debugging. {@static} + * @const {Number} + * @static + */ DEBUG_MODE_NONE: 0, + /** + * Debug mode: Info, warning, error to console. + * @const {Number} + * @static + */ DEBUG_MODE_INFO: 1, + /** + * Debug mode: Warning, error to console. + * @const {Number} + * @static + */ DEBUG_MODE_WARN: 2, + /** + * Debug mode: Error to console. + * @const {Number} + * @static + */ DEBUG_MODE_ERROR: 3, + /** + * Debug mode: Info, warning, error to web page. + * @const {Number} + * @static + */ DEBUG_MODE_INFO_FOR_WEB_PAGE: 4, + /** + * Debug mode: Warning, error to web page. + * @const {Number} + * @static + */ DEBUG_MODE_WARN_FOR_WEB_PAGE: 5, + /** + * Debug mode: Error to web page. + * @const {Number} + * @static + */ DEBUG_MODE_ERROR_FOR_WEB_PAGE: 6, + /** + * Event that is fired when the game is hidden. + * @constant {String} + */ EVENT_HIDE: "game_on_hide", + /** + * Event that is fired when the game is shown. + * @constant {String} + */ EVENT_SHOW: "game_on_show", + /** + * Event that is fired when the game is resized. + * @constant {String} + */ + EVENT_RESIZE: "game_on_resize", + /** + * Event that is fired when the renderer is done being initialized. + * @constant {String} + */ + EVENT_RENDERER_INITED: "renderer_inited", + + /** @constant {Number} */ + RENDER_TYPE_CANVAS: 0, + /** @constant {Number} */ + RENDER_TYPE_WEBGL: 1, + /** @constant {Number} */ + RENDER_TYPE_OPENGL: 2, + _eventHide: null, _eventShow: null, - _onBeforeStartArr: [], /** - * Key of config + * Keys found in project.json. + * * @constant * @type {Object} + * + * @prop {String} engineDir - In debug mode, if you use the whole engine to develop your game, you should specify its relative path with "engineDir". + * @prop {String} modules - Defines which modules you will need in your game, it's useful only on web + * @prop {String} debugMode - Debug mode, see DEBUG_MODE_XXX constant definitions. + * @prop {String} exposeClassName - Expose class name to chrome debug tools + * @prop {String} showFPS - Left bottom corner fps information will show when "showFPS" equals true, otherwise it will be hide. + * @prop {String} frameRate - Sets the wanted frame rate for your game, but the real fps depends on your game implementation and the running environment. + * @prop {String} id - Sets the id of your canvas element on the web page, it's useful only on web. + * @prop {String} renderMode - Sets the renderer type, only useful on web, 0: Automatic, 1: Canvas, 2: WebGL + * @prop {String} jsList - Sets the list of js files in your game. */ CONFIG_KEY: { + width: "width", + height: "height", engineDir: "engineDir", - dependencies: "dependencies", + modules: "modules", debugMode: "debugMode", + exposeClassName: "exposeClassName", showFPS: "showFPS", frameRate: "frameRate", id: "id", renderMode: "renderMode", - jsList: "jsList", - classReleaseMode: "classReleaseMode" + jsList: "jsList" }, + // states + _paused: true,//whether the game is paused + _configLoaded: false,//whether config loaded _prepareCalled: false,//whether the prepare function has been called _prepared: false,//whether the engine has prepared - _paused: true,//whether the game is paused + _rendererInitialized: false, + + _renderContext: null, _intervalId: null,//interval target of main - + _lastTime: null, _frameTime: null, + /** + * The outer frame of the game canvas, parent of cc.container + * @type {Object} + */ + frame: null, + /** + * The container of game canvas, equals to cc.container + * @type {Object} + */ + container: null, + /** + * The canvas of the game, equals to cc._canvas + * @type {Object} + */ + canvas: null, + /** * Config of game * @type {Object} @@ -2087,16 +2396,19 @@ cc.game = /** @lends cc.game# */{ /** * Callback when the scripts of engine have been load. - * @type {Function} + * @type {Function|null} */ onStart: null, /** * Callback when game exits. - * @type {Function} + * @type {Function|null} */ onStop: null, +//@Public Methods + +// @Game play control /** * Set frameRate of game. * @param frameRate @@ -2106,14 +2418,192 @@ cc.game = /** @lends cc.game# */{ config[CONFIG_KEY.frameRate] = frameRate; if (self._intervalId) window.cancelAnimationFrame(self._intervalId); + self._intervalId = 0; self._paused = true; self._setAnimFrame(); self._runMainLoop(); }, + + /** + * Run the game frame by frame. + */ + step: function () { + cc.director.mainLoop(); + }, + + /** + * Pause the game. + */ + pause: function () { + if (this._paused) return; + this._paused = true; + // Pause audio engine + if (cc.audioEngine) { + cc.audioEngine._pausePlaying(); + } + // Pause main loop + if (this._intervalId) + window.cancelAnimationFrame(this._intervalId); + this._intervalId = 0; + }, + + /** + * Resume the game from pause. + */ + resume: function () { + if (!this._paused) return; + this._paused = false; + // Resume audio engine + if (cc.audioEngine) { + cc.audioEngine._resumePlaying(); + } + // Resume main loop + this._runMainLoop(); + }, + + /** + * Check whether the game is paused. + */ + isPaused: function () { + return this._paused; + }, + + /** + * Restart game. + */ + restart: function () { + cc.director.popToSceneStackLevel(0); + // Clean up audio + cc.audioEngine && cc.audioEngine.end(); + + cc.game.onStart(); + }, + + /** + * End game, it will close the game window + */ + end: function () { + close(); + }, + +// @Game loading + /** + * Prepare game. + * @param cb + */ + prepare: function (cb) { + var self = this, + config = self.config, + CONFIG_KEY = self.CONFIG_KEY; + + // Config loaded + if (!this._configLoaded) { + this._loadConfig(function () { + self.prepare(cb); + }); + return; + } + + // Already prepared + if (this._prepared) { + if (cb) cb(); + return; + } + // Prepare called, but not done yet + if (this._prepareCalled) { + return; + } + // Prepare never called and engine ready + if (cc._engineLoaded) { + this._prepareCalled = true; + + this._initRenderer(config[CONFIG_KEY.width], config[CONFIG_KEY.height]); + + /** + * cc.view is the shared view object. + * @type {cc.EGLView} + * @name cc.view + * @memberof cc + */ + cc.view = cc.EGLView._getInstance(); + + /** + * @type {cc.Director} + * @name cc.director + * @memberof cc + */ + cc.director = cc.Director._getInstance(); + if (cc.director.setOpenGLView) + cc.director.setOpenGLView(cc.view); + /** + * cc.winSize is the alias object for the size of the current game window. + * @type {cc.Size} + * @name cc.winSize + * @memberof cc + */ + cc.winSize = cc.director.getWinSize(); + + this._initEvents(); + + this._setAnimFrame(); + this._runMainLoop(); + + // Load game scripts + var jsList = config[CONFIG_KEY.jsList]; + if (jsList) { + cc.loader.loadJsWithImg(jsList, function (err) { + if (err) throw new Error(err); + self._prepared = true; + if (cb) cb(); + }); + } + else { + if (cb) cb(); + } + + return; + } + + // Engine not loaded yet + cc.initEngine(this.config, function () { + self.prepare(cb); + }); + }, + + /** + * Run game with configuration object and onStart function. + * @param {Object|Function} [config] Pass configuration object or onStart function + * @param {onStart} [onStart] onStart function to be executed after game initialized + */ + run: function (config, onStart) { + if (typeof config === 'function') { + cc.game.onStart = config; + } + else { + if (config) { + if (typeof config === 'string') { + if (!cc.game.config) this._loadConfig(); + cc.game.config[cc.game.CONFIG_KEY.id] = config; + } else { + cc.game.config = config; + } + } + if (typeof onStart === 'function') { + cc.game.onStart = onStart; + } + } + + this.prepare(cc.game.onStart && cc.game.onStart.bind(cc.game)); + }, + +//@Private Methods + +// @Time ticker section _setAnimFrame: function () { this._lastTime = new Date(); - this._frameTime = 1000 / cc.game.config[cc.game.CONFIG_KEY.frameRate]; - if((cc.sys.os === cc.sys.OS_IOS && cc.sys.browserType === cc.sys.BROWSER_TYPE_WECHAT) || cc.game.config[cc.game.CONFIG_KEY.frameRate] !== 60) { + var frameRate = cc.game.config[cc.game.CONFIG_KEY.frameRate]; + this._frameTime = 1000 / frameRate; + if (frameRate !== 60 && frameRate !== 30) { window.requestAnimFrame = this._stTime; window.cancelAnimationFrame = this._ctTime; } @@ -2137,7 +2627,7 @@ cc.game = /** @lends cc.game# */{ this._ctTime; } }, - _stTime: function(callback){ + _stTime: function (callback) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, cc.game._frameTime - (currTime - cc.game._lastTime)); var id = window.setTimeout(function() { callback(); }, @@ -2145,188 +2635,244 @@ cc.game = /** @lends cc.game# */{ cc.game._lastTime = currTime + timeToCall; return id; }, - _ctTime: function(id){ + _ctTime: function (id) { window.clearTimeout(id); }, //Run game. _runMainLoop: function () { var self = this, callback, config = self.config, CONFIG_KEY = self.CONFIG_KEY, - director = cc.director; + director = cc.director, + skip = true, frameRate = config[CONFIG_KEY.frameRate]; director.setDisplayStats(config[CONFIG_KEY.showFPS]); callback = function () { if (!self._paused) { + if (frameRate === 30) { + if (skip = !skip) { + self._intervalId = window.requestAnimFrame(callback); + return; + } + } + director.mainLoop(); - if(self._intervalId) - window.cancelAnimationFrame(self._intervalId); self._intervalId = window.requestAnimFrame(callback); } }; - window.requestAnimFrame(callback); + self._intervalId = window.requestAnimFrame(callback); self._paused = false; }, - /** - * Restart game. - */ - restart: function () { - cc.director.popToSceneStackLevel(0); - // Clean up audio - cc.audioEngine && cc.audioEngine.end(); - - cc.game.onStart(); - }, - - /** - * Run game. - */ - run: function (id) { - var self = this; - var _run = function () { - if (id) { - self.config[self.CONFIG_KEY.id] = id; +// @Game loading section + _loadConfig: function (cb) { + // Load config + var config = this.config || document["ccConfig"]; + // Already loaded or Load from document.ccConfig + if (config) { + this._initConfig(config); + cb && cb(); + } + // Load from project.json + else { + var cocos_script = document.getElementsByTagName('script'); + for (var i = 0; i < cocos_script.length; i++) { + var _t = cocos_script[i].getAttribute('cocos'); + if (_t === '' || _t) { + break; + } } - if (!self._prepareCalled) { - self.prepare(function () { - self._prepared = true; - }); + var self = this; + var loaded = function (err, txt) { + var data = JSON.parse(txt); + self._initConfig(data); + cb && cb(); + }; + var _src, txt, _resPath; + if (i < cocos_script.length) { + _src = cocos_script[i].src; + if (_src) { + _resPath = /(.*)\//.exec(_src)[0]; + cc.loader.resPath = _resPath; + _src = cc.path.join(_resPath, 'project.json'); + } + cc.loader.loadTxt(_src, loaded); } - if (cc._supportRender) { - self._checkPrepare = setInterval(function () { - if (self._prepared) { - cc._setup(self.config[self.CONFIG_KEY.id]); - self._runMainLoop(); - self._eventHide = self._eventHide || new cc.EventCustom(self.EVENT_HIDE); - self._eventHide.setUserData(self); - self._eventShow = self._eventShow || new cc.EventCustom(self.EVENT_SHOW); - self._eventShow.setUserData(self); - self.onStart(); - clearInterval(self._checkPrepare); - } - }, 10); + if (!txt) { + cc.loader.loadTxt("project.json", loaded); } - }; - document.body ? - _run() : - cc._addEventListener(window, 'load', function () { - this.removeEventListener('load', arguments.callee, false); - _run(); - }, false); + } }, - _initConfig: function () { - var self = this, CONFIG_KEY = self.CONFIG_KEY; - var _init = function (cfg) { - cfg[CONFIG_KEY.engineDir] = cfg[CONFIG_KEY.engineDir] || "frameworks/cocos2d-html5"; - if(cfg[CONFIG_KEY.debugMode] == null) - cfg[CONFIG_KEY.debugMode] = 0; - cfg[CONFIG_KEY.frameRate] = cfg[CONFIG_KEY.frameRate] || 60; - if(cfg[CONFIG_KEY.renderMode] == null) - cfg[CONFIG_KEY.renderMode] = 1; - return cfg; - }; - if (document["ccConfig"]) { - self.config = _init(document["ccConfig"]); + _initConfig: function (config) { + var CONFIG_KEY = this.CONFIG_KEY, + modules = config[CONFIG_KEY.modules]; + + // Configs adjustment + config[CONFIG_KEY.showFPS] = typeof config[CONFIG_KEY.showFPS] === 'undefined' ? true : config[CONFIG_KEY.showFPS]; + config[CONFIG_KEY.engineDir] = config[CONFIG_KEY.engineDir] || "frameworks/cocos2d-html5"; + if (config[CONFIG_KEY.debugMode] == null) + config[CONFIG_KEY.debugMode] = 0; + config[CONFIG_KEY.exposeClassName] = !!config[CONFIG_KEY.exposeClassName]; + config[CONFIG_KEY.frameRate] = config[CONFIG_KEY.frameRate] || 60; + if (config[CONFIG_KEY.renderMode] == null) + config[CONFIG_KEY.renderMode] = 0; + if (config[CONFIG_KEY.registerSystemEvent] == null) + config[CONFIG_KEY.registerSystemEvent] = true; + + // Modules adjustment + if (modules && modules.indexOf("core") < 0) modules.splice(0, 0, "core"); + modules && (config[CONFIG_KEY.modules] = modules); + this.config = config; + this._configLoaded = true; + }, + + _initRenderer: function (width, height) { + // Avoid setup to be called twice. + if (this._rendererInitialized) return; + + if (!cc._supportRender) { + throw new Error("The renderer doesn't support the renderMode " + this.config[this.CONFIG_KEY.renderMode]); + } + + var el = this.config[cc.game.CONFIG_KEY.id], + win = window, + element = cc.$(el) || cc.$('#' + el), + localCanvas, localContainer, localConStyle; + + if (element.tagName === "CANVAS") { + width = width || element.width; + height = height || element.height; + + //it is already a canvas, we wrap it around with a div + this.canvas = cc._canvas = localCanvas = element; + this.container = cc.container = localContainer = document.createElement("DIV"); + if (localCanvas.parentNode) + localCanvas.parentNode.insertBefore(localContainer, localCanvas); } else { - try { - var cocos_script = document.getElementsByTagName('script'); - for(var i=0;i