/*! WebUploader 0.1.5 */ /** * @fileOverview 让内部å„个部件的代ç å¯ä»¥ç”¨[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模å—定义方å¼ç»„织起æ¥ã€‚ * * AMD API 内部的简å•ä¸å®Œå…¨å®žçŽ°ï¼Œè¯·å¿½ç•¥ã€‚åªæœ‰å½“WebUploader被åˆå¹¶æˆä¸€ä¸ªæ–‡ä»¶çš„æ—¶å€™æ‰ä¼šå¼•入。 */ (function( root, factory ) { var modules = {}, // 内部require, 简å•ä¸å®Œå…¨å®žçŽ°ã€‚ // https://github.com/amdjs/amdjs-api/wiki/require _require = function( deps, callback ) { var args, len, i; // 如果deps䏿˜¯æ•°ç»„,则直接返回指定module if ( typeof deps === 'string' ) { return getModule( deps ); } else { args = []; for( len = deps.length, i = 0; i < len; i++ ) { args.push( getModule( deps[ i ] ) ); } return callback.apply( null, args ); } }, // 内部defineï¼Œæš‚æ—¶ä¸æ”¯æŒä¸æŒ‡å®šid. _define = function( id, deps, factory ) { if ( arguments.length === 2 ) { factory = deps; deps = null; } _require( deps || [], function() { setModule( id, factory, arguments ); }); }, // 设置module, 兼容CommonJs写法。 setModule = function( id, factory, args ) { var module = { exports: factory }, returned; if ( typeof factory === 'function' ) { args.length || (args = [ _require, module.exports, module ]); returned = factory.apply( null, args ); returned !== undefined && (module.exports = returned); } modules[ id ] = module.exports; }, // æ ¹æ®id获å–module getModule = function( id ) { var module = modules[ id ] || root[ id ]; if ( !module ) { throw new Error( '`' + id + '` is undefined' ); } return module; }, // 将所有modules,将路径idsè£…æ¢æˆå¯¹è±¡ã€‚ exportsTo = function( obj ) { var key, host, parts, part, last, ucFirst; // make the first character upper case. ucFirst = function( str ) { return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 )); }; for ( key in modules ) { host = obj; if ( !modules.hasOwnProperty( key ) ) { continue; } parts = key.split('/'); last = ucFirst( parts.pop() ); while( (part = ucFirst( parts.shift() )) ) { host[ part ] = host[ part ] || {}; host = host[ part ]; } host[ last ] = modules[ key ]; } return obj; }, makeExport = function( dollar ) { root.__dollar = dollar; // exports every module. return exportsTo( factory( root, _define, _require ) ); }, origin; if ( typeof module === 'object' && typeof module.exports === 'object' ) { // For CommonJS and CommonJS-like environments where a proper window is present, module.exports = makeExport(); } else if ( typeof define === 'function' && define.amd ) { // Allow using this built library as an AMD module // in another project. That other project will only // see this AMD call, not the internal modules in // the closure below. define([ 'jquery' ], makeExport ); } else { // Browser globals case. Just assign the // result to a property on the global. origin = root.WebUploader; root.WebUploader = makeExport(); root.WebUploader.noConflict = function() { root.WebUploader = origin; }; } })( window, function( window, define, require ) { /** * @fileOverview jQuery or Zepto */ define('dollar-third',[],function() { var $ = window.__dollar || window.jQuery || window.Zepto; if ( !$ ) { throw new Error('jQuery or Zepto not found!'); } return $; }); /** * @fileOverview Dom æ“作相关 */ define('dollar',[ 'dollar-third' ], function( _ ) { return _; }); /** * @fileOverview 使用jQueryçš„Promise */ define('promise-third',[ 'dollar' ], function( $ ) { return { Deferred: $.Deferred, when: $.when, isPromise: function( anything ) { return anything && typeof anything.then === 'function'; } }; }); /** * @fileOverview Promise/A+ */ define('promise',[ 'promise-third' ], function( _ ) { return _; }); /** * @fileOverview 基础类方法。 */ /** * Web Uploader内部类的详细说明,以下æåŠçš„功能类,都å¯ä»¥åœ¨`WebUploader`这个å˜é‡ä¸è®¿é—®åˆ°ã€‚ * * As you know, Web Uploaderçš„æ¯ä¸ªæ–‡ä»¶éƒ½æ˜¯ç”¨è¿‡[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范ä¸çš„`define`组织起æ¥çš„, æ¯ä¸ªModule都会有个module id. * 默认module id为该文件的路径,而æ¤è·¯å¾„将会转化æˆåå—ç©ºé—´å˜æ”¾åœ¨WebUploaderä¸ã€‚如: * * * module `base`:WebUploader.Base * * module `file`: WebUploader.File * * module `lib/dnd`: WebUploader.Lib.Dnd * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd * * * 以下文档ä¸å¯¹ç±»çš„使用å¯èƒ½çœç•¥æŽ‰äº†`WebUploader`å‰ç¼€ã€‚ * @module WebUploader * @title WebUploader API文档 */ define('base',[ 'dollar', 'promise' ], function( $, promise ) { var noop = function() {}, call = Function.call; // http://jsperf.com/uncurrythis // å科里化 function uncurryThis( fn ) { return function() { return call.apply( fn, arguments ); }; } function bindFn( fn, context ) { return function() { return fn.apply( context, arguments ); }; } function createObject( proto ) { var f; if ( Object.create ) { return Object.create( proto ); } else { f = function() {}; f.prototype = proto; return new f(); } } /** * 基础类,æä¾›ä¸€äº›ç®€å•常用的方法。 * @class Base */ return { /** * @property {String} version 当å‰ç‰ˆæœ¬å·ã€‚ */ version: '0.1.5', /** * @property {jQuery|Zepto} $ 引用ä¾èµ–çš„jQuery或者Zepto对象。 */ $: $, Deferred: promise.Deferred, isPromise: promise.isPromise, when: promise.when, /** * @description 简å•çš„æµè§ˆå™¨æ£€æŸ¥ç»“果。 * * * `webkit` webkit版本å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºéžwebkitå†…æ ¸ï¼Œæ¤å±žæ€§ä¸º`undefined`。 * * `chrome` chromeæµè§ˆå™¨ç‰ˆæœ¬å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºchrome,æ¤å±žæ€§ä¸º`undefined`。 * * `ie` ieæµè§ˆå™¨ç‰ˆæœ¬å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºéžie,æ¤å±žæ€§ä¸º`undefined`。**æš‚ä¸æ”¯æŒie10+** * * `firefox` firefoxæµè§ˆå™¨ç‰ˆæœ¬å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºéžfirefox,æ¤å±žæ€§ä¸º`undefined`。 * * `safari` safariæµè§ˆå™¨ç‰ˆæœ¬å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºéžsafari,æ¤å±žæ€§ä¸º`undefined`。 * * `opera` operaæµè§ˆå™¨ç‰ˆæœ¬å·ï¼Œå¦‚æžœæµè§ˆå™¨ä¸ºéžopera,æ¤å±žæ€§ä¸º`undefined`。 * * @property {Object} [browser] */ browser: (function( ua ) { var ret = {}, webkit = ua.match( /WebKit\/([\d.]+)/ ), chrome = ua.match( /Chrome\/([\d.]+)/ ) || ua.match( /CriOS\/([\d.]+)/ ), ie = ua.match( /MSIE\s([\d\.]+)/ ) || ua.match( /(?:trident)(?:.*rv:([\w.]+))?/i ), firefox = ua.match( /Firefox\/([\d.]+)/ ), safari = ua.match( /Safari\/([\d.]+)/ ), opera = ua.match( /OPR\/([\d.]+)/ ); webkit && (ret.webkit = parseFloat( webkit[ 1 ] )); chrome && (ret.chrome = parseFloat( chrome[ 1 ] )); ie && (ret.ie = parseFloat( ie[ 1 ] )); firefox && (ret.firefox = parseFloat( firefox[ 1 ] )); safari && (ret.safari = parseFloat( safari[ 1 ] )); opera && (ret.opera = parseFloat( opera[ 1 ] )); return ret; })( navigator.userAgent ), /** * @description æ“作系统检查结果。 * * * `android` 如果在androidæµè§ˆå™¨çŽ¯å¢ƒä¸‹ï¼Œæ¤å€¼ä¸ºå¯¹åº”çš„android版本å·ï¼Œå¦åˆ™ä¸º`undefined`。 * * `ios` 如果在iosæµè§ˆå™¨çŽ¯å¢ƒä¸‹ï¼Œæ¤å€¼ä¸ºå¯¹åº”çš„ios版本å·ï¼Œå¦åˆ™ä¸º`undefined`。 * @property {Object} [os] */ os: (function( ua ) { var ret = {}, // osx = !!ua.match( /\(Macintosh\; Intel / ), android = ua.match( /(?:Android);?[\s\/]+([\d.]+)?/ ), ios = ua.match( /(?:iPad|iPod|iPhone).*OS\s([\d_]+)/ ); // osx && (ret.osx = true); android && (ret.android = parseFloat( android[ 1 ] )); ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) )); return ret; })( navigator.userAgent ), /** * 实现类与类之间的继承。 * @method inherits * @grammar Base.inherits( super ) => child * @grammar Base.inherits( super, protos ) => child * @grammar Base.inherits( super, protos, statics ) => child * @param {Class} super 父类 * @param {Object | Function} [protos] å类或者对象。如果对象ä¸åŒ…å«constructor,å类将是用æ¤å±žæ€§å€¼ã€‚ * @param {Function} [protos.constructor] åç±»æž„é€ å™¨ï¼Œä¸æŒ‡å®šçš„è¯å°†åˆ›å»ºä¸ªä¸´æ—¶çš„ç›´æŽ¥æ‰§è¡Œçˆ¶ç±»æž„é€ å™¨çš„æ–¹æ³•ã€‚ * @param {Object} [statics] 陿€å±žæ€§æˆ–方法。 * @return {Class} 返回å类。 * @example * function Person() { * console.log( 'Super' ); * } * Person.prototype.hello = function() { * console.log( 'hello' ); * }; * * var Manager = Base.inherits( Person, { * world: function() { * console.log( 'World' ); * } * }); * * // å› ä¸ºæ²¡æœ‰æŒ‡å®šæž„é€ å™¨ï¼Œçˆ¶ç±»çš„æž„é€ å™¨å°†ä¼šæ‰§è¡Œã€‚ * var instance = new Manager(); // => Super * * // 继承å父类的方法 * instance.hello(); // => hello * instance.world(); // => World * * // å类的__super__属性指å‘父类 * console.log( Manager.__super__ === Person ); // => true */ inherits: function( Super, protos, staticProtos ) { var child; if ( typeof protos === 'function' ) { child = protos; protos = null; } else if ( protos && protos.hasOwnProperty('constructor') ) { child = protos.constructor; } else { child = function() { return Super.apply( this, arguments ); }; } // å¤åˆ¶é™æ€æ–¹æ³• $.extend( true, child, Super, staticProtos || {} ); /* jshint camelcase: false */ // 让å类的__super__属性指å‘父类。 child.__super__ = Super.prototype; // æž„å»ºåŽŸåž‹ï¼Œæ·»åŠ åŽŸåž‹æ–¹æ³•æˆ–å±žæ€§ã€‚ // 暂时用Object.create实现。 child.prototype = createObject( Super.prototype ); protos && $.extend( true, child.prototype, protos ); return child; }, /** * 一个ä¸åšä»»ä½•事情的方法。å¯ä»¥ç”¨æ¥èµ‹å€¼ç»™é»˜è®¤çš„callback. * @method noop */ noop: noop, /** * è¿”å›žä¸€ä¸ªæ–°çš„æ–¹æ³•ï¼Œæ¤æ–¹æ³•将已指定的`context`æ¥æ‰§è¡Œã€‚ * @grammar Base.bindFn( fn, context ) => Function * @method bindFn * @example * var doSomething = function() { * console.log( this.name ); * }, * obj = { * name: 'Object Name' * }, * aliasFn = Base.bind( doSomething, obj ); * * aliasFn(); // => Object Name * */ bindFn: bindFn, /** * 引用Console.log如果å˜åœ¨çš„è¯ï¼Œå¦åˆ™å¼•用一个[空函数noop](#WebUploader:Base.noop)。 * @grammar Base.log( args... ) => undefined * @method log */ log: (function() { if ( window.console ) { return bindFn( console.log, console ); } return noop; })(), nextTick: (function() { return function( cb ) { setTimeout( cb, 1 ); }; // @bug 当æµè§ˆå™¨ä¸åœ¨å½“å‰çª—壿—¶å°±åœäº†ã€‚ // var next = window.requestAnimationFrame || // window.webkitRequestAnimationFrame || // window.mozRequestAnimationFrame || // function( cb ) { // window.setTimeout( cb, 1000 / 60 ); // }; // // fix: Uncaught TypeError: Illegal invocation // return bindFn( next, window ); })(), /** * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。 * 将用æ¥å°†éžæ•°ç»„å¯¹è±¡è½¬åŒ–æˆæ•°ç»„对象。 * @grammar Base.slice( target, start[, end] ) => Array * @method slice * @example * function doSomthing() { * var args = Base.slice( arguments, 1 ); * console.log( args ); * } * * doSomthing( 'ignored', 'arg2', 'arg3' ); // => Array ["arg2", "arg3"] */ slice: uncurryThis( [].slice ), /** * 生æˆå”¯ä¸€çš„ID * @method guid * @grammar Base.guid() => String * @grammar Base.guid( prefx ) => String */ guid: (function() { var counter = 0; return function( prefix ) { var guid = (+new Date()).toString( 32 ), i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } return (prefix || 'wu_') + guid + (counter++).toString( 32 ); }; })(), /** * æ ¼å¼åŒ–文件大å°, 输出æˆå¸¦å•ä½çš„å—符串 * @method formatSize * @grammar Base.formatSize( size ) => String * @grammar Base.formatSize( size, pointLength ) => String * @grammar Base.formatSize( size, pointLength, units ) => String * @param {Number} size æ–‡ä»¶å¤§å° * @param {Number} [pointLength=2] ç²¾ç¡®åˆ°çš„å°æ•°ç‚¹æ•°ã€‚ * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] å•使•°ç»„。从å—节,到åƒå—节,一直往上指定。如果å•使•°ç»„里é¢åªæŒ‡å®šäº†åˆ°äº†K(åƒå—节)ï¼ŒåŒæ—¶æ–‡ä»¶å¤§å°å¤§äºŽM, æ¤æ–¹æ³•的输出将还是显示æˆå¤šå°‘K. * @example * console.log( Base.formatSize( 100 ) ); // => 100B * console.log( Base.formatSize( 1024 ) ); // => 1.00K * console.log( Base.formatSize( 1024, 0 ) ); // => 1K * console.log( Base.formatSize( 1024 * 1024 ) ); // => 1.00M * console.log( Base.formatSize( 1024 * 1024 * 1024 ) ); // => 1.00G * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) ); // => 1024MB */ formatSize: function( size, pointLength, units ) { var unit; units = units || [ 'B', 'K', 'M', 'G', 'TB' ]; while ( (unit = units.shift()) && size > 1024 ) { size = size / 1024; } return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) + unit; } }; }); /** * 事件处ç†ç±»ï¼Œå¯ä»¥ç‹¬ç«‹ä½¿ç”¨ï¼Œä¹Ÿå¯ä»¥æ‰©å±•给对象使用。 * @fileOverview Mediator */ define('mediator',[ 'base' ], function( Base ) { var $ = Base.$, slice = [].slice, separator = /\s+/, protos; // æ ¹æ®æ¡ä»¶è¿‡æ»¤å‡ºäº‹ä»¶handlers. function findHandlers( arr, name, callback, context ) { return $.grep( arr, function( handler ) { return handler && (!name || handler.e === name) && (!callback || handler.cb === callback || handler.cb._cb === callback) && (!context || handler.ctx === context); }); } function eachEvent( events, callback, iterator ) { // 䏿”¯æŒå¯¹è±¡ï¼Œåªæ”¯æŒå¤šä¸ªeventç”¨ç©ºæ ¼éš”å¼€ $.each( (events || '').split( separator ), function( _, key ) { iterator( key, callback ); }); } function triggerHanders( events, args ) { var stoped = false, i = -1, len = events.length, handler; while ( ++i < len ) { handler = events[ i ]; if ( handler.cb.apply( handler.ctx2, args ) === false ) { stoped = true; break; } } return !stoped; } protos = { /** * 绑定事件。 * * `callback`方法在执行时,argumentså°†ä¼šæ¥æºäºŽtrigger的时候æºå¸¦çš„傿•°ã€‚如 * ```javascript * var obj = {}; * * // 使得obj有事件行为 * Mediator.installTo( obj ); * * obj.on( 'testa', function( arg1, arg2 ) { * console.log( arg1, arg2 ); // => 'arg1', 'arg2' * }); * * obj.trigger( 'testa', 'arg1', 'arg2' ); * ``` * * 如果`callback`ä¸ï¼ŒæŸä¸€ä¸ªæ–¹æ³•`return false`了,则åŽç»çš„å…¶ä»–`callback`都ä¸ä¼šè¢«æ‰§è¡Œåˆ°ã€‚ * 切会影å“到`trigger`方法的返回值,为`false`。 * * `on`还å¯ä»¥ç”¨æ¥æ·»åŠ ä¸€ä¸ªç‰¹æ®Šäº‹ä»¶`all`, è¿™æ ·æ‰€æœ‰çš„äº‹ä»¶è§¦å‘都会å“åº”åˆ°ã€‚åŒæ—¶æ¤ç±»`callback`ä¸çš„arguments有一个ä¸åŒå¤„, * å°±æ˜¯ç¬¬ä¸€ä¸ªå‚æ•°ä¸º`type`ï¼Œè®°å½•å½“å‰æ˜¯ä»€ä¹ˆäº‹ä»¶åœ¨è§¦å‘。æ¤ç±»`callback`çš„ä¼˜å…ˆçº§æ¯”è„šä½Žï¼Œä¼šå†æ£å¸¸`callback`执行完åŽè§¦å‘。 * ```javascript * obj.on( 'all', function( type, arg1, arg2 ) { * console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2' * }); * ``` * * @method on * @grammar on( name, callback[, context] ) => self * @param {String} name 事件å,支æŒå¤šä¸ªäº‹ä»¶ç”¨ç©ºæ ¼éš”å¼€ * @param {Function} callback 事件处ç†å™¨ * @param {Object} [context] 事件处ç†å™¨çš„上下文。 * @return {self} è¿”å›žè‡ªèº«ï¼Œæ–¹ä¾¿é“¾å¼ * @chainable * @class Mediator */ on: function( name, callback, context ) { var me = this, set; if ( !callback ) { return this; } set = this._events || (this._events = []); eachEvent( name, callback, function( name, callback ) { var handler = { e: name }; handler.cb = callback; handler.ctx = context; handler.ctx2 = context || me; handler.id = set.length; set.push( handler ); }); return this; }, /** * 绑定事件,且当handler执行完åŽï¼Œè‡ªåŠ¨è§£é™¤ç»‘å®šã€‚ * @method once * @grammar once( name, callback[, context] ) => self * @param {String} name 事件å * @param {Function} callback 事件处ç†å™¨ * @param {Object} [context] 事件处ç†å™¨çš„上下文。 * @return {self} è¿”å›žè‡ªèº«ï¼Œæ–¹ä¾¿é“¾å¼ * @chainable */ once: function( name, callback, context ) { var me = this; if ( !callback ) { return me; } eachEvent( name, callback, function( name, callback ) { var once = function() { me.off( name, once ); return callback.apply( context || me, arguments ); }; once._cb = callback; me.on( name, once, context ); }); return me; }, /** * 解除事件绑定 * @method off * @grammar off( [name[, callback[, context] ] ] ) => self * @param {String} [name] 事件å * @param {Function} [callback] 事件处ç†å™¨ * @param {Object} [context] 事件处ç†å™¨çš„上下文。 * @return {self} è¿”å›žè‡ªèº«ï¼Œæ–¹ä¾¿é“¾å¼ * @chainable */ off: function( name, cb, ctx ) { var events = this._events; if ( !events ) { return this; } if ( !name && !cb && !ctx ) { this._events = []; return this; } eachEvent( name, cb, function( name, cb ) { $.each( findHandlers( events, name, cb, ctx ), function() { delete events[ this.id ]; }); }); return this; }, /** * 触å‘事件 * @method trigger * @grammar trigger( name[, args...] ) => self * @param {String} type 事件å * @param {*} [...] ä»»æ„傿•° * @return {Boolean} 如果handlerä¸return false了,则返回false, å¦åˆ™è¿”回true */ trigger: function( type ) { var args, events, allEvents; if ( !this._events || !type ) { return this; } args = slice.call( arguments, 1 ); events = findHandlers( this._events, type ); allEvents = findHandlers( this._events, 'all' ); return triggerHanders( events, args ) && triggerHanders( allEvents, arguments ); } }; /** * ä¸ä»‹è€…,它本身是个å•例,但å¯ä»¥é€šè¿‡[installTo](#WebUploader:Mediator:installTo)方法,使任何对象具备事件行为。 * 主è¦ç›®çš„æ˜¯è´Ÿè´£æ¨¡å—与模å—之间的åˆä½œï¼Œé™ä½Žè€¦åˆåº¦ã€‚ * * @class Mediator */ return $.extend({ /** * å¯ä»¥é€šè¿‡è¿™ä¸ªæŽ¥å£ï¼Œä½¿ä»»ä½•对象具备事件功能。 * @method installTo * @param {Object} obj 需è¦å…·å¤‡äº‹ä»¶è¡Œä¸ºçš„对象。 * @return {Object} 返回obj. */ installTo: function( obj ) { return $.extend( obj, protos ); } }, protos ); }); /** * @fileOverview Uploaderä¸Šä¼ ç±» */ define('uploader',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$; /** * ä¸Šä¼ å…¥å£ç±»ã€‚ * @class Uploader * @constructor * @grammar new Uploader( opts ) => Uploader * @example * var uploader = WebUploader.Uploader({ * swf: 'path_of_swf/Uploader.swf', * * // å¼€èµ·åˆ†ç‰‡ä¸Šä¼ ã€‚ * chunked: true * }); */ function Uploader( opts ) { this.options = $.extend( true, {}, Uploader.options, opts ); this._init( this.options ); } // default Options // widgets䏿œ‰ç›¸åº”扩展 Uploader.options = {}; Mediator.installTo( Uploader.prototype ); // æ‰¹é‡æ·»åŠ çº¯å‘½ä»¤å¼æ–¹æ³•。 $.each({ upload: 'start-upload', stop: 'stop-upload', getFile: 'get-file', getFiles: 'get-files', addFile: 'add-file', addFiles: 'add-file', sort: 'sort-files', removeFile: 'remove-file', cancelFile: 'cancel-file', skipFile: 'skip-file', retry: 'retry', isInProgress: 'is-in-progress', makeThumb: 'make-thumb', md5File: 'md5-file', getDimension: 'get-dimension', addButton: 'add-btn', predictRuntimeType: 'predict-runtime-type', refresh: 'refresh', disable: 'disable', enable: 'enable', reset: 'reset' }, function( fn, command ) { Uploader.prototype[ fn ] = function() { return this.request( command, arguments ); }; }); $.extend( Uploader.prototype, { state: 'pending', _init: function( opts ) { var me = this; me.request( 'init', opts, function() { me.state = 'ready'; me.trigger('ready'); }); }, /** * èŽ·å–æˆ–者设置Uploaderé…置项。 * @method option * @grammar option( key ) => * * @grammar option( key, val ) => self * @example * * // åˆå§‹çжæ€å›¾ç‰‡ä¸Šä¼ å‰ä¸ä¼šåŽ‹ç¼© * var uploader = new WebUploader.Uploader({ * compress: null; * }); * * // 修改åŽå›¾ç‰‡ä¸Šä¼ å‰ï¼Œå°è¯•将图片压缩到1600 * 1600 * uploader.option( 'compress', { * width: 1600, * height: 1600 * }); */ option: function( key, val ) { var opts = this.options; // setter if ( arguments.length > 1 ) { if ( $.isPlainObject( val ) && $.isPlainObject( opts[ key ] ) ) { $.extend( opts[ key ], val ); } else { opts[ key ] = val; } } else { // getter return key ? opts[ key ] : opts; } }, /** * èŽ·å–æ–‡ä»¶ç»Ÿè®¡ä¿¡æ¯ã€‚返回一个包å«ä¸€ä¸‹ä¿¡æ¯çš„对象。 * * `successNum` ä¸Šä¼ æˆåŠŸçš„æ–‡ä»¶æ•° * * `progressNum` ä¸Šä¼ ä¸çš„æ–‡ä»¶æ•° * * `cancelNum` è¢«åˆ é™¤çš„æ–‡ä»¶æ•° * * `invalidNum` æ— æ•ˆçš„æ–‡ä»¶æ•° * * `uploadFailNum` ä¸Šä¼ å¤±è´¥çš„æ–‡ä»¶æ•° * * `queueNum` 还在队列ä¸çš„æ–‡ä»¶æ•° * * `interruptNum` 被暂åœçš„æ–‡ä»¶æ•° * @method getStats * @grammar getStats() => Object */ getStats: function() { // return this._mgr.getStats.apply( this._mgr, arguments ); var stats = this.request('get-stats'); return stats ? { successNum: stats.numOfSuccess, progressNum: stats.numOfProgress, // who care? // queueFailNum: 0, cancelNum: stats.numOfCancel, invalidNum: stats.numOfInvalid, uploadFailNum: stats.numOfUploadFailed, queueNum: stats.numOfQueue, interruptNum: stats.numofInterrupt } : {}; }, // 需è¦é‡å†™æ¤æ–¹æ³•æ¥æ¥æ”¯æŒopts.onEventå’Œinstance.onEvent的处ç†å™¨ trigger: function( type/*, args...*/ ) { var args = [].slice.call( arguments, 1 ), opts = this.options, name = 'on' + type.substring( 0, 1 ).toUpperCase() + type.substring( 1 ); if ( // 调用通过on方法注册的handler. Mediator.trigger.apply( this, arguments ) === false || // 调用opts.onEvent $.isFunction( opts[ name ] ) && opts[ name ].apply( this, args ) === false || // 调用this.onEvent $.isFunction( this[ name ] ) && this[ name ].apply( this, args ) === false || // å¹¿æ’æ‰€æœ‰uploader的事件。 Mediator.trigger.apply( Mediator, [ this, type ].concat( args ) ) === false ) { return false; } return true; }, /** * é”€æ¯ webuploader 实例 * @method destroy * @grammar destroy() => undefined */ destroy: function() { this.request( 'destroy', arguments ); this.off(); }, // widgets/widget.jså°†è¡¥å……æ¤æ–¹æ³•的详细文档。 request: Base.noop }); /** * 创建Uploader实例,ç‰åŒäºŽnew Uploader( opts ); * @method create * @class Base * @static * @grammar Base.create( opts ) => Uploader */ Base.create = Uploader.create = function( opts ) { return new Uploader( opts ); }; // 暴露Uploader,å¯ä»¥é€šè¿‡å®ƒæ¥æ‰©å±•业务逻辑。 Base.Uploader = Uploader; return Uploader; }); /** * @fileOverview Runtime管ç†å™¨ï¼Œè´Ÿè´£Runtime的选择, 连接 */ define('runtime/runtime',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, factories = {}, // 获å–对象的第一个key getFirstKey = function( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } return null; }; // 接å£ç±»ã€‚ function Runtime( options ) { this.options = $.extend({ container: document.body }, options ); this.uid = Base.guid('rt_'); } $.extend( Runtime.prototype, { getContainer: function() { var opts = this.options, parent, container; if ( this._container ) { return this._container; } parent = $( opts.container || document.body ); container = $( document.createElement('div') ); container.attr( 'id', 'rt_' + this.uid ); container.css({ position: 'absolute', top: '0px', left: '0px', width: '1px', height: '1px', overflow: 'hidden' }); parent.append( container ); parent.addClass('webuploader-container'); this._container = container; this._parent = parent; return container; }, init: Base.noop, exec: Base.noop, destroy: function() { this._container && this._container.remove(); this._parent && this._parent.removeClass('webuploader-container'); this.off(); } }); Runtime.orders = 'html5,flash'; /** * æ·»åŠ Runtime实现。 * @param {String} type 类型 * @param {Runtime} factory 具体Runtime实现。 */ Runtime.addRuntime = function( type, factory ) { factories[ type ] = factory; }; Runtime.hasRuntime = function( type ) { return !!(type ? factories[ type ] : getFirstKey( factories )); }; Runtime.create = function( opts, orders ) { var type, runtime; orders = orders || Runtime.orders; $.each( orders.split( /\s*,\s*/g ), function() { if ( factories[ this ] ) { type = this; return false; } }); type = type || getFirstKey( factories ); if ( !type ) { throw new Error('Runtime Error'); } runtime = new factories[ type ]( opts ); return runtime; }; Mediator.installTo( Runtime.prototype ); return Runtime; }); /** * @fileOverview Runtime管ç†å™¨ï¼Œè´Ÿè´£Runtime的选择, 连接 */ define('runtime/client',[ 'base', 'mediator', 'runtime/runtime' ], function( Base, Mediator, Runtime ) { var cache; cache = (function() { var obj = {}; return { add: function( runtime ) { obj[ runtime.uid ] = runtime; }, get: function( ruid, standalone ) { var i; if ( ruid ) { return obj[ ruid ]; } for ( i in obj ) { // 有些类型ä¸èƒ½é‡ç”¨ï¼Œæ¯”如filepicker. if ( standalone && obj[ i ].__standalone ) { continue; } return obj[ i ]; } return null; }, remove: function( runtime ) { delete obj[ runtime.uid ]; } }; })(); function RuntimeClient( component, standalone ) { var deferred = Base.Deferred(), runtime; this.uid = Base.guid('client_'); // å…许runtime没有åˆå§‹åŒ–之å‰ï¼Œæ³¨å†Œä¸€äº›æ–¹æ³•在åˆå§‹åŒ–åŽæ‰§è¡Œã€‚ this.runtimeReady = function( cb ) { return deferred.done( cb ); }; this.connectRuntime = function( opts, cb ) { // already connected. if ( runtime ) { throw new Error('already connected!'); } deferred.done( cb ); if ( typeof opts === 'string' && cache.get( opts ) ) { runtime = cache.get( opts ); } // åƒfilePickeråªèƒ½ç‹¬ç«‹å˜åœ¨ï¼Œä¸èƒ½å…¬ç”¨ã€‚ runtime = runtime || cache.get( null, standalone ); // 需è¦åˆ›å»º if ( !runtime ) { runtime = Runtime.create( opts, opts.runtimeOrder ); runtime.__promise = deferred.promise(); runtime.once( 'ready', deferred.resolve ); runtime.init(); cache.add( runtime ); runtime.__client = 1; } else { // æ¥è‡ªcache Base.$.extend( runtime.options, opts ); runtime.__promise.then( deferred.resolve ); runtime.__client++; } standalone && (runtime.__standalone = standalone); return runtime; }; this.getRuntime = function() { return runtime; }; this.disconnectRuntime = function() { if ( !runtime ) { return; } runtime.__client--; if ( runtime.__client <= 0 ) { cache.remove( runtime ); delete runtime.__promise; runtime.destroy(); } runtime = null; }; this.exec = function() { if ( !runtime ) { return; } var args = Base.slice( arguments ); component && args.unshift( component ); return runtime.exec.apply( this, args ); }; this.getRuid = function() { return runtime && runtime.uid; }; this.destroy = (function( destroy ) { return function() { destroy && destroy.apply( this, arguments ); this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }; })( this.destroy ); } Mediator.installTo( RuntimeClient.prototype ); return RuntimeClient; }); /** * @fileOverview Blob */ define('lib/blob',[ 'base', 'runtime/client' ], function( Base, RuntimeClient ) { function Blob( ruid, source ) { var me = this; me.source = source; me.ruid = ruid; this.size = source.size || 0; // 如果没有指定 mimetype, ä½†æ˜¯çŸ¥é“æ–‡ä»¶åŽç¼€ã€‚ if ( !source.type && this.ext && ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) { this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext); } else { this.type = source.type || 'application/octet-stream'; } RuntimeClient.call( me, 'Blob' ); this.uid = source.uid || this.uid; if ( ruid ) { me.connectRuntime( ruid ); } } Base.inherits( RuntimeClient, { constructor: Blob, slice: function( start, end ) { return this.exec( 'slice', start, end ); }, getSource: function() { return this.source; } }); return Blob; }); /** * 为了统一化Flashçš„Fileå’ŒHTML5çš„File而å˜åœ¨ã€‚ * 以至于è¦è°ƒç”¨Flash里é¢çš„File,也å¯ä»¥åƒè°ƒç”¨HTML5版本的File一下。 * @fileOverview File */ define('lib/file',[ 'base', 'lib/blob' ], function( Base, Blob ) { var uid = 1, rExt = /\.([^.]+)$/; function File( ruid, file ) { var ext; this.name = file.name || ('untitled' + uid++); ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : ''; // todo 支æŒå…¶ä»–类型文件的转æ¢ã€‚ // 如果有 mimetype, 但是文件åé‡Œé¢æ²¡æœ‰æ‰¾å‡ºåŽç¼€è§„律 if ( !ext && file.type ) { ext = /\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ? RegExp.$1.toLowerCase() : ''; this.name += '.' + ext; } this.ext = ext; this.lastModifiedDate = file.lastModifiedDate || (new Date()).toLocaleString(); Blob.apply( this, arguments ); } return Base.inherits( Blob, File ); }); /** * @fileOverview é”™è¯¯ä¿¡æ¯ */ define('lib/filepicker',[ 'base', 'runtime/client', 'lib/file' ], function( Base, RuntimeClent, File ) { var $ = Base.$; function FilePicker( opts ) { opts = this.options = $.extend({}, FilePicker.options, opts ); opts.container = $( opts.id ); if ( !opts.container.length ) { throw new Error('按钮指定错误'); } opts.innerHTML = opts.innerHTML || opts.label || opts.container.html() || ''; opts.button = $( opts.button || document.createElement('div') ); opts.button.html( opts.innerHTML ); opts.container.html( opts.button ); RuntimeClent.call( this, 'FilePicker', true ); } FilePicker.options = { button: null, container: null, label: null, innerHTML: null, multiple: true, accept: null, name: 'file' }; Base.inherits( RuntimeClent, { constructor: FilePicker, init: function() { var me = this, opts = me.options, button = opts.button; button.addClass('webuploader-pick'); me.on( 'all', function( type ) { var files; switch ( type ) { case 'mouseenter': button.addClass('webuploader-pick-hover'); break; case 'mouseleave': button.removeClass('webuploader-pick-hover'); break; case 'change': files = me.exec('getFiles'); me.trigger( 'select', $.map( files, function( file ) { file = new File( me.getRuid(), file ); // è®°å½•æ¥æºã€‚ file._refer = opts.container; return file; }), opts.container ); break; } }); me.connectRuntime( opts, function() { me.refresh(); me.exec( 'init', opts ); me.trigger('ready'); }); this._resizeHandler = Base.bindFn( this.refresh, this ); $( window ).on( 'resize', this._resizeHandler ); }, refresh: function() { var shimContainer = this.getRuntime().getContainer(), button = this.options.button, width = button.outerWidth ? button.outerWidth() : button.width(), height = button.outerHeight ? button.outerHeight() : button.height(), pos = button.offset(); width && height && shimContainer.css({ bottom: 'auto', right: 'auto', width: width + 'px', height: height + 'px' }).offset( pos ); }, enable: function() { var btn = this.options.button; btn.removeClass('webuploader-pick-disable'); this.refresh(); }, disable: function() { var btn = this.options.button; this.getRuntime().getContainer().css({ top: '-99999px' }); btn.addClass('webuploader-pick-disable'); }, destroy: function() { var btn = this.options.button; $( window ).off( 'resize', this._resizeHandler ); btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' + 'webuploader-pick'); } }); return FilePicker; }); /** * @fileOverview 组件基类。 */ define('widgets/widget',[ 'base', 'uploader' ], function( Base, Uploader ) { var $ = Base.$, _init = Uploader.prototype._init, _destroy = Uploader.prototype.destroy, IGNORE = {}, widgetClass = []; function isArrayLike( obj ) { if ( !obj ) { return false; } var length = obj.length, type = $.type( obj ); if ( obj.nodeType === 1 && length ) { return true; } return type === 'array' || type !== 'function' && type !== 'string' && (length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj); } function Widget( uploader ) { this.owner = uploader; this.options = uploader.options; } $.extend( Widget.prototype, { init: Base.noop, // ç±»Backbone的事件监å¬å£°æ˜Žï¼Œç›‘å¬uploader实例上的事件 // widgetç›´æŽ¥æ— æ³•ç›‘å¬äº‹ä»¶ï¼Œäº‹ä»¶åªèƒ½é€šè¿‡uploaderæ¥ä¼ 递 invoke: function( apiName, args ) { /* { 'make-thumb': 'makeThumb' } */ var map = this.responseMap; // å¦‚æžœæ— APIå“应声明则忽略 if ( !map || !(apiName in map) || !(map[ apiName ] in this) || !$.isFunction( this[ map[ apiName ] ] ) ) { return IGNORE; } return this[ map[ apiName ] ].apply( this, args ); }, /** * å‘é€å‘½ä»¤ã€‚å½“ä¼ å…¥`callback`或者`handler`ä¸è¿”回`promise`时。返回一个当所有`handler`ä¸çš„promise都完æˆåŽå®Œæˆçš„æ–°`promise`。 * @method request * @grammar request( command, args ) => * | Promise * @grammar request( command, args, callback ) => Promise * @for Uploader */ request: function() { return this.owner.request.apply( this.owner, arguments ); } }); // 扩展Uploader. $.extend( Uploader.prototype, { /** * @property {String | Array} [disableWidgets=undefined] * @namespace options * @for Uploader * @description 默认所有 Uploader.register 了的 widget éƒ½ä¼šè¢«åŠ è½½ï¼Œå¦‚æžœç¦ç”¨æŸä¸€éƒ¨åˆ†ï¼Œè¯·é€šè¿‡æ¤ option 指定黑åå•。 */ // 覆写_init用æ¥åˆå§‹åŒ–widgets _init: function() { var me = this, widgets = me._widgets = [], deactives = me.options.disableWidgets || ''; $.each( widgetClass, function( _, klass ) { (!deactives || !~deactives.indexOf( klass._name )) && widgets.push( new klass( me ) ); }); return _init.apply( me, arguments ); }, request: function( apiName, args, callback ) { var i = 0, widgets = this._widgets, len = widgets && widgets.length, rlts = [], dfds = [], widget, rlt, promise, key; args = isArrayLike( args ) ? args : [ args ]; for ( ; i < len; i++ ) { widget = widgets[ i ]; rlt = widget.invoke( apiName, args ); if ( rlt !== IGNORE ) { // Deferred对象 if ( Base.isPromise( rlt ) ) { dfds.push( rlt ); } else { rlts.push( rlt ); } } } // 如果有callbackï¼Œåˆ™ç”¨å¼‚æ¥æ–¹å¼ã€‚ if ( callback || dfds.length ) { promise = Base.when.apply( Base, dfds ); key = promise.pipe ? 'pipe' : 'then'; // 很é‡è¦ä¸èƒ½åˆ é™¤ã€‚åˆ é™¤äº†ä¼šæ»å¾ªçŽ¯ã€‚ // ä¿è¯æ‰§è¡Œé¡ºåºã€‚让callback总是在下一个 tick 䏿‰§è¡Œã€‚ return promise[ key ](function() { var deferred = Base.Deferred(), args = arguments; if ( args.length === 1 ) { args = args[ 0 ]; } setTimeout(function() { deferred.resolve( args ); }, 1 ); return deferred.promise(); })[ callback ? key : 'done' ]( callback || Base.noop ); } else { return rlts[ 0 ]; } }, destroy: function() { _destroy.apply( this, arguments ); this._widgets = null; } }); /** * æ·»åŠ ç»„ä»¶ * @grammar Uploader.register(proto); * @grammar Uploader.register(map, proto); * @param {object} responseMap API åç§°ä¸Žå‡½æ•°å®žçŽ°çš„æ˜ å°„ * @param {object} proto ç»„ä»¶åŽŸåž‹ï¼Œæž„é€ å‡½æ•°é€šè¿‡ constructor 属性定义 * @method Uploader.register * @for Uploader * @example * Uploader.register({ * 'make-thumb': 'makeThumb' * }, { * init: function( options ) {}, * makeThumb: function() {} * }); * * Uploader.register({ * 'make-thumb': function() { * * } * }); */ Uploader.register = Widget.register = function( responseMap, widgetProto ) { var map = { init: 'init', destroy: 'destroy', name: 'anonymous' }, klass; if ( arguments.length === 1 ) { widgetProto = responseMap; // è‡ªåŠ¨ç”Ÿæˆ map 表。 $.each(widgetProto, function(key) { if ( key[0] === '_' || key === 'name' ) { key === 'name' && (map.name = widgetProto.name); return; } map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key; }); } else { map = $.extend( map, responseMap ); } widgetProto.responseMap = map; klass = Base.inherits( Widget, widgetProto ); klass._name = map.name; widgetClass.push( klass ); return klass; }; /** * åˆ é™¤æ’ä»¶ï¼Œåªæœ‰åœ¨æ³¨å†Œæ—¶æŒ‡å®šäº†åå—çš„æ‰èƒ½è¢«åˆ 除。 * @grammar Uploader.unRegister(name); * @param {string} name 组件åå— * @method Uploader.unRegister * @for Uploader * @example * * Uploader.register({ * name: 'custom', * * 'make-thumb': function() { * * } * }); * * Uploader.unRegister('custom'); */ Uploader.unRegister = Widget.unRegister = function( name ) { if ( !name || name === 'anonymous' ) { return; } // åˆ é™¤æŒ‡å®šçš„æ’件。 for ( var i = widgetClass.length; i--; ) { if ( widgetClass[i]._name === name ) { widgetClass.splice(i, 1) } } }; return Widget; }); /** * @fileOverview 文件选择相关 */ define('widgets/filepicker',[ 'base', 'uploader', 'lib/filepicker', 'widgets/widget' ], function( Base, Uploader, FilePicker ) { var $ = Base.$; $.extend( Uploader.options, { /** * @property {Selector | Object} [pick=undefined] * @namespace options * @for Uploader * @description æŒ‡å®šé€‰æ‹©æ–‡ä»¶çš„æŒ‰é’®å®¹å™¨ï¼Œä¸æŒ‡å®šåˆ™ä¸åˆ›å»ºæŒ‰é’®ã€‚ * * * `id` {Seletor|dom} æŒ‡å®šé€‰æ‹©æ–‡ä»¶çš„æŒ‰é’®å®¹å™¨ï¼Œä¸æŒ‡å®šåˆ™ä¸åˆ›å»ºæŒ‰é’®ã€‚**注æ„** 这里虽然写的是 id, ä½†æ˜¯ä¸æ˜¯åªæ”¯æŒ id, è¿˜æ”¯æŒ class, 或者 dom 节点。 * * `label` {String} 请采用 `innerHTML` 代替 * * `innerHTML` {String} 指定按钮文å—ã€‚ä¸æŒ‡å®šæ—¶ä¼˜å…ˆä»ŽæŒ‡å®šçš„容器ä¸çœ‹æ˜¯å¦è‡ªå¸¦æ–‡å—。 * * `multiple` {Boolean} 是å¦å¼€èµ·åŒæ—¶é€‰æ‹©å¤šä¸ªæ–‡ä»¶èƒ½åŠ›ã€‚ */ pick: null, /** * @property {Arroy} [accept=null] * @namespace options * @for Uploader * @description 指定接å—哪些类型的文件。 由于目å‰è¿˜æœ‰ext转mimeType表,所以这里需è¦åˆ†å¼€æŒ‡å®šã€‚ * * * `title` {String} æ–‡å—æè¿° * * `extensions` {String} å…许的文件åŽç¼€ï¼Œä¸å¸¦ç‚¹ï¼Œå¤šä¸ªç”¨é€—å·åˆ†å‰²ã€‚ * * `mimeTypes` {String} 多个用逗å·åˆ†å‰²ã€‚ * * 如: * * ``` * { * title: 'Images', * extensions: 'gif,jpg,jpeg,bmp,png', * mimeTypes: 'image/*' * } * ``` */ accept: null/*{ title: 'Images', extensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: 'image/*' }*/ }); return Uploader.register({ name: 'picker', init: function( opts ) { this.pickers = []; return opts.pick && this.addBtn( opts.pick ); }, refresh: function() { $.each( this.pickers, function() { this.refresh(); }); }, /** * @method addButton * @for Uploader * @grammar addButton( pick ) => Promise * @description * æ·»åŠ æ–‡ä»¶é€‰æ‹©æŒ‰é’®ï¼Œå¦‚æžœä¸€ä¸ªæŒ‰é’®ä¸å¤Ÿï¼Œéœ€è¦è°ƒç”¨æ¤æ–¹æ³•æ¥æ·»åŠ ã€‚å‚æ•°è·Ÿ[options.pick](#WebUploader:Uploader:options)一致。 * @example * uploader.addButton({ * id: '#btnContainer', * innerHTML: '选择文件' * }); */ addBtn: function( pick ) { var me = this, opts = me.options, accept = opts.accept, promises = []; if ( !pick ) { return; } $.isPlainObject( pick ) || (pick = { id: pick }); $( pick.id ).each(function() { var options, picker, deferred; deferred = Base.Deferred(); options = $.extend({}, pick, { accept: $.isPlainObject( accept ) ? [ accept ] : accept, swf: opts.swf, runtimeOrder: opts.runtimeOrder, id: this }); picker = new FilePicker( options ); picker.once( 'ready', deferred.resolve ); picker.on( 'select', function( files ) { me.owner.request( 'add-file', [ files ]); }); picker.init(); me.pickers.push( picker ); promises.push( deferred.promise() ); }); return Base.when.apply( Base, promises ); }, disable: function() { $.each( this.pickers, function() { this.disable(); }); }, enable: function() { $.each( this.pickers, function() { this.enable(); }); }, destroy: function() { $.each( this.pickers, function() { this.destroy(); }); this.pickers = null; } }); }); /** * @fileOverview Image */ define('lib/image',[ 'base', 'runtime/client', 'lib/blob' ], function( Base, RuntimeClient, Blob ) { var $ = Base.$; // æž„é€ å™¨ã€‚ function Image( opts ) { this.options = $.extend({}, Image.options, opts ); RuntimeClient.call( this, 'Image' ); this.on( 'load', function() { this._info = this.exec('info'); this._meta = this.exec('meta'); }); } // 默认选项。 Image.options = { // 默认的图片处ç†è´¨é‡ quality: 90, // 是å¦è£å‰ª crop: false, // 是å¦ä¿ç•™å¤´éƒ¨ä¿¡æ¯ preserveHeaders: false, // 是å¦å…许放大。 allowMagnify: false }; // 继承RuntimeClient. Base.inherits( RuntimeClient, { constructor: Image, info: function( val ) { // setter if ( val ) { this._info = val; return this; } // getter return this._info; }, meta: function( val ) { // setter if ( val ) { this._meta = val; return this; } // getter return this._meta; }, loadFromBlob: function( blob ) { var me = this, ruid = blob.getRuid(); this.connectRuntime( ruid, function() { me.exec( 'init', me.options ); me.exec( 'loadFromBlob', blob ); }); }, resize: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'resize' ].concat( args ) ); }, crop: function() { var args = Base.slice( arguments ); return this.exec.apply( this, [ 'crop' ].concat( args ) ); }, getAsDataUrl: function( type ) { return this.exec( 'getAsDataUrl', type ); }, getAsBlob: function( type ) { var blob = this.exec( 'getAsBlob', type ); return new Blob( this.getRuid(), blob ); } }); return Image; }); /** * @fileOverview 图片æ“作, è´Ÿè´£é¢„è§ˆå›¾ç‰‡å’Œä¸Šä¼ å‰åŽ‹ç¼©å›¾ç‰‡ */ define('widgets/image',[ 'base', 'uploader', 'lib/image', 'widgets/widget' ], function( Base, Uploader, Image ) { var $ = Base.$, throttle; // æ ¹æ®è¦å¤„ç†çš„æ–‡ä»¶å¤§å°æ¥èŠ‚æµï¼Œä¸€æ¬¡ä¸èƒ½å¤„ç†å¤ªå¤šï¼Œä¼šå¡ã€‚ throttle = (function( max ) { var occupied = 0, waiting = [], tick = function() { var item; while ( waiting.length && occupied < max ) { item = waiting.shift(); occupied += item[ 0 ]; item[ 1 ](); } }; return function( emiter, size, cb ) { waiting.push([ size, cb ]); emiter.once( 'destroy', function() { occupied -= size; setTimeout( tick, 1 ); }); setTimeout( tick, 1 ); }; })( 5 * 1024 * 1024 ); $.extend( Uploader.options, { /** * @property {Object} [thumb] * @namespace options * @for Uploader * @description é…置生æˆç¼©ç•¥å›¾çš„选项。 * * 默认为: * * ```javascript * { * width: 110, * height: 110, * * // 图片质é‡ï¼Œåªæœ‰type为`image/jpeg`çš„æ—¶å€™æ‰æœ‰æ•ˆã€‚ * quality: 70, * * // 是å¦å…许放大,如果想è¦ç”Ÿæˆå°å›¾çš„æ—¶å€™ä¸å¤±çœŸï¼Œæ¤é€‰é¡¹åº”该设置为false. * allowMagnify: true, * * // 是å¦å…许è£å‰ªã€‚ * crop: true, * * // 为空的è¯åˆ™ä¿ç•™åŽŸæœ‰å›¾ç‰‡æ ¼å¼ã€‚ * // å¦åˆ™å¼ºåˆ¶è½¬æ¢æˆæŒ‡å®šçš„类型。 * type: 'image/jpeg' * } * ``` */ thumb: { width: 110, height: 110, quality: 70, allowMagnify: true, crop: true, preserveHeaders: false, // 为空的è¯åˆ™ä¿ç•™åŽŸæœ‰å›¾ç‰‡æ ¼å¼ã€‚ // å¦åˆ™å¼ºåˆ¶è½¬æ¢æˆæŒ‡å®šçš„类型。 // IE 8ä¸‹é¢ base64 大å°ä¸èƒ½è¶…过 32K å¦åˆ™é¢„è§ˆå¤±è´¥ï¼Œè€Œéž jpeg ç¼–ç çš„å›¾ç‰‡å¾ˆå¯ // 能会超过 32k, 所以这里设置æˆé¢„览的时候都是 image/jpeg type: 'image/jpeg' }, /** * @property {Object} [compress] * @namespace options * @for Uploader * @description é…置压缩的图片的选项。如果æ¤é€‰é¡¹ä¸º`false`, åˆ™å›¾ç‰‡åœ¨ä¸Šä¼ å‰ä¸è¿›è¡ŒåŽ‹ç¼©ã€‚ * * 默认为: * * ```javascript * { * width: 1600, * height: 1600, * * // 图片质é‡ï¼Œåªæœ‰type为`image/jpeg`çš„æ—¶å€™æ‰æœ‰æ•ˆã€‚ * quality: 90, * * // 是å¦å…许放大,如果想è¦ç”Ÿæˆå°å›¾çš„æ—¶å€™ä¸å¤±çœŸï¼Œæ¤é€‰é¡¹åº”该设置为false. * allowMagnify: false, * * // 是å¦å…许è£å‰ªã€‚ * crop: false, * * // 是å¦ä¿ç•™å¤´éƒ¨metaä¿¡æ¯ã€‚ * preserveHeaders: true, * * // 如果å‘çŽ°åŽ‹ç¼©åŽæ–‡ä»¶å¤§å°æ¯”原æ¥è¿˜å¤§ï¼Œåˆ™ä½¿ç”¨åŽŸæ¥å›¾ç‰‡ * // æ¤å±žæ€§å¯èƒ½ä¼šå½±å“å›¾ç‰‡è‡ªåŠ¨çº æ£åŠŸèƒ½ * noCompressIfLarger: false, * * // å•ä½å—节,如果图片大å°å°äºŽæ¤å€¼ï¼Œä¸ä¼šé‡‡ç”¨åŽ‹ç¼©ã€‚ * compressSize: 0 * } * ``` */ compress: { width: 1600, height: 1600, quality: 90, allowMagnify: false, crop: false, preserveHeaders: true } }); return Uploader.register({ name: 'image', /** * 生æˆç¼©ç•¥å›¾ï¼Œæ¤è¿‡ç¨‹ä¸ºå¼‚æ¥ï¼Œæ‰€ä»¥éœ€è¦ä¼ å…¥`callback`。 * é€šå¸¸æƒ…å†µåœ¨å›¾ç‰‡åŠ å…¥é˜Ÿé‡ŒåŽè°ƒç”¨æ¤æ–¹æ³•æ¥ç”Ÿæˆé¢„览图以增强交互效果。 * * 当 width 或者 height 的值介于 0 - 1 时,被当æˆç™¾åˆ†æ¯”使用。 * * `callback`ä¸å¯ä»¥æŽ¥æ”¶åˆ°ä¸¤ä¸ªå‚数。 * * 第一个为error,如果生æˆç¼©ç•¥å›¾æœ‰é”™è¯¯ï¼Œæ¤error将为真。 * * 第二个为ret, 缩略图的Data URL值。 * * **注æ„** * Date URL在IE6/7ä¸ä¸æ”¯æŒï¼Œæ‰€ä»¥ä¸ç”¨è°ƒç”¨æ¤æ–¹æ³•äº†ï¼Œç›´æŽ¥æ˜¾ç¤ºä¸€å¼ æš‚ä¸æ”¯æŒé¢„览图片好了。 * 也å¯ä»¥å€ŸåŠ©æœåŠ¡ç«¯ï¼Œå°† base64 æ•°æ®ä¼ ç»™æœåŠ¡ç«¯ï¼Œç”Ÿæˆä¸€ä¸ªä¸´æ—¶æ–‡ä»¶ä¾›é¢„览。 * * @method makeThumb * @grammar makeThumb( file, callback ) => undefined * @grammar makeThumb( file, callback, width, height ) => undefined * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.makeThumb( file, function( error, ret ) { * if ( error ) { * $li.text('预览错误'); * } else { * $li.append('<img alt="" src="' + ret + '" />'); * } * }); * * }); */ makeThumb: function( file, cb, width, height ) { var opts, image; file = this.request( 'get-file', file ); // åªé¢„è§ˆå›¾ç‰‡æ ¼å¼ã€‚ if ( !file.type.match( /^image/ ) ) { cb( true ); return; } opts = $.extend({}, this.options.thumb ); // å¦‚æžœä¼ å…¥çš„æ˜¯object. if ( $.isPlainObject( width ) ) { opts = $.extend( opts, width ); width = null; } width = width || opts.width; height = height || opts.height; image = new Image( opts ); image.once( 'load', function() { file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // åŒæ ·çš„规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); // 当 resize å®ŒåŽ image.once( 'complete', function() { cb( false, image.getAsDataUrl( opts.type ) ); image.destroy(); }); image.once( 'error', function( reason ) { cb( reason || true ); image.destroy(); }); throttle( image, file.source.size, function() { file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); }); }, beforeSendFile: function( file ) { var opts = this.options.compress || this.options.resize, compressSize = opts && opts.compressSize || 0, noCompressIfLarger = opts && opts.noCompressIfLarger || false, image, deferred; file = this.request( 'get-file', file ); // åªåŽ‹ç¼© jpeg å›¾ç‰‡æ ¼å¼ã€‚ // gif å¯èƒ½ä¼šä¸¢å¤±é’ˆ // bmp png 基本上尺寸都ä¸å¤§ï¼Œä¸”压缩比比较å°ã€‚ if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) || file.size < compressSize || file._compressed ) { return; } opts = $.extend({}, opts ); deferred = Base.Deferred(); image = new Image( opts ); deferred.always(function() { image.destroy(); image = null; }); image.once( 'error', deferred.reject ); image.once( 'load', function() { var width = opts.width, height = opts.height; file._info = file._info || image.info(); file._meta = file._meta || image.meta(); // 如果 width 的值介于 0 - 1 // 说明设置的是百分比。 if ( width <= 1 && width > 0 ) { width = file._info.width * width; } // åŒæ ·çš„规则应用于 height if ( height <= 1 && height > 0 ) { height = file._info.height * height; } image.resize( width, height ); }); image.once( 'complete', function() { var blob, size; // 移动端 UC / qq æµè§ˆå™¨çš„æ— 图模å¼ä¸‹ // ctx.getImageData 处ç†å¤§å›¾çš„æ—¶å€™ä¼šæŠ¥ Exception // INDEX_SIZE_ERR: DOM Exception 1 try { blob = image.getAsBlob( opts.type ); size = file.size; // 如果压缩åŽï¼Œæ¯”原æ¥è¿˜å¤§åˆ™ä¸ç”¨åŽ‹ç¼©åŽçš„。 if ( !noCompressIfLarger || blob.size < size ) { // file.source.destroy && file.source.destroy(); file.source = blob; file.size = blob.size; file.trigger( 'resize', blob.size, size ); } // æ ‡è®°ï¼Œé¿å…é‡å¤åŽ‹ç¼©ã€‚ file._compressed = true; deferred.resolve(); } catch ( e ) { // 出错了直接继ç»ï¼Œè®©å…¶ä¸Šä¼ 原始图片 deferred.resolve(); } }); file._info && image.info( file._info ); file._meta && image.meta( file._meta ); image.loadFromBlob( file.source ); return deferred.promise(); } }); }); /** * @fileOverview 文件属性å°è£… */ define('file',[ 'base', 'mediator' ], function( Base, Mediator ) { var $ = Base.$, idPrefix = 'WU_FILE_', idSuffix = 0, rExt = /\.([^.]+)$/, statusMap = {}; function gid() { return idPrefix + idSuffix++; } /** * 文件类 * @class File * @constructor æž„é€ å‡½æ•° * @grammar new File( source ) => File * @param {Lib.File} source [lib.File](#Lib.File)实例, æ¤source对象是带有Runtimeä¿¡æ¯çš„。 */ function WUFile( source ) { /** * 文件å,包括扩展å(åŽç¼€ï¼‰ * @property name * @type {string} */ this.name = source.name || 'Untitled'; /** * 文件体积(å—节) * @property size * @type {uint} * @default 0 */ this.size = source.size || 0; /** * 文件MIMETYPE类型,与文件类型的对应关系请å‚考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny) * @property type * @type {string} * @default 'application/octet-stream' */ this.type = source.type || 'application/octet-stream'; /** * 文件最åŽä¿®æ”¹æ—¥æœŸ * @property lastModifiedDate * @type {int} * @default 当剿—¶é—´æˆ³ */ this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1); /** * 文件ID,æ¯ä¸ªå¯¹è±¡å…·æœ‰å”¯ä¸€IDï¼Œä¸Žæ–‡ä»¶åæ— å…³ * @property id * @type {string} */ this.id = gid(); /** * 文件扩展å,通过文件å获å–,例如test.png的扩展å为png * @property ext * @type {string} */ this.ext = rExt.exec( this.name ) ? RegExp.$1 : ''; /** * çŠ¶æ€æ–‡å—说明。在ä¸åŒçš„statusè¯å¢ƒä¸‹æœ‰ä¸åŒçš„用途。 * @property statusText * @type {string} */ this.statusText = ''; // å˜å‚¨æ–‡ä»¶çжæ€ï¼Œé˜²æ¢é€šè¿‡å±žæ€§ç›´æŽ¥ä¿®æ”¹ statusMap[ this.id ] = WUFile.Status.INITED; this.source = source; this.loaded = 0; this.on( 'error', function( msg ) { this.setStatus( WUFile.Status.ERROR, msg ); }); } $.extend( WUFile.prototype, { /** * 设置状æ€ï¼Œçжæ€å˜åŒ–时会触å‘`change`事件。 * @method setStatus * @grammar setStatus( status[, statusText] ); * @param {File.Status|String} status [文件状æ€å€¼](#WebUploader:File:File.Status) * @param {String} [statusText=''] 状æ€è¯´æ˜Žï¼Œå¸¸åœ¨error时使用,用http, abort,serverç‰æ¥æ ‡è®°æ˜¯ç”±äºŽä»€ä¹ˆåŽŸå› å¯¼è‡´æ–‡ä»¶é”™è¯¯ã€‚ */ setStatus: function( status, text ) { var prevStatus = statusMap[ this.id ]; typeof text !== 'undefined' && (this.statusText = text); if ( status !== prevStatus ) { statusMap[ this.id ] = status; /** * 文件状æ€å˜åŒ– * @event statuschange */ this.trigger( 'statuschange', status, prevStatus ); } }, /** * èŽ·å–æ–‡ä»¶çŠ¶æ€ * @return {File.Status} * @example 文件状æ€å…·ä½“åŒ…æ‹¬ä»¥ä¸‹å‡ ç§ç±»åž‹ï¼š { // åˆå§‹åŒ– INITED: 0, // 已入队列 QUEUED: 1, // æ£åœ¨ä¸Šä¼ PROGRESS: 2, // ä¸Šä¼ å‡ºé”™ ERROR: 3, // ä¸Šä¼ æˆåŠŸ COMPLETE: 4, // ä¸Šä¼ å–æ¶ˆ CANCELLED: 5 } */ getStatus: function() { return statusMap[ this.id ]; }, /** * èŽ·å–æ–‡ä»¶åŽŸå§‹ä¿¡æ¯ã€‚ * @return {*} */ getSource: function() { return this.source; }, destroy: function() { this.off(); delete statusMap[ this.id ]; } }); Mediator.installTo( WUFile.prototype ); /** * 文件状æ€å€¼ï¼Œå…·ä½“åŒ…æ‹¬ä»¥ä¸‹å‡ ç§ç±»åž‹ï¼š * * `inited` åˆå§‹çŠ¶æ€ * * `queued` å·²ç»è¿›å…¥é˜Ÿåˆ—, ç‰å¾…ä¸Šä¼ * * `progress` ä¸Šä¼ ä¸ * * `complete` ä¸Šä¼ å®Œæˆã€‚ * * `error` ä¸Šä¼ å‡ºé”™ï¼Œå¯é‡è¯• * * `interrupt` ä¸Šä¼ ä¸æ–,å¯ç»ä¼ 。 * * `invalid` 文件ä¸åˆæ ¼ï¼Œä¸èƒ½é‡è¯•ä¸Šä¼ ã€‚ä¼šè‡ªåŠ¨ä»Žé˜Ÿåˆ—ä¸ç§»é™¤ã€‚ * * `cancelled` 文件被移除。 * @property {Object} Status * @namespace File * @class File * @static */ WUFile.Status = { INITED: 'inited', // åˆå§‹çŠ¶æ€ QUEUED: 'queued', // å·²ç»è¿›å…¥é˜Ÿåˆ—, ç‰å¾…ä¸Šä¼ PROGRESS: 'progress', // ä¸Šä¼ ä¸ ERROR: 'error', // ä¸Šä¼ å‡ºé”™ï¼Œå¯é‡è¯• COMPLETE: 'complete', // ä¸Šä¼ å®Œæˆã€‚ CANCELLED: 'cancelled', // ä¸Šä¼ å–æ¶ˆã€‚ INTERRUPT: 'interrupt', // ä¸Šä¼ ä¸æ–,å¯ç»ä¼ 。 INVALID: 'invalid' // 文件ä¸åˆæ ¼ï¼Œä¸èƒ½é‡è¯•ä¸Šä¼ ã€‚ }; return WUFile; }); /** * @fileOverview 文件队列 */ define('queue',[ 'base', 'mediator', 'file' ], function( Base, Mediator, WUFile ) { var $ = Base.$, STATUS = WUFile.Status; /** * 文件队列, 用æ¥å˜å‚¨å„个状æ€ä¸çš„æ–‡ä»¶ã€‚ * @class Queue * @extends Mediator */ function Queue() { /** * 统计文件数。 * * `numOfQueue` 队列ä¸çš„æ–‡ä»¶æ•°ã€‚ * * `numOfSuccess` ä¸Šä¼ æˆåŠŸçš„æ–‡ä»¶æ•° * * `numOfCancel` è¢«å–æ¶ˆçš„æ–‡ä»¶æ•° * * `numOfProgress` æ£åœ¨ä¸Šä¼ ä¸çš„æ–‡ä»¶æ•° * * `numOfUploadFailed` ä¸Šä¼ é”™è¯¯çš„æ–‡ä»¶æ•°ã€‚ * * `numOfInvalid` æ— æ•ˆçš„æ–‡ä»¶æ•°ã€‚ * * `numofDeleted` 被移除的文件数。 * @property {Object} stats */ this.stats = { numOfQueue: 0, numOfSuccess: 0, numOfCancel: 0, numOfProgress: 0, numOfUploadFailed: 0, numOfInvalid: 0, numofDeleted: 0, numofInterrupt: 0 }; // ä¸Šä¼ é˜Ÿåˆ—ï¼Œä»…åŒ…æ‹¬ç‰å¾…ä¸Šä¼ çš„æ–‡ä»¶ this._queue = []; // å˜å‚¨æ‰€æœ‰æ–‡ä»¶ this._map = {}; } $.extend( Queue.prototype, { /** * å°†æ–°æ–‡ä»¶åŠ å…¥å¯¹é˜Ÿåˆ—å°¾éƒ¨ * * @method append * @param {File} file 文件对象 */ append: function( file ) { this._queue.push( file ); this._fileAdded( file ); return this; }, /** * å°†æ–°æ–‡ä»¶åŠ å…¥å¯¹é˜Ÿåˆ—å¤´éƒ¨ * * @method prepend * @param {File} file 文件对象 */ prepend: function( file ) { this._queue.unshift( file ); this._fileAdded( file ); return this; }, /** * èŽ·å–æ–‡ä»¶å¯¹è±¡ * * @method getFile * @param {String} fileId 文件ID * @return {File} */ getFile: function( fileId ) { if ( typeof fileId !== 'string' ) { return fileId; } return this._map[ fileId ]; }, /** * 从队列ä¸å–出一个指定状æ€çš„æ–‡ä»¶ã€‚ * @grammar fetch( status ) => File * @method fetch * @param {String} status [文件状æ€å€¼](#WebUploader:File:File.Status) * @return {File} [File](#WebUploader:File) */ fetch: function( status ) { var len = this._queue.length, i, file; status = status || STATUS.QUEUED; for ( i = 0; i < len; i++ ) { file = this._queue[ i ]; if ( status === file.getStatus() ) { return file; } } return null; }, /** * 对队列进行排åºï¼Œèƒ½å¤ŸæŽ§åˆ¶æ–‡ä»¶ä¸Šä¼ 顺åºã€‚ * @grammar sort( fn ) => undefined * @method sort * @param {Function} fn æŽ’åºæ–¹æ³• */ sort: function( fn ) { if ( typeof fn === 'function' ) { this._queue.sort( fn ); } }, /** * èŽ·å–æŒ‡å®šç±»åž‹çš„æ–‡ä»¶åˆ—表, åˆ—è¡¨ä¸æ¯ä¸€ä¸ªæˆå‘˜ä¸º[File](#WebUploader:File)对象。 * @grammar getFiles( [status1[, status2 ...]] ) => Array * @method getFiles * @param {String} [status] [文件状æ€å€¼](#WebUploader:File:File.Status) */ getFiles: function() { var sts = [].slice.call( arguments, 0 ), ret = [], i = 0, len = this._queue.length, file; for ( ; i < len; i++ ) { file = this._queue[ i ]; if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) { continue; } ret.push( file ); } return ret; }, /** * 在队列ä¸åˆ 除文件。 * @grammar removeFile( file ) => Array * @method removeFile * @param {File} 文件对象。 */ removeFile: function( file ) { var me = this, existing = this._map[ file.id ]; if ( existing ) { delete this._map[ file.id ]; file.destroy(); this.stats.numofDeleted++; } }, _fileAdded: function( file ) { var me = this, existing = this._map[ file.id ]; if ( !existing ) { this._map[ file.id ] = file; file.on( 'statuschange', function( cur, pre ) { me._onFileStatusChange( cur, pre ); }); } }, _onFileStatusChange: function( curStatus, preStatus ) { var stats = this.stats; switch ( preStatus ) { case STATUS.PROGRESS: stats.numOfProgress--; break; case STATUS.QUEUED: stats.numOfQueue --; break; case STATUS.ERROR: stats.numOfUploadFailed--; break; case STATUS.INVALID: stats.numOfInvalid--; break; case STATUS.INTERRUPT: stats.numofInterrupt--; break; } switch ( curStatus ) { case STATUS.QUEUED: stats.numOfQueue++; break; case STATUS.PROGRESS: stats.numOfProgress++; break; case STATUS.ERROR: stats.numOfUploadFailed++; break; case STATUS.COMPLETE: stats.numOfSuccess++; break; case STATUS.CANCELLED: stats.numOfCancel++; break; case STATUS.INVALID: stats.numOfInvalid++; break; case STATUS.INTERRUPT: stats.numofInterrupt++; break; } } }); Mediator.installTo( Queue.prototype ); return Queue; }); /** * @fileOverview 队列 */ define('widgets/queue',[ 'base', 'uploader', 'queue', 'file', 'lib/file', 'runtime/client', 'widgets/widget' ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) { var $ = Base.$, rExt = /\.\w+$/, Status = WUFile.Status; return Uploader.register({ name: 'queue', init: function( opts ) { var me = this, deferred, len, i, item, arr, accept, runtime; if ( $.isPlainObject( opts.accept ) ) { opts.accept = [ opts.accept ]; } // acceptä¸çš„ä¸ç”ŸæˆåŒ¹é…æ£åˆ™ã€‚ if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].extensions; item && arr.push( item ); } if ( arr.length ) { accept = '\\.' + arr.join(',') .replace( /,/g, '$|\\.' ) .replace( /\*/g, '.*' ) + '$'; } me.accept = new RegExp( accept, 'i' ); } me.queue = new Queue(); me.stats = me.queue.stats; // 如果当å‰ä¸æ˜¯html5è¿è¡Œæ—¶ï¼Œé‚£å°±ç®—了。 // 䏿‰§è¡ŒåŽç»æ“作 if ( this.request('predict-runtime-type') !== 'html5' ) { return; } // 创建一个 html5 è¿è¡Œæ—¶çš„ placeholder // ä»¥è‡³äºŽå¤–éƒ¨æ·»åŠ åŽŸç”Ÿ File 对象的时候能æ£ç¡®åŒ…裹一下供 webuploader 使用。 deferred = Base.Deferred(); this.placeholder = runtime = new RuntimeClient('Placeholder'); runtime.connectRuntime({ runtimeOrder: 'html5' }, function() { me._ruid = runtime.getRuid(); deferred.resolve(); }); return deferred.promise(); }, // 为了支æŒå¤–éƒ¨ç›´æŽ¥æ·»åŠ ä¸€ä¸ªåŽŸç”ŸFile对象。 _wrapFile: function( file ) { if ( !(file instanceof WUFile) ) { if ( !(file instanceof File) ) { if ( !this._ruid ) { throw new Error('Can\'t add external files.'); } file = new File( this._ruid, file ); } file = new WUFile( file ); } return file; }, // åˆ¤æ–æ–‡ä»¶æ˜¯å¦å¯ä»¥è¢«åŠ å…¥é˜Ÿåˆ— acceptFile: function( file ) { var invalid = !file || !file.size || this.accept && // 如果åå—䏿œ‰åŽç¼€ï¼Œæ‰åšåŽç¼€ç™½åå•处ç†ã€‚ rExt.exec( file.name ) && !this.accept.test( file.name ); return !invalid; }, /** * @event beforeFileQueued * @param {File} file File对象 * @description å½“æ–‡ä»¶è¢«åŠ å…¥é˜Ÿåˆ—ä¹‹å‰è§¦å‘,æ¤äº‹ä»¶çš„handler返回值为`false`ï¼Œåˆ™æ¤æ–‡ä»¶ä¸ä¼šè¢«æ·»åŠ è¿›å…¥é˜Ÿåˆ—ã€‚ * @for Uploader */ /** * @event fileQueued * @param {File} file File对象 * @description å½“æ–‡ä»¶è¢«åŠ å…¥é˜Ÿåˆ—ä»¥åŽè§¦å‘。 * @for Uploader */ _addFile: function( file ) { var me = this; file = me._wrapFile( file ); // ä¸è¿‡ç±»åž‹åˆ¤æ–å…许ä¸å…è®¸ï¼Œå…ˆæ´¾é€ `beforeFileQueued` if ( !me.owner.trigger( 'beforeFileQueued', file ) ) { return; } // 类型ä¸åŒ¹é…,则派é€é”™è¯¯äº‹ä»¶ï¼Œå¹¶è¿”回。 if ( !me.acceptFile( file ) ) { me.owner.trigger( 'error', 'Q_TYPE_DENIED', file ); return; } me.queue.append( file ); me.owner.trigger( 'fileQueued', file ); return file; }, getFile: function( fileId ) { return this.queue.getFile( fileId ); }, /** * @event filesQueued * @param {File} files 数组,内容为原始File(lib/File)对象。 * @description å½“ä¸€æ‰¹æ–‡ä»¶æ·»åŠ è¿›é˜Ÿåˆ—ä»¥åŽè§¦å‘。 * @for Uploader */ /** * @property {Boolean} [auto=false] * @namespace options * @for Uploader * @description 设置为 true åŽï¼Œä¸éœ€è¦æ‰‹åŠ¨è°ƒç”¨ä¸Šä¼ ï¼Œæœ‰æ–‡ä»¶é€‰æ‹©å³å¼€å§‹ä¸Šä¼ 。 * */ /** * @method addFiles * @grammar addFiles( file ) => undefined * @grammar addFiles( [file1, file2 ...] ) => undefined * @param {Array of File or File} [files] Files 对象 数组 * @description æ·»åŠ æ–‡ä»¶åˆ°é˜Ÿåˆ— * @for Uploader */ addFile: function( files ) { var me = this; if ( !files.length ) { files = [ files ]; } files = $.map( files, function( file ) { return me._addFile( file ); }); me.owner.trigger( 'filesQueued', files ); if ( me.options.auto ) { setTimeout(function() { me.request('start-upload'); }, 20 ); } }, getStats: function() { return this.stats; }, /** * @event fileDequeued * @param {File} file File对象 * @description 当文件被移除队列åŽè§¦å‘。 * @for Uploader */ /** * @method removeFile * @grammar removeFile( file ) => undefined * @grammar removeFile( id ) => undefined * @grammar removeFile( file, true ) => undefined * @grammar removeFile( id, true ) => undefined * @param {File|id} file File对象或这File对象的id * @description 移除æŸä¸€æ–‡ä»¶, 默认åªä¼šæ ‡è®°æ–‡ä»¶çжæ€ä¸ºå·²å–æ¶ˆï¼Œå¦‚æžœç¬¬äºŒä¸ªå‚æ•°ä¸º `true` 则会从 queue ä¸ç§»é™¤ã€‚ * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.removeFile( file ); * }) */ removeFile: function( file, remove ) { var me = this; file = file.id ? file : me.queue.getFile( file ); this.request( 'cancel-file', file ); if ( remove ) { this.queue.removeFile( file ); } }, /** * @method getFiles * @grammar getFiles() => Array * @grammar getFiles( status1, status2, status... ) => Array * @description 返回指定状æ€çš„æ–‡ä»¶é›†åˆï¼Œä¸ä¼ 傿•°å°†è¿”回所有状æ€çš„æ–‡ä»¶ã€‚ * @for Uploader * @example * console.log( uploader.getFiles() ); // => all files * console.log( uploader.getFiles('error') ) // => all error files. */ getFiles: function() { return this.queue.getFiles.apply( this.queue, arguments ); }, fetchFile: function() { return this.queue.fetch.apply( this.queue, arguments ); }, /** * @method retry * @grammar retry() => undefined * @grammar retry( file ) => undefined * @description é‡è¯•ä¸Šä¼ ï¼Œé‡è¯•æŒ‡å®šæ–‡ä»¶ï¼Œæˆ–è€…ä»Žå‡ºé”™çš„æ–‡ä»¶å¼€å§‹é‡æ–°ä¸Šä¼ 。 * @for Uploader * @example * function retry() { * uploader.retry(); * } */ retry: function( file, noForceStart ) { var me = this, files, i, len; if ( file ) { file = file.id ? file : me.queue.getFile( file ); file.setStatus( Status.QUEUED ); noForceStart || me.request('start-upload'); return; } files = me.queue.getFiles( Status.ERROR ); i = 0; len = files.length; for ( ; i < len; i++ ) { file = files[ i ]; file.setStatus( Status.QUEUED ); } me.request('start-upload'); }, /** * @method sort * @grammar sort( fn ) => undefined * @description 排åºé˜Ÿåˆ—ä¸çš„æ–‡ä»¶ï¼Œåœ¨ä¸Šä¼ 之å‰è°ƒæ•´å¯ä»¥æŽ§åˆ¶ä¸Šä¼ 顺åºã€‚ * @for Uploader */ sortFiles: function() { return this.queue.sort.apply( this.queue, arguments ); }, /** * @event reset * @description 当 uploader 被é‡ç½®çš„æ—¶å€™è§¦å‘。 * @for Uploader */ /** * @method reset * @grammar reset() => undefined * @description é‡ç½®uploader。目å‰åªé‡ç½®äº†é˜Ÿåˆ—。 * @for Uploader * @example * uploader.reset(); */ reset: function() { this.owner.trigger('reset'); this.queue = new Queue(); this.stats = this.queue.stats; }, destroy: function() { this.reset(); this.placeholder && this.placeholder.destroy(); } }); }); /** * @fileOverview æ·»åŠ èŽ·å–Runtime相关信æ¯çš„æ–¹æ³•。 */ define('widgets/runtime',[ 'uploader', 'runtime/runtime', 'widgets/widget' ], function( Uploader, Runtime ) { Uploader.support = function() { return Runtime.hasRuntime.apply( Runtime, arguments ); }; /** * @property {Object} [runtimeOrder=html5,flash] * @namespace options * @for Uploader * @description 指定è¿è¡Œæ—¶å¯åŠ¨é¡ºåºã€‚默认会想å°è¯• html5 æ˜¯å¦æ”¯æŒï¼Œå¦‚果支æŒåˆ™ä½¿ç”¨ html5, å¦åˆ™åˆ™ä½¿ç”¨ flash. * * å¯ä»¥å°†æ¤å€¼è®¾ç½®æˆ `flash`,æ¥å¼ºåˆ¶ä½¿ç”¨ flash è¿è¡Œæ—¶ã€‚ */ return Uploader.register({ name: 'runtime', init: function() { if ( !this.predictRuntimeType() ) { throw Error('Runtime Error'); } }, /** * 预测Uploader将采用哪个`Runtime` * @grammar predictRuntimeType() => String * @method predictRuntimeType * @for Uploader */ predictRuntimeType: function() { var orders = this.options.runtimeOrder || Runtime.orders, type = this.type, i, len; if ( !type ) { orders = orders.split( /\s*,\s*/g ); for ( i = 0, len = orders.length; i < len; i++ ) { if ( Runtime.hasRuntime( orders[ i ] ) ) { this.type = type = orders[ i ]; break; } } } return type; } }); }); /** * @fileOverview Transport */ define('lib/transport',[ 'base', 'runtime/client', 'mediator' ], function( Base, RuntimeClient, Mediator ) { var $ = Base.$; function Transport( opts ) { var me = this; opts = me.options = $.extend( true, {}, Transport.options, opts || {} ); RuntimeClient.call( this, 'Transport' ); this._blob = null; this._formData = opts.formData || {}; this._headers = opts.headers || {}; this.on( 'progress', this._timeout ); this.on( 'load error', function() { me.trigger( 'progress', 1 ); clearTimeout( me._timer ); }); } Transport.options = { server: '', method: 'POST', // 跨域时,是å¦å…许æºå¸¦cookie, åªæœ‰html5 runtimeæ‰æœ‰æ•ˆ withCredentials: false, fileVal: 'file', timeout: 2 * 60 * 1000, // 2分钟 formData: {}, headers: {}, sendAsBinary: false }; $.extend( Transport.prototype, { // æ·»åŠ Blob, åªèƒ½æ·»åŠ ä¸€æ¬¡ï¼Œæœ€åŽä¸€æ¬¡æœ‰æ•ˆã€‚ appendBlob: function( key, blob, filename ) { var me = this, opts = me.options; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的åŒä¸€ä¸ªruntime. me.connectRuntime( blob.ruid, function() { me.exec('init'); }); me._blob = blob; opts.fileVal = key || opts.fileVal; opts.filename = filename || opts.filename; }, // æ·»åŠ å…¶ä»–å—æ®µ append: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._formData, key ); } else { this._formData[ key ] = value; } }, setRequestHeader: function( key, value ) { if ( typeof key === 'object' ) { $.extend( this._headers, key ); } else { this._headers[ key ] = value; } }, send: function( method ) { this.exec( 'send', method ); this._timeout(); }, abort: function() { clearTimeout( this._timer ); return this.exec('abort'); }, destroy: function() { this.trigger('destroy'); this.off(); this.exec('destroy'); this.disconnectRuntime(); }, getResponse: function() { return this.exec('getResponse'); }, getResponseAsJson: function() { return this.exec('getResponseAsJson'); }, getStatus: function() { return this.exec('getStatus'); }, _timeout: function() { var me = this, duration = me.options.timeout; if ( !duration ) { return; } clearTimeout( me._timer ); me._timer = setTimeout(function() { me.abort(); me.trigger( 'error', 'timeout' ); }, duration ); } }); // 让Transport具备事件功能。 Mediator.installTo( Transport.prototype ); return Transport; }); /** * @fileOverview è´Ÿè´£æ–‡ä»¶ä¸Šä¼ ç›¸å…³ã€‚ */ define('widgets/upload',[ 'base', 'uploader', 'file', 'lib/transport', 'widgets/widget' ], function( Base, Uploader, WUFile, Transport ) { var $ = Base.$, isPromise = Base.isPromise, Status = WUFile.Status; // æ·»åŠ é»˜è®¤é…置项 $.extend( Uploader.options, { /** * @property {Boolean} [prepareNextFile=false] * @namespace options * @for Uploader * @description 是å¦å…è®¸åœ¨æ–‡ä»¶ä¼ è¾“æ—¶æå‰æŠŠä¸‹ä¸€ä¸ªæ–‡ä»¶å‡†å¤‡å¥½ã€‚ * 对于一个文件的准备工作比较耗时,比如图片压缩,md5åºåˆ—化。 * 如果能æå‰åœ¨å½“剿–‡ä»¶ä¼ 输期处ç†ï¼Œå¯ä»¥èŠ‚çœæ€»ä½“耗时。 */ prepareNextFile: false, /** * @property {Boolean} [chunked=false] * @namespace options * @for Uploader * @description 是å¦è¦åˆ†ç‰‡å¤„ç†å¤§æ–‡ä»¶ä¸Šä¼ 。 */ chunked: false, /** * @property {Boolean} [chunkSize=5242880] * @namespace options * @for Uploader * @description 如果è¦åˆ†ç‰‡ï¼Œåˆ†å¤šå¤§ä¸€ç‰‡ï¼Ÿ 默认大å°ä¸º5M. */ chunkSize: 5 * 1024 * 1024, /** * @property {Boolean} [chunkRetry=2] * @namespace options * @for Uploader * @description 如果æŸä¸ªåˆ†ç‰‡ç”±äºŽç½‘络问题出错,å…许自动é‡ä¼ 多少次? */ chunkRetry: 2, /** * @property {Boolean} [threads=3] * @namespace options * @for Uploader * @description ä¸Šä¼ å¹¶å‘æ•°ã€‚å…è®¸åŒæ—¶æœ€å¤§ä¸Šä¼ 进程数。 */ threads: 3, /** * @property {Object} [formData={}] * @namespace options * @for Uploader * @description æ–‡ä»¶ä¸Šä¼ è¯·æ±‚çš„å‚æ•°è¡¨ï¼Œæ¯æ¬¡å‘é€éƒ½ä¼šå‘逿¤å¯¹è±¡ä¸çš„傿•°ã€‚ */ formData: {} /** * @property {Object} [fileVal='file'] * @namespace options * @for Uploader * @description è®¾ç½®æ–‡ä»¶ä¸Šä¼ åŸŸçš„name。 */ /** * @property {Object} [method='POST'] * @namespace options * @for Uploader * @description æ–‡ä»¶ä¸Šä¼ æ–¹å¼ï¼Œ`POST`或者`GET`。 */ /** * @property {Object} [sendAsBinary=false] * @namespace options * @for Uploader * @description 是å¦å·²äºŒè¿›åˆ¶çš„æµçš„æ–¹å¼å‘逿–‡ä»¶ï¼Œè¿™æ ·æ•´ä¸ªä¸Šä¼ 内容`php://input`都为文件内容, * 其他傿•°åœ¨$_GET数组ä¸ã€‚ */ }); // 负责将文件切片。 function CuteFile( file, chunkSize ) { var pending = [], blob = file.source, total = blob.size, chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1, start = 0, index = 0, len, api; api = { file: file, has: function() { return !!pending.length; }, shift: function() { return pending.shift(); }, unshift: function( block ) { pending.unshift( block ); } }; while ( index < chunks ) { len = Math.min( chunkSize, total - start ); pending.push({ file: file, start: start, end: chunkSize ? (start + len) : total, total: total, chunks: chunks, chunk: index++, cuted: api }); start += len; } file.blocks = pending.concat(); file.remaning = pending.length; return api; } Uploader.register({ name: 'upload', init: function() { var owner = this.owner, me = this; this.runing = false; this.progress = false; owner .on( 'startUpload', function() { me.progress = true; }) .on( 'uploadFinished', function() { me.progress = false; }); // è®°å½•å½“å‰æ£åœ¨ä¼ 的数æ®ï¼Œè·Ÿthreads相关 this.pool = []; // 缓å˜åˆ†å¥½ç‰‡çš„æ–‡ä»¶ã€‚ this.stack = []; // 缓å˜å³å°†ä¸Šä¼ 的文件。 this.pending = []; // è·Ÿè¸ªè¿˜æœ‰å¤šå°‘åˆ†ç‰‡åœ¨ä¸Šä¼ ä¸ä½†æ˜¯æ²¡æœ‰å®Œæˆä¸Šä¼ 。 this.remaning = 0; this.__tick = Base.bindFn( this._tick, this ); owner.on( 'uploadComplete', function( file ) { // 把其他å—å–æ¶ˆäº†ã€‚ file.blocks && $.each( file.blocks, function( _, v ) { v.transport && (v.transport.abort(), v.transport.destroy()); delete v.transport; }); delete file.blocks; delete file.remaning; }); }, reset: function() { this.request( 'stop-upload', true ); this.runing = false; this.pool = []; this.stack = []; this.pending = []; this.remaning = 0; this._trigged = false; this._promise = null; }, /** * @event startUpload * @description å½“å¼€å§‹ä¸Šä¼ æµç¨‹æ—¶è§¦å‘。 * @for Uploader */ /** * å¼€å§‹ä¸Šä¼ ã€‚æ¤æ–¹æ³•å¯ä»¥ä»Žåˆå§‹çжæ€è°ƒç”¨å¼€å§‹ä¸Šä¼ æµç¨‹ï¼Œä¹Ÿå¯ä»¥ä»Žæš‚åœçжæ€è°ƒç”¨ï¼Œç»§ç»ä¸Šä¼ æµç¨‹ã€‚ * * å¯ä»¥æŒ‡å®šå¼€å§‹æŸä¸€ä¸ªæ–‡ä»¶ã€‚ * @grammar upload() => undefined * @grammar upload( file | fileId) => undefined * @method upload * @for Uploader */ startUpload: function(file) { var me = this; // 移出invalid的文件 $.each( me.request( 'get-files', Status.INVALID ), function() { me.request( 'remove-file', this ); }); // 如果指定了开始æŸä¸ªæ–‡ä»¶ï¼Œåˆ™åªå¼€å§‹æŒ‡å®šæ–‡ä»¶ã€‚ if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if (file.getStatus() === Status.INTERRUPT) { $.each( me.pool, function( _, v ) { // 之剿š‚åœè¿‡ã€‚ if (v.file !== file) { return; } v.transport && v.transport.send(); }); file.setStatus( Status.QUEUED ); } else if (file.getStatus() === Status.PROGRESS) { return; } else { file.setStatus( Status.QUEUED ); } } else { $.each( me.request( 'get-files', [ Status.INITED ] ), function() { this.setStatus( Status.QUEUED ); }); } if ( me.runing ) { return; } me.runing = true; var files = []; // 如果有暂åœçš„,则ç»ä¼ $.each( me.pool, function( _, v ) { var file = v.file; if ( file.getStatus() === Status.INTERRUPT ) { files.push(file); me._trigged = false; v.transport && v.transport.send(); } }); var file; while ( (file = files.shift()) ) { file.setStatus( Status.PROGRESS ); } file || $.each( me.request( 'get-files', Status.INTERRUPT ), function() { this.setStatus( Status.PROGRESS ); }); me._trigged = false; Base.nextTick( me.__tick ); me.owner.trigger('startUpload'); }, /** * @event stopUpload * @description å½“å¼€å§‹ä¸Šä¼ æµç¨‹æš‚åœæ—¶è§¦å‘。 * @for Uploader */ /** * æš‚åœä¸Šä¼ ã€‚ç¬¬ä¸€ä¸ªå‚æ•°ä¸ºæ˜¯å¦ä¸æ–ä¸Šä¼ å½“å‰æ£åœ¨ä¸Šä¼ 的文件。 * * å¦‚æžœç¬¬ä¸€ä¸ªå‚æ•°æ˜¯æ–‡ä»¶ï¼Œåˆ™åªæš‚åœæŒ‡å®šæ–‡ä»¶ã€‚ * @grammar stop() => undefined * @grammar stop( true ) => undefined * @grammar stop( file ) => undefined * @method stop * @for Uploader */ stopUpload: function( file, interrupt ) { var me = this; if (file === true) { interrupt = file; file = null; } if ( me.runing === false ) { return; } // å¦‚æžœåªæ˜¯æš‚åœæŸä¸ªæ–‡ä»¶ã€‚ if ( file ) { file = file.id ? file : me.request( 'get-file', file ); if ( file.getStatus() !== Status.PROGRESS && file.getStatus() !== Status.QUEUED ) { return; } file.setStatus( Status.INTERRUPT ); $.each( me.pool, function( _, v ) { // åª abort 指定的文件。 if (v.file !== file) { return; } v.transport && v.transport.abort(); me._putback(v); me._popBlock(v); }); return Base.nextTick( me.__tick ); } me.runing = false; if (this._promise && this._promise.file) { this._promise.file.setStatus( Status.INTERRUPT ); } interrupt && $.each( me.pool, function( _, v ) { v.transport && v.transport.abort(); v.file.setStatus( Status.INTERRUPT ); }); me.owner.trigger('stopUpload'); }, /** * @method cancelFile * @grammar cancelFile( file ) => undefined * @grammar cancelFile( id ) => undefined * @param {File|id} file File对象或这File对象的id * @description æ ‡è®°æ–‡ä»¶çŠ¶æ€ä¸ºå·²å–消, åŒæ—¶å°†ä¸æ–æ–‡ä»¶ä¼ è¾“ã€‚ * @for Uploader * @example * * $li.on('click', '.remove-this', function() { * uploader.cancelFile( file ); * }) */ cancelFile: function( file ) { file = file.id ? file : this.request( 'get-file', file ); // 如果æ£åœ¨ä¸Šä¼ 。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); file.setStatus( Status.CANCELLED ); this.owner.trigger( 'fileDequeued', file ); }, /** * 判æ–`Uplaode`ræ˜¯å¦æ£åœ¨ä¸Šä¼ ä¸ã€‚ * @grammar isInProgress() => Boolean * @method isInProgress * @for Uploader */ isInProgress: function() { return !!this.progress; }, _getStats: function() { return this.request('get-stats'); }, /** * æŽ‰è¿‡ä¸€ä¸ªæ–‡ä»¶ä¸Šä¼ ï¼Œç›´æŽ¥æ ‡è®°æŒ‡å®šæ–‡ä»¶ä¸ºå·²ä¸Šä¼ çŠ¶æ€ã€‚ * @grammar skipFile( file ) => undefined * @method skipFile * @for Uploader */ skipFile: function( file, status ) { file = file.id ? file : this.request( 'get-file', file ); file.setStatus( status || Status.COMPLETE ); file.skipped = true; // 如果æ£åœ¨ä¸Šä¼ 。 file.blocks && $.each( file.blocks, function( _, v ) { var _tr = v.transport; if ( _tr ) { _tr.abort(); _tr.destroy(); delete v.transport; } }); this.owner.trigger( 'uploadSkip', file ); }, /** * @event uploadFinished * @description å½“æ‰€æœ‰æ–‡ä»¶ä¸Šä¼ ç»“æŸæ—¶è§¦å‘。 * @for Uploader */ _tick: function() { var me = this, opts = me.options, fn, val; // 上一个promise还没有结æŸï¼Œåˆ™ç‰å¾…完æˆåŽå†æ‰§è¡Œã€‚ if ( me._promise ) { return me._promise.always( me.__tick ); } // 还有ä½ç½®ï¼Œä¸”还有文件è¦å¤„ç†çš„è¯ã€‚ if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) { me._trigged = false; fn = function( val ) { me._promise = null; // 有å¯èƒ½æ˜¯reject过æ¥çš„ï¼Œæ‰€ä»¥è¦æ£€æµ‹val的类型。 val && val.file && me._startSend( val ); Base.nextTick( me.__tick ); }; me._promise = isPromise( val ) ? val.always( fn ) : fn( val ); // 没有è¦ä¸Šä¼ 的了,且没有æ£åœ¨ä¼ 输的了。 } else if ( !me.remaning && !me._getStats().numOfQueue && !me._getStats().numofInterrupt ) { me.runing = false; me._trigged || Base.nextTick(function() { me.owner.trigger('uploadFinished'); }); me._trigged = true; } }, _putback: function(block) { var idx; block.cuted.unshift(block); idx = this.stack.indexOf(block.cuted); if (!~idx) { this.stack.unshift(block.cuted); } }, _getStack: function() { var i = 0, act; while ( (act = this.stack[ i++ ]) ) { if ( act.has() && act.file.getStatus() === Status.PROGRESS ) { return act; } else if (!act.has() || act.file.getStatus() !== Status.PROGRESS && act.file.getStatus() !== Status.INTERRUPT ) { // 把已ç»å¤„ç†å®Œäº†çš„,或者,状æ€ä¸ºéž progressï¼ˆä¸Šä¼ ä¸ï¼‰ã€ // interupt(暂åœä¸ï¼‰ 的移除。 this.stack.splice( --i, 1 ); } } return null; }, _nextBlock: function() { var me = this, opts = me.options, act, next, done, preparing; // å¦‚æžœå½“å‰æ–‡ä»¶è¿˜æœ‰æ²¡æœ‰éœ€è¦ä¼ 输的,则直接返回剩下的。 if ( (act = this._getStack()) ) { // æ˜¯å¦æå‰å‡†å¤‡ä¸‹ä¸€ä¸ªæ–‡ä»¶ if ( opts.prepareNextFile && !me.pending.length ) { me._prepareNextFile(); } return act.shift(); // å¦åˆ™ï¼Œå¦‚æžœæ£åœ¨è¿è¡Œï¼Œåˆ™å‡†å¤‡ä¸‹ä¸€ä¸ªæ–‡ä»¶ï¼Œå¹¶ç‰å¾…完æˆåŽè¿”回下个分片。 } else if ( me.runing ) { // 如果缓å˜ä¸æœ‰ï¼Œåˆ™ç›´æŽ¥åœ¨ç¼“å˜ä¸å–,没有则去queueä¸å–。 if ( !me.pending.length && me._getStats().numOfQueue ) { me._prepareNextFile(); } next = me.pending.shift(); done = function( file ) { if ( !file ) { return null; } act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 ); me.stack.push(act); return act.shift(); }; // 文件å¯èƒ½è¿˜åœ¨prepareä¸ï¼Œä¹Ÿæœ‰å¯èƒ½å·²ç»å®Œå…¨å‡†å¤‡å¥½äº†ã€‚ if ( isPromise( next) ) { preparing = next.file; next = next[ next.pipe ? 'pipe' : 'then' ]( done ); next.file = preparing; return next; } return done( next ); } }, /** * @event uploadStart * @param {File} file File对象 * @description æŸä¸ªæ–‡ä»¶å¼€å§‹ä¸Šä¼ å‰è§¦å‘,一个文件åªä¼šè§¦å‘一次。 * @for Uploader */ _prepareNextFile: function() { var me = this, file = me.request('fetch-file'), pending = me.pending, promise; if ( file ) { promise = me.request( 'before-send-file', file, function() { // 有å¯èƒ½æ–‡ä»¶è¢«skip掉了。文件被skip掉åŽï¼Œçжæ€å‘å®šä¸æ˜¯Queued. if ( file.getStatus() === Status.PROGRESS || file.getStatus() === Status.INTERRUPT ) { return file; } return me._finishFile( file ); }); me.owner.trigger( 'uploadStart', file ); file.setStatus( Status.PROGRESS ); promise.file = file; // 如果还在pendingä¸ï¼Œåˆ™æ›¿æ¢æˆæ–‡ä»¶æœ¬èº«ã€‚ promise.done(function() { var idx = $.inArray( promise, pending ); ~idx && pending.splice( idx, 1, file ); }); // befeore-send-file的钩å就有错误å‘生。 promise.fail(function( reason ) { file.setStatus( Status.ERROR, reason ); me.owner.trigger( 'uploadError', file, reason ); me.owner.trigger( 'uploadComplete', file ); }); pending.push( promise ); } }, // 让出ä½ç½®äº†ï¼Œå¯ä»¥è®©å…¶ä»–åˆ†ç‰‡å¼€å§‹ä¸Šä¼ _popBlock: function( block ) { var idx = $.inArray( block, this.pool ); this.pool.splice( idx, 1 ); block.file.remaning--; this.remaning--; }, // å¼€å§‹ä¸Šä¼ ï¼Œå¯ä»¥è¢«æŽ‰è¿‡ã€‚如果promise被reject了,则表示跳过æ¤åˆ†ç‰‡ã€‚ _startSend: function( block ) { var me = this, file = block.file, promise; // 有å¯èƒ½åœ¨ before-send-file çš„ promise 期间改å˜äº†æ–‡ä»¶çжæ€ã€‚ // 如:暂åœï¼Œå–消 // 我们ä¸èƒ½ä¸æ– promise, 但是å¯ä»¥åœ¨ promise 完åŽï¼Œä¸åšä¸Šä¼ æ“作。 if ( file.getStatus() !== Status.PROGRESS ) { // å¦‚æžœæ˜¯ä¸æ–ï¼Œåˆ™è¿˜éœ€è¦æ”¾å›žåŽ»ã€‚ if (file.getStatus() === Status.INTERRUPT) { me._putback(block); } return; } me.pool.push( block ); me.remaning++; // 如果没有分片,则直接使用原始的。 // ä¸ä¼šä¸¢å¤±content-typeä¿¡æ¯ã€‚ block.blob = block.chunks === 1 ? file.source : file.source.slice( block.start, block.end ); // hook, æ¯ä¸ªåˆ†ç‰‡å‘é€ä¹‹å‰å¯èƒ½è¦åšäº›å¼‚æ¥çš„事情。 promise = me.request( 'before-send', block, function() { // 有å¯èƒ½æ–‡ä»¶å·²ç»ä¸Šä¼ 出错了,所以ä¸éœ€è¦å†ä¼ 输了。 if ( file.getStatus() === Status.PROGRESS ) { me._doSend( block ); } else { me._popBlock( block ); Base.nextTick( me.__tick ); } }); // 如果为fail了,则跳过æ¤åˆ†ç‰‡ã€‚ promise.fail(function() { if ( file.remaning === 1 ) { me._finishFile( file ).always(function() { block.percentage = 1; me._popBlock( block ); me.owner.trigger( 'uploadComplete', file ); Base.nextTick( me.__tick ); }); } else { block.percentage = 1; me.updateFileProgress( file ); me._popBlock( block ); Base.nextTick( me.__tick ); } }); }, /** * @event uploadBeforeSend * @param {Object} object * @param {Object} data é»˜è®¤çš„ä¸Šä¼ å‚æ•°ï¼Œå¯ä»¥æ‰©å±•æ¤å¯¹è±¡æ¥æŽ§åˆ¶ä¸Šä¼ 傿•°ã€‚ * @param {Object} headers å¯ä»¥æ‰©å±•æ¤å¯¹è±¡æ¥æŽ§åˆ¶ä¸Šä¼ 头部。 * @description 当æŸä¸ªæ–‡ä»¶çš„分å—在å‘é€å‰è§¦å‘,主è¦ç”¨æ¥è¯¢é—®æ˜¯å¦è¦æ·»åР附另傿•°ï¼Œå¤§æ–‡ä»¶åœ¨å¼€èµ·åˆ†ç‰‡ä¸Šä¼ çš„å‰æä¸‹æ¤äº‹ä»¶å¯èƒ½ä¼šè§¦å‘多次。 * @for Uploader */ /** * @event uploadAccept * @param {Object} object * @param {Object} ret æœåŠ¡ç«¯çš„è¿”å›žæ•°æ®ï¼Œjsonæ ¼å¼ï¼Œå¦‚æžœæœåŠ¡ç«¯ä¸æ˜¯jsonæ ¼å¼ï¼Œä»Žret._rawä¸å–æ•°æ®ï¼Œè‡ªè¡Œè§£æžã€‚ * @description 当æŸä¸ªæ–‡ä»¶ä¸Šä¼ 到æœåŠ¡ç«¯å“应åŽï¼Œä¼šæ´¾é€æ¤äº‹ä»¶æ¥è¯¢é—®æœåŠ¡ç«¯å“åº”æ˜¯å¦æœ‰æ•ˆã€‚如果æ¤äº‹ä»¶handler返回值为`false`, åˆ™æ¤æ–‡ä»¶å°†æ´¾é€`server`类型的`uploadError`事件。 * @for Uploader */ /** * @event uploadProgress * @param {File} file File对象 * @param {Number} percentage ä¸Šä¼ è¿›åº¦ * @description ä¸Šä¼ è¿‡ç¨‹ä¸è§¦å‘,æºå¸¦ä¸Šä¼ 进度。 * @for Uploader */ /** * @event uploadError * @param {File} file File对象 * @param {String} reason 出错的code * @description å½“æ–‡ä»¶ä¸Šä¼ å‡ºé”™æ—¶è§¦å‘。 * @for Uploader */ /** * @event uploadSuccess * @param {File} file File对象 * @param {Object} response æœåŠ¡ç«¯è¿”å›žçš„æ•°æ® * @description å½“æ–‡ä»¶ä¸Šä¼ æˆåŠŸæ—¶è§¦å‘。 * @for Uploader */ /** * @event uploadComplete * @param {File} [file] File对象 * @description ä¸ç®¡æˆåŠŸæˆ–è€…å¤±è´¥ï¼Œæ–‡ä»¶ä¸Šä¼ å®Œæˆæ—¶è§¦å‘。 * @for Uploader */ // åšä¸Šä¼ æ“作。 _doSend: function( block ) { var me = this, owner = me.owner, opts = me.options, file = block.file, tr = new Transport( opts ), data = $.extend({}, opts.formData ), headers = $.extend({}, opts.headers ), requestAccept, ret; block.transport = tr; tr.on( 'destroy', function() { delete block.transport; me._popBlock( block ); Base.nextTick( me.__tick ); }); // 广æ’ä¸Šä¼ è¿›åº¦ã€‚ä»¥æ–‡ä»¶ä¸ºå•ä½ã€‚ tr.on( 'progress', function( percentage ) { block.percentage = percentage; me.updateFileProgress( file ); }); // 用æ¥è¯¢é—®ï¼Œæ˜¯å¦è¿”回的结果是有错误的。 requestAccept = function( reject ) { var fn; ret = tr.getResponseAsJson() || {}; ret._raw = tr.getResponse(); fn = function( value ) { reject = value; }; // æœåŠ¡ç«¯å“应了,ä¸ä»£è¡¨æˆåŠŸäº†ï¼Œè¯¢é—®æ˜¯å¦å“应æ£ç¡®ã€‚ if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) { reject = reject || 'server'; } return reject; }; // å°è¯•é‡è¯•,然åŽå¹¿æ’æ–‡ä»¶ä¸Šä¼ å‡ºé”™ã€‚ tr.on( 'error', function( type, flag ) { block.retried = block.retried || 0; // 自动é‡è¯• if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) && block.retried < opts.chunkRetry ) { block.retried++; tr.send(); } else { // http status 500 ~ 600 if ( !flag && type === 'server' ) { type = requestAccept( type ); } file.setStatus( Status.ERROR, type ); owner.trigger( 'uploadError', file, type ); owner.trigger( 'uploadComplete', file ); } }); // ä¸Šä¼ æˆåŠŸ tr.on( 'load', function() { var reason; // 如果éžé¢„期,转å‘ä¸Šä¼ å‡ºé”™ã€‚ if ( (reason = requestAccept()) ) { tr.trigger( 'error', reason, true ); return; } // å…¨éƒ¨ä¸Šä¼ å®Œæˆã€‚ if ( file.remaning === 1 ) { me._finishFile( file, ret ); } else { tr.destroy(); } }); // é…ç½®é»˜è®¤çš„ä¸Šä¼ å—æ®µã€‚ data = $.extend( data, { id: file.id, name: file.name, type: file.type, lastModifiedDate: file.lastModifiedDate, size: file.size }); block.chunks > 1 && $.extend( data, { chunks: block.chunks, chunk: block.chunk }); // 在å‘é€ä¹‹é—´å¯ä»¥æ·»åŠ å—æ®µä»€ä¹ˆçš„。。。 // å¦‚æžœé»˜è®¤çš„å—æ®µä¸å¤Ÿä½¿ç”¨ï¼Œå¯ä»¥é€šè¿‡ç›‘嬿¤äº‹ä»¶æ¥æ‰©å±• owner.trigger( 'uploadBeforeSend', block, data, headers ); // 开始å‘é€ã€‚ tr.appendBlob( opts.fileVal, block.blob, file.name ); tr.append( data ); tr.setRequestHeader( headers ); tr.send(); }, // 完æˆä¸Šä¼ 。 _finishFile: function( file, ret, hds ) { var owner = this.owner; return owner .request( 'after-send-file', arguments, function() { file.setStatus( Status.COMPLETE ); owner.trigger( 'uploadSuccess', file, ret, hds ); }) .fail(function( reason ) { // å¦‚æžœå¤–éƒ¨å·²ç»æ ‡è®°ä¸ºinvalid什么的,ä¸å†æ”¹çжæ€ã€‚ if ( file.getStatus() === Status.PROGRESS ) { file.setStatus( Status.ERROR, reason ); } owner.trigger( 'uploadError', file, reason ); }) .always(function() { owner.trigger( 'uploadComplete', file ); }); }, updateFileProgress: function(file) { var totalPercent = 0, uploaded = 0; if (!file.blocks) { return; } $.each( file.blocks, function( _, v ) { uploaded += (v.percentage || 0) * (v.end - v.start); }); totalPercent = uploaded / file.size; this.owner.trigger( 'uploadProgress', file, totalPercent || 0 ); } }); }); /** * @fileOverview å„ç§éªŒè¯ï¼ŒåŒ…æ‹¬æ–‡ä»¶æ€»å¤§å°æ˜¯å¦è¶…出ã€å•文件是å¦è¶…出和文件是å¦é‡å¤ã€‚ */ define('widgets/validator',[ 'base', 'uploader', 'file', 'widgets/widget' ], function( Base, Uploader, WUFile ) { var $ = Base.$, validators = {}, api; /** * @event error * @param {String} type 错误类型。 * @description 当validateä¸é€šè¿‡æ—¶ï¼Œä¼šä»¥æ´¾é€é”™è¯¯äº‹ä»¶çš„å½¢å¼é€šçŸ¥è°ƒç”¨è€…。通过`upload.on('error', handler)`å¯ä»¥æ•获到æ¤ç±»é”™è¯¯ï¼Œç›®å‰æœ‰ä»¥ä¸‹é”™è¯¯ä¼šåœ¨ç‰¹å®šçš„æƒ…况下派é€é”™æ¥ã€‚ * * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且å°è¯•ç»™`uploader`æ·»åŠ çš„æ–‡ä»¶æ•°é‡è¶…出这个值时派é€ã€‚ * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且å°è¯•ç»™`uploader`æ·»åŠ çš„æ–‡ä»¶æ€»å¤§å°è¶…出这个值时派é€ã€‚ * * `Q_TYPE_DENIED` å½“æ–‡ä»¶ç±»åž‹ä¸æ»¡è¶³æ—¶è§¦å‘。。 * @for Uploader */ // 暴露给外é¢çš„api api = { // æ·»åŠ éªŒè¯å™¨ addValidator: function( type, cb ) { validators[ type ] = cb; }, // 移除验è¯å™¨ removeValidator: function( type ) { delete validators[ type ]; } }; // 在Uploaderåˆå§‹åŒ–的时候å¯åЍValidatorsçš„åˆå§‹åŒ– Uploader.register({ name: 'validator', init: function() { var me = this; Base.nextTick(function() { $.each( validators, function() { this.call( me.owner ); }); }); } }); /** * @property {int} [fileNumLimit=undefined] * @namespace options * @for Uploader * @description éªŒè¯æ–‡ä»¶æ€»æ•°é‡, 超出则ä¸å…è®¸åŠ å…¥é˜Ÿåˆ—ã€‚ */ api.addValidator( 'fileNumLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileNumLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( count >= max && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return count >= max ? false : true; }); uploader.on( 'fileQueued', function() { count++; }); uploader.on( 'fileDequeued', function() { count--; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSizeLimit=undefined] * @namespace options * @for Uploader * @description éªŒè¯æ–‡ä»¶æ€»å¤§å°æ˜¯å¦è¶…出é™åˆ¶, 超出则ä¸å…è®¸åŠ å…¥é˜Ÿåˆ—ã€‚ */ api.addValidator( 'fileSizeLimit', function() { var uploader = this, opts = uploader.options, count = 0, max = parseInt( opts.fileSizeLimit, 10 ), flag = true; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { var invalid = count + file.size > max; if ( invalid && flag ) { flag = false; this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file ); setTimeout(function() { flag = true; }, 1 ); } return invalid ? false : true; }); uploader.on( 'fileQueued', function( file ) { count += file.size; }); uploader.on( 'fileDequeued', function( file ) { count -= file.size; }); uploader.on( 'reset', function() { count = 0; }); }); /** * @property {int} [fileSingleSizeLimit=undefined] * @namespace options * @for Uploader * @description 验è¯å•ä¸ªæ–‡ä»¶å¤§å°æ˜¯å¦è¶…出é™åˆ¶, 超出则ä¸å…è®¸åŠ å…¥é˜Ÿåˆ—ã€‚ */ api.addValidator( 'fileSingleSizeLimit', function() { var uploader = this, opts = uploader.options, max = opts.fileSingleSizeLimit; if ( !max ) { return; } uploader.on( 'beforeFileQueued', function( file ) { if ( file.size > max ) { file.setStatus( WUFile.Status.INVALID, 'exceed_size' ); this.trigger( 'error', 'F_EXCEED_SIZE', max, file ); return false; } }); }); /** * @property {Boolean} [duplicate=undefined] * @namespace options * @for Uploader * @description 去é‡ï¼Œ æ ¹æ®æ–‡ä»¶åå—ã€æ–‡ä»¶å¤§å°å’Œæœ€åŽä¿®æ”¹æ—¶é—´æ¥ç”Ÿæˆhash Key. */ api.addValidator( 'duplicate', function() { var uploader = this, opts = uploader.options, mapping = {}; if ( opts.duplicate ) { return; } function hashString( str ) { var hash = 0, i = 0, len = str.length, _char; for ( ; i < len; i++ ) { _char = str.charCodeAt( i ); hash = _char + (hash << 6) + (hash << 16) - hash; } return hash; } uploader.on( 'beforeFileQueued', function( file ) { var hash = file.__hash || (file.__hash = hashString( file.name + file.size + file.lastModifiedDate )); // å·²ç»é‡å¤äº† if ( mapping[ hash ] ) { this.trigger( 'error', 'F_DUPLICATE', file ); return false; } }); uploader.on( 'fileQueued', function( file ) { var hash = file.__hash; hash && (mapping[ hash ] = true); }); uploader.on( 'fileDequeued', function( file ) { var hash = file.__hash; hash && (delete mapping[ hash ]); }); uploader.on( 'reset', function() { mapping = {}; }); }); return api; }); /** * @fileOverview Runtime管ç†å™¨ï¼Œè´Ÿè´£Runtime的选择, 连接 */ define('runtime/compbase',[],function() { function CompBase( owner, runtime ) { this.owner = owner; this.options = owner.options; this.getRuntime = function() { return runtime; }; this.getRuid = function() { return runtime.uid; }; this.trigger = function() { return owner.trigger.apply( owner, arguments ); }; } return CompBase; }); /** * @fileOverview FlashRuntime */ define('runtime/flash/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var $ = Base.$, type = 'flash', components = {}; function getFlashVersion() { var version; try { version = navigator.plugins[ 'Shockwave Flash' ]; version = version.description; } catch ( ex ) { try { version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash') .GetVariable('$version'); } catch ( ex2 ) { version = '0.0'; } } version = version.match( /\d+/g ); return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 ); } function FlashRuntime() { var pool = {}, clients = {}, destroy = this.destroy, me = this, jsreciver = Base.guid('webuploader_'); Runtime.apply( me, arguments ); me.type = type; // 这个方法的调用者,实际上是RuntimeClient me.exec = function( comp, fn/*, args...*/ ) { var client = this, uid = client.uid, args = Base.slice( arguments, 2 ), instance; clients[ uid ] = client; if ( components[ comp ] ) { if ( !pool[ uid ] ) { pool[ uid ] = new components[ comp ]( client, me ); } instance = pool[ uid ]; if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } return me.flashExec.apply( client, arguments ); }; function handler( evt, obj ) { var type = evt.type || evt, parts, uid; parts = type.split('::'); uid = parts[ 0 ]; type = parts[ 1 ]; // console.log.apply( console, arguments ); if ( type === 'Ready' && uid === me.uid ) { me.trigger('ready'); } else if ( clients[ uid ] ) { clients[ uid ].trigger( type.toLowerCase(), evt, obj ); } // Base.log( evt, obj ); } // flash的接å—器。 window[ jsreciver ] = function() { var args = arguments; // 为了能æ•获得到。 setTimeout(function() { handler.apply( null, args ); }, 1 ); }; this.jsreciver = jsreciver; this.destroy = function() { // @todo åˆ é™¤æ± åä¸çš„æ‰€æœ‰å®žä¾‹ return destroy && destroy.apply( this, arguments ); }; this.flashExec = function( comp, fn ) { var flash = me.getFlash(), args = Base.slice( arguments, 2 ); return flash.exec( this.uid, comp, fn, args ); }; // @todo } Base.inherits( Runtime, { constructor: FlashRuntime, init: function() { var container = this.getContainer(), opts = this.options, html; // if not the minimal height, shims are not initialized // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc) container.css({ position: 'absolute', top: '-8px', left: '-8px', width: '9px', height: '9px', overflow: 'hidden' }); // insert flash object html = '<object id="' + this.uid + '" type="application/' + 'x-shockwave-flash" data="' + opts.swf + '" '; if ( Base.browser.ie ) { html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '; } html += 'width="100%" height="100%" style="outline:0">' + '<param name="movie" value="' + opts.swf + '" />' + '<param name="flashvars" value="uid=' + this.uid + '&jsreciver=' + this.jsreciver + '" />' + '<param name="wmode" value="transparent" />' + '<param name="allowscriptaccess" value="always" />' + '</object>'; container.html( html ); }, getFlash: function() { if ( this._flash ) { return this._flash; } this._flash = $( '#' + this.uid ).get( 0 ); return this._flash; } }); FlashRuntime.register = function( name, component ) { component = components[ name ] = Base.inherits( CompBase, $.extend({ // @todo fix this later flashExec: function() { var owner = this.owner, runtime = this.getRuntime(); return runtime.flashExec.apply( owner, arguments ); } }, component ) ); return component; }; if ( getFlashVersion() >= 11.4 ) { Runtime.addRuntime( type, FlashRuntime ); } return FlashRuntime; }); /** * @fileOverview FilePicker */ define('runtime/flash/filepicker',[ 'base', 'runtime/flash/runtime' ], function( Base, FlashRuntime ) { var $ = Base.$; return FlashRuntime.register( 'FilePicker', { init: function( opts ) { var copy = $.extend({}, opts ), len, i; // ä¿®å¤Flash冿²¡æœ‰è®¾ç½®titleçš„æƒ…å†µä¸‹æ— æ³•å¼¹å‡ºflash文件选择框的bug. len = copy.accept && copy.accept.length; for ( i = 0; i < len; i++ ) { if ( !copy.accept[ i ].title ) { copy.accept[ i ].title = 'Files'; } } delete copy.button; delete copy.id; delete copy.container; this.flashExec( 'FilePicker', 'init', copy ); }, destroy: function() { this.flashExec( 'FilePicker', 'destroy' ); } }); }); /** * @fileOverview 图片压缩 */ define('runtime/flash/image',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Image', { // init: function( options ) { // var owner = this.owner; // this.flashExec( 'Image', 'init', options ); // owner.on( 'load', function() { // debugger; // }); // }, loadFromBlob: function( blob ) { var owner = this.owner; owner.info() && this.flashExec( 'Image', 'info', owner.info() ); owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() ); this.flashExec( 'Image', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview Blob Html实现 */ define('runtime/flash/blob',[ 'runtime/flash/runtime', 'lib/blob' ], function( FlashRuntime, Blob ) { return FlashRuntime.register( 'Blob', { slice: function( start, end ) { var blob = this.flashExec( 'Blob', 'slice', start, end ); return new Blob( blob.uid, blob ); } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/flash/transport',[ 'base', 'runtime/flash/runtime', 'runtime/client' ], function( Base, FlashRuntime, RuntimeClient ) { var $ = Base.$; return FlashRuntime.register( 'Transport', { init: function() { this._status = 0; this._response = null; this._responseJson = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, binary; xhr.connectRuntime( blob.ruid ); if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.uid; } else { $.each( owner._formData, function( k, v ) { xhr.exec( 'append', k, v ); }); xhr.exec( 'appendBlob', opts.fileVal, blob.uid, opts.filename || owner._formData.name || '' ); } this._setRequestHeader( xhr, opts.headers ); xhr.exec( 'send', { method: opts.method, url: server, forceURLStream: opts.forceURLStream, mimeType: 'application/octet-stream' }, binary ); }, getStatus: function() { return this._status; }, getResponse: function() { return this._response || ''; }, getResponseAsJson: function() { return this._responseJson; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.exec('abort'); xhr.destroy(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new RuntimeClient('XMLHttpRequest'); xhr.on( 'uploadprogress progress', function( e ) { var percent = e.loaded / e.total; percent = Math.min( 1, Math.max( 0, percent ) ); return me.trigger( 'progress', percent ); }); xhr.on( 'load', function() { var status = xhr.exec('getStatus'), readBody = false, err = '', p; xhr.off(); me._xhr = null; if ( status >= 200 && status < 300 ) { readBody = true; } else if ( status >= 500 && status < 600 ) { readBody = true; err = 'server'; } else { err = 'http'; } if ( readBody ) { me._response = xhr.exec('getResponse'); me._response = decodeURIComponent( me._response ); // flash 处ç†å¯èƒ½å˜åœ¨ bug, 没辙åªèƒ½é js 了 // try { // me._responseJson = xhr.exec('getResponseAsJson'); // } catch ( error ) { p = window.JSON && window.JSON.parse || function( s ) { try { return new Function('return ' + s).call(); } catch ( err ) { return {}; } }; me._responseJson = me._response ? p(me._response) : {}; // } } xhr.destroy(); xhr = null; return err ? me.trigger( 'error', err ) : me.trigger('load'); }); xhr.on( 'error', function() { xhr.off(); me._xhr = null; me.trigger( 'error', 'http' ); }); me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.exec( 'setRequestHeader', key, val ); }); } }); }); /** * @fileOverview åªæœ‰flash实现的文件版本。 */ define('preset/flashonly',[ 'base', // widgets 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', // runtimes // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/blob', 'runtime/flash/transport' ], function( Base ) { return Base; }); define('webuploader',[ 'preset/flashonly' ], function( preset ) { return preset; }); return require('webuploader'); });