Socket
Socket
Sign inDemoInstall

resource-loader

Package Overview
Dependencies
2
Maintainers
1
Versions
57
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.8.0 to 2.0.0

2

dist/resource-loader.min.js

@@ -1,1 +0,1 @@

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Loader=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){"use strict";var has=Object.prototype.hasOwnProperty,prefix="~";function Events(){}if(Object.create){Events.prototype=Object.create(null);if(!(new Events).__proto__)prefix=false}function EE(fn,context,once){this.fn=fn;this.context=context;this.once=once||false}function EventEmitter(){this._events=new Events;this._eventsCount=0}EventEmitter.prototype.eventNames=function eventNames(){var names=[],events,name;if(this._eventsCount===0)return names;for(name in events=this._events){if(has.call(events,name))names.push(prefix?name.slice(1):name)}if(Object.getOwnPropertySymbols){return names.concat(Object.getOwnPropertySymbols(events))}return names};EventEmitter.prototype.listeners=function listeners(event,exists){var evt=prefix?prefix+event:event,available=this._events[evt];if(exists)return!!available;if(!available)return[];if(available.fn)return[available.fn];for(var i=0,l=available.length,ee=new Array(l);i<l;i++){ee[i]=available[i].fn}return ee};EventEmitter.prototype.emit=function emit(event,a1,a2,a3,a4,a5){var evt=prefix?prefix+event:event;if(!this._events[evt])return false;var listeners=this._events[evt],len=arguments.length,args,i;if(listeners.fn){if(listeners.once)this.removeListener(event,listeners.fn,undefined,true);switch(len){case 1:return listeners.fn.call(listeners.context),true;case 2:return listeners.fn.call(listeners.context,a1),true;case 3:return listeners.fn.call(listeners.context,a1,a2),true;case 4:return listeners.fn.call(listeners.context,a1,a2,a3),true;case 5:return listeners.fn.call(listeners.context,a1,a2,a3,a4),true;case 6:return listeners.fn.call(listeners.context,a1,a2,a3,a4,a5),true}for(i=1,args=new Array(len-1);i<len;i++){args[i-1]=arguments[i]}listeners.fn.apply(listeners.context,args)}else{var length=listeners.length,j;for(i=0;i<length;i++){if(listeners[i].once)this.removeListener(event,listeners[i].fn,undefined,true);switch(len){case 1:listeners[i].fn.call(listeners[i].context);break;case 2:listeners[i].fn.call(listeners[i].context,a1);break;case 3:listeners[i].fn.call(listeners[i].context,a1,a2);break;case 4:listeners[i].fn.call(listeners[i].context,a1,a2,a3);break;default:if(!args)for(j=1,args=new Array(len-1);j<len;j++){args[j-1]=arguments[j]}listeners[i].fn.apply(listeners[i].context,args)}}}return true};EventEmitter.prototype.on=function on(event,fn,context){var listener=new EE(fn,context||this),evt=prefix?prefix+event:event;if(!this._events[evt])this._events[evt]=listener,this._eventsCount++;else if(!this._events[evt].fn)this._events[evt].push(listener);else this._events[evt]=[this._events[evt],listener];return this};EventEmitter.prototype.once=function once(event,fn,context){var listener=new EE(fn,context||this,true),evt=prefix?prefix+event:event;if(!this._events[evt])this._events[evt]=listener,this._eventsCount++;else if(!this._events[evt].fn)this._events[evt].push(listener);else this._events[evt]=[this._events[evt],listener];return this};EventEmitter.prototype.removeListener=function removeListener(event,fn,context,once){var evt=prefix?prefix+event:event;if(!this._events[evt])return this;if(!fn){if(--this._eventsCount===0)this._events=new Events;else delete this._events[evt];return this}var listeners=this._events[evt];if(listeners.fn){if(listeners.fn===fn&&(!once||listeners.once)&&(!context||listeners.context===context)){if(--this._eventsCount===0)this._events=new Events;else delete this._events[evt]}}else{for(var i=0,events=[],length=listeners.length;i<length;i++){if(listeners[i].fn!==fn||once&&!listeners[i].once||context&&listeners[i].context!==context){events.push(listeners[i])}}if(events.length)this._events[evt]=events.length===1?events[0]:events;else if(--this._eventsCount===0)this._events=new Events;else delete this._events[evt]}return this};EventEmitter.prototype.removeAllListeners=function removeAllListeners(event){var evt;if(event){evt=prefix?prefix+event:event;if(this._events[evt]){if(--this._eventsCount===0)this._events=new Events;else delete this._events[evt]}}else{this._events=new Events;this._eventsCount=0}return this};EventEmitter.prototype.off=EventEmitter.prototype.removeListener;EventEmitter.prototype.addListener=EventEmitter.prototype.on;EventEmitter.prototype.setMaxListeners=function setMaxListeners(){return this};EventEmitter.prefixed=prefix;EventEmitter.EventEmitter=EventEmitter;if("undefined"!==typeof module){module.exports=EventEmitter}},{}],2:[function(require,module,exports){"use strict";module.exports=function parseURI(str,opts){opts=opts||{};var o={key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};var m=o.parser[opts.strictMode?"strict":"loose"].exec(str);var uri={};var i=14;while(i--)uri[o.key[i]]=m[i]||"";uri[o.q.name]={};uri[o.key[12]].replace(o.q.parser,function($0,$1,$2){if($1)uri[o.q.name][$1]=$2});return uri}},{}],3:[function(require,module,exports){"use strict";var parseUri=require("parse-uri");var async=require("./async");var Resource=require("./Resource");var EventEmitter=require("eventemitter3");var DEFAULT_CONCURRENCY=10;var MAX_PROGRESS=100;function Loader(baseUrl,concurrency){EventEmitter.call(this);concurrency=concurrency||DEFAULT_CONCURRENCY;this.baseUrl=baseUrl||"";this.progress=0;this.loading=false;this._progressChunk=0;this._beforeMiddleware=[];this._afterMiddleware=[];this._boundLoadResource=this._loadResource.bind(this);this._buffer=[];this._numToLoad=0;this._queue=async.queue(this._boundLoadResource,concurrency);this.resources={}}Loader.prototype=Object.create(EventEmitter.prototype);Loader.prototype.constructor=Loader;module.exports=Loader;Loader.prototype.add=Loader.prototype.enqueue=function(name,url,options,cb){if(Array.isArray(name)){for(var i=0;i<name.length;++i){this.add(name[i])}return this}if(typeof name==="object"){cb=url||name.callback||name.onComplete;options=name;url=name.url;name=name.name||name.key||name.url}if(typeof url!=="string"){cb=options;options=url;url=name}if(typeof url!=="string"){throw new Error("No url passed to add resource to loader.")}if(typeof options==="function"){cb=options;options=null}if(this.resources[name]){throw new Error('Resource with name "'+name+'" already exists.')}url=this._prepareUrl(url);this.resources[name]=new Resource(name,url,options);if(typeof cb==="function"){this.resources[name].once("afterMiddleware",cb)}this._numToLoad++;if(this._queue.started){this._queue.push(this.resources[name]);this._progressChunk=(MAX_PROGRESS-this.progress)/(this._queue.length()+this._queue.running())}else{this._buffer.push(this.resources[name]);this._progressChunk=MAX_PROGRESS/this._buffer.length}return this};Loader.prototype.before=Loader.prototype.pre=function(fn){this._beforeMiddleware.push(fn);return this};Loader.prototype.after=Loader.prototype.use=function(fn){this._afterMiddleware.push(fn);return this};Loader.prototype.reset=function(){this.progress=0;this.loading=false;this._progressChunk=0;this._buffer.length=0;this._numToLoad=0;this._queue.kill();this._queue.started=false;for(var k in this.resources){var res=this.resources[k];res.off("complete",this._onLoad,this);if(res.isLoading){res.abort()}}this.resources={};return this};Loader.prototype.load=function(cb){if(typeof cb==="function"){this.once("complete",cb)}if(this._queue.started){return this}this.emit("start",this);this.loading=true;for(var i=0;i<this._buffer.length;++i){this._queue.push(this._buffer[i])}this._buffer.length=0;return this};Loader.prototype._prepareUrl=function(url){var parsedUrl=parseUri(url,{strictMode:true});if(parsedUrl.protocol||!parsedUrl.path||parsedUrl.path.indexOf("//")===0){return url}if(this.baseUrl.length&&this.baseUrl.lastIndexOf("/")!==this.baseUrl.length-1&&url.charAt(0)!=="/"){return this.baseUrl+"/"+url}return this.baseUrl+url};Loader.prototype._loadResource=function(resource,dequeue){var self=this;resource._dequeue=dequeue;async.eachSeries(this._beforeMiddleware,function(fn,next){fn.call(self,resource,function(){next(resource.isComplete?{}:null)})},function(){if(resource.isComplete){self._onLoad(resource)}else{resource.once("complete",self._onLoad,self);resource.load()}})};Loader.prototype._onComplete=function(){this.loading=false;this.emit("complete",this,this.resources)};Loader.prototype._onLoad=function(resource){var self=this;async.eachSeries(this._afterMiddleware,function(fn,next){fn.call(self,resource,next)},function(){resource.emit("afterMiddleware",resource);self._numToLoad--;self.progress+=self._progressChunk;self.emit("progress",self,resource);if(resource.error){self.emit("error",resource.error,self,resource)}else{self.emit("load",self,resource)}if(self._numToLoad===0){self.progress=100;self._onComplete()}});resource._dequeue()};Loader.LOAD_TYPE=Resource.LOAD_TYPE;Loader.XHR_RESPONSE_TYPE=Resource.XHR_RESPONSE_TYPE},{"./Resource":4,"./async":5,eventemitter3:1,"parse-uri":2}],4:[function(require,module,exports){"use strict";var EventEmitter=require("eventemitter3");var parseUri=require("parse-uri");var useXdr=!!(window.XDomainRequest&&!("withCredentials"in new XMLHttpRequest));var tempAnchor=null;var STATUS_NONE=0;var STATUS_OK=200;var STATUS_EMPTY=204;function Resource(name,url,options){EventEmitter.call(this);options=options||{};if(typeof name!=="string"||typeof url!=="string"){throw new Error("Both name and url are required for constructing a resource.")}this.name=name;this.url=url;this.isDataUrl=this.url.indexOf("data:")===0;this.data=null;this.crossOrigin=options.crossOrigin===true?"anonymous":options.crossOrigin;this.loadType=options.loadType||this._determineLoadType();this.xhrType=options.xhrType;this.metadata=options.metadata||{};this.error=null;this.xhr=null;this.isJson=false;this.isXml=false;this.isImage=false;this.isAudio=false;this.isVideo=false;this.isComplete=false;this.isLoading=false;this._dequeue=null;this._boundComplete=this.complete.bind(this);this._boundOnError=this._onError.bind(this);this._boundOnProgress=this._onProgress.bind(this);this._boundXhrOnError=this._xhrOnError.bind(this);this._boundXhrOnAbort=this._xhrOnAbort.bind(this);this._boundXhrOnLoad=this._xhrOnLoad.bind(this);this._boundXdrOnTimeout=this._xdrOnTimeout.bind(this)}Resource.prototype=Object.create(EventEmitter.prototype);Resource.prototype.constructor=Resource;module.exports=Resource;Resource.prototype.complete=function(){if(this.data&&this.data.removeEventListener){this.data.removeEventListener("error",this._boundOnError,false);this.data.removeEventListener("load",this._boundComplete,false);this.data.removeEventListener("progress",this._boundOnProgress,false);this.data.removeEventListener("canplaythrough",this._boundComplete,false)}if(this.xhr){if(this.xhr.removeEventListener){this.xhr.removeEventListener("error",this._boundXhrOnError,false);this.xhr.removeEventListener("abort",this._boundXhrOnAbort,false);this.xhr.removeEventListener("progress",this._boundOnProgress,false);this.xhr.removeEventListener("load",this._boundXhrOnLoad,false)}else{this.xhr.onerror=null;this.xhr.ontimeout=null;this.xhr.onprogress=null;this.xhr.onload=null}}if(this.isComplete){throw new Error("Complete called again for an already completed resource.")}this.isComplete=true;this.isLoading=false;this.emit("complete",this)};Resource.prototype.abort=function(message){if(this.error){return}this.error=new Error(message);if(this.xhr){this.xhr.abort()}else if(this.xdr){this.xdr.abort()}else if(this.data){if(typeof this.data.src!=="undefined"){this.data.src=""}else{while(this.data.firstChild){this.data.removeChild(this.data.firstChild)}}}this.complete()};Resource.prototype.load=function(cb){if(this.isLoading){return}if(this.isComplete){if(cb){var self=this;setTimeout(function(){cb(self)},1)}return}else if(cb){this.once("complete",cb)}this.isLoading=true;this.emit("start",this);if(this.crossOrigin===false||typeof this.crossOrigin!=="string"){this.crossOrigin=this._determineCrossOrigin(this.url)}switch(this.loadType){case Resource.LOAD_TYPE.IMAGE:this._loadElement("image");break;case Resource.LOAD_TYPE.AUDIO:this._loadSourceElement("audio");break;case Resource.LOAD_TYPE.VIDEO:this._loadSourceElement("video");break;case Resource.LOAD_TYPE.XHR:default:if(useXdr&&this.crossOrigin){this._loadXdr()}else{this._loadXhr()}break}};Resource.prototype._loadElement=function(type){if(this.metadata.loadElement){this.data=this.metadata.loadElement}else if(type==="image"&&typeof window.Image!=="undefined"){this.data=new Image}else{this.data=document.createElement(type)}if(this.crossOrigin){this.data.crossOrigin=this.crossOrigin}if(!this.metadata.skipSource){this.data.src=this.url}var typeName="is"+type[0].toUpperCase()+type.substring(1);if(this[typeName]===false){this[typeName]=true}this.data.addEventListener("error",this._boundOnError,false);this.data.addEventListener("load",this._boundComplete,false);this.data.addEventListener("progress",this._boundOnProgress,false)};Resource.prototype._loadSourceElement=function(type){if(this.metadata.loadElement){this.data=this.metadata.loadElement}else if(type==="audio"&&typeof window.Audio!=="undefined"){this.data=new Audio}else{this.data=document.createElement(type)}if(this.data===null){this.abort("Unsupported element "+type);return}if(!this.metadata.skipSource){if(navigator.isCocoonJS){this.data.src=Array.isArray(this.url)?this.url[0]:this.url}else if(Array.isArray(this.url)){for(var i=0;i<this.url.length;++i){this.data.appendChild(this._createSource(type,this.url[i]))}}else{this.data.appendChild(this._createSource(type,this.url))}}this["is"+type[0].toUpperCase()+type.substring(1)]=true;this.data.addEventListener("error",this._boundOnError,false);this.data.addEventListener("load",this._boundComplete,false);this.data.addEventListener("progress",this._boundOnProgress,false);this.data.addEventListener("canplaythrough",this._boundComplete,false);this.data.load()};Resource.prototype._loadXhr=function(){if(typeof this.xhrType!=="string"){this.xhrType=this._determineXhrType()}var xhr=this.xhr=new XMLHttpRequest;xhr.open("GET",this.url,true);if(this.xhrType===Resource.XHR_RESPONSE_TYPE.JSON||this.xhrType===Resource.XHR_RESPONSE_TYPE.DOCUMENT){xhr.responseType=Resource.XHR_RESPONSE_TYPE.TEXT}else{xhr.responseType=this.xhrType}xhr.addEventListener("error",this._boundXhrOnError,false);xhr.addEventListener("abort",this._boundXhrOnAbort,false);xhr.addEventListener("progress",this._boundOnProgress,false);xhr.addEventListener("load",this._boundXhrOnLoad,false);xhr.send()};Resource.prototype._loadXdr=function(){if(typeof this.xhrType!=="string"){this.xhrType=this._determineXhrType()}var xdr=this.xhr=new XDomainRequest;xdr.timeout=5e3;xdr.onerror=this._boundXhrOnError;xdr.ontimeout=this._boundXdrOnTimeout;xdr.onprogress=this._boundOnProgress;xdr.onload=this._boundXhrOnLoad;xdr.open("GET",this.url,true);setTimeout(function(){xdr.send()},0)};Resource.prototype._createSource=function(type,url,mime){if(!mime){mime=type+"/"+url.substr(url.lastIndexOf(".")+1)}var source=document.createElement("source");source.src=url;source.type=mime;return source};Resource.prototype._onError=function(event){this.abort("Failed to load element using "+event.target.nodeName)};Resource.prototype._onProgress=function(event){if(event&&event.lengthComputable){this.emit("progress",this,event.loaded/event.total)}};Resource.prototype._xhrOnError=function(){var xhr=this.xhr;this.abort(reqType(xhr)+" Request failed. Status: "+xhr.status+', text: "'+xhr.statusText+'"')};Resource.prototype._xhrOnAbort=function(){this.abort(reqType(this.xhr)+" Request was aborted by the user.")};Resource.prototype._xdrOnTimeout=function(){this.abort(reqType(this.xhr)+" Request timed out.")};Resource.prototype._xhrOnLoad=function(){var xhr=this.xhr;var status=typeof xhr.status==="undefined"?xhr.status:STATUS_OK;if(status===STATUS_OK||status===STATUS_EMPTY||status===STATUS_NONE&&xhr.responseText.length>0){if(this.xhrType===Resource.XHR_RESPONSE_TYPE.TEXT){this.data=xhr.responseText}else if(this.xhrType===Resource.XHR_RESPONSE_TYPE.JSON){try{this.data=JSON.parse(xhr.responseText);this.isJson=true}catch(e){this.abort("Error trying to parse loaded json:",e);return}}else if(this.xhrType===Resource.XHR_RESPONSE_TYPE.DOCUMENT){try{if(window.DOMParser){var domparser=new DOMParser;this.data=domparser.parseFromString(xhr.responseText,"text/xml")}else{var div=document.createElement("div");div.innerHTML=xhr.responseText;this.data=div}this.isXml=true}catch(e){this.abort("Error trying to parse loaded xml:",e);return}}else{this.data=xhr.response||xhr.responseText}}else{this.abort("["+xhr.status+"]"+xhr.statusText+":"+xhr.responseURL);return}this.complete()};Resource.prototype._determineCrossOrigin=function(url,loc){if(url.indexOf("data:")===0){return""}loc=loc||window.location;if(!tempAnchor){tempAnchor=document.createElement("a")}tempAnchor.href=url;url=parseUri(tempAnchor.href,{strictMode:true});var samePort=!url.port&&loc.port===""||url.port===loc.port;var protocol=url.protocol?url.protocol+":":"";if(url.host!==loc.hostname||!samePort||protocol!==loc.protocol){return"anonymous"}return""};Resource.prototype._determineXhrType=function(){return Resource._xhrTypeMap[this._getExtension()]||Resource.XHR_RESPONSE_TYPE.TEXT};Resource.prototype._determineLoadType=function(){return Resource._loadTypeMap[this._getExtension()]||Resource.LOAD_TYPE.XHR};Resource.prototype._getExtension=function(){var url=this.url;var ext="";if(this.isDataUrl){var slashIndex=url.indexOf("/");ext=url.substring(slashIndex+1,url.indexOf(";",slashIndex))}else{var queryStart=url.indexOf("?");if(queryStart!==-1){url=url.substring(0,queryStart)}ext=url.substring(url.lastIndexOf(".")+1)}return ext.toLowerCase()};Resource.prototype._getMimeFromXhrType=function(type){switch(type){case Resource.XHR_RESPONSE_TYPE.BUFFER:return"application/octet-binary";case Resource.XHR_RESPONSE_TYPE.BLOB:return"application/blob";case Resource.XHR_RESPONSE_TYPE.DOCUMENT:return"application/xml";case Resource.XHR_RESPONSE_TYPE.JSON:return"application/json";case Resource.XHR_RESPONSE_TYPE.DEFAULT:case Resource.XHR_RESPONSE_TYPE.TEXT:default:return"text/plain"}};function reqType(xhr){return xhr.toString().replace("object ","")}Resource.LOAD_TYPE={XHR:1,IMAGE:2,AUDIO:3,VIDEO:4};Resource.XHR_RESPONSE_TYPE={DEFAULT:"text",BUFFER:"arraybuffer",BLOB:"blob",DOCUMENT:"document",JSON:"json",TEXT:"text"};Resource._loadTypeMap={gif:Resource.LOAD_TYPE.IMAGE,png:Resource.LOAD_TYPE.IMAGE,bmp:Resource.LOAD_TYPE.IMAGE,jpg:Resource.LOAD_TYPE.IMAGE,jpeg:Resource.LOAD_TYPE.IMAGE,tif:Resource.LOAD_TYPE.IMAGE,tiff:Resource.LOAD_TYPE.IMAGE,webp:Resource.LOAD_TYPE.IMAGE,tga:Resource.LOAD_TYPE.IMAGE,"svg+xml":Resource.LOAD_TYPE.IMAGE};Resource._xhrTypeMap={xhtml:Resource.XHR_RESPONSE_TYPE.DOCUMENT,html:Resource.XHR_RESPONSE_TYPE.DOCUMENT,htm:Resource.XHR_RESPONSE_TYPE.DOCUMENT,xml:Resource.XHR_RESPONSE_TYPE.DOCUMENT,tmx:Resource.XHR_RESPONSE_TYPE.DOCUMENT,tsx:Resource.XHR_RESPONSE_TYPE.DOCUMENT,svg:Resource.XHR_RESPONSE_TYPE.DOCUMENT,gif:Resource.XHR_RESPONSE_TYPE.BLOB,png:Resource.XHR_RESPONSE_TYPE.BLOB,bmp:Resource.XHR_RESPONSE_TYPE.BLOB,jpg:Resource.XHR_RESPONSE_TYPE.BLOB,jpeg:Resource.XHR_RESPONSE_TYPE.BLOB,tif:Resource.XHR_RESPONSE_TYPE.BLOB,tiff:Resource.XHR_RESPONSE_TYPE.BLOB,webp:Resource.XHR_RESPONSE_TYPE.BLOB,tga:Resource.XHR_RESPONSE_TYPE.BLOB,json:Resource.XHR_RESPONSE_TYPE.JSON,text:Resource.XHR_RESPONSE_TYPE.TEXT,txt:Resource.XHR_RESPONSE_TYPE.TEXT};Resource.setExtensionLoadType=function(extname,loadType){setExtMap(Resource._loadTypeMap,extname,loadType)};Resource.setExtensionXhrType=function(extname,xhrType){setExtMap(Resource._xhrTypeMap,extname,xhrType)};function setExtMap(map,extname,val){if(extname&&extname.indexOf(".")===0){extname=extname.substring(1)}if(!extname){return}map[extname]=val}},{eventemitter3:1,"parse-uri":2}],5:[function(require,module,exports){"use strict";module.exports={eachSeries:asyncEachSeries,queue:asyncQueue};function _noop(){}function asyncEachSeries(array,iterator,callback){var i=0;var len=array.length;(function next(err){if(err||i===len){if(callback){callback(err)}return}iterator(array[i++],next)})()}function onlyOnce(fn){return function onceWrapper(){if(fn===null){throw new Error("Callback was already called.")}var callFn=fn;fn=null;callFn.apply(this,arguments)}}function asyncQueue(worker,concurrency){if(concurrency==null){concurrency=1}else if(concurrency===0){throw new Error("Concurrency must not be zero")}var workers=0;var q={_tasks:[],concurrency:concurrency,saturated:_noop,unsaturated:_noop,buffer:concurrency/4,empty:_noop,drain:_noop,error:_noop,started:false,paused:false,push:function(data,callback){_insert(data,false,callback)},kill:function(){q.drain=_noop;q._tasks=[]},unshift:function(data,callback){_insert(data,true,callback)},process:function(){while(!q.paused&&workers<q.concurrency&&q._tasks.length){var task=q._tasks.shift();if(q._tasks.length===0){q.empty()}workers+=1;if(workers===q.concurrency){q.saturated()}worker(task.data,onlyOnce(_next(task)))}},length:function(){return q._tasks.length},running:function(){return workers},idle:function(){return q._tasks.length+workers===0},pause:function(){if(q.paused===true){return}q.paused=true},resume:function(){if(q.paused===false){return}q.paused=false;for(var w=1;w<=q.concurrency;w++){q.process()}}};function _insert(data,insertAtFront,callback){if(callback!=null&&typeof callback!=="function"){throw new Error("task callback must be a function")}q.started=true;if(data==null&&q.idle()){setTimeout(function(){q.drain()},1);return}var item={data:data,callback:typeof callback==="function"?callback:_noop};if(insertAtFront){q._tasks.unshift(item)}else{q._tasks.push(item)}setTimeout(function(){q.process()},1)}function _next(task){return function(){workers-=1;task.callback.apply(task,arguments);if(arguments[0]!=null){q.error(arguments[0],task.data)}if(workers<=q.concurrency-q.buffer){q.unsaturated()}if(q.idle()){q.drain()}q.process()}}return q}},{}],6:[function(require,module,exports){"use strict";module.exports={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encodeBinary:function(input){var output="";var bytebuffer;var encodedCharIndexes=new Array(4);var inx=0;var jnx=0;var paddingBytes=0;while(inx<input.length){bytebuffer=new Array(3);for(jnx=0;jnx<bytebuffer.length;jnx++){if(inx<input.length){bytebuffer[jnx]=input.charCodeAt(inx++)&255}else{bytebuffer[jnx]=0}}encodedCharIndexes[0]=bytebuffer[0]>>2;encodedCharIndexes[1]=(bytebuffer[0]&3)<<4|bytebuffer[1]>>4;encodedCharIndexes[2]=(bytebuffer[1]&15)<<2|bytebuffer[2]>>6;encodedCharIndexes[3]=bytebuffer[2]&63;paddingBytes=inx-(input.length-1);switch(paddingBytes){case 2:encodedCharIndexes[3]=64;encodedCharIndexes[2]=64;break;case 1:encodedCharIndexes[3]=64;break;default:break}for(jnx=0;jnx<encodedCharIndexes.length;jnx++){output+=this._keyStr.charAt(encodedCharIndexes[jnx])}}return output}}},{}],7:[function(require,module,exports){"use strict";module.exports=require("./Loader");module.exports.Resource=require("./Resource");module.exports.middleware={caching:{memory:require("./middlewares/caching/memory")},parsing:{blob:require("./middlewares/parsing/blob")}};module.exports.async=require("./async")},{"./Loader":3,"./Resource":4,"./async":5,"./middlewares/caching/memory":8,"./middlewares/parsing/blob":9}],8:[function(require,module,exports){"use strict";var cache={};module.exports=function(){return function(resource,next){if(cache[resource.url]){resource.data=cache[resource.url];resource.complete()}else{resource.once("complete",function(){cache[this.url]=this.data})}next()}}},{}],9:[function(require,module,exports){"use strict";var Resource=require("../../Resource");var b64=require("../../b64");var Url=window.URL||window.webkitURL;module.exports=function(){return function(resource,next){if(!resource.data){next();return}if(resource.xhr&&resource.xhrType===Resource.XHR_RESPONSE_TYPE.BLOB){if(!window.Blob||typeof resource.data==="string"){var type=resource.xhr.getResponseHeader("content-type");if(type&&type.indexOf("image")===0){resource.data=new Image;resource.data.src="data:"+type+";base64,"+b64.encodeBinary(resource.xhr.responseText);resource.isImage=true;resource.data.onload=function(){resource.data.onload=null;next()};return}}else if(resource.data.type.indexOf("image")===0){var src=Url.createObjectURL(resource.data);resource.blob=resource.data;resource.data=new Image;resource.data.src=src;resource.isImage=true;resource.data.onload=function(){Url.revokeObjectURL(src);resource.data.onload=null;next()};return}}next()}}},{"../../Resource":4,"../../b64":6}]},{},[7])(7)});
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Loader=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor}}();function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var MiniSignalBinding=function(){function MiniSignalBinding(fn,once,thisArg){if(once===undefined)once=false;_classCallCheck(this,MiniSignalBinding);this._fn=fn;this._once=once;this._thisArg=thisArg;this._next=this._prev=this._owner=null}_createClass(MiniSignalBinding,[{key:"detach",value:function detach(){if(this._owner===null)return false;this._owner.detach(this);return true}}]);return MiniSignalBinding}();function _addMiniSignalBinding(self,node){if(!self._head){self._head=node;self._tail=node}else{self._tail._next=node;node._prev=self._tail;self._tail=node}node._owner=self;return node}var MiniSignal=function(){function MiniSignal(){_classCallCheck(this,MiniSignal);this._head=this._tail=undefined}_createClass(MiniSignal,[{key:"handlers",value:function handlers(){var exists=arguments.length<=0||arguments[0]===undefined?false:arguments[0];var node=this._head;if(exists)return!!node;var ee=[];while(node){ee.push(node);node=node._next}return ee}},{key:"has",value:function has(node){if(!(node instanceof MiniSignalBinding)){throw new Error("MiniSignal#has(): First arg must be a MiniSignalBinding object.")}return node._owner===this}},{key:"dispatch",value:function dispatch(){var node=this._head;if(!node)return false;while(node){if(node._once)this.detach(node);node._fn.apply(node._thisArg,arguments);node=node._next}return true}},{key:"add",value:function add(fn){var thisArg=arguments.length<=1||arguments[1]===undefined?null:arguments[1];if(typeof fn!=="function"){throw new Error("MiniSignal#add(): First arg must be a Function.")}return _addMiniSignalBinding(this,new MiniSignalBinding(fn,false,thisArg))}},{key:"once",value:function once(fn){var thisArg=arguments.length<=1||arguments[1]===undefined?null:arguments[1];if(typeof fn!=="function"){throw new Error("MiniSignal#once(): First arg must be a Function.")}return _addMiniSignalBinding(this,new MiniSignalBinding(fn,true,thisArg))}},{key:"detach",value:function detach(node){if(!(node instanceof MiniSignalBinding)){throw new Error("MiniSignal#detach(): First arg must be a MiniSignalBinding object.")}if(node._owner!==this)return this;if(node._prev)node._prev._next=node._next;if(node._next)node._next._prev=node._prev;if(node===this._head){this._head=node._next;if(node._next===null){this._tail=null}}else if(node===this._tail){this._tail=node._prev;this._tail._next=null}node._owner=null;return this}},{key:"detachAll",value:function detachAll(){var node=this._head;if(!node)return this;this._head=this._tail=null;while(node){node._owner=null;node=node._next}return this}}]);return MiniSignal}();MiniSignal.MiniSignalBinding=MiniSignalBinding;exports["default"]=MiniSignal;module.exports=exports["default"]},{}],2:[function(require,module,exports){"use strict";module.exports=function parseURI(str,opts){opts=opts||{};var o={key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};var m=o.parser[opts.strictMode?"strict":"loose"].exec(str);var uri={};var i=14;while(i--)uri[o.key[i]]=m[i]||"";uri[o.q.name]={};uri[o.key[12]].replace(o.q.parser,function($0,$1,$2){if($1)uri[o.q.name][$1]=$2});return uri}},{}],3:[function(require,module,exports){"use strict";exports.__esModule=true;var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj};var _miniSignals=require("mini-signals");var _miniSignals2=_interopRequireDefault(_miniSignals);var _parseUri=require("parse-uri");var _parseUri2=_interopRequireDefault(_parseUri);var _async=require("./async");var async=_interopRequireWildcard(_async);var _Resource=require("./Resource");var _Resource2=_interopRequireDefault(_Resource);function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj}else{var newObj={};if(obj!=null){for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key]=obj[key]}}newObj.default=obj;return newObj}}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var MAX_PROGRESS=100;var rgxExtractUrlHash=/(#[\w\-]+)?$/;var Loader=function(){function Loader(){var _this=this;var baseUrl=arguments.length>0&&arguments[0]!==undefined?arguments[0]:"";var concurrency=arguments.length>1&&arguments[1]!==undefined?arguments[1]:10;_classCallCheck(this,Loader);this.baseUrl=baseUrl;this.progress=0;this.loading=false;this.defaultQueryString="";this._beforeMiddleware=[];this._afterMiddleware=[];this._boundLoadResource=function(r,d){return _this._loadResource(r,d)};this._queue=async.queue(this._boundLoadResource,concurrency);this._queue.pause();this.resources={};this.onProgress=new _miniSignals2.default;this.onError=new _miniSignals2.default;this.onLoad=new _miniSignals2.default;this.onStart=new _miniSignals2.default;this.onComplete=new _miniSignals2.default}Loader.prototype.add=function add(name,url,options,cb){if(Array.isArray(name)){for(var i=0;i<name.length;++i){this.add(name[i])}return this}if((typeof name==="undefined"?"undefined":_typeof(name))==="object"){cb=url||name.callback||name.onComplete;options=name;url=name.url;name=name.name||name.key||name.url}if(typeof url!=="string"){cb=options;options=url;url=name}if(typeof url!=="string"){throw new Error("No url passed to add resource to loader.")}if(typeof options==="function"){cb=options;options=null}if(this.loading&&(!options||!options.parentResource)){throw new Error("Cannot add resources while the loader is running.")}if(this.resources[name]){throw new Error('Resource named "'+name+'" already exists.')}url=this._prepareUrl(url);this.resources[name]=new _Resource2.default(name,url,options);if(typeof cb==="function"){this.resources[name].onAfterMiddleware.once(cb)}if(this.loading){var parent=options.parentResource;var fullChunk=parent.progressChunk*(parent.children.length+1);var eachChunk=fullChunk/(parent.children.length+2);parent.children.push(this.resources[name]);parent.progressChunk=eachChunk;for(var _i=0;_i<parent.children.length;++_i){parent.children[_i].progressChunk=eachChunk}}this._queue.push(this.resources[name]);return this};Loader.prototype.pre=function pre(fn){this._beforeMiddleware.push(fn);return this};Loader.prototype.use=function use(fn){this._afterMiddleware.push(fn);return this};Loader.prototype.reset=function reset(){this.progress=0;this.loading=false;this._queue.kill();this._queue.pause();for(var k in this.resources){var res=this.resources[k];if(res._onLoadBinding){res._onLoadBinding.detach()}if(res.isLoading){res.abort()}}this.resources={};return this};Loader.prototype.load=function load(cb){if(typeof cb==="function"){this.onComplete.once(cb)}if(this.loading){return this}var chunk=100/this._queue._tasks.length;for(var i=0;i<this._queue._tasks.length;++i){this._queue._tasks[i].data.progressChunk=chunk}this.loading=true;this.onStart.dispatch(this);this._queue.resume();return this};Loader.prototype._prepareUrl=function _prepareUrl(url){var parsedUrl=(0,_parseUri2.default)(url,{strictMode:true});var result=void 0;if(parsedUrl.protocol||!parsedUrl.path||url.indexOf("//")===0){result=url}else if(this.baseUrl.length&&this.baseUrl.lastIndexOf("/")!==this.baseUrl.length-1&&url.charAt(0)!=="/"){result=this.baseUrl+"/"+url}else{result=this.baseUrl+url}if(this.defaultQueryString){var hash=rgxExtractUrlHash.exec(result)[0];result=result.substr(0,result.length-hash.length);if(result.indexOf("?")!==-1){result+="&"+this.defaultQueryString}else{result+="?"+this.defaultQueryString}result+=hash}return result};Loader.prototype._loadResource=function _loadResource(resource,dequeue){var _this2=this;resource._dequeue=dequeue;async.eachSeries(this._beforeMiddleware,function(fn,next){fn.call(_this2,resource,function(){next(resource.isComplete?{}:null)})},function(){if(resource.isComplete){_this2._onLoad(resource)}else{resource._onLoadBinding=resource.onComplete.once(_this2._onLoad,_this2);resource.load()}})};Loader.prototype._onComplete=function _onComplete(){this.loading=false;this.onComplete.dispatch(this,this.resources)};Loader.prototype._onLoad=function _onLoad(resource){var _this3=this;resource._onLoadBinding=null;async.eachSeries(this._afterMiddleware,function(fn,next){fn.call(_this3,resource,next)},function(){resource.onAfterMiddleware.dispatch(resource);_this3.progress+=resource.progressChunk;_this3.onProgress.dispatch(_this3,resource);if(resource.error){_this3.onError.dispatch(resource.error,_this3,resource)}else{_this3.onLoad.dispatch(_this3,resource)}resource._dequeue();if(_this3._queue.idle()){_this3.progress=MAX_PROGRESS;_this3._onComplete()}})};return Loader}();exports.default=Loader},{"./Resource":4,"./async":5,"mini-signals":1,"parse-uri":2}],4:[function(require,module,exports){"use strict";exports.__esModule=true;var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor}}();var _parseUri=require("parse-uri");var _parseUri2=_interopRequireDefault(_parseUri);var _miniSignals=require("mini-signals");var _miniSignals2=_interopRequireDefault(_miniSignals);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var useXdr=!!(window.XDomainRequest&&!("withCredentials"in new XMLHttpRequest));var tempAnchor=null;var STATUS_NONE=0;var STATUS_OK=200;var STATUS_EMPTY=204;function _noop(){}var Resource=function(){Resource.setExtensionLoadType=function setExtensionLoadType(extname,loadType){setExtMap(Resource._loadTypeMap,extname,loadType)};Resource.setExtensionXhrType=function setExtensionXhrType(extname,xhrType){setExtMap(Resource._xhrTypeMap,extname,xhrType)};function Resource(name,url,options){_classCallCheck(this,Resource);if(typeof name!=="string"||typeof url!=="string"){throw new Error("Both name and url are required for constructing a resource.")}options=options||{};this._flags=0;this._setFlag(Resource.STATUS_FLAGS.DATA_URL,url.indexOf("data:")===0);this.name=name;this.url=url;this.data=null;this.crossOrigin=options.crossOrigin===true?"anonymous":options.crossOrigin;this.loadType=options.loadType||this._determineLoadType();this.xhrType=options.xhrType;this.metadata=options.metadata||{};this.error=null;this.xhr=null;this.children=[];this.type=Resource.TYPE.UNKNOWN;this.progressChunk=0;this._dequeue=_noop;this._onLoadBinding=null;this._boundComplete=this.complete.bind(this);this._boundOnError=this._onError.bind(this);this._boundOnProgress=this._onProgress.bind(this);this._boundXhrOnError=this._xhrOnError.bind(this);this._boundXhrOnAbort=this._xhrOnAbort.bind(this);this._boundXhrOnLoad=this._xhrOnLoad.bind(this);this._boundXdrOnTimeout=this._xdrOnTimeout.bind(this);this.onStart=new _miniSignals2.default;this.onProgress=new _miniSignals2.default;this.onComplete=new _miniSignals2.default;this.onAfterMiddleware=new _miniSignals2.default}Resource.prototype.complete=function complete(){if(this.data&&this.data.removeEventListener){this.data.removeEventListener("error",this._boundOnError,false);this.data.removeEventListener("load",this._boundComplete,false);this.data.removeEventListener("progress",this._boundOnProgress,false);this.data.removeEventListener("canplaythrough",this._boundComplete,false)}if(this.xhr){if(this.xhr.removeEventListener){this.xhr.removeEventListener("error",this._boundXhrOnError,false);this.xhr.removeEventListener("abort",this._boundXhrOnAbort,false);this.xhr.removeEventListener("progress",this._boundOnProgress,false);this.xhr.removeEventListener("load",this._boundXhrOnLoad,false)}else{this.xhr.onerror=null;this.xhr.ontimeout=null;this.xhr.onprogress=null;this.xhr.onload=null}}if(this.isComplete){throw new Error("Complete called again for an already completed resource.")}this._setFlag(Resource.STATUS_FLAGS.COMPLETE,true);this._setFlag(Resource.STATUS_FLAGS.LOADING,false);this.onComplete.dispatch(this)};Resource.prototype.abort=function abort(message){if(this.error){return}this.error=new Error(message);if(this.xhr){this.xhr.abort()}else if(this.xdr){this.xdr.abort()}else if(this.data){if(this.data.src){this.data.src=Resource.EMPTY_GIF}else{while(this.data.firstChild){this.data.removeChild(this.data.firstChild)}}}this.complete()};Resource.prototype.load=function load(cb){var _this=this;if(this.isLoading){return}if(this.isComplete){if(cb){setTimeout(function(){return cb(_this)},1)}return}else if(cb){this.onComplete.once(cb)}this._setFlag(Resource.STATUS_FLAGS.LOADING,true);this.onStart.dispatch(this);if(this.crossOrigin===false||typeof this.crossOrigin!=="string"){this.crossOrigin=this._determineCrossOrigin(this.url)}switch(this.loadType){case Resource.LOAD_TYPE.IMAGE:this.type=Resource.TYPE.IMAGE;this._loadElement("image");break;case Resource.LOAD_TYPE.AUDIO:this.type=Resource.TYPE.AUDIO;this._loadSourceElement("audio");break;case Resource.LOAD_TYPE.VIDEO:this.type=Resource.TYPE.VIDEO;this._loadSourceElement("video");break;case Resource.LOAD_TYPE.XHR:default:if(useXdr&&this.crossOrigin){this._loadXdr()}else{this._loadXhr()}break}};Resource.prototype._hasFlag=function _hasFlag(flag){return!!(this._flags&flag)};Resource.prototype._setFlag=function _setFlag(flag,value){this._flags=value?this._flags|flag:this._flags&~flag};Resource.prototype._loadElement=function _loadElement(type){if(this.metadata.loadElement){this.data=this.metadata.loadElement}else if(type==="image"&&typeof window.Image!=="undefined"){this.data=new Image}else{this.data=document.createElement(type)}if(this.crossOrigin){this.data.crossOrigin=this.crossOrigin}if(!this.metadata.skipSource){this.data.src=this.url}this.data.addEventListener("error",this._boundOnError,false);this.data.addEventListener("load",this._boundComplete,false);this.data.addEventListener("progress",this._boundOnProgress,false)};Resource.prototype._loadSourceElement=function _loadSourceElement(type){if(this.metadata.loadElement){this.data=this.metadata.loadElement}else if(type==="audio"&&typeof window.Audio!=="undefined"){this.data=new Audio}else{this.data=document.createElement(type)}if(this.data===null){this.abort("Unsupported element: "+type);return}if(!this.metadata.skipSource){if(navigator.isCocoonJS){this.data.src=Array.isArray(this.url)?this.url[0]:this.url}else if(Array.isArray(this.url)){for(var i=0;i<this.url.length;++i){this.data.appendChild(this._createSource(type,this.url[i]))}}else{this.data.appendChild(this._createSource(type,this.url))}}this.data.addEventListener("error",this._boundOnError,false);this.data.addEventListener("load",this._boundComplete,false);this.data.addEventListener("progress",this._boundOnProgress,false);this.data.addEventListener("canplaythrough",this._boundComplete,false);this.data.load()};Resource.prototype._loadXhr=function _loadXhr(){if(typeof this.xhrType!=="string"){this.xhrType=this._determineXhrType()}var xhr=this.xhr=new XMLHttpRequest;xhr.open("GET",this.url,true);if(this.xhrType===Resource.XHR_RESPONSE_TYPE.JSON||this.xhrType===Resource.XHR_RESPONSE_TYPE.DOCUMENT){xhr.responseType=Resource.XHR_RESPONSE_TYPE.TEXT}else{xhr.responseType=this.xhrType}xhr.addEventListener("error",this._boundXhrOnError,false);xhr.addEventListener("abort",this._boundXhrOnAbort,false);xhr.addEventListener("progress",this._boundOnProgress,false);xhr.addEventListener("load",this._boundXhrOnLoad,false);xhr.send()};Resource.prototype._loadXdr=function _loadXdr(){if(typeof this.xhrType!=="string"){this.xhrType=this._determineXhrType()}var xdr=this.xhr=new XDomainRequest;xdr.timeout=5e3;xdr.onerror=this._boundXhrOnError;xdr.ontimeout=this._boundXdrOnTimeout;xdr.onprogress=this._boundOnProgress;xdr.onload=this._boundXhrOnLoad;xdr.open("GET",this.url,true);setTimeout(function(){return xdr.send()},1)};Resource.prototype._createSource=function _createSource(type,url,mime){if(!mime){mime=type+"/"+url.substr(url.lastIndexOf(".")+1)}var source=document.createElement("source");source.src=url;source.type=mime;return source};Resource.prototype._onError=function _onError(event){this.abort("Failed to load element using: "+event.target.nodeName)};Resource.prototype._onProgress=function _onProgress(event){if(event&&event.lengthComputable){this.onProgress.dispatch(this,event.loaded/event.total)}};Resource.prototype._xhrOnError=function _xhrOnError(){var xhr=this.xhr;this.abort(reqType(xhr)+" Request failed. Status: "+xhr.status+', text: "'+xhr.statusText+'"')};Resource.prototype._xhrOnAbort=function _xhrOnAbort(){this.abort(reqType(this.xhr)+" Request was aborted by the user.")};Resource.prototype._xdrOnTimeout=function _xdrOnTimeout(){this.abort(reqType(this.xhr)+" Request timed out.")};Resource.prototype._xhrOnLoad=function _xhrOnLoad(){var xhr=this.xhr;var status=typeof xhr.status==="undefined"?xhr.status:STATUS_OK;if(status===STATUS_OK||status===STATUS_EMPTY||status===STATUS_NONE&&xhr.responseText.length>0){if(this.xhrType===Resource.XHR_RESPONSE_TYPE.TEXT){this.data=xhr.responseText;this.type=Resource.TYPE.TEXT}else if(this.xhrType===Resource.XHR_RESPONSE_TYPE.JSON){try{this.data=JSON.parse(xhr.responseText);this.type=Resource.TYPE.JSON}catch(e){this.abort("Error trying to parse loaded json: "+e);return}}else if(this.xhrType===Resource.XHR_RESPONSE_TYPE.DOCUMENT){try{if(window.DOMParser){var domparser=new DOMParser;this.data=domparser.parseFromString(xhr.responseText,"text/xml")}else{var div=document.createElement("div");div.innerHTML=xhr.responseText;this.data=div}this.type=Resource.TYPE.XML}catch(e){this.abort("Error trying to parse loaded xml: "+e);return}}else{this.data=xhr.response||xhr.responseText}}else{this.abort("["+xhr.status+"] "+xhr.statusText+": "+xhr.responseURL);return}this.complete()};Resource.prototype._determineCrossOrigin=function _determineCrossOrigin(url,loc){if(url.indexOf("data:")===0){return""}loc=loc||window.location;if(!tempAnchor){tempAnchor=document.createElement("a")}tempAnchor.href=url;url=(0,_parseUri2.default)(tempAnchor.href,{strictMode:true});var samePort=!url.port&&loc.port===""||url.port===loc.port;var protocol=url.protocol?url.protocol+":":"";if(url.host!==loc.hostname||!samePort||protocol!==loc.protocol){return"anonymous"}return""};Resource.prototype._determineXhrType=function _determineXhrType(){return Resource._xhrTypeMap[this._getExtension()]||Resource.XHR_RESPONSE_TYPE.TEXT};Resource.prototype._determineLoadType=function _determineLoadType(){return Resource._loadTypeMap[this._getExtension()]||Resource.LOAD_TYPE.XHR};Resource.prototype._getExtension=function _getExtension(){var url=this.url;var ext="";if(this.isDataUrl){var slashIndex=url.indexOf("/");ext=url.substring(slashIndex+1,url.indexOf(";",slashIndex))}else{var queryStart=url.indexOf("?");if(queryStart!==-1){url=url.substring(0,queryStart)}ext=url.substring(url.lastIndexOf(".")+1)}return ext.toLowerCase()};Resource.prototype._getMimeFromXhrType=function _getMimeFromXhrType(type){switch(type){case Resource.XHR_RESPONSE_TYPE.BUFFER:return"application/octet-binary";case Resource.XHR_RESPONSE_TYPE.BLOB:return"application/blob";case Resource.XHR_RESPONSE_TYPE.DOCUMENT:return"application/xml";case Resource.XHR_RESPONSE_TYPE.JSON:return"application/json";case Resource.XHR_RESPONSE_TYPE.DEFAULT:case Resource.XHR_RESPONSE_TYPE.TEXT:default:return"text/plain"}};_createClass(Resource,[{key:"isDataUrl",get:function get(){return this._hasFlag(Resource.STATUS_FLAGS.DATA_URL)}},{key:"isComplete",get:function get(){return this._hasFlag(Resource.STATUS_FLAGS.COMPLETE)}},{key:"isLoading",get:function get(){return this._hasFlag(Resource.STATUS_FLAGS.LOADING)}}]);return Resource}();exports.default=Resource;Resource.STATUS_FLAGS={NONE:0,DATA_URL:1<<0,COMPLETE:1<<1,LOADING:1<<2};Resource.TYPE={UNKNOWN:0,JSON:1,XML:2,IMAGE:3,AUDIO:4,VIDEO:5,TEXT:6};Resource.LOAD_TYPE={XHR:1,IMAGE:2,AUDIO:3,VIDEO:4};Resource.XHR_RESPONSE_TYPE={DEFAULT:"text",BUFFER:"arraybuffer",BLOB:"blob",DOCUMENT:"document",JSON:"json",TEXT:"text"};Resource._loadTypeMap={gif:Resource.LOAD_TYPE.IMAGE,png:Resource.LOAD_TYPE.IMAGE,bmp:Resource.LOAD_TYPE.IMAGE,jpg:Resource.LOAD_TYPE.IMAGE,jpeg:Resource.LOAD_TYPE.IMAGE,tif:Resource.LOAD_TYPE.IMAGE,tiff:Resource.LOAD_TYPE.IMAGE,webp:Resource.LOAD_TYPE.IMAGE,tga:Resource.LOAD_TYPE.IMAGE,svg:Resource.LOAD_TYPE.IMAGE,"svg+xml":Resource.LOAD_TYPE.IMAGE,mp3:Resource.LOAD_TYPE.AUDIO,ogg:Resource.LOAD_TYPE.AUDIO,wav:Resource.LOAD_TYPE.AUDIO,mp4:Resource.LOAD_TYPE.VIDEO,webm:Resource.LOAD_TYPE.VIDEO};Resource._xhrTypeMap={xhtml:Resource.XHR_RESPONSE_TYPE.DOCUMENT,html:Resource.XHR_RESPONSE_TYPE.DOCUMENT,htm:Resource.XHR_RESPONSE_TYPE.DOCUMENT,xml:Resource.XHR_RESPONSE_TYPE.DOCUMENT,tmx:Resource.XHR_RESPONSE_TYPE.DOCUMENT,svg:Resource.XHR_RESPONSE_TYPE.DOCUMENT,tsx:Resource.XHR_RESPONSE_TYPE.DOCUMENT,gif:Resource.XHR_RESPONSE_TYPE.BLOB,png:Resource.XHR_RESPONSE_TYPE.BLOB,bmp:Resource.XHR_RESPONSE_TYPE.BLOB,jpg:Resource.XHR_RESPONSE_TYPE.BLOB,jpeg:Resource.XHR_RESPONSE_TYPE.BLOB,tif:Resource.XHR_RESPONSE_TYPE.BLOB,tiff:Resource.XHR_RESPONSE_TYPE.BLOB,webp:Resource.XHR_RESPONSE_TYPE.BLOB,tga:Resource.XHR_RESPONSE_TYPE.BLOB,json:Resource.XHR_RESPONSE_TYPE.JSON,text:Resource.XHR_RESPONSE_TYPE.TEXT,txt:Resource.XHR_RESPONSE_TYPE.TEXT,ttf:Resource.XHR_RESPONSE_TYPE.BUFFER,otf:Resource.XHR_RESPONSE_TYPE.BUFFER};Resource.EMPTY_GIF="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";function setExtMap(map,extname,val){if(extname&&extname.indexOf(".")===0){extname=extname.substring(1)}if(!extname){return}map[extname]=val}function reqType(xhr){return xhr.toString().replace("object ","")}},{"mini-signals":1,"parse-uri":2}],5:[function(require,module,exports){"use strict";exports.__esModule=true;exports.eachSeries=eachSeries;exports.queue=queue;function _noop(){}function eachSeries(array,iterator,callback){var i=0;var len=array.length;(function next(err){if(err||i===len){if(callback){callback(err)}return}iterator(array[i++],next)})()}function onlyOnce(fn){return function onceWrapper(){if(fn===null){throw new Error("Callback was already called.")}var callFn=fn;fn=null;callFn.apply(this,arguments)}}function queue(worker,concurrency){if(concurrency==null){concurrency=1}else if(concurrency===0){throw new Error("Concurrency must not be zero")}var workers=0;var q={_tasks:[],concurrency:concurrency,saturated:_noop,unsaturated:_noop,buffer:concurrency/4,empty:_noop,drain:_noop,error:_noop,started:false,paused:false,push:function push(data,callback){_insert(data,false,callback)},kill:function kill(){workers=0;q.drain=_noop;q.started=false;q._tasks=[]},unshift:function unshift(data,callback){_insert(data,true,callback)},process:function process(){while(!q.paused&&workers<q.concurrency&&q._tasks.length){var task=q._tasks.shift();if(q._tasks.length===0){q.empty()}workers+=1;if(workers===q.concurrency){q.saturated()}worker(task.data,onlyOnce(_next(task)))}},length:function length(){return q._tasks.length},running:function running(){return workers},idle:function idle(){return q._tasks.length+workers===0},pause:function pause(){if(q.paused===true){return}q.paused=true},resume:function resume(){if(q.paused===false){return}q.paused=false;for(var w=1;w<=q.concurrency;w++){q.process()}}};function _insert(data,insertAtFront,callback){if(callback!=null&&typeof callback!=="function"){throw new Error("task callback must be a function")}q.started=true;if(data==null&&q.idle()){setTimeout(function(){return q.drain()},1);return}var item={data:data,callback:typeof callback==="function"?callback:_noop};if(insertAtFront){q._tasks.unshift(item)}else{q._tasks.push(item)}setTimeout(function(){return q.process()},1)}function _next(task){return function next(){workers-=1;task.callback.apply(task,arguments);if(arguments[0]!=null){q.error(arguments[0],task.data)}if(workers<=q.concurrency-q.buffer){q.unsaturated()}if(q.idle()){q.drain()}q.process()}}return q}},{}],6:[function(require,module,exports){"use strict";exports.__esModule=true;exports.encodeBinary=encodeBinary;var _keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";function encodeBinary(input){var output="";var inx=0;while(inx<input.length){var bytebuffer=[0,0,0];var encodedCharIndexes=[0,0,0,0];for(var jnx=0;jnx<bytebuffer.length;++jnx){if(inx<input.length){bytebuffer[jnx]=input.charCodeAt(inx++)&255}else{bytebuffer[jnx]=0}}encodedCharIndexes[0]=bytebuffer[0]>>2;encodedCharIndexes[1]=(bytebuffer[0]&3)<<4|bytebuffer[1]>>4;encodedCharIndexes[2]=(bytebuffer[1]&15)<<2|bytebuffer[2]>>6;encodedCharIndexes[3]=bytebuffer[2]&63;var paddingBytes=inx-(input.length-1);switch(paddingBytes){case 2:encodedCharIndexes[3]=64;encodedCharIndexes[2]=64;break;case 1:encodedCharIndexes[3]=64;break;default:break}for(var _jnx=0;_jnx<encodedCharIndexes.length;++_jnx){output+=_keyStr.charAt(encodedCharIndexes[_jnx])}}return output}},{}],7:[function(require,module,exports){"use strict";var _Loader=require("./Loader");var _Loader2=_interopRequireDefault(_Loader);var _Resource=require("./Resource");var _Resource2=_interopRequireDefault(_Resource);var _async=require("./async");var async=_interopRequireWildcard(_async);var _b=require("./b64");var b64=_interopRequireWildcard(_b);function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj}else{var newObj={};if(obj!=null){for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key]=obj[key]}}newObj.default=obj;return newObj}}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}_Loader2.default.Resource=_Resource2.default;_Loader2.default.async=async;_Loader2.default.base64=b64;module.exports=_Loader2.default},{"./Loader":3,"./Resource":4,"./async":5,"./b64":6}]},{},[7])(7)});
{
"name": "resource-loader",
"version": "1.8.0",
"main": "./src/index.js",
"version": "2.0.0",
"main": "./lib/index.js",
"description": "A generic asset loader, made with web games in mind.",
"author": "Chad Engler <chad@pantherdev.com>",
"license": "MIT",
"homepage": "https://github.com/englercj/asset-loader",
"homepage": "https://github.com/englercj/resource-loader",
"repository": {
"type": "git",
"url": "https://github.com/englercj/asset-loader.git"
"url": "https://github.com/englercj/resource-loader.git"
},
"bugs": {
"url": "https://github.com/englercj/asset-loader/issues"
"url": "https://github.com/englercj/resource-loader/issues"
},

@@ -19,17 +19,19 @@ "keywords": [],

"dist",
"lib",
"src"
],
"scripts": {
"test": "npm run build && testem ci",
"start": "npm run build",
"travis": "npm run lint && npm test",
"clean": "rm -rf ./dist && mkdir dist",
"build": "browserify -d -t babelify -s Loader -e ./src/index.js -o ./dist/resource-loader.js",
"minify": "uglifyjs --output ./dist/resource-loader.min.js -- ./dist/resource-loader.js",
"build": "npm run clean && browserify -d -s Loader -e ./src/index.js -o ./dist/resource-loader.js && npm run minify",
"dev": "npm run clean && watchify -d -s Loader -e ./src/index.js -o ./dist/resource-loader.js",
"lint": "eslint src/ && eslint test/",
"docs": "jsdoc -c ./gulp/util/jsdoc.conf.json"
"dev": "watchify -d -t babelify -s Loader -e ./src/index.js -o ./dist/resource-loader.js",
"lint": "eslint src/ test/",
"start": "npm run clean && npm run build",
"test": "npm run test-dev -- --single-run",
"test-dev": "karma start test/karma.conf.js",
"docs": "jsdoc -c jsdoc.conf.json -R README.md",
"prepublish": "npm run build && npm run minify"
},
"dependencies": {
"eventemitter3": "^2.0.0",
"mini-signals": "^1.1.1",
"parse-uri": "^1.0.0"

@@ -39,13 +41,26 @@ },

"@englercj/code-style": "^1.0.6",
"babel-preset-es2015": "^6.16.0",
"babelify": "^7.3.0",
"browserify": "^13.1.0",
"chai": "^3.5.0",
"eslint": "^3.1.1",
"ink-docstrap": "^1.2.1",
"jsdoc": "^3.4.0",
"sinon": "^1.17.5",
"eslint": "^3.7.1",
"ink-docstrap": "^1.3.0",
"jsdoc": "^3.4.2",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-firefox-launcher": "^1.0.0",
"karma-mocha": "^1.2.0",
"karma-mocha-reporter": "^2.2.0",
"karma-sinon-chai": "^1.2.4",
"mocha": "^3.1.2",
"sinon": "^1.17.6",
"sinon-chai": "^2.8.0",
"testem": "^1.10.2",
"uglify-js": "^2.7.0",
"uglify-js": "^2.7.3",
"watchify": "^3.7.0"
},
"babel": {
"presets": [
["es2015", { "loose": true }]
]
}
}

