1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 //TODO: 超时timeout,失败重连次数maxTries,更多的下载器Loader,队列暂停恢复等。 8 9 /** 10 * @class LoadQueue是一个队列下载工具。 11 * @param {Object} source 要下载的资源。可以是单个资源对象或多个资源的数组。 12 * @module hilo/loader/LoadQueue 13 * @requires hilo/core/Class 14 * @requires hilo/event/EventMixin 15 * @requires hilo/loader/ImageLoader 16 * @requires hilo/loader/ScriptLoader 17 * @property {Int} maxConnections 同时下载的最大连接数。默认为2。 18 */ 19 var LoadQueue = Class.create(/** @lends LoadQueue.prototype */{ 20 Mixes: EventMixin, 21 constructor: function(source){ 22 this._source = []; 23 this.add(source); 24 }, 25 26 maxConnections: 2, //TODO: 应该是每个host的最大连接数。 27 28 _source: null, 29 _loaded: 0, 30 _connections: 0, 31 _currentIndex: -1, 32 33 /** 34 * 增加要下载的资源。可以是单个资源对象或多个资源的数组。 35 * @param {Object|Array} source 资源对象或资源对象数组。每个资源对象包含以下属性: 36 * <ul> 37 * <li><b>id</b> - 资源的唯一标识符。可用于从下载队列获取目标资源。</li> 38 * <li><b>src</b> - 资源的地址url。</li> 39 * <li><b>type</b> - 指定资源的类型。默认会根据资源文件的后缀来自动判断类型,不同的资源类型会使用不同的加载器来加载资源。</li> 40 * <li><b>loader</b> - 指定资源的加载器。默认会根据资源类型来自动选择加载器,若指定loader,则会使用指定的loader来加载资源。</li> 41 * <li><b>noCache</b> - 指示加载资源时是否增加时间标签以防止缓存。</li> 42 * <li><b>size</b> - 资源对象的预计大小。可用于预估下载进度。</li> 43 * </ul> 44 * @returns {LoadQueue} 下载队列实例本身。 45 */ 46 add: function(source){ 47 var me = this; 48 if(source){ 49 source = source instanceof Array ? source : [source]; 50 me._source = me._source.concat(source); 51 } 52 return me; 53 }, 54 55 /** 56 * 根据id或src地址获取资源对象。 57 * @param {String} id 指定资源的id或src。 58 * @returns {Object} 资源对象。 59 */ 60 get: function(id){ 61 if(id){ 62 var source = this._source; 63 for(var i = 0; i < source.length; i++){ 64 var item = source[i]; 65 if(item.id === id || item.src === id){ 66 return item; 67 } 68 } 69 } 70 return null; 71 }, 72 73 /** 74 * 根据id或src地址获取资源内容。 75 * @param {String} id 指定资源的id或src。 76 * @returns {Object} 资源内容。 77 */ 78 getContent: function(id){ 79 var item = this.get(id); 80 return item && item.content; 81 }, 82 83 /** 84 * 开始下载队列。 85 * @returns {LoadQueue} 下载队列实例本身。 86 */ 87 start: function(){ 88 var me = this; 89 me._loadNext(); 90 return me; 91 }, 92 93 /** 94 * @private 95 */ 96 _loadNext: function(){ 97 var me = this, source = me._source, len = source.length; 98 99 //all items loaded 100 if(me._loaded >= len){ 101 me.fire('complete'); 102 return; 103 } 104 105 if(me._currentIndex < len - 1 && me._connections < me.maxConnections){ 106 var index = ++me._currentIndex; 107 var item = source[index]; 108 var loader = me._getLoader(item); 109 110 if(loader){ 111 var onLoad = loader.onLoad, onError = loader.onError; 112 113 loader.onLoad = function(e){ 114 loader.onLoad = onLoad; 115 loader.onError = onError; 116 var content = onLoad && onLoad.call(loader, e) || e.target; 117 me._onItemLoad(index, content); 118 }; 119 loader.onError = function(e){ 120 loader.onLoad = onLoad; 121 loader.onError = onError; 122 onError && onError.call(loader, e); 123 me._onItemError(index, e); 124 }; 125 me._connections++; 126 } 127 128 me._loadNext(); 129 loader && loader.load(item); 130 } 131 }, 132 133 /** 134 * @private 135 */ 136 _getLoader: function(item){ 137 var me = this, loader = item.loader; 138 if(loader) return loader; 139 140 var type = item.type || getExtension(item.src); 141 142 switch(type){ 143 case 'png': 144 case 'jpg': 145 case 'jpeg': 146 case 'gif': 147 loader = new ImageLoader(); 148 break; 149 case 'js': 150 case 'jsonp': 151 loader = new ScriptLoader(); 152 break; 153 } 154 155 return loader; 156 }, 157 158 /** 159 * @private 160 */ 161 _onItemLoad: function(index, content){ 162 var me = this, item = me._source[index]; 163 item.loaded = true; 164 item.content = content; 165 me._connections--; 166 me._loaded++; 167 me.fire('load', item); 168 me._loadNext(); 169 }, 170 171 /** 172 * @private 173 */ 174 _onItemError: function(index, e){ 175 var me = this, item = me._source[index]; 176 item.error = e; 177 me._connections--; 178 me._loaded++; 179 me.fire('error', item); 180 me._loadNext(); 181 }, 182 183 /** 184 * 获取全部或已下载的资源的字节大小。 185 * @param {Boolean} loaded 指示是已下载的资源还是全部资源。默认为全部。 186 * @returns {Number} 指定资源的字节大小。 187 */ 188 getSize: function(loaded){ 189 var size = 0, source = this._source; 190 for(var i = 0; i < source.length; i++){ 191 var item = source[i]; 192 size += (loaded ? item.loaded && item.size : item.size) || 0; 193 } 194 return size; 195 }, 196 197 /** 198 * 获取已下载的资源数量。 199 * @returns {Uint} 已下载的资源数量。 200 */ 201 getLoaded: function(){ 202 return this._loaded; 203 }, 204 205 /** 206 * 获取所有资源的数量。 207 * @returns {Uint} 所有资源的数量。 208 */ 209 getTotal: function(){ 210 return this._source.length; 211 } 212 213 }); 214 215 /** 216 * @private 217 */ 218 function getExtension(src){ 219 var extRegExp = /\/?[^/]+\.(\w+)(\?\S+)?$/i, match, extension; 220 if(match = src.match(extRegExp)){ 221 extension = match[1].toLowerCase(); 222 } 223 return extension || null; 224 }