/** * Hilo * Copyright 2015 alibaba.com * Licensed under the MIT License */ //TODO: 超时timeout,失败重连次数maxTries,更多的下载器Loader,队列暂停恢复等。 /** * @class LoadQueue is a queue-like loader. * @mixes EventMixin * @borrows EventMixin#on as #on * @borrows EventMixin#off as #off * @borrows EventMixin#fire as #fire * @param {Object} source resource that need to be loaded,could be a single object or array resource. * @module hilo/loader/LoadQueue * @requires hilo/core/Class * @requires hilo/event/EventMixin * @requires hilo/loader/ImageLoader * @requires hilo/loader/ScriptLoader * @property {Int} maxConnections the limited concurrent connections. default value 2. */ var LoadQueue = Class.create(/** @lends LoadQueue.prototype */{ Mixes: EventMixin, constructor: function(source){ this._source = []; this.add(source); }, maxConnections: 2, //TODO: 应该是每个host的最大连接数。 _source: null, _loaded: 0, _connections: 0, _currentIndex: -1, /** * Add desired resource,could be a single object or array resource. * @param {Object|Array} source ,a single object or array resource. Each resource contains properties like below: * * @returns {LoadQueue} 下载队列实例本身。 */ add: function(source){ var me = this; if(source){ source = source instanceof Array ? source : [source]; me._source = me._source.concat(source); } return me; }, /** * get resource object by id or src * @param {String} specified id or src * @returns {Object} resource object */ get: function(id){ if(id){ var source = this._source; for(var i = 0; i < source.length; i++){ var item = source[i]; if(item.id === id || item.src === id){ return item; } } } return null; }, /** * get resource object content by id or src * @param {String} specified id or src * @returns {Object} resource object content */ getContent: function(id){ var item = this.get(id); return item && item.content; }, /** * remove resource object content by id or src * @param {String} specified id or src */ removeContent: function(id){ if(id){ var source = this._source; for(var i = 0; i < source.length; i++){ var item = source[i]; if(item.id === id || item.src === id){ source.splice(i, 1); return; } } } }, /** * start loading * @returns {LoadQueue} the loading instance */ start: function(){ var me = this; me._loadNext(); return me; }, /** * @private */ _loadNext: function(){ var me = this, source = me._source, len = source.length; //all items loaded if(me._loaded >= len){ me.fire('complete'); return; } if(me._currentIndex < len - 1 && me._connections < me.maxConnections){ var index = ++me._currentIndex; var item = source[index]; var loader = me._getLoader(item); if(loader){ var onLoad = loader.onLoad, onError = loader.onError; loader.onLoad = function(e){ loader.onLoad = onLoad; loader.onError = onError; var content = onLoad && onLoad.call(loader, e) || e.target; me._onItemLoad(index, content); }; loader.onError = function(e){ loader.onLoad = onLoad; loader.onError = onError; onError && onError.call(loader, e); me._onItemError(index, e); }; me._connections++; } me._loadNext(); loader && loader.load(item); } }, /** * @private */ _getLoader: function(item){ var loader = item.loader; if(loader) return loader; var type = item.type || getExtension(item.src); switch(type){ case 'png': case 'jpg': case 'jpeg': case 'gif': case 'webp': loader = new ImageLoader(); break; case 'js': case 'jsonp': loader = new ScriptLoader(); break; } return loader; }, /** * @private */ _onItemLoad: function(index, content){ var me = this, item = me._source[index]; item.loaded = true; item.content = content; me._connections--; me._loaded++; me.fire('load', item); me._loadNext(); }, /** * @private */ _onItemError: function(index, e){ var me = this, item = me._source[index]; item.error = e; me._connections--; me._loaded++; me.fire('error', item); me._loadNext(); }, /** * get resource size, loaded or all. * @param {Boolean} identify loaded or all resource. default is false, return all resource size. when set true, return loaded resource size. * @returns {Number} resource size. */ getSize: function(loaded){ var size = 0, source = this._source; for(var i = 0; i < source.length; i++){ var item = source[i]; size += (loaded ? item.loaded && item.size : item.size) || 0; } return size; }, /** * get loaded resource count * @returns {Uint} loaded resource count */ getLoaded: function(){ return this._loaded; }, /** * get all resource count * @returns {Uint} all resource count */ getTotal: function(){ return this._source.length; } }); /** * @private */ function getExtension(src){ var extRegExp = /\/?[^/]+\.(\w+)(\?\S+)?$/i, match, extension; if(match = src.match(extRegExp)){ extension = match[1].toLowerCase(); } return extension || null; }