@@ -9,20 +9,19 @@ # Resource Loader [![Build Status](https://travis-ci.org/englercj/resource-loader.svg?branch=master)](https://travis-ci.org/englercj/resource-loader)

// ctor
var loader = new Loader();
const loader = new Loader();
loader
// chainable `add` to enqueue a resource
// Chainable `add` to enqueue a resource
.add(name, url, options)
// chainable `before` to add a middleware that runs for each resource, *before* loading a resource.
// this is useful to implement custom caching modules (using filesystem, indexeddb, memory, etc).
.before(cachingMiddleware);
// Chainable `pre` to add a middleware that runs for each resource, *before* loading that resource.
// This is useful to implement custom caching modules (using filesystem, indexeddb, memory, etc).
.pre(cachingMiddleware)
// chainable `after` to add a middleware that runs for each resource, *after* loading a resource.
// this is useful to implement custom parsing modules (like spritesheet parsers, spine parser, etc).
.after(parsingMiddleware);
// Chainable `use` to add a middleware that runs for each resource, *after* loading that resource.
// This is useful to implement custom parsing modules (like spritesheet parsers, spine parser, etc).
.use(parsingMiddleware)
// `load` method loads the queue of resources, and calls the passed in callback called once all
// The `load` method loads the queue of resources, and calls the passed in callback called once all
// resources have loaded.
.load(function (loader, resources) {
.load((loader, resources) => {
// resources is an object where the key is the name of the resource loaded and the value is the resource object.

@@ -36,7 +35,7 @@ // They have a couple default properties:

// throughout the process multiple events can happen.
loader.on('progress', ...); // called once per loaded/errored file
loader.on('error', ...); // called once per errored file
loader.on('load', ...); // called once per loaded file
loader.on('complete', ...); // called once when the queued resources all load.
// throughout the process multiple signals can be dispatched.
loader.onProgress.add(() => {}); // called once per loaded/errored file
loader.onError.add(() => {}); // called once per errored file
loader.onLoad.add(() => {}); // called once per loaded file
loader.onComplete.add(() => {}); // called once when the queued resources all load.
```

@@ -66,1 +65,8 @@

- Opera 12.1+
## Upgrading to v2
- No more events, all signals now
- No more isJson, isXml, etc. Now use `res.type === Resource.TYPE.JSON`, etc.
- Removed `before` (in favor of `pre`) and `after` (in favor of `use`).
- If a middleware adds more resources, it *must* pass in the parent resource in options for `.add()`.

@@ -1,3 +0,1 @@

'use strict';
/**

@@ -7,8 +5,2 @@ * Smaller version of the async library constructs.

*/
module.exports = {
eachSeries: asyncEachSeries,
queue: asyncQueue
};
function _noop() { /* empty */ }

@@ -23,5 +15,5 @@

*/
function asyncEachSeries(array, iterator, callback) {
var i = 0;
var len = array.length;
export function eachSeries(array, iterator, callback) {
let i = 0;
const len = array.length;

@@ -53,3 +45,3 @@ (function next(err) {

var callFn = fn;
const callFn = fn;

@@ -68,3 +60,3 @@ fn = null;

*/
function asyncQueue(worker, concurrency) {
export function queue(worker, concurrency) {
if (concurrency == null) { // eslint-disable-line no-eq-null,eqeqeq

@@ -77,6 +69,6 @@ concurrency = 1;

var workers = 0;
var q = {
let workers = 0;
const q = {
_tasks: [],
concurrency: concurrency,
concurrency,
saturated: _noop,

@@ -90,15 +82,17 @@ unsaturated: _noop,

paused: false,
push: function (data, callback) {
push(data, callback) {
_insert(data, false, callback);
},
kill: function () {
kill() {
workers = 0;
q.drain = _noop;
q.started = false;
q._tasks = [];
},
unshift: function (data, callback) {
unshift(data, callback) {
_insert(data, true, callback);
},
process: function () {
process() {
while (!q.paused && workers < q.concurrency && q._tasks.length) {
var task = q._tasks.shift();
const task = q._tasks.shift();

@@ -118,12 +112,12 @@ if (q._tasks.length === 0) {

},
length: function () {
length() {
return q._tasks.length;
},
running: function () {
running() {
return workers;
},
idle: function () {
idle() {
return q._tasks.length + workers === 0;
},
pause: function () {
pause() {
if (q.paused === true) {

@@ -135,3 +129,3 @@ return;

},
resume: function () {
resume() {
if (q.paused === false) {

@@ -145,6 +139,6 @@ return;

// worker to preserve full concurrency after pause
for (var w = 1; w <= q.concurrency; w++) {
for (let w = 1; w <= q.concurrency; w++) {
q.process();
}
}
},
};

@@ -161,5 +155,3 @@

// call drain immediately if there are no tasks
setTimeout(function () {
q.drain();
}, 1);
setTimeout(() => q.drain(), 1);

@@ -169,5 +161,5 @@ return;

var item = {
data: data,
callback: typeof callback === 'function' ? callback : _noop
const item = {
data,
callback: typeof callback === 'function' ? callback : _noop,
};

@@ -182,9 +174,7 @@

setTimeout(function () {
q.process();
}, 1);
setTimeout(() => q.process(), 1);
}
function _next(task) {
return function () {
return function next() {
workers -= 1;

@@ -191,0 +181,0 @@

@@ -1,68 +0,63 @@

/* eslint no-magic-numbers: 0 */
'use strict';
const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
module.exports = {
// private property
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
export function encodeBinary(input) {
let output = '';
let inx = 0;
encodeBinary: function (input) {
var output = '';
var bytebuffer;
var encodedCharIndexes = new Array(4);
var inx = 0;
var jnx = 0;
var paddingBytes = 0;
while (inx < input.length) {
// Fill byte buffer array
const bytebuffer = [0, 0, 0];
const encodedCharIndexes = [0, 0, 0, 0];
while (inx < input.length) {
// Fill byte buffer array
bytebuffer = new Array(3);
for (jnx = 0; jnx < bytebuffer.length; jnx++) {
if (inx < input.length) {
// throw away high-order byte, as documented at:
// https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
}
else {
bytebuffer[jnx] = 0;
}
for (let jnx = 0; jnx < bytebuffer.length; ++jnx) {
if (inx < input.length) {
// throw away high-order byte, as documented at:
// https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
}
else {
bytebuffer[jnx] = 0;
}
}
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Get each encoded character, 6 bits at a time
// index 1: first 6 bits
encodedCharIndexes[0] = bytebuffer[0] >> 2;
// Determine whether padding happened, and adjust accordingly
paddingBytes = inx - (input.length - 1);
switch (paddingBytes) {
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
default:
break; // No padding - proceed
}
// index 3: forth 6 bits (6 least significant bits from input byte 3)
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for (jnx = 0; jnx < encodedCharIndexes.length; jnx++) {
output += this._keyStr.charAt(encodedCharIndexes[jnx]);
}
// Determine whether padding happened, and adjust accordingly
const paddingBytes = inx - (input.length - 1);
switch (paddingBytes) {
case 2:
// Set last 2 characters to padding char
encodedCharIndexes[3] = 64;
encodedCharIndexes[2] = 64;
break;
case 1:
// Set last character to padding char
encodedCharIndexes[3] = 64;
break;
default:
break; // No padding - proceed
}
return output;
// Now we will grab each appropriate character out of our keystring
// based on our index array and append it to the output string
for (let jnx = 0; jnx < encodedCharIndexes.length; ++jnx) {
output += _keyStr.charAt(encodedCharIndexes[jnx]);
}
}
};
return output;
}

@@ -1,15 +0,10 @@

/* eslint global-require: 0 */
'use strict';
import Loader from './Loader';
import Resource from './Resource';
import * as async from './async';
import * as b64 from './b64';
module.exports = require('./Loader');
module.exports.Resource = require('./Resource');
module.exports.middleware = {
caching: {
memory: require('./middlewares/caching/memory')
},
parsing: {
blob: require('./middlewares/parsing/blob')
}
};
Loader.Resource = Resource;
Loader.async = async;
Loader.base64 = b64;
module.exports.async = require('./async');
module.exports = Loader; // eslint-disable-line no-undef

@@ -1,11 +0,9 @@

'use strict';
import Signal from 'mini-signals';
import parseUri from 'parse-uri';
import * as async from './async';
import Resource from './Resource';
var parseUri = require('parse-uri');
var async = require('./async');
var Resource = require('./Resource');
var EventEmitter = require('eventemitter3');
// some constants
var DEFAULT_CONCURRENCY = 10;
var MAX_PROGRESS = 100;
const MAX_PROGRESS = 100;
const rgxExtractUrlHash = /(#[\w\-]+)?$/;

@@ -16,478 +14,543 @@ /**

* @class
* @param {string} [baseUrl=''] - The base url for all resources loaded by this loader.
* @param {number} [concurrency=10] - The number of resources to load concurrently.
*/
function Loader(baseUrl, concurrency) {
EventEmitter.call(this);
concurrency = concurrency || DEFAULT_CONCURRENCY;
export default class Loader {
/**
* The base url for all resources loaded by this loader.
*
* @member {string}
* @param {string} [baseUrl=''] - The base url for all resources loaded by this loader.
* @param {number} [concurrency=10] - The number of resources to load concurrently.
*/
this.baseUrl = baseUrl || '';
constructor(baseUrl = '', concurrency = 10) {
/**
* The base url for all resources loaded by this loader.
*
* @member {string}
*/
this.baseUrl = baseUrl;
/**
* The progress percent of the loader going through the queue.
*
* @member {number}
*/
this.progress = 0;
/**
* The progress percent of the loader going through the queue.
*
* @member {number}
*/
this.progress = 0;
/**
* Loading state of the loader, true if it is currently loading resources.
*
* @member {boolean}
*/
this.loading = false;
/**
* Loading state of the loader, true if it is currently loading resources.
*
* @member {boolean}
*/
this.loading = false;
/**
* The percentage of total progress that a single resource represents.
*
* @member {number}
*/
this._progressChunk = 0;
/**
* A querystring to append to every URL added to the loader.
*
* This should be a valid query string *without* the question-mark (`?`). The loader will
* also *not* escape values for you. Make sure to escape your parameters with
* [`encodeURIComponent`](https://mdn.io/encodeURIComponent) before assigning this property.
*
* @example
*
* ```js
* const loader = new Loader();
*
* loader.defaultQueryString = 'user=me&password=secret';
*
* // This will request 'image.png?user=me&password=secret'
* loader.add('image.png').load();
*
* loader.reset();
*
* // This will request 'image.png?v=1&user=me&password=secret'
* loader.add('iamge.png?v=1').load();
* ```
*/
this.defaultQueryString = '';
/**
* The middleware to run before loading each resource.
*
* @member {function[]}
*/
this._beforeMiddleware = [];
/**
* The middleware to run before loading each resource.
*
* @member {function[]}
*/
this._beforeMiddleware = [];
/**
* The middleware to run after loading each resource.
*
* @member {function[]}
*/
this._afterMiddleware = [];
/**
* The middleware to run after loading each resource.
*
* @member {function[]}
*/
this._afterMiddleware = [];
/**
* The `_loadResource` function bound with this object context.
*
* @private
* @member {function}
*/
this._boundLoadResource = this._loadResource.bind(this);
/**
* The `_loadResource` function bound with this object context.
*
* @private
* @member {function}
* @param {Resource} r - The resource to load
* @param {Function} d - The dequeue function
* @return {undefined}
*/
this._boundLoadResource = (r, d) => this._loadResource(r, d);
/**
* The resource buffer that fills until `load` is called to start loading resources.
*
* @private
* @member {Resource[]}
*/
this._buffer = [];
/**
* The resources waiting to be loaded.
*
* @private
* @member {Resource[]}
*/
this._queue = async.queue(this._boundLoadResource, concurrency);
/**
* Used to track load completion.
*
* @private
* @member {number}
*/
this._numToLoad = 0;
this._queue.pause();
/**
* The resources waiting to be loaded.
*
* @private
* @member {Resource[]}
*/
this._queue = async.queue(this._boundLoadResource, concurrency);
/**
* All the resources for this loader keyed by name.
*
* @member {object<string, Resource>}
*/
this.resources = {};
/**
* All the resources for this loader keyed by name.
*
* @member {object<string, Resource>}
*/
this.resources = {};
/**
* Dispatched once per loaded or errored resource.
*
* The callback looks like {@link Loader.OnProgressSignal}.
*
* @member {Signal}
*/
this.onProgress = new Signal();
/**
* Emitted once per loaded or errored resource.
*
* @event progress
* @memberof Loader#
*/
/**
* Dispatched once per errored resource.
*
* The callback looks like {@link Loader.OnErrorSignal}.
*
* @member {Signal}
*/
this.onError = new Signal();
/**
* Emitted once per errored resource.
*
* @event error
* @memberof Loader#
*/
/**
* Dispatched once per loaded resource.
*
* The callback looks like {@link Loader.OnLoadSignal}.
*
* @member {Signal}
*/
this.onLoad = new Signal();
/**
* Emitted once per loaded resource.
*
* @event load
* @memberof Loader#
*/
/**
* Dispatched when the loader begins to process the queue.
*
* The callback looks like {@link Loader.OnStartSignal}.
*
* @member {Signal}
*/
this.onStart = new Signal();
/**
* Emitted when the loader begins to process the queue.
*
* @event start
* @memberof Loader#
*/
/**
* Dispatched when the queued resources all load.
*
* The callback looks like {@link Loader.OnCompleteSignal}.
*
* @member {Signal}
*/
this.onComplete = new Signal();
/**
* Emitted when the queued resources all load.
*
* @event complete
* @memberof Loader#
*/
}
/**
* When the progress changes the loader and resource are disaptched.
*
* @memberof Loader
* @callback OnProgressSignal
* @param {Loader} loader - The loader the progress is advancing on.
* @param {Resource} resource - The resource that has completed or failed to cause the progress to advance.
*/
Loader.prototype = Object.create(EventEmitter.prototype);
Loader.prototype.constructor = Loader;
module.exports = Loader;
/**
* When an error occurrs the loader and resource are disaptched.
*
* @memberof Loader
* @callback OnErrorSignal
* @param {Loader} loader - The loader the error happened in.
* @param {Resource} resource - The resource that caused the error.
*/
/**
* Adds a resource (or multiple resources) to the loader queue.
*
* This function can take a wide variety of different parameters. The only thing that is always
* required the url to load. All the following will work:
*
* ```js
* loader
* // normal param syntax
* .add('key', 'http://...', function () {})
* .add('http://...', function () {})
* .add('http://...')
*
* // object syntax
* .add({
* name: 'key2',
* url: 'http://...'
* }, function () {})
* .add({
* url: 'http://...'
* }, function () {})
* .add({
* name: 'key3',
* url: 'http://...'
* onComplete: function () {}
* })
* .add({
* url: 'https://...',
* onComplete: function () {},
* crossOrigin: true
* })
*
* // you can also pass an array of objects or urls or both
* .add([
* { name: 'key4', url: 'http://...', onComplete: function () {} },
* { url: 'http://...', onComplete: function () {} },
* 'http://...'
* ])
*
* // and you can use both params and options
* .add('key', 'http://...', { crossOrigin: true }, function () {})
* .add('http://...', { crossOrigin: true }, function () {});
* ```
*
* @alias enqueue
* @param {string} [name] - The name of the resource to load, if not passed the url is used.
* @param {string} [url] - The url for this resource, relative to the baseUrl of this loader.
* @param {object} [options] - The options for the load.
* @param {boolean} [options.crossOrigin] - Is this request cross-origin? Default is to determine automatically.
* @param {Resource.XHR_LOAD_TYPE} [options.loadType=Resource.LOAD_TYPE.XHR] - How should this resource be loaded?
* @param {Resource.XHR_RESPONSE_TYPE} [options.xhrType=Resource.XHR_RESPONSE_TYPE.DEFAULT] - How should the data being
* loaded be interpreted when using XHR?
* @param {function} [cb] - Function to call when this specific resource completes loading.
* @return {Loader} Returns itself.
*/
Loader.prototype.add = Loader.prototype.enqueue = function (name, url, options, cb) {
// special case of an array of objects or urls
if (Array.isArray(name)) {
for (var i = 0; i < name.length; ++i) {
this.add(name[i]);
}
/**
* When a load completes the loader and resource are disaptched.
*
* @memberof Loader
* @callback OnLoadSignal
* @param {Loader} loader - The loader that laoded the resource.
* @param {Resource} resource - The resource that has completed loading.
*/
return this;
}
/**
* When the loader starts loading resources it dispatches this callback.
*
* @memberof Loader
* @callback OnStartSignal
* @param {Loader} loader - The loader that has started loading resources.
*/
// if an object is passed instead of params
if (typeof name === 'object') {
cb = url || name.callback || name.onComplete;
options = name;
url = name.url;
name = name.name || name.key || name.url;
/**
* When the loader completes loading resources it dispatches this callback.
*
* @memberof Loader
* @callback OnCompleteSignal
* @param {Loader} loader - The loader that has finished loading resources.
*/
}
// case where no name is passed shift all args over by one.
if (typeof url !== 'string') {
cb = options;
options = url;
url = name;
}
/**
* Adds a resource (or multiple resources) to the loader queue.
*
* This function can take a wide variety of different parameters. The only thing that is always
* required the url to load. All the following will work:
*
* ```js
* loader
* // normal param syntax
* .add('key', 'http://...', function () {})
* .add('http://...', function () {})
* .add('http://...')
*
* // object syntax
* .add({
* name: 'key2',
* url: 'http://...'
* }, function () {})
* .add({
* url: 'http://...'
* }, function () {})
* .add({
* name: 'key3',
* url: 'http://...'
* onComplete: function () {}
* })
* .add({
* url: 'https://...',
* onComplete: function () {},
* crossOrigin: true
* })
*
* // you can also pass an array of objects or urls or both
* .add([
* { name: 'key4', url: 'http://...', onComplete: function () {} },
* { url: 'http://...', onComplete: function () {} },
* 'http://...'
* ])
*
* // and you can use both params and options
* .add('key', 'http://...', { crossOrigin: true }, function () {})
* .add('http://...', { crossOrigin: true }, function () {});
* ```
*
* @param {string} [name] - The name of the resource to load, if not passed the url is used.
* @param {string} [url] - The url for this resource, relative to the baseUrl of this loader.
* @param {object} [options] - The options for the load.
* @param {boolean} [options.crossOrigin] - Is this request cross-origin? Default is to determine automatically.
* @param {Resource.LOAD_TYPE} [options.loadType=Resource.LOAD_TYPE.XHR] - How should this resource be loaded?
* @param {Resource.XHR_RESPONSE_TYPE} [options.xhrType=Resource.XHR_RESPONSE_TYPE.DEFAULT] - How should
* the data being loaded be interpreted when using XHR?
* @param {object} [options.metadata] - Extra configuration for middleware and the Resource object.
* @param {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [options.metadata.loadElement=null] - The
* element to use for loading, instead of creating one.
* @param {boolean} [options.metadata.skipSource=false] - Skips adding source(s) to the load element. This
* is useful if you want to pass in a `loadElement` that you already added load sources to.
* @param {function} [cb] - Function to call when this specific resource completes loading.
* @return {Loader} Returns itself.
*/
add(name, url, options, cb) {
// special case of an array of objects or urls
if (Array.isArray(name)) {
for (let i = 0; i < name.length; ++i) {
this.add(name[i]);
}
// now that we shifted make sure we have a proper url.
if (typeof url !== 'string') {
throw new Error('No url passed to add resource to loader.');
}
return this;
}
// options are optional so people might pass a function and no options
if (typeof options === 'function') {
cb = options;
options = null;
}
// if an object is passed instead of params
if (typeof name === 'object') {
cb = url || name.callback || name.onComplete;
options = name;
url = name.url;
name = name.name || name.key || name.url;
}
// check if resource already exists.
if (this.resources[name]) {
throw new Error('Resource with name "' + name + '" already exists.');
}
// case where no name is passed shift all args over by one.
if (typeof url !== 'string') {
cb = options;
options = url;
url = name;
}
// add base url if this isn't an absolute url
url = this._prepareUrl(url);
// now that we shifted make sure we have a proper url.
if (typeof url !== 'string') {
throw new Error('No url passed to add resource to loader.');
}
// create the store the resource
this.resources[name] = new Resource(name, url, options);
// options are optional so people might pass a function and no options
if (typeof options === 'function') {
cb = options;
options = null;
}
if (typeof cb === 'function') {
this.resources[name].once('afterMiddleware', cb);
}
// if loading already you can only add resources that have a parent.
if (this.loading && (!options || !options.parentResource)) {
throw new Error('Cannot add resources while the loader is running.');
}
this._numToLoad++;
// check if resource already exists.
if (this.resources[name]) {
throw new Error(`Resource named "${name}" already exists.`);
}
// if already loading add it to the worker queue
if (this._queue.started) {
this._queue.push(this.resources[name]);
this._progressChunk = (MAX_PROGRESS - this.progress) / (this._queue.length() + this._queue.running());
}
// otherwise buffer it to be added to the queue later
else {
this._buffer.push(this.resources[name]);
this._progressChunk = MAX_PROGRESS / this._buffer.length;
}
// add base url if this isn't an absolute url
url = this._prepareUrl(url);
return this;
};
// create the store the resource
this.resources[name] = new Resource(name, url, options);
/**
* Sets up a middleware function that will run *before* the
* resource is loaded.
*
* @alias pre
* @method before
* @param {function} fn - The middleware function to register.
* @return {Loader} Returns itself.
*/
Loader.prototype.before = Loader.prototype.pre = function (fn) {
this._beforeMiddleware.push(fn);
if (typeof cb === 'function') {
this.resources[name].onAfterMiddleware.once(cb);
}
return this;
};
// if loading make sure to adjust progress chunks for that parent and its children
if (this.loading) {
const parent = options.parentResource;
const fullChunk = parent.progressChunk * (parent.children.length + 1); // +1 for parent
const eachChunk = fullChunk / (parent.children.length + 2); // +2 for parent & new child
/**
* Sets up a middleware function that will run *after* the
* resource is loaded.
*
* @alias use
* @method after
* @param {function} fn - The middleware function to register.
* @return {Loader} Returns itself.
*/
Loader.prototype.after = Loader.prototype.use = function (fn) {
this._afterMiddleware.push(fn);
parent.children.push(this.resources[name]);
parent.progressChunk = eachChunk;
return this;
};
for (let i = 0; i < parent.children.length; ++i) {
parent.children[i].progressChunk = eachChunk;
}
}
/**
* Resets the queue of the loader to prepare for a new load.
*
* @return {Loader} Returns itself.
*/
Loader.prototype.reset = function () {
// this.baseUrl = baseUrl || '';
// add the resource to the queue
this._queue.push(this.resources[name]);
this.progress = 0;
return this;
}
this.loading = false;
/**
* Sets up a middleware function that will run *before* the
* resource is loaded.
*
* @method before
* @param {function} fn - The middleware function to register.
* @return {Loader} Returns itself.
*/
pre(fn) {
this._beforeMiddleware.push(fn);
this._progressChunk = 0;
return this;
}
// this._beforeMiddleware.length = 0;
// this._afterMiddleware.length = 0;
/**
* Sets up a middleware function that will run *after* the
* resource is loaded.
*
* @alias use
* @method after
* @param {function} fn - The middleware function to register.
* @return {Loader} Returns itself.
*/
use(fn) {
this._afterMiddleware.push(fn);
this._buffer.length = 0;
return this;
}
this._numToLoad = 0;
/**
* Resets the queue of the loader to prepare for a new load.
*
* @return {Loader} Returns itself.
*/
reset() {
this.progress = 0;
this.loading = false;
this._queue.kill();
this._queue.started = false;
this._queue.kill();
this._queue.pause();
// abort all resource loads
for (var k in this.resources) {
var res = this.resources[k];
// abort all resource loads
for (const k in this.resources) {
const res = this.resources[k];
res.off('complete', this._onLoad, this);
if (res._onLoadBinding) {
res._onLoadBinding.detach();
}
if (res.isLoading) {
res.abort();
if (res.isLoading) {
res.abort();
}
}
}
this.resources = {};
this.resources = {};
return this;
};
/**
* Starts loading the queued resources.
*
* @fires start
* @param {function} [cb] - Optional callback that will be bound to the `complete` event.
* @return {Loader} Returns itself.
*/
Loader.prototype.load = function (cb) {
// register complete callback if they pass one
if (typeof cb === 'function') {
this.once('complete', cb);
}
// if the queue has already started we are done here
if (this._queue.started) {
return this;
}
// notify of start
this.emit('start', this);
/**
* Starts loading the queued resources.
*
* @param {function} [cb] - Optional callback that will be bound to the `complete` event.
* @return {Loader} Returns itself.
*/
load(cb) {
// register complete callback if they pass one
if (typeof cb === 'function') {
this.onComplete.once(cb);
}
// update loading state
this.loading = true;
// if the queue has already started we are done here
if (this.loading) {
return this;
}
// start the internal queue
for (var i = 0; i < this._buffer.length; ++i) {
this._queue.push(this._buffer[i]);
}
// distribute progress chunks
const chunk = 100 / this._queue._tasks.length;
// empty the buffer
this._buffer.length = 0;
for (let i = 0; i < this._queue._tasks.length; ++i) {
this._queue._tasks[i].data.progressChunk = chunk;
}
return this;
};
// update loading state
this.loading = true;
/**
* Prepares a url for usage based on the configuration of this object
*
* @private
* @param {string} url - The url to prepare.
* @return {string} The prepared url.
*/
Loader.prototype._prepareUrl = function (url) {
var parsedUrl = parseUri(url, { strictMode: true });
// notify of start
this.onStart.dispatch(this);
// absolute url, just use it as is.
if (parsedUrl.protocol || !parsedUrl.path || parsedUrl.path.indexOf('//') === 0) {
return url;
}
// start loading
this._queue.resume();
// if baseUrl doesn't end in slash and url doesn't start with slash, then add a slash inbetween
if (this.baseUrl.length
&& this.baseUrl.lastIndexOf('/') !== this.baseUrl.length - 1
&& url.charAt(0) !== '/'
) {
return this.baseUrl + '/' + url;
return this;
}
return this.baseUrl + url;
};
/**
* Prepares a url for usage based on the configuration of this object
*
* @private
* @param {string} url - The url to prepare.
* @return {string} The prepared url.
*/
_prepareUrl(url) {
const parsedUrl = parseUri(url, { strictMode: true });
let result;
/**
* Loads a single resource.
*
* @private
* @param {Resource} resource - The resource to load.
* @param {function} dequeue - The function to call when we need to dequeue this item.
*/
Loader.prototype._loadResource = function (resource, dequeue) {
var self = this;
// absolute url, just use it as is.
if (parsedUrl.protocol || !parsedUrl.path || url.indexOf('//') === 0) {
result = url;
}
// if baseUrl doesn't end in slash and url doesn't start with slash, then add a slash inbetween
else if (this.baseUrl.length
&& this.baseUrl.lastIndexOf('/') !== this.baseUrl.length - 1
&& url.charAt(0) !== '/'
) {
result = `${this.baseUrl}/${url}`;
}
else {
result = this.baseUrl + url;
}
resource._dequeue = dequeue;
// if we need to add a default querystring, there is a bit more work
if (this.defaultQueryString) {
const hash = rgxExtractUrlHash.exec(result)[0];
// run before middleware
async.eachSeries(
this._beforeMiddleware,
function (fn, next) {
fn.call(self, resource, function () {
// if the before middleware marks the resource as complete,
// break and don't process any more before middleware
next(resource.isComplete ? {} : null);
});
},
function () {
// resource.on('progress', self.emit.bind(self, 'progress'));
result = result.substr(0, result.length - hash.length);
if (resource.isComplete) {
self._onLoad(resource);
if (result.indexOf('?') !== -1) {
result += `&${this.defaultQueryString}`;
}
else {
resource.once('complete', self._onLoad, self);
resource.load();
result += `?${this.defaultQueryString}`;
}
result += hash;
}
);
};
/**
* Called once each resource has loaded.
*
* @fires complete
* @private
*/
Loader.prototype._onComplete = function () {
this.loading = false;
return result;
}
this.emit('complete', this, this.resources);
};
/**
* Loads a single resource.
*
* @private
* @param {Resource} resource - The resource to load.
* @param {function} dequeue - The function to call when we need to dequeue this item.
*/
_loadResource(resource, dequeue) {
resource._dequeue = dequeue;
/**
* Called each time a resources is loaded.
*
* @fires progress
* @fires error
* @fires load
* @private
* @param {Resource} resource - The resource that was loaded
*/
Loader.prototype._onLoad = function (resource) {
var self = this;
// run before middleware
async.eachSeries(
this._beforeMiddleware,
(fn, next) => {
fn.call(this, resource, () => {
// if the before middleware marks the resource as complete,
// break and don't process any more before middleware
next(resource.isComplete ? {} : null);
});
},
() => {
if (resource.isComplete) {
this._onLoad(resource);
}
else {
resource._onLoadBinding = resource.onComplete.once(this._onLoad, this);
resource.load();
}
}
);
}
// run middleware, this *must* happen before dequeue so sub-assets get added properly
async.eachSeries(
this._afterMiddleware,
function (fn, next) {
fn.call(self, resource, next);
},
function () {
resource.emit('afterMiddleware', resource);
/**
* Called once each resource has loaded.
*
* @private
*/
_onComplete() {
this.loading = false;
self._numToLoad--;
this.onComplete.dispatch(this, this.resources);
}
self.progress += self._progressChunk;
self.emit('progress', self, resource);
/**
* Called each time a resources is loaded.
*
* @private
* @param {Resource} resource - The resource that was loaded
*/
_onLoad(resource) {
resource._onLoadBinding = null;
if (resource.error) {
self.emit('error', resource.error, self, resource);
}
else {
self.emit('load', self, resource);
}
// run middleware, this *must* happen before dequeue so sub-assets get added properly
async.eachSeries(
this._afterMiddleware,
(fn, next) => {
fn.call(this, resource, next);
},
() => {
resource.onAfterMiddleware.dispatch(resource);
// do completion check
if (self._numToLoad === 0) {
self.progress = 100;
self._onComplete();
}
}
);
this.progress += resource.progressChunk;
this.onProgress.dispatch(this, resource);
// remove this resource from the async queue
resource._dequeue();
};
if (resource.error) {
this.onError.dispatch(resource.error, this, resource);
}
else {
this.onLoad.dispatch(this, resource);
}
Loader.LOAD_TYPE = Resource.LOAD_TYPE;
Loader.XHR_RESPONSE_TYPE = Resource.XHR_RESPONSE_TYPE;
// remove this resource from the async queue
resource._dequeue();
// do completion check
if (this._queue.idle()) {
this.progress = MAX_PROGRESS;
this._onComplete();
}
}
);
}
}

@@ -1,8 +0,6 @@

'use strict';
// a simple in-memory cache for resources
var cache = {};
const cache = {};
module.exports = function () {
return function (resource, next) {
export function memoryMiddlewareFactory() {
return function memoryMiddleware(resource, next) {
// if cached, then set data and complete the resource

@@ -15,5 +13,3 @@ if (cache[resource.url]) {

else {
resource.once('complete', function () {
cache[this.url] = this.data;
});
resource.onComplete.once(() => (cache[this.url] = this.data));
}

@@ -23,2 +19,2 @@

};
};
}

@@ -1,12 +0,9 @@

'use strict';
import Resource from '../../Resource';
import b64 from '../../b64';
var Resource = require('../../Resource');
var b64 = require('../../b64');
const Url = window.URL || window.webkitURL;
var Url = window.URL || window.webkitURL;
// a middleware for transforming XHR loaded Blobs into more useful objects
module.exports = function () {
return function (resource, next) {
export function blobMiddlewareFactory() {
return function blobMiddleware(resource, next) {
if (!resource.data) {

@@ -22,3 +19,3 @@ next();

if (!window.Blob || typeof resource.data === 'string') {
var type = resource.xhr.getResponseHeader('content-type');
const type = resource.xhr.getResponseHeader('content-type');

@@ -28,8 +25,8 @@ // this is an image, convert the binary string into a data url

resource.data = new Image();
resource.data.src = 'data:' + type + ';base64,' + b64.encodeBinary(resource.xhr.responseText);
resource.data.src = `data:${type};base64,${b64.encodeBinary(resource.xhr.responseText)}`;
resource.isImage = true;
resource.type = Resource.TYPE.IMAGE;
// wait until the image loads and then callback
resource.data.onload = function () {
resource.data.onload = () => {
resource.data.onload = null;

@@ -46,3 +43,3 @@

else if (resource.data.type.indexOf('image') === 0) {
var src = Url.createObjectURL(resource.data);
const src = Url.createObjectURL(resource.data);

@@ -53,6 +50,7 @@ resource.blob = resource.data;

resource.isImage = true;
resource.type = Resource.TYPE.IMAGE;
// cleanup the no longer used blob after the image loads
resource.data.onload = function () {
// TODO: Is this correct? Will the image be invalid after revoking?
resource.data.onload = () => {
Url.revokeObjectURL(src);

@@ -71,2 +69,2 @@ resource.data.onload = null;

};
};
}

@@ -1,160 +0,301 @@

'use strict';
import parseUri from 'parse-uri';
import Signal from 'mini-signals';
var EventEmitter = require('eventemitter3');
var parseUri = require('parse-uri');
// tests is CORS is supported in XHR, if not we need to use XDR
var useXdr = !!(window.XDomainRequest && !('withCredentials' in (new XMLHttpRequest())));
var tempAnchor = null;
const useXdr = !!(window.XDomainRequest && !('withCredentials' in (new XMLHttpRequest())));
let tempAnchor = null;
// some status constants
var STATUS_NONE = 0;
var STATUS_OK = 200;
var STATUS_EMPTY = 204;
const STATUS_NONE = 0;
const STATUS_OK = 200;
const STATUS_EMPTY = 204;
// noop
function _noop() { /* empty */ }
/**
* Manages the state and loading of a single resource represented by
* a single URL.
* Manages the state and loading of a resource and all child resources.
*
* @class
* @param {string} name - The name of the resource to load.
* @param {string|string[]} url - The url for this resource, for audio/video loads you can pass an array of sources.
* @param {object} [options] - The options for the load.
* @param {string|boolean} [options.crossOrigin] - Is this request cross-origin? Default is to determine automatically.
* @param {Resource.LOAD_TYPE} [options.loadType=Resource.LOAD_TYPE.XHR] - How should this resource be loaded?
* @param {Resource.XHR_RESPONSE_TYPE} [options.xhrType=Resource.XHR_RESPONSE_TYPE.DEFAULT] - How should the data being
* loaded be interpreted when using XHR?
* @param {object} [options.metadata] - Extra info for middleware.
*/
function Resource(name, url, options) {
EventEmitter.call(this);
options = options || {};
if (typeof name !== 'string' || typeof url !== 'string') {
throw new Error('Both name and url are required for constructing a resource.');
}
export default class Resource {
/**
* The name of this resource.
* Sets the load type to be used for a specific extension.
*
* @member {string}
* @readonly
* @static
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
* @param {Resource.LOAD_TYPE} loadType - The load type to set it to.
*/
this.name = name;
static setExtensionLoadType(extname, loadType) {
setExtMap(Resource._loadTypeMap, extname, loadType);
}
/**
* The url used to load this resource.
* Sets the load type to be used for a specific extension.
*
* @member {string}
* @readonly
* @static
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
* @param {Resource.XHR_RESPONSE_TYPE} xhrType - The xhr type to set it to.
*/
this.url = url;
static setExtensionXhrType(extname, xhrType) {
setExtMap(Resource._xhrTypeMap, extname, xhrType);
}
/**
* Stores whether or not this url is a data url.
*
* @member {boolean}
* @readonly
* @param {string} name - The name of the resource to load.
* @param {string|string[]} url - The url for this resource, for audio/video loads you can pass
* an array of sources.
* @param {object} [options] - The options for the load.
* @param {string|boolean} [options.crossOrigin] - Is this request cross-origin? Default is to
* determine automatically.
* @param {Resource.LOAD_TYPE} [options.loadType=Resource.LOAD_TYPE.XHR] - How should this resource
* be loaded?
* @param {Resource.XHR_RESPONSE_TYPE} [options.xhrType=Resource.XHR_RESPONSE_TYPE.DEFAULT] - How
* should the data being loaded be interpreted when using XHR?
* @param {object} [options.metadata] - Extra configuration for middleware and the Resource object.
* @param {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [options.metadata.loadElement=null] - The
* element to use for loading, instead of creating one.
* @param {boolean} [options.metadata.skipSource=false] - Skips adding source(s) to the load element. This
* is useful if you want to pass in a `loadElement` that you already added load sources to.
*/
this.isDataUrl = this.url.indexOf('data:') === 0;
constructor(name, url, options) {
if (typeof name !== 'string' || typeof url !== 'string') {
throw new Error('Both name and url are required for constructing a resource.');
}
/**
* The data that was loaded by the resource.
*
* @member {any}
*/
this.data = null;
options = options || {};
/**
* Is this request cross-origin? If unset, determined automatically.
*
* @member {string}
*/
this.crossOrigin = options.crossOrigin === true ? 'anonymous' : options.crossOrigin;
/**
* The state flags of this resource.
*
* @member {number}
*/
this._flags = 0;
/**
* The method of loading to use for this resource.
*
* @member {Resource.LOAD_TYPE}
*/
this.loadType = options.loadType || this._determineLoadType();
// set data url flag, needs to be set early for some _determineX checks to work.
this._setFlag(Resource.STATUS_FLAGS.DATA_URL, url.indexOf('data:') === 0);
/**
* The type used to load the resource via XHR. If unset, determined automatically.
*
* @member {string}
*/
this.xhrType = options.xhrType;
/**
* The name of this resource.
*
* @member {string}
* @readonly
*/
this.name = name;
/**
* Extra info for middleware, and controlling specifics about how the resource loads.
*
* Note that if you pass in a `loadElement`, the Resource class takes ownership of it.
* Meaning it will modify it as it sees fit.
*
* @member {object}
* @property {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [loadElement=null] - The
* element to use for loading, instead of creating one.
* @property {boolean} [skipSource=false] - Skips adding source(s) to the load element. This
* is useful if you want to pass in a `loadElement` that you already added load sources
* to.
*/
this.metadata = options.metadata || {};
/**
* The url used to load this resource.
*
* @member {string}
* @readonly
*/
this.url = url;
/**
* The error that occurred while loading (if any).
*
* @member {Error}
* @readonly
*/
this.error = null;
/**
* The data that was loaded by the resource.
*
* @member {any}
*/
this.data = null;
/**
* The XHR object that was used to load this resource. This is only set
* when `loadType` is `Resource.LOAD_TYPE.XHR`.
*
* @member {XMLHttpRequest}
*/
this.xhr = null;
/**
* Is this request cross-origin? If unset, determined automatically.
*
* @member {string}
*/
this.crossOrigin = options.crossOrigin === true ? 'anonymous' : options.crossOrigin;
/**
* Describes if this resource was loaded as json. Only valid after the resource
* has completely loaded.
*
* @member {boolean}
*/
this.isJson = false;
/**
* The method of loading to use for this resource.
*
* @member {Resource.LOAD_TYPE}
*/
this.loadType = options.loadType || this._determineLoadType();
/**
* Describes if this resource was loaded as xml. Only valid after the resource
* has completely loaded.
*
* @member {boolean}
*/
this.isXml = false;
/**
* The type used to load the resource via XHR. If unset, determined automatically.
*
* @member {string}
*/
this.xhrType = options.xhrType;
/**
* Describes if this resource was loaded as an image tag. Only valid after the resource
* has completely loaded.
*
* @member {boolean}
*/
this.isImage = false;
/**
* Extra info for middleware, and controlling specifics about how the resource loads.
*
* Note that if you pass in a `loadElement`, the Resource class takes ownership of it.
* Meaning it will modify it as it sees fit.
*
* @member {object}
* @property {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [loadElement=null] - The
* element to use for loading, instead of creating one.
* @property {boolean} [skipSource=false] - Skips adding source(s) to the load element. This
* is useful if you want to pass in a `loadElement` that you already added load sources
* to.
*/
this.metadata = options.metadata || {};
/**
* Describes if this resource was loaded as an audio tag. Only valid after the resource
* has completely loaded.
*
* @member {boolean}
*/
this.isAudio = false;
/**
* The error that occurred while loading (if any).
*
* @member {Error}
* @readonly
*/
this.error = null;
/**
* The XHR object that was used to load this resource. This is only set
* when `loadType` is `Resource.LOAD_TYPE.XHR`.
*
* @member {XMLHttpRequest}
* @readonly
*/
this.xhr = null;
/**
* The child resources this resource owns.
*
* @member {Resource[]}
* @readonly
*/
this.children = [];
/**
* The resource type.
*
* @member {Resource.TYPE}
* @readonly
*/
this.type = Resource.TYPE.UNKNOWN;
/**
* The progress chunk owned by this resource.
*
* @member {number}
* @readonly
*/
this.progressChunk = 0;
/**
* The `dequeue` method that will be used a storage place for the async queue dequeue method
* used privately by the loader.
*
* @private
* @member {function}
*/
this._dequeue = _noop;
/**
* Used a storage place for the on load binding used privately by the loader.
*
* @private
* @member {function}
*/
this._onLoadBinding = null;
/**
* The `complete` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundComplete = this.complete.bind(this);
/**
* The `_onError` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundOnError = this._onError.bind(this);
/**
* The `_onProgress` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundOnProgress = this._onProgress.bind(this);
// xhr callbacks
this._boundXhrOnError = this._xhrOnError.bind(this);
this._boundXhrOnAbort = this._xhrOnAbort.bind(this);
this._boundXhrOnLoad = this._xhrOnLoad.bind(this);
this._boundXdrOnTimeout = this._xdrOnTimeout.bind(this);
/**
* Dispatched when the resource beings to load.
*
* The callback looks like {@link Resource.OnStartSignal}.
*
* @member {Signal}
*/
this.onStart = new Signal();
/**
* Dispatched each time progress of this resource load updates.
* Not all resources types and loader systems can support this event
* so sometimes it may not be available. If the resource
* is being loaded on a modern browser, using XHR, and the remote server
* properly sets Content-Length headers, then this will be available.
*
* The callback looks like {@link Resource.OnProgressSignal}.
*
* @member {Signal}
*/
this.onProgress = new Signal();
/**
* Dispatched once this resource has loaded, if there was an error it will
* be in the `error` property.
*
* The callback looks like {@link Resource.OnCompleteSignal}.
*
* @member {Signal}
*/
this.onComplete = new Signal();
/**
* Dispatched after this resource has had all the *after* middleware run on it.
*
* The callback looks like {@link Resource.OnCompleteSignal}.
*
* @member {Signal}
*/
this.onAfterMiddleware = new Signal();
/**
* When the resource starts to load.
*
* @memberof Resource
* @callback OnStartSignal
* @param {Resource} resource - The resource that the event happened on.
*/
/**
* When the resource reports loading progress.
*
* @memberof Resource
* @callback OnProgressSignal
* @param {Resource} resource - The resource that the event happened on.
* @param {number} percentage - The progress of the load in the range [0, 1].
*/
/**
* When the resource finishes loading.
*
* @memberof Resource
* @callback OnCompleteSignal
* @param {Resource} resource - The resource that the event happened on.
*/
}
/**
* Describes if this resource was loaded as a video tag. Only valid after the resource
* has completely loaded.
* Stores whether or not this url is a data url.
*
* @member {boolean}
* @readonly
*/
this.isVideo = false;
get isDataUrl() {
return this._hasFlag(Resource.STATUS_FLAGS.DATA_URL);
}

@@ -166,4 +307,7 @@ /**

* @member {boolean}
* @readonly
*/
this.isComplete = false;
get isComplete() {
return this._hasFlag(Resource.STATUS_FLAGS.COMPLETE);
}

@@ -175,625 +319,606 @@ /**

* @member {boolean}
* @readonly
*/
this.isLoading = false;
get isLoading() {
return this._hasFlag(Resource.STATUS_FLAGS.LOADING);
}
/**
* The `dequeue` method that will be used a storage place for the async queue dequeue method
* used privately by the loader.
* Marks the resource as complete.
*
* @private
* @member {function}
*/
this._dequeue = null;
complete() {
// TODO: Clean this up in a wrapper or something...gross....
if (this.data && this.data.removeEventListener) {
this.data.removeEventListener('error', this._boundOnError, false);
this.data.removeEventListener('load', this._boundComplete, false);
this.data.removeEventListener('progress', this._boundOnProgress, false);
this.data.removeEventListener('canplaythrough', this._boundComplete, false);
}
/**
* The `complete` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundComplete = this.complete.bind(this);
if (this.xhr) {
if (this.xhr.removeEventListener) {
this.xhr.removeEventListener('error', this._boundXhrOnError, false);
this.xhr.removeEventListener('abort', this._boundXhrOnAbort, false);
this.xhr.removeEventListener('progress', this._boundOnProgress, false);
this.xhr.removeEventListener('load', this._boundXhrOnLoad, false);
}
else {
this.xhr.onerror = null;
this.xhr.ontimeout = null;
this.xhr.onprogress = null;
this.xhr.onload = null;
}
}
/**
* The `_onError` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundOnError = this._onError.bind(this);
if (this.isComplete) {
throw new Error('Complete called again for an already completed resource.');
}
/**
* The `_onProgress` function bound to this resource's context.
*
* @private
* @member {function}
*/
this._boundOnProgress = this._onProgress.bind(this);
this._setFlag(Resource.STATUS_FLAGS.COMPLETE, true);
this._setFlag(Resource.STATUS_FLAGS.LOADING, false);
// xhr callbacks
this._boundXhrOnError = this._xhrOnError.bind(this);
this._boundXhrOnAbort = this._xhrOnAbort.bind(this);
this._boundXhrOnLoad = this._xhrOnLoad.bind(this);
this._boundXdrOnTimeout = this._xdrOnTimeout.bind(this);
this.onComplete.dispatch(this);
}
/**
* Emitted when the resource beings to load.
* Aborts the loading of this resource, with an optional message.
*
* @event start
* @memberof Resource#
* @param {string} message - The message to use for the error
*/
abort(message) {
// abort can be called multiple times, ignore subsequent calls.
if (this.error) {
return;
}
/**
* Emitted each time progress of this resource load updates.
* Not all resources types and loader systems can support this event
* so sometimes it may not be available. If the resource
* is being loaded on a modern browser, using XHR, and the remote server
* properly sets Content-Length headers, then this will be available.
*
* @event progress
* @memberof Resource#
*/
// store error
this.error = new Error(message);
// abort the actual loading
if (this.xhr) {
this.xhr.abort();
}
else if (this.xdr) {
this.xdr.abort();
}
else if (this.data) {
// single source
if (this.data.src) {
this.data.src = Resource.EMPTY_GIF;
}
// multi-source
else {
while (this.data.firstChild) {
this.data.removeChild(this.data.firstChild);
}
}
}
// done now.
this.complete();
}
/**
* Emitted once this resource has loaded, if there was an error it will
* be in the `error` property.
* Kicks off loading of this resource. This method is asynchronous.
*
* @event complete
* @memberof Resource#
* @param {function} [cb] - Optional callback to call once the resource is loaded.
*/
}
load(cb) {
if (this.isLoading) {
return;
}
Resource.prototype = Object.create(EventEmitter.prototype);
Resource.prototype.constructor = Resource;
module.exports = Resource;
if (this.isComplete) {
if (cb) {
setTimeout(() => cb(this), 1);
}
/**
* Marks the resource as complete.
*
* @fires complete
*/
Resource.prototype.complete = function () {
// TODO: Clean this up in a wrapper or something...gross....
if (this.data && this.data.removeEventListener) {
this.data.removeEventListener('error', this._boundOnError, false);
this.data.removeEventListener('load', this._boundComplete, false);
this.data.removeEventListener('progress', this._boundOnProgress, false);
this.data.removeEventListener('canplaythrough', this._boundComplete, false);
}
if (this.xhr) {
if (this.xhr.removeEventListener) {
this.xhr.removeEventListener('error', this._boundXhrOnError, false);
this.xhr.removeEventListener('abort', this._boundXhrOnAbort, false);
this.xhr.removeEventListener('progress', this._boundOnProgress, false);
this.xhr.removeEventListener('load', this._boundXhrOnLoad, false);
return;
}
else {
this.xhr.onerror = null;
this.xhr.ontimeout = null;
this.xhr.onprogress = null;
this.xhr.onload = null;
else if (cb) {
this.onComplete.once(cb);
}
}
if (this.isComplete) {
throw new Error('Complete called again for an already completed resource.');
}
this._setFlag(Resource.STATUS_FLAGS.LOADING, true);
this.isComplete = true;
this.isLoading = false;
this.onStart.dispatch(this);
this.emit('complete', this);
};
// if unset, determine the value
if (this.crossOrigin === false || typeof this.crossOrigin !== 'string') {
this.crossOrigin = this._determineCrossOrigin(this.url);
}
/**
* Aborts the loading of this resource, with an optional message.
*
* @param {string} message - The message to use for the error
*/
Resource.prototype.abort = function (message) {
// abort can be called multiple times, ignore subsequent calls.
if (this.error) {
return;
}
switch (this.loadType) {
case Resource.LOAD_TYPE.IMAGE:
this.type = Resource.TYPE.IMAGE;
this._loadElement('image');
break;
// store error
this.error = new Error(message);
case Resource.LOAD_TYPE.AUDIO:
this.type = Resource.TYPE.AUDIO;
this._loadSourceElement('audio');
break;
// abort the actual loading
if (this.xhr) {
this.xhr.abort();
}
else if (this.xdr) {
this.xdr.abort();
}
else if (this.data) {
// single source
if (typeof this.data.src !== 'undefined') {
this.data.src = '';
case Resource.LOAD_TYPE.VIDEO:
this.type = Resource.TYPE.VIDEO;
this._loadSourceElement('video');
break;
case Resource.LOAD_TYPE.XHR:
/* falls through */
default:
if (useXdr && this.crossOrigin) {
this._loadXdr();
}
else {
this._loadXhr();
}
break;
}
// multi-source
else {
while (this.data.firstChild) {
this.data.removeChild(this.data.firstChild);
}
}
}
// done now.
this.complete();
};
/**
* Checks if the flag is set.
*
* @private
* @param {number} flag - The flag to check.
* @return {boolean} True if the flag is set.
*/
_hasFlag(flag) {
return !!(this._flags & flag);
}
/**
* Kicks off loading of this resource. This method is asynchronous.
*
* @fires start
* @param {function} [cb] - Optional callback to call once the resource is loaded.
*/
Resource.prototype.load = function (cb) {
if (this.isLoading) {
return;
/**
* (Un)Sets the flag.
*
* @private
* @param {number} flag - The flag to (un)set.
* @param {boolean} value - Whether to set or (un)set the flag.
*/
_setFlag(flag, value) {
this._flags = value ? (this._flags | flag) : (this._flags & ~flag);
}
if (this.isComplete) {
if (cb) {
var self = this;
/**
* Loads this resources using an element that has a single source,
* like an HTMLImageElement.
*
* @private
* @param {string} type - The type of element to use.
*/
_loadElement(type) {
if (this.metadata.loadElement) {
this.data = this.metadata.loadElement;
}
else if (type === 'image' && typeof window.Image !== 'undefined') {
this.data = new Image();
}
else {
this.data = document.createElement(type);
}
setTimeout(function () {
cb(self);
}, 1);
if (this.crossOrigin) {
this.data.crossOrigin = this.crossOrigin;
}
return;
}
else if (cb) {
this.once('complete', cb);
}
if (!this.metadata.skipSource) {
this.data.src = this.url;
}
this.isLoading = true;
this.emit('start', this);
// if unset, determine the value
if (this.crossOrigin === false || typeof this.crossOrigin !== 'string') {
this.crossOrigin = this._determineCrossOrigin(this.url);
this.data.addEventListener('error', this._boundOnError, false);
this.data.addEventListener('load', this._boundComplete, false);
this.data.addEventListener('progress', this._boundOnProgress, false);
}
switch (this.loadType) {
case Resource.LOAD_TYPE.IMAGE:
this._loadElement('image');
break;
/**
* Loads this resources using an element that has multiple sources,
* like an HTMLAudioElement or HTMLVideoElement.
*
* @private
* @param {string} type - The type of element to use.
*/
_loadSourceElement(type) {
if (this.metadata.loadElement) {
this.data = this.metadata.loadElement;
}
else if (type === 'audio' && typeof window.Audio !== 'undefined') {
this.data = new Audio();
}
else {
this.data = document.createElement(type);
}
case Resource.LOAD_TYPE.AUDIO:
this._loadSourceElement('audio');
break;
if (this.data === null) {
this.abort(`Unsupported element: ${type}`);
case Resource.LOAD_TYPE.VIDEO:
this._loadSourceElement('video');
break;
return;
}
case Resource.LOAD_TYPE.XHR:
/* falls through */
default:
if (useXdr && this.crossOrigin) {
this._loadXdr();
if (!this.metadata.skipSource) {
// support for CocoonJS Canvas+ runtime, lacks document.createElement('source')
if (navigator.isCocoonJS) {
this.data.src = Array.isArray(this.url) ? this.url[0] : this.url;
}
else if (Array.isArray(this.url)) {
for (let i = 0; i < this.url.length; ++i) {
this.data.appendChild(this._createSource(type, this.url[i]));
}
}
else {
this._loadXhr();
this.data.appendChild(this._createSource(type, this.url));
}
break;
}
};
}
/**
* Loads this resources using an element that has a single source,
* like an HTMLImageElement.
*
* @private
* @param {string} type - The type of element to use.
*/
Resource.prototype._loadElement = function (type) {
if (this.metadata.loadElement) {
this.data = this.metadata.loadElement;
}
else if (type === 'image' && typeof window.Image !== 'undefined') {
this.data = new Image();
}
else {
this.data = document.createElement(type);
}
this.data.addEventListener('error', this._boundOnError, false);
this.data.addEventListener('load', this._boundComplete, false);
this.data.addEventListener('progress', this._boundOnProgress, false);
this.data.addEventListener('canplaythrough', this._boundComplete, false);
if (this.crossOrigin) {
this.data.crossOrigin = this.crossOrigin;
this.data.load();
}
if (!this.metadata.skipSource) {
this.data.src = this.url;
}
/**
* Loads this resources using an XMLHttpRequest.
*
* @private
*/
_loadXhr() {
// if unset, determine the value
if (typeof this.xhrType !== 'string') {
this.xhrType = this._determineXhrType();
}
var typeName = 'is' + type[0].toUpperCase() + type.substring(1);
const xhr = this.xhr = new XMLHttpRequest();
if (this[typeName] === false) {
this[typeName] = true;
}
// set the request type and url
xhr.open('GET', this.url, true);
this.data.addEventListener('error', this._boundOnError, false);
this.data.addEventListener('load', this._boundComplete, false);
this.data.addEventListener('progress', this._boundOnProgress, false);
};
/**
* Loads this resources using an element that has multiple sources,
* like an HTMLAudioElement or HTMLVideoElement.
*
* @private
* @param {string} type - The type of element to use.
*/
Resource.prototype._loadSourceElement = function (type) {
if (this.metadata.loadElement) {
this.data = this.metadata.loadElement;
}
else if (type === 'audio' && typeof window.Audio !== 'undefined') {
this.data = new Audio();
}
else {
this.data = document.createElement(type);
}
if (this.data === null) {
this.abort('Unsupported element ' + type);
return;
}
if (!this.metadata.skipSource) {
// support for CocoonJS Canvas+ runtime, lacks document.createElement('source')
if (navigator.isCocoonJS) {
this.data.src = Array.isArray(this.url) ? this.url[0] : this.url;
// load json as text and parse it ourselves. We do this because some browsers
// *cough* safari *cough* can't deal with it.
if (this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON || this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT) {
xhr.responseType = Resource.XHR_RESPONSE_TYPE.TEXT;
}
else if (Array.isArray(this.url)) {
for (var i = 0; i < this.url.length; ++i) {
this.data.appendChild(this._createSource(type, this.url[i]));
}
}
else {
this.data.appendChild(this._createSource(type, this.url));
xhr.responseType = this.xhrType;
}
}
this['is' + type[0].toUpperCase() + type.substring(1)] = true;
xhr.addEventListener('error', this._boundXhrOnError, false);
xhr.addEventListener('abort', this._boundXhrOnAbort, false);
xhr.addEventListener('progress', this._boundOnProgress, false);
xhr.addEventListener('load', this._boundXhrOnLoad, false);
this.data.addEventListener('error', this._boundOnError, false);
this.data.addEventListener('load', this._boundComplete, false);
this.data.addEventListener('progress', this._boundOnProgress, false);
this.data.addEventListener('canplaythrough', this._boundComplete, false);
this.data.load();
};
/**
* Loads this resources using an XMLHttpRequest.
*
* @private
*/
Resource.prototype._loadXhr = function () {
// if unset, determine the value
if (typeof this.xhrType !== 'string') {
this.xhrType = this._determineXhrType();
xhr.send();
}
var xhr = this.xhr = new XMLHttpRequest();
/**
* Loads this resources using an XDomainRequest. This is here because we need to support IE9 (gross).
*
* @private
*/
_loadXdr() {
// if unset, determine the value
if (typeof this.xhrType !== 'string') {
this.xhrType = this._determineXhrType();
}
// set the request type and url
xhr.open('GET', this.url, true);
const xdr = this.xhr = new XDomainRequest();
// load json as text and parse it ourselves. We do this because some browsers
// *cough* safari *cough* can't deal with it.
if (this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON || this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT) {
xhr.responseType = Resource.XHR_RESPONSE_TYPE.TEXT;
}
else {
xhr.responseType = this.xhrType;
}
// XDomainRequest has a few quirks. Occasionally it will abort requests
// A way to avoid this is to make sure ALL callbacks are set even if not used
// More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
xdr.timeout = 5000;
xhr.addEventListener('error', this._boundXhrOnError, false);
xhr.addEventListener('abort', this._boundXhrOnAbort, false);
xhr.addEventListener('progress', this._boundOnProgress, false);
xhr.addEventListener('load', this._boundXhrOnLoad, false);
xdr.onerror = this._boundXhrOnError;
xdr.ontimeout = this._boundXdrOnTimeout;
xdr.onprogress = this._boundOnProgress;
xdr.onload = this._boundXhrOnLoad;
xhr.send();
};
xdr.open('GET', this.url, true);
/**
* Loads this resources using an XDomainRequest. This is here because we need to support IE9 (gross).
*
* @private
*/
Resource.prototype._loadXdr = function () {
// if unset, determine the value
if (typeof this.xhrType !== 'string') {
this.xhrType = this._determineXhrType();
// Note: The xdr.send() call is wrapped in a timeout to prevent an
// issue with the interface where some requests are lost if multiple
// XDomainRequests are being sent at the same time.
// Some info here: https://github.com/photonstorm/phaser/issues/1248
setTimeout(() => xdr.send(), 1);
}
var xdr = this.xhr = new XDomainRequest();
/**
* Creates a source used in loading via an element.
*
* @private
* @param {string} type - The element type (video or audio).
* @param {string} url - The source URL to load from.
* @param {string} [mime] - The mime type of the video
* @return {HTMLSourceElement} The source element.
*/
_createSource(type, url, mime) {
if (!mime) {
mime = `${type}/${url.substr(url.lastIndexOf('.') + 1)}`;
}
// XDomainRequest has a few quirks. Occasionally it will abort requests
// A way to avoid this is to make sure ALL callbacks are set even if not used
// More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
xdr.timeout = 5000;
const source = document.createElement('source');
xdr.onerror = this._boundXhrOnError;
xdr.ontimeout = this._boundXdrOnTimeout;
xdr.onprogress = this._boundOnProgress;
xdr.onload = this._boundXhrOnLoad;
source.src = url;
source.type = mime;
xdr.open('GET', this.url, true);
return source;
}
// Note: The xdr.send() call is wrapped in a timeout to prevent an
// issue with the interface where some requests are lost if multiple
// XDomainRequests are being sent at the same time.
// Some info here: https://github.com/photonstorm/phaser/issues/1248
setTimeout(function () {
xdr.send();
}, 0);
};
/**
* Called if a load errors out.
*
* @param {Event} event - The error event from the element that emits it.
* @private
*/
_onError(event) {
this.abort(`Failed to load element using: ${event.target.nodeName}`);
}
/**
* Creates a source used in loading via an element.
*
* @private
* @param {string} type - The element type (video or audio).
* @param {string} url - The source URL to load from.
* @param {string} [mime] - The mime type of the video
* @return {HTMLSourceElement} The source element.
*/
Resource.prototype._createSource = function (type, url, mime) {
if (!mime) {
mime = type + '/' + url.substr(url.lastIndexOf('.') + 1);
/**
* Called if a load progress event fires for xhr/xdr.
*
* @private
* @param {XMLHttpRequestProgressEvent|Event} event - Progress event.
*/
_onProgress(event) {
if (event && event.lengthComputable) {
this.onProgress.dispatch(this, event.loaded / event.total);
}
}
var source = document.createElement('source');
/**
* Called if an error event fires for xhr/xdr.
*
* @private
* @param {XMLHttpRequestErrorEvent|Event} event - Error event.
*/
_xhrOnError() {
const xhr = this.xhr;
source.src = url;
source.type = mime;
this.abort(`${reqType(xhr)} Request failed. Status: ${xhr.status}, text: "${xhr.statusText}"`);
}
return source;
};
/**
* Called if an abort event fires for xhr.
*
* @private
* @param {XMLHttpRequestAbortEvent} event - Abort Event
*/
_xhrOnAbort() {
this.abort(`${reqType(this.xhr)} Request was aborted by the user.`);
}
/**
* Called if a load errors out.
*
* @param {Event} event - The error event from the element that emits it.
* @private
*/
Resource.prototype._onError = function (event) {
this.abort('Failed to load element using ' + event.target.nodeName);
};
/**
* Called if a load progress event fires for xhr/xdr.
*
* @fires progress
* @private
* @param {XMLHttpRequestProgressEvent|Event} event - Progress event.
*/
Resource.prototype._onProgress = function (event) {
if (event && event.lengthComputable) {
this.emit('progress', this, event.loaded / event.total);
/**
* Called if a timeout event fires for xdr.
*
* @private
* @param {Event} event - Timeout event.
*/
_xdrOnTimeout() {
this.abort(`${reqType(this.xhr)} Request timed out.`);
}
};
/**
* Called if an error event fires for xhr/xdr.
*
* @private
* @param {XMLHttpRequestErrorEvent|Event} event - Error event.
*/
Resource.prototype._xhrOnError = function () {
var xhr = this.xhr;
/**
* Called when data successfully loads from an xhr/xdr request.
*
* @private
* @param {XMLHttpRequestLoadEvent|Event} event - Load event
*/
_xhrOnLoad() {
const xhr = this.xhr;
const status = typeof xhr.status === 'undefined' ? xhr.status : STATUS_OK; // XDR has no `.status`, assume 200.
this.abort(reqType(xhr) + ' Request failed. Status: ' + xhr.status + ', text: "' + xhr.statusText + '"');
};
// status can be 0 when using the `file://` protocol so we also check if a response is set
if (status === STATUS_OK
|| status === STATUS_EMPTY
|| (status === STATUS_NONE && xhr.responseText.length > 0)
) {
// if text, just return it
if (this.xhrType === Resource.XHR_RESPONSE_TYPE.TEXT) {
this.data = xhr.responseText;
this.type = Resource.TYPE.TEXT;
}
// if json, parse into json object
else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON) {
try {
this.data = JSON.parse(xhr.responseText);
this.type = Resource.TYPE.JSON;
}
catch (e) {
this.abort(`Error trying to parse loaded json: ${e}`);
/**
* Called if an abort event fires for xhr.
*
* @private
* @param {XMLHttpRequestAbortEvent} event - Abort Event
*/
Resource.prototype._xhrOnAbort = function () {
this.abort(reqType(this.xhr) + ' Request was aborted by the user.');
};
return;
}
}
// if xml, parse into an xml document or div element
else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT) {
try {
if (window.DOMParser) {
const domparser = new DOMParser();
/**
* Called if a timeout event fires for xdr.
*
* @private
* @param {Event} event - Timeout event.
*/
Resource.prototype._xdrOnTimeout = function () {
this.abort(reqType(this.xhr) + ' Request timed out.');
};
this.data = domparser.parseFromString(xhr.responseText, 'text/xml');
}
else {
const div = document.createElement('div');
/**
* Called when data successfully loads from an xhr/xdr request.
*
* @private
* @param {XMLHttpRequestLoadEvent|Event} event - Load event
*/
Resource.prototype._xhrOnLoad = function () {
var xhr = this.xhr;
var status = typeof xhr.status === 'undefined' ? xhr.status : STATUS_OK; // XDR has no `.status`, assume 200.
div.innerHTML = xhr.responseText;
// status can be 0 when using the file:// protocol, also check if a response was found
if (status === STATUS_OK || status === STATUS_EMPTY || (status === STATUS_NONE && xhr.responseText.length > 0)) {
// if text, just return it
if (this.xhrType === Resource.XHR_RESPONSE_TYPE.TEXT) {
this.data = xhr.responseText;
}
// if json, parse into json object
else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.JSON) {
try {
this.data = JSON.parse(xhr.responseText);
this.isJson = true;
}
catch (e) {
this.abort('Error trying to parse loaded json:', e);
this.data = div;
}
return;
}
}
// if xml, parse into an xml document or div element
else if (this.xhrType === Resource.XHR_RESPONSE_TYPE.DOCUMENT) {
try {
if (window.DOMParser) {
var domparser = new DOMParser();
this.data = domparser.parseFromString(xhr.responseText, 'text/xml');
this.type = Resource.TYPE.XML;
}
else {
var div = document.createElement('div');
catch (e) {
this.abort(`Error trying to parse loaded xml: ${e}`);
div.innerHTML = xhr.responseText;
this.data = div;
return;
}
this.isXml = true;
}
catch (e) {
this.abort('Error trying to parse loaded xml:', e);
return;
// other types just return the response
else {
this.data = xhr.response || xhr.responseText;
}
}
// other types just return the response
else {
this.data = xhr.response || xhr.responseText;
this.abort(`[${xhr.status}] ${xhr.statusText}: ${xhr.responseURL}`);
return;
}
}
else {
this.abort('[' + xhr.status + ']' + xhr.statusText + ':' + xhr.responseURL);
return;
this.complete();
}
this.complete();
};
/**
* Sets the `crossOrigin` property for this resource based on if the url
* for this resource is cross-origin. If crossOrigin was manually set, this
* function does nothing.
*
* @private
* @param {string} url - The url to test.
* @param {object} [loc=window.location] - The location object to test against.
* @return {string} The crossOrigin value to use (or empty string for none).
*/
_determineCrossOrigin(url, loc) {
// data: and javascript: urls are considered same-origin
if (url.indexOf('data:') === 0) {
return '';
}
/**
* Sets the `crossOrigin` property for this resource based on if the url
* for this resource is cross-origin. If crossOrigin was manually set, this
* function does nothing.
*
* @private
* @param {string} url - The url to test.
* @param {object} [loc=window.location] - The location object to test against.
* @return {string} The crossOrigin value to use (or empty string for none).
*/
Resource.prototype._determineCrossOrigin = function (url, loc) {
// data: and javascript: urls are considered same-origin
if (url.indexOf('data:') === 0) {
return '';
}
// default is window.location
loc = loc || window.location;
// default is window.location
loc = loc || window.location;
if (!tempAnchor) {
tempAnchor = document.createElement('a');
}
if (!tempAnchor) {
tempAnchor = document.createElement('a');
}
// let the browser determine the full href for the url of this resource and then
// parse with the node url lib, we can't use the properties of the anchor element
// because they don't work in IE9 :(
tempAnchor.href = url;
url = parseUri(tempAnchor.href, { strictMode: true });
// let the browser determine the full href for the url of this resource and then
// parse with the node url lib, we can't use the properties of the anchor element
// because they don't work in IE9 :(
tempAnchor.href = url;
url = parseUri(tempAnchor.href, { strictMode: true });
const samePort = (!url.port && loc.port === '') || (url.port === loc.port);
const protocol = url.protocol ? `${url.protocol}:` : '';
var samePort = (!url.port && loc.port === '') || (url.port === loc.port);
var protocol = url.protocol ? url.protocol + ':' : '';
// if cross origin
if (url.host !== loc.hostname || !samePort || protocol !== loc.protocol) {
return 'anonymous';
}
// if cross origin
if (url.host !== loc.hostname || !samePort || protocol !== loc.protocol) {
return 'anonymous';
return '';
}
return '';
};
/**
* Determines the responseType of an XHR request based on the extension of the
* resource being loaded.
*
* @private
* @return {Resource.XHR_RESPONSE_TYPE} The responseType to use.
*/
_determineXhrType() {
return Resource._xhrTypeMap[this._getExtension()] || Resource.XHR_RESPONSE_TYPE.TEXT;
}
/**
* Determines the responseType of an XHR request based on the extension of the
* resource being loaded.
*
* @private
* @return {Resource.XHR_RESPONSE_TYPE} The responseType to use.
*/
Resource.prototype._determineXhrType = function () {
return Resource._xhrTypeMap[this._getExtension()] || Resource.XHR_RESPONSE_TYPE.TEXT;
};
/**
* Determines the loadType of a resource based on the extension of the
* resource being loaded.
*
* @private
* @return {Resource.LOAD_TYPE} The loadType to use.
*/
_determineLoadType() {
return Resource._loadTypeMap[this._getExtension()] || Resource.LOAD_TYPE.XHR;
}
Resource.prototype._determineLoadType = function () {
return Resource._loadTypeMap[this._getExtension()] || Resource.LOAD_TYPE.XHR;
};
/**
* Extracts the extension (sans '.') of the file being loaded by the resource.
*
* @private
* @return {string} The extension.
*/
_getExtension() {
let url = this.url;
let ext = '';
Resource.prototype._getExtension = function () {
var url = this.url;
var ext = '';
if (this.isDataUrl) {
const slashIndex = url.indexOf('/');
if (this.isDataUrl) {
var slashIndex = url.indexOf('/');
ext = url.substring(slashIndex + 1, url.indexOf(';', slashIndex));
}
else {
const queryStart = url.indexOf('?');
ext = url.substring(slashIndex + 1, url.indexOf(';', slashIndex));
}
else {
var queryStart = url.indexOf('?');
if (queryStart !== -1) {
url = url.substring(0, queryStart);
}
if (queryStart !== -1) {
url = url.substring(0, queryStart);
ext = url.substring(url.lastIndexOf('.') + 1);
}
ext = url.substring(url.lastIndexOf('.') + 1);
return ext.toLowerCase();
}
return ext.toLowerCase();
};
/**
* Determines the mime type of an XHR request based on the responseType of
* resource being loaded.
*
* @private
* @param {Resource.XHR_RESPONSE_TYPE} type - The type to get a mime type for.
* @return {string} The mime type to use.
*/
_getMimeFromXhrType(type) {
switch (type) {
case Resource.XHR_RESPONSE_TYPE.BUFFER:
return 'application/octet-binary';
/**
* Determines the mime type of an XHR request based on the responseType of
* resource being loaded.
*
* @private
* @param {Resource.XHR_RESPONSE_TYPE} type - The type to get a mime type for.
* @return {string} The mime type to use.
*/
Resource.prototype._getMimeFromXhrType = function (type) {
switch (type) {
case Resource.XHR_RESPONSE_TYPE.BUFFER:
return 'application/octet-binary';
case Resource.XHR_RESPONSE_TYPE.BLOB:
return 'application/blob';
case Resource.XHR_RESPONSE_TYPE.BLOB:
return 'application/blob';
case Resource.XHR_RESPONSE_TYPE.DOCUMENT:
return 'application/xml';
case Resource.XHR_RESPONSE_TYPE.DOCUMENT:
return 'application/xml';
case Resource.XHR_RESPONSE_TYPE.JSON:
return 'application/json';
case Resource.XHR_RESPONSE_TYPE.JSON:
return 'application/json';
case Resource.XHR_RESPONSE_TYPE.DEFAULT:
case Resource.XHR_RESPONSE_TYPE.TEXT:
/* falls through */
default:
return 'text/plain';
case Resource.XHR_RESPONSE_TYPE.DEFAULT:
case Resource.XHR_RESPONSE_TYPE.TEXT:
/* falls through */
default:
return 'text/plain';
}
}
}
}
/**
* The types of resources a resource could represent.
*
* @static
* @readonly
* @enum {number}
*/
Resource.STATUS_FLAGS = {
NONE: 0,
DATA_URL: (1 << 0),
COMPLETE: (1 << 1),
LOADING: (1 << 2),
};
/**
* Quick helper to get string xhr type.
* The types of resources a resource could represent.
*
* @ignore
* @param {XMLHttpRequest|XDomainRequest} xhr - The request to check.
* @return {string} The type.
* @static
* @readonly
* @enum {number}
*/
function reqType(xhr) {
return xhr.toString().replace('object ', '');
}
Resource.TYPE = {
UNKNOWN: 0,
JSON: 1,
XML: 2,
IMAGE: 3,
AUDIO: 4,
VIDEO: 5,
TEXT: 6,
};

@@ -815,3 +940,3 @@ /**

/** Uses a `Video` object to load the resource. */
VIDEO: 4
VIDEO: 4,
};

@@ -827,3 +952,3 @@

Resource.XHR_RESPONSE_TYPE = {
/** defaults to text */
/** string */
DEFAULT: 'text',

@@ -839,16 +964,27 @@ /** ArrayBuffer */

/** String */
TEXT: 'text'
TEXT: 'text',
};
Resource._loadTypeMap = {
gif: Resource.LOAD_TYPE.IMAGE,
png: Resource.LOAD_TYPE.IMAGE,
bmp: Resource.LOAD_TYPE.IMAGE,
jpg: Resource.LOAD_TYPE.IMAGE,
jpeg: Resource.LOAD_TYPE.IMAGE,
tif: Resource.LOAD_TYPE.IMAGE,
tiff: Resource.LOAD_TYPE.IMAGE,
webp: Resource.LOAD_TYPE.IMAGE,
tga: Resource.LOAD_TYPE.IMAGE,
'svg+xml': Resource.LOAD_TYPE.IMAGE
// images
gif: Resource.LOAD_TYPE.IMAGE,
png: Resource.LOAD_TYPE.IMAGE,
bmp: Resource.LOAD_TYPE.IMAGE,
jpg: Resource.LOAD_TYPE.IMAGE,
jpeg: Resource.LOAD_TYPE.IMAGE,
tif: Resource.LOAD_TYPE.IMAGE,
tiff: Resource.LOAD_TYPE.IMAGE,
webp: Resource.LOAD_TYPE.IMAGE,
tga: Resource.LOAD_TYPE.IMAGE,
svg: Resource.LOAD_TYPE.IMAGE,
'svg+xml': Resource.LOAD_TYPE.IMAGE, // for SVG data urls
// audio
mp3: Resource.LOAD_TYPE.AUDIO,
ogg: Resource.LOAD_TYPE.AUDIO,
wav: Resource.LOAD_TYPE.AUDIO,
// videos
mp4: Resource.LOAD_TYPE.VIDEO,
webm: Resource.LOAD_TYPE.VIDEO,
};

@@ -858,51 +994,49 @@

// xml
xhtml: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
html: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
htm: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
xml: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
tmx: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
tsx: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
svg: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
xhtml: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
html: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
htm: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
xml: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
tmx: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
svg: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
// This was added to handle Tiled Tileset XML, but .tsx is also a TypeScript React Component.
// Since it is way less likely for people to be loading TypeScript files instead of Tiled files,
// this should probably be fine.
tsx: Resource.XHR_RESPONSE_TYPE.DOCUMENT,
// images
gif: Resource.XHR_RESPONSE_TYPE.BLOB,
png: Resource.XHR_RESPONSE_TYPE.BLOB,
bmp: Resource.XHR_RESPONSE_TYPE.BLOB,
jpg: Resource.XHR_RESPONSE_TYPE.BLOB,
jpeg: Resource.XHR_RESPONSE_TYPE.BLOB,
tif: Resource.XHR_RESPONSE_TYPE.BLOB,
tiff: Resource.XHR_RESPONSE_TYPE.BLOB,
webp: Resource.XHR_RESPONSE_TYPE.BLOB,
tga: Resource.XHR_RESPONSE_TYPE.BLOB,
gif: Resource.XHR_RESPONSE_TYPE.BLOB,
png: Resource.XHR_RESPONSE_TYPE.BLOB,
bmp: Resource.XHR_RESPONSE_TYPE.BLOB,
jpg: Resource.XHR_RESPONSE_TYPE.BLOB,
jpeg: Resource.XHR_RESPONSE_TYPE.BLOB,
tif: Resource.XHR_RESPONSE_TYPE.BLOB,
tiff: Resource.XHR_RESPONSE_TYPE.BLOB,
webp: Resource.XHR_RESPONSE_TYPE.BLOB,
tga: Resource.XHR_RESPONSE_TYPE.BLOB,
// json
json: Resource.XHR_RESPONSE_TYPE.JSON,
json: Resource.XHR_RESPONSE_TYPE.JSON,
// text
text: Resource.XHR_RESPONSE_TYPE.TEXT,
txt: Resource.XHR_RESPONSE_TYPE.TEXT
};
text: Resource.XHR_RESPONSE_TYPE.TEXT,
txt: Resource.XHR_RESPONSE_TYPE.TEXT,
/**
* Sets the load type to be used for a specific extension.
*
* @static
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
* @param {Resource.LOAD_TYPE} loadType - The load type to set it to.
*/
Resource.setExtensionLoadType = function (extname, loadType) {
setExtMap(Resource._loadTypeMap, extname, loadType);
// fonts
ttf: Resource.XHR_RESPONSE_TYPE.BUFFER,
otf: Resource.XHR_RESPONSE_TYPE.BUFFER,
};
// We can't set the `src` attribute to empty string, so on abort we set it to this 1px transparent gif
Resource.EMPTY_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
/**
* Sets the load type to be used for a specific extension.
* Quick helper to set a value on one of the extension maps. Ensures there is no
* dot at the start of the extension.
*
* @static
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
* @param {Resource.XHR_RESPONSE_TYPE} xhrType - The xhr type to set it to.
* @ignore
* @param {object} map - The map to set on.
* @param {string} extname - The extension (or key) to set.
* @param {number} val - The value to set.
*/
Resource.setExtensionXhrType = function (extname, xhrType) {
setExtMap(Resource._xhrTypeMap, extname, xhrType);
};
function setExtMap(map, extname, val) {

@@ -919,1 +1053,12 @@ if (extname && extname.indexOf('.') === 0) {

}
/**
* Quick helper to get string xhr type.
*
* @ignore
* @param {XMLHttpRequest|XDomainRequest} xhr - The request to check.
* @return {string} The type.
*/
function reqType(xhr) {
return xhr.toString().replace('object ', '');
}

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc