/*! 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 é”™è¯¯ä¿¡æ¯ */ define('lib/dnd',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function DragAndDrop( opts ) { opts = this.options = $.extend({}, DragAndDrop.options, opts ); opts.container = $( opts.container ); if ( !opts.container.length ) { return; } RuntimeClent.call( this, 'DragAndDrop' ); } DragAndDrop.options = { accept: null, disableGlobalDnd: false }; Base.inherits( RuntimeClent, { constructor: DragAndDrop, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( DragAndDrop.prototype ); return DragAndDrop; }); /** * @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 DragAndDrop Widget。 */ define('widgets/filednd',[ 'base', 'uploader', 'lib/dnd', 'widgets/widget' ], function( Base, Uploader, Dnd ) { var $ = Base.$; Uploader.options.dnd = ''; /** * @property {Selector} [dnd=undefined] 指定Drag And Dropæ‹–æ‹½çš„å®¹å™¨ï¼Œå¦‚æžœä¸æŒ‡å®šï¼Œåˆ™ä¸å¯åŠ¨ã€‚ * @namespace options * @for Uploader */ /** * @property {Selector} [disableGlobalDnd=false] 是å¦ç¦æŽ‰æ•´ä¸ªé¡µé¢çš„æ‹–拽功能,如果ä¸ç¦ç”¨ï¼Œå›¾ç‰‡æ‹–è¿›æ¥çš„æ—¶å€™ä¼šé»˜è®¤è¢«æµè§ˆå™¨æ‰“开。 * @namespace options * @for Uploader */ /** * @event dndAccept * @param {DataTransferItemList} items DataTransferItem * @description é˜»æ¢æ¤äº‹ä»¶å¯ä»¥æ‹’ç»æŸäº›ç±»åž‹çš„æ–‡ä»¶æ‹–入进æ¥ã€‚ç›®å‰åªæœ‰ chrome æä¾›è¿™æ ·çš„ API,且åªèƒ½é€šè¿‡ mime-type 验è¯ã€‚ * @for Uploader */ return Uploader.register({ name: 'dnd', init: function( opts ) { if ( !opts.dnd || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { disableGlobalDnd: opts.disableGlobalDnd, container: opts.dnd, accept: opts.accept }), dnd; this.dnd = dnd = new Dnd( options ); dnd.once( 'ready', deferred.resolve ); dnd.on( 'drop', function( files ) { me.request( 'add-file', [ files ]); }); // 检测文件是å¦å…¨éƒ¨å…è®¸æ·»åŠ ã€‚ dnd.on( 'accept', function( items ) { return me.owner.trigger( 'dndAccept', items ); }); dnd.init(); return deferred.promise(); }, destroy: function() { this.dnd && this.dnd.destroy(); } }); }); /** * @fileOverview é”™è¯¯ä¿¡æ¯ */ define('lib/filepaste',[ 'base', 'mediator', 'runtime/client' ], function( Base, Mediator, RuntimeClent ) { var $ = Base.$; function FilePaste( opts ) { opts = this.options = $.extend({}, opts ); opts.container = $( opts.container || document.body ); RuntimeClent.call( this, 'FilePaste' ); } Base.inherits( RuntimeClent, { constructor: FilePaste, init: function() { var me = this; me.connectRuntime( me.options, function() { me.exec('init'); me.trigger('ready'); }); } }); Mediator.installTo( FilePaste.prototype ); return FilePaste; }); /** * @fileOverview 组件基类。 */ define('widgets/filepaste',[ 'base', 'uploader', 'lib/filepaste', 'widgets/widget' ], function( Base, Uploader, FilePaste ) { var $ = Base.$; /** * @property {Selector} [paste=undefined] 指定监å¬pasteäº‹ä»¶çš„å®¹å™¨ï¼Œå¦‚æžœä¸æŒ‡å®šï¼Œä¸å¯ç”¨æ¤åŠŸèƒ½ã€‚æ¤åŠŸèƒ½ä¸ºé€šè¿‡ç²˜è´´æ¥æ·»åŠ æˆªå±çš„图片。建议设置为`document.body`. * @namespace options * @for Uploader */ return Uploader.register({ name: 'paste', init: function( opts ) { if ( !opts.paste || this.request('predict-runtime-type') !== 'html5' ) { return; } var me = this, deferred = Base.Deferred(), options = $.extend({}, { container: opts.paste, accept: opts.accept }), paste; this.paste = paste = new FilePaste( options ); paste.once( 'ready', deferred.resolve ); paste.on( 'paste', function( files ) { me.owner.request( 'add-file', [ files ]); }); paste.init(); return deferred.promise(); }, destroy: function() { this.paste && this.paste.destroy(); } }); }); /** * @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/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 Md5 */ define('lib/md5',[ 'runtime/client', 'mediator' ], function( RuntimeClient, Mediator ) { function Md5() { RuntimeClient.call( this, 'Md5' ); } // 让 Md5 具备事件功能。 Mediator.installTo( Md5.prototype ); Md5.prototype.loadFromBlob = function( blob ) { var me = this; if ( me.getRuid() ) { me.disconnectRuntime(); } // 连接到blob归属的åŒä¸€ä¸ªruntime. me.connectRuntime( blob.ruid, function() { me.exec('init'); me.exec( 'loadFromBlob', blob ); }); }; Md5.prototype.getResult = function() { return this.exec('getResult'); }; return Md5; }); /** * @fileOverview 图片æ“作, è´Ÿè´£é¢„è§ˆå›¾ç‰‡å’Œä¸Šä¼ å‰åŽ‹ç¼©å›¾ç‰‡ */ define('widgets/md5',[ 'base', 'uploader', 'lib/md5', 'lib/blob', 'widgets/widget' ], function( Base, Uploader, Md5, Blob ) { return Uploader.register({ name: 'md5', /** * 计算文件 md5 值,返回一个 promise 对象,å¯ä»¥ç›‘å¬ progress 进度。 * * * @method md5File * @grammar md5File( file[, start[, end]] ) => promise * @for Uploader * @example * * uploader.on( 'fileQueued', function( file ) { * var $li = ...; * * uploader.md5File( file ) * * // åŠæ—¶æ˜¾ç¤ºè¿›åº¦ * .progress(function(percentage) { * console.log('Percentage:', percentage); * }) * * // å®Œæˆ * .then(function(val) { * console.log('md5 result:', val); * }); * * }); */ md5File: function( file, start, end ) { var md5 = new Md5(), deferred = Base.Deferred(), blob = (file instanceof Blob) ? file : this.request( 'get-file', file ).source; md5.on( 'progress load', function( e ) { e = e || {}; deferred.notify( e.total ? e.loaded / e.total : 1 ); }); md5.on( 'complete', function() { deferred.resolve( md5.getResult() ); }); md5.on( 'error', function( reason ) { deferred.reject( reason ); }); if ( arguments.length > 1 ) { start = start || 0; end = end || 0; start < 0 && (start = blob.size + start); end < 0 && (end = blob.size + end); end = Math.min( end, blob.size ); blob = blob.slice( start, end ); } md5.loadFromBlob( blob ); return deferred.promise(); } }); }); /** * @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 Html5Runtime */ define('runtime/html5/runtime',[ 'base', 'runtime/runtime', 'runtime/compbase' ], function( Base, Runtime, CompBase ) { var type = 'html5', components = {}; function Html5Runtime() { var pool = {}, me = this, destroy = this.destroy; 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; if ( components[ comp ] ) { instance = pool[ uid ] = pool[ uid ] || new components[ comp ]( client, me ); if ( instance[ fn ] ) { return instance[ fn ].apply( instance, args ); } } }; me.destroy = function() { // @todo åˆ é™¤æ± åä¸çš„æ‰€æœ‰å®žä¾‹ return destroy && destroy.apply( this, arguments ); }; } Base.inherits( Runtime, { constructor: Html5Runtime, // ä¸éœ€è¦è¿žæŽ¥å…¶ä»–程åºï¼Œç›´æŽ¥æ‰§è¡Œcallback init: function() { var me = this; setTimeout(function() { me.trigger('ready'); }, 1 ); } }); // 注册Components Html5Runtime.register = function( name, component ) { var klass = components[ name ] = Base.inherits( CompBase, component ); return klass; }; // 注册html5è¿è¡Œæ—¶ã€‚ // åªæœ‰åœ¨æ”¯æŒçš„å‰æä¸‹æ³¨å†Œã€‚ if ( window.Blob && window.FileReader && window.DataView ) { Runtime.addRuntime( type, Html5Runtime ); } return Html5Runtime; }); /** * @fileOverview Blob Html实现 */ define('runtime/html5/blob',[ 'runtime/html5/runtime', 'lib/blob' ], function( Html5Runtime, Blob ) { return Html5Runtime.register( 'Blob', { slice: function( start, end ) { var blob = this.owner.source, slice = blob.slice || blob.webkitSlice || blob.mozSlice; blob = slice.call( blob, start, end ); return new Blob( this.getRuid(), blob ); } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/dnd',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { var $ = Base.$, prefix = 'webuploader-dnd-'; return Html5Runtime.register( 'DragAndDrop', { init: function() { var elem = this.elem = this.options.container; this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this ); this.dragOverHandler = Base.bindFn( this._dragOverHandler, this ); this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this ); this.dropHandler = Base.bindFn( this._dropHandler, this ); this.dndOver = false; elem.on( 'dragenter', this.dragEnterHandler ); elem.on( 'dragover', this.dragOverHandler ); elem.on( 'dragleave', this.dragLeaveHandler ); elem.on( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).on( 'dragover', this.dragOverHandler ); $( document ).on( 'drop', this.dropHandler ); } }, _dragEnterHandler: function( e ) { var me = this, denied = me._denied || false, items; e = e.originalEvent || e; if ( !me.dndOver ) { me.dndOver = true; // 注æ„åªæœ‰ chrome 支æŒã€‚ items = e.dataTransfer.items; if ( items && items.length ) { me._denied = denied = !me.trigger( 'accept', items ); } me.elem.addClass( prefix + 'over' ); me.elem[ denied ? 'addClass' : 'removeClass' ]( prefix + 'denied' ); } e.dataTransfer.dropEffect = denied ? 'none' : 'copy'; return false; }, _dragOverHandler: function( e ) { // åªå¤„ç†æ¡†å†…的。 var parentElem = this.elem.parent().get( 0 ); if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } clearTimeout( this._leaveTimer ); this._dragEnterHandler.call( this, e ); return false; }, _dragLeaveHandler: function() { var me = this, handler; handler = function() { me.dndOver = false; me.elem.removeClass( prefix + 'over ' + prefix + 'denied' ); }; clearTimeout( me._leaveTimer ); me._leaveTimer = setTimeout( handler, 100 ); return false; }, _dropHandler: function( e ) { var me = this, ruid = me.getRuid(), parentElem = me.elem.parent().get( 0 ), dataTransfer, data; // åªå¤„ç†æ¡†å†…的。 if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) { return false; } e = e.originalEvent || e; dataTransfer = e.dataTransfer; // 如果是页é¢å†…拖拽,还ä¸èƒ½å¤„ç†ï¼Œä¸é˜»æ¢äº‹ä»¶ã€‚ // æ¤å¤„ ie11 ä¸‹ä¼šæŠ¥å‚æ•°é”™è¯¯ï¼Œ try { data = dataTransfer.getData('text/html'); } catch( err ) { } if ( data ) { return; } me._getTansferFiles( dataTransfer, function( results ) { me.trigger( 'drop', $.map( results, function( file ) { return new File( ruid, file ); }) ); }); me.dndOver = false; me.elem.removeClass( prefix + 'over' ); return false; }, // å¦‚æžœä¼ å…¥ callback 则去查看文件夹,å¦åˆ™åªç®¡å½“剿–‡ä»¶å¤¹ã€‚ _getTansferFiles: function( dataTransfer, callback ) { var results = [], promises = [], items, files, file, item, i, len, canAccessFolder; items = dataTransfer.items; files = dataTransfer.files; canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry); for ( i = 0, len = files.length; i < len; i++ ) { file = files[ i ]; item = items && items[ i ]; if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) { promises.push( this._traverseDirectoryTree( item.webkitGetAsEntry(), results ) ); } else { results.push( file ); } } Base.when.apply( Base, promises ).done(function() { if ( !results.length ) { return; } callback( results ); }); }, _traverseDirectoryTree: function( entry, results ) { var deferred = Base.Deferred(), me = this; if ( entry.isFile ) { entry.file(function( file ) { results.push( file ); deferred.resolve(); }); } else if ( entry.isDirectory ) { entry.createReader().readEntries(function( entries ) { var len = entries.length, promises = [], arr = [], // 为了ä¿è¯é¡ºåºã€‚ i; for ( i = 0; i < len; i++ ) { promises.push( me._traverseDirectoryTree( entries[ i ], arr ) ); } Base.when.apply( Base, promises ).then(function() { results.push.apply( results, arr ); deferred.resolve(); }, deferred.reject ); }); } return deferred.promise(); }, destroy: function() { var elem = this.elem; // 还没 init 就调用 destroy if (!elem) { return; } elem.off( 'dragenter', this.dragEnterHandler ); elem.off( 'dragover', this.dragOverHandler ); elem.off( 'dragleave', this.dragLeaveHandler ); elem.off( 'drop', this.dropHandler ); if ( this.options.disableGlobalDnd ) { $( document ).off( 'dragover', this.dragOverHandler ); $( document ).off( 'drop', this.dropHandler ); } } }); }); /** * @fileOverview FilePaste */ define('runtime/html5/filepaste',[ 'base', 'runtime/html5/runtime', 'lib/file' ], function( Base, Html5Runtime, File ) { return Html5Runtime.register( 'FilePaste', { init: function() { var opts = this.options, elem = this.elem = opts.container, accept = '.*', arr, i, len, item; // accetpçš„mimeTypesä¸ç”ŸæˆåŒ¹é…æ£åˆ™ã€‚ if ( opts.accept ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { item = opts.accept[ i ].mimeTypes; item && arr.push( item ); } if ( arr.length ) { accept = arr.join(','); accept = accept.replace( /,/g, '|' ).replace( /\*/g, '.*' ); } } this.accept = accept = new RegExp( accept, 'i' ); this.hander = Base.bindFn( this._pasteHander, this ); elem.on( 'paste', this.hander ); }, _pasteHander: function( e ) { var allowed = [], ruid = this.getRuid(), items, item, blob, i, len; e = e.originalEvent || e; items = e.clipboardData.items; for ( i = 0, len = items.length; i < len; i++ ) { item = items[ i ]; if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) { continue; } allowed.push( new File( ruid, blob ) ); } if ( allowed.length ) { // ä¸é˜»æ¢éžæ–‡ä»¶ç²˜è´´ï¼ˆæ–‡å—粘贴)的事件冒泡 e.preventDefault(); e.stopPropagation(); this.trigger( 'paste', allowed ); } }, destroy: function() { this.elem.off( 'paste', this.hander ); } }); }); /** * @fileOverview FilePicker */ define('runtime/html5/filepicker',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var $ = Base.$; return Html5Runtime.register( 'FilePicker', { init: function() { var container = this.getRuntime().getContainer(), me = this, owner = me.owner, opts = me.options, label = this.label = $( document.createElement('label') ), input = this.input = $( document.createElement('input') ), arr, i, len, mouseHandler; input.attr( 'type', 'file' ); input.attr( 'name', opts.name ); input.addClass('webuploader-element-invisible'); label.on( 'click', function() { input.trigger('click'); }); label.css({ opacity: 0, width: '100%', height: '100%', display: 'block', cursor: 'pointer', background: '#ffffff' }); if ( opts.multiple ) { input.attr( 'multiple', 'multiple' ); } // @todo Firefox䏿”¯æŒå•独指定åŽç¼€ if ( opts.accept && opts.accept.length > 0 ) { arr = []; for ( i = 0, len = opts.accept.length; i < len; i++ ) { arr.push( opts.accept[ i ].mimeTypes ); } input.attr( 'accept', arr.join(',') ); } container.append( input ); container.append( label ); mouseHandler = function( e ) { owner.trigger( e.type ); }; input.on( 'change', function( e ) { var fn = arguments.callee, clone; me.files = e.target.files; // reset input clone = this.cloneNode( true ); clone.value = null; this.parentNode.replaceChild( clone, this ); input.off(); input = $( clone ).on( 'change', fn ) .on( 'mouseenter mouseleave', mouseHandler ); owner.trigger('change'); }); label.on( 'mouseenter mouseleave', mouseHandler ); }, getFiles: function() { return this.files; }, destroy: function() { this.input.off(); this.label.off(); } }); }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/util',[ 'base' ], function( Base ) { var urlAPI = window.createObjectURL && window || window.URL && URL.revokeObjectURL && URL || window.webkitURL, createObjectURL = Base.noop, revokeObjectURL = createObjectURL; if ( urlAPI ) { // 更安全的方å¼è°ƒç”¨ï¼Œæ¯”如android里é¢å°±èƒ½æŠŠcontext改æˆå…¶ä»–的对象。 createObjectURL = function() { return urlAPI.createObjectURL.apply( urlAPI, arguments ); }; revokeObjectURL = function() { return urlAPI.revokeObjectURL.apply( urlAPI, arguments ); }; } return { createObjectURL: createObjectURL, revokeObjectURL: revokeObjectURL, dataURL2Blob: function( dataURI ) { var byteStr, intArray, ab, i, mimetype, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } ab = new ArrayBuffer( byteStr.length ); intArray = new Uint8Array( ab ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ]; return this.arrayBufferToBlob( ab, mimetype ); }, dataURL2ArrayBuffer: function( dataURI ) { var byteStr, intArray, i, parts; parts = dataURI.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { byteStr = atob( parts[ 1 ] ); } else { byteStr = decodeURIComponent( parts[ 1 ] ); } intArray = new Uint8Array( byteStr.length ); for ( i = 0; i < byteStr.length; i++ ) { intArray[ i ] = byteStr.charCodeAt( i ); } return intArray.buffer; }, arrayBufferToBlob: function( buffer, type ) { var builder = window.BlobBuilder || window.WebKitBlobBuilder, bb; // android䏿”¯æŒç›´æŽ¥new Blob, åªèƒ½å€ŸåŠ©blobbuilder. if ( builder ) { bb = new builder(); bb.append( buffer ); return bb.getBlob( type ); } return new Blob([ buffer ], type ? { type: type } : {} ); }, // 抽出æ¥ä¸»è¦æ˜¯ä¸ºäº†è§£å†³android下é¢canvas.toDataUrl䏿”¯æŒjpeg. // ä½ å¾—åˆ°çš„ç»“æžœæ˜¯png. canvasToDataUrl: function( canvas, type, quality ) { return canvas.toDataURL( type, quality / 100 ); }, // imagemeat会å¤å†™è¿™ä¸ªæ–¹æ³•ï¼Œå¦‚æžœç”¨æˆ·é€‰æ‹©åŠ è½½é‚£ä¸ªæ–‡ä»¶äº†çš„è¯ã€‚ parseMeta: function( blob, callback ) { callback( false, {}); }, // imagemeat会å¤å†™è¿™ä¸ªæ–¹æ³•ï¼Œå¦‚æžœç”¨æˆ·é€‰æ‹©åŠ è½½é‚£ä¸ªæ–‡ä»¶äº†çš„è¯ã€‚ updateImageHead: function( data ) { return data; } }; }); /** * Terms: * * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer * @fileOverview Image控件 */ define('runtime/html5/imagemeta',[ 'runtime/html5/util' ], function( Util ) { var api; api = { parsers: { 0xffe1: [] }, maxMetaDataSize: 262144, parse: function( blob, cb ) { var me = this, fr = new FileReader(); fr.onload = function() { cb( false, me._parse( this.result ) ); fr = fr.onload = fr.onerror = null; }; fr.onerror = function( e ) { cb( e.message ); fr = fr.onload = fr.onerror = null; }; blob = blob.slice( 0, me.maxMetaDataSize ); fr.readAsArrayBuffer( blob.getSource() ); }, _parse: function( buffer, noParse ) { if ( buffer.byteLength < 6 ) { return; } var dataview = new DataView( buffer ), offset = 2, maxOffset = dataview.byteLength - 4, headLength = offset, ret = {}, markerBytes, markerLength, parsers, i; if ( dataview.getUint16( 0 ) === 0xffd8 ) { while ( offset < maxOffset ) { markerBytes = dataview.getUint16( offset ); if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef || markerBytes === 0xfffe ) { markerLength = dataview.getUint16( offset + 2 ) + 2; if ( offset + markerLength > dataview.byteLength ) { break; } parsers = api.parsers[ markerBytes ]; if ( !noParse && parsers ) { for ( i = 0; i < parsers.length; i += 1 ) { parsers[ i ].call( api, dataview, offset, markerLength, ret ); } } offset += markerLength; headLength = offset; } else { break; } } if ( headLength > 6 ) { if ( buffer.slice ) { ret.imageHead = buffer.slice( 2, headLength ); } else { // Workaround for IE10, which does not yet // support ArrayBuffer.slice: ret.imageHead = new Uint8Array( buffer ) .subarray( 2, headLength ); } } } return ret; }, updateImageHead: function( buffer, head ) { var data = this._parse( buffer, true ), buf1, buf2, bodyoffset; bodyoffset = 2; if ( data.imageHead ) { bodyoffset = 2 + data.imageHead.byteLength; } if ( buffer.slice ) { buf2 = buffer.slice( bodyoffset ); } else { buf2 = new Uint8Array( buffer ).subarray( bodyoffset ); } buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength ); buf1[ 0 ] = 0xFF; buf1[ 1 ] = 0xD8; buf1.set( new Uint8Array( head ), 2 ); buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 ); return buf1.buffer; } }; Util.parseMeta = function() { return api.parse.apply( api, arguments ); }; Util.updateImageHead = function() { return api.updateImageHead.apply( api, arguments ); }; return api; }); /** * ä»£ç æ¥è‡ªäºŽï¼šhttps://github.com/blueimp/JavaScript-Load-Image * 暂时项目ä¸åªç”¨äº†orientation. * * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail. * @fileOverview EXIFè§£æž */ // Sample // ==================================== // Make : Apple // Model : iPhone 4S // Orientation : 1 // XResolution : 72 [72/1] // YResolution : 72 [72/1] // ResolutionUnit : 2 // Software : QuickTime 7.7.1 // DateTime : 2013:09:01 22:53:55 // ExifIFDPointer : 190 // ExposureTime : 0.058823529411764705 [1/17] // FNumber : 2.4 [12/5] // ExposureProgram : Normal program // ISOSpeedRatings : 800 // ExifVersion : 0220 // DateTimeOriginal : 2013:09:01 22:52:51 // DateTimeDigitized : 2013:09:01 22:52:51 // ComponentsConfiguration : YCbCr // ShutterSpeedValue : 4.058893515764426 // ApertureValue : 2.5260688216892597 [4845/1918] // BrightnessValue : -0.3126686601998395 // MeteringMode : Pattern // Flash : Flash did not fire, compulsory flash mode // FocalLength : 4.28 [107/25] // SubjectArea : [4 values] // FlashpixVersion : 0100 // ColorSpace : 1 // PixelXDimension : 2448 // PixelYDimension : 3264 // SensingMethod : One-chip color area sensor // ExposureMode : 0 // WhiteBalance : Auto white balance // FocalLengthIn35mmFilm : 35 // SceneCaptureType : Standard define('runtime/html5/imagemeta/exif',[ 'base', 'runtime/html5/imagemeta' ], function( Base, ImageMeta ) { var EXIF = {}; EXIF.ExifMap = function() { return this; }; EXIF.ExifMap.prototype.map = { 'Orientation': 0x0112 }; EXIF.ExifMap.prototype.get = function( id ) { return this[ id ] || this[ this.map[ id ] ]; }; EXIF.exifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function( dataView, dataOffset ) { return dataView.getUint8( dataOffset ); }, size: 1 }, // ascii, 8-bit byte: 2: { getValue: function( dataView, dataOffset ) { return String.fromCharCode( dataView.getUint8( dataOffset ) ); }, size: 1, ascii: true }, // short, 16 bit int: 3: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint16( dataOffset, littleEndian ); }, size: 2 }, // long, 32 bit int: 4: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ); }, size: 4 }, // rational = two long values, // first is numerator, second is denominator: 5: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getUint32( dataOffset, littleEndian ) / dataView.getUint32( dataOffset + 4, littleEndian ); }, size: 8 }, // slong, 32 bit signed int: 9: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ); }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { getValue: function( dataView, dataOffset, littleEndian ) { return dataView.getInt32( dataOffset, littleEndian ) / dataView.getInt32( dataOffset + 4, littleEndian ); }, size: 8 } }; // undefined, 8-bit byte, value depending on field: EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ]; EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length, littleEndian ) { var tagType = EXIF.exifTagTypes[ type ], tagSize, dataOffset, values, i, str, c; if ( !tagType ) { Base.log('Invalid Exif data: Invalid tag type.'); return; } tagSize = tagType.size * length; // Determine if the value is contained in the dataOffset bytes, // or if the value at the dataOffset is a pointer to the actual data: dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8, littleEndian ) : (offset + 8); if ( dataOffset + tagSize > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid data offset.'); return; } if ( length === 1 ) { return tagType.getValue( dataView, dataOffset, littleEndian ); } values = []; for ( i = 0; i < length; i += 1 ) { values[ i ] = tagType.getValue( dataView, dataOffset + i * tagType.size, littleEndian ); } if ( tagType.ascii ) { str = ''; // Concatenate the chars: for ( i = 0; i < values.length; i += 1 ) { c = values[ i ]; // Ignore the terminating NULL byte(s): if ( c === '\u0000' ) { break; } str += c; } return str; } return values; }; EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian, data ) { var tag = dataView.getUint16( offset, littleEndian ); data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset, dataView.getUint16( offset + 2, littleEndian ), // tag type dataView.getUint32( offset + 4, littleEndian ), // tag length littleEndian ); }; EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset, littleEndian, data ) { var tagsNumber, dirEndOffset, i; if ( dirOffset + 6 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory offset.'); return; } tagsNumber = dataView.getUint16( dirOffset, littleEndian ); dirEndOffset = dirOffset + 2 + 12 * tagsNumber; if ( dirEndOffset + 4 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid directory size.'); return; } for ( i = 0; i < tagsNumber; i += 1 ) { this.parseExifTag( dataView, tiffOffset, dirOffset + 2 + 12 * i, // tag offset littleEndian, data ); } // Return the offset to the next directory: return dataView.getUint32( dirEndOffset, littleEndian ); }; // EXIF.getExifThumbnail = function(dataView, offset, length) { // var hexData, // i, // b; // if (!length || offset + length > dataView.byteLength) { // Base.log('Invalid Exif data: Invalid thumbnail data.'); // return; // } // hexData = []; // for (i = 0; i < length; i += 1) { // b = dataView.getUint8(offset + i); // hexData.push((b < 16 ? '0' : '') + b.toString(16)); // } // return 'data:image/jpeg,%' + hexData.join('%'); // }; EXIF.parseExifData = function( dataView, offset, length, data ) { var tiffOffset = offset + 10, littleEndian, dirOffset; // Check for the ASCII code for "Exif" (0x45786966): if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) { // No Exif data, might be XMP data instead return; } if ( tiffOffset + 8 > dataView.byteLength ) { Base.log('Invalid Exif data: Invalid segment size.'); return; } // Check for the two null bytes: if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) { Base.log('Invalid Exif data: Missing byte alignment offset.'); return; } // Check the byte alignment: switch ( dataView.getUint16( tiffOffset ) ) { case 0x4949: littleEndian = true; break; case 0x4D4D: littleEndian = false; break; default: Base.log('Invalid Exif data: Invalid byte alignment marker.'); return; } // Check for the TIFF tag marker (0x002A): if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) { Base.log('Invalid Exif data: Missing TIFF marker.'); return; } // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian ); // Create the exif object to store the tags: data.exif = new EXIF.ExifMap(); // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: dirOffset = EXIF.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, data ); // å°è¯•读å–缩略图 // if ( dirOffset ) { // thumbnailData = {exif: {}}; // dirOffset = EXIF.parseExifTags( // dataView, // tiffOffset, // tiffOffset + dirOffset, // littleEndian, // thumbnailData // ); // // Check for JPEG Thumbnail offset: // if (thumbnailData.exif[0x0201]) { // data.exif.Thumbnail = EXIF.getExifThumbnail( // dataView, // tiffOffset + thumbnailData.exif[0x0201], // thumbnailData.exif[0x0202] // Thumbnail data length // ); // } // } }; ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData ); return EXIF; }); /** * è¿™ä¸ªæ–¹å¼æ€§èƒ½ä¸è¡Œï¼Œä½†æ˜¯å¯ä»¥è§£å†³android里é¢çš„toDataUrlçš„bug * android里é¢toDataUrl('image/jpege')å¾—åˆ°çš„ç»“æžœå´æ˜¯png. * * 所以这里没辙,åªèƒ½å€ŸåŠ©è¿™ä¸ªå·¥å…· * @fileOverview jpeg encoder */ define('runtime/html5/jpegencoder',[], function( require, exports, module ) { /* Copyright (c) 2008, Adobe Systems Incorporated All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adobe Systems Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009 Basic GUI blocking jpeg encoder */ function JPEGEncoder(quality) { var self = this; var fround = Math.round; var ffloor = Math.floor; var YTable = new Array(64); var UVTable = new Array(64); var fdtbl_Y = new Array(64); var fdtbl_UV = new Array(64); var YDC_HT; var UVDC_HT; var YAC_HT; var UVAC_HT; var bitcode = new Array(65535); var category = new Array(65535); var outputfDCTQuant = new Array(64); var DU = new Array(64); var byteout = []; var bytenew = 0; var bytepos = 7; var YDU = new Array(64); var UDU = new Array(64); var VDU = new Array(64); var clt = new Array(256); var RGB_YUV_TABLE = new Array(2048); var currentQuality; var ZigZag = [ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54, 20,22,33,38,46,51,55,60, 21,34,37,47,50,56,59,61, 35,36,48,49,57,58,62,63 ]; var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; var std_ac_luminance_values = [ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11]; var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; var std_ac_chrominance_values = [ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 0xf9,0xfa ]; function initQuantTables(sf){ var YQT = [ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68,109,103, 77, 24, 35, 55, 64, 81,104,113, 92, 49, 64, 78, 87,103,121,120,101, 72, 92, 95, 98,112,100,103, 99 ]; for (var i = 0; i < 64; i++) { var t = ffloor((YQT[i]*sf+50)/100); if (t < 1) { t = 1; } else if (t > 255) { t = 255; } YTable[ZigZag[i]] = t; } var UVQT = [ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 ]; for (var j = 0; j < 64; j++) { var u = ffloor((UVQT[j]*sf+50)/100); if (u < 1) { u = 1; } else if (u > 255) { u = 255; } UVTable[ZigZag[j]] = u; } var aasf = [ 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 ]; var k = 0; for (var row = 0; row < 8; row++) { for (var col = 0; col < 8; col++) { fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); k++; } } } function computeHuffmanTbl(nrcodes, std_table){ var codevalue = 0; var pos_in_table = 0; var HT = new Array(); for (var k = 1; k <= 16; k++) { for (var j = 1; j <= nrcodes[k]; j++) { HT[std_table[pos_in_table]] = []; HT[std_table[pos_in_table]][0] = codevalue; HT[std_table[pos_in_table]][1] = k; pos_in_table++; codevalue++; } codevalue*=2; } return HT; } function initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); } function initCategoryNumber() { var nrlower = 1; var nrupper = 2; for (var cat = 1; cat <= 15; cat++) { //Positive numbers for (var nr = nrlower; nr<nrupper; nr++) { category[32767+nr] = cat; bitcode[32767+nr] = []; bitcode[32767+nr][1] = cat; bitcode[32767+nr][0] = nr; } //Negative numbers for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) { category[32767+nrneg] = cat; bitcode[32767+nrneg] = []; bitcode[32767+nrneg][1] = cat; bitcode[32767+nrneg][0] = nrupper-1+nrneg; } nrlower <<= 1; nrupper <<= 1; } } function initRGBYUVTable() { for(var i = 0; i < 256;i++) { RGB_YUV_TABLE[i] = 19595 * i; RGB_YUV_TABLE[(i+ 256)>>0] = 38470 * i; RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000; RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i; RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i; RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF; RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i; RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i; } } // IO functions function writeBits(bs) { var value = bs[0]; var posval = bs[1]-1; while ( posval >= 0 ) { if (value & (1 << posval) ) { bytenew |= (1 << bytepos); } posval--; bytepos--; if (bytepos < 0) { if (bytenew == 0xFF) { writeByte(0xFF); writeByte(0); } else { writeByte(bytenew); } bytepos=7; bytenew=0; } } } function writeByte(value) { byteout.push(clt[value]); // write char directly instead of converting later } function writeWord(value) { writeByte((value>>8)&0xFF); writeByte((value )&0xFF); } // DCT & quantization core function fDCTQuant(data, fdtbl) { var d0, d1, d2, d3, d4, d5, d6, d7; /* Pass 1: process rows. */ var dataOff=0; var i; var I8 = 8; var I64 = 64; for (i=0; i<I8; ++i) { d0 = data[dataOff]; d1 = data[dataOff+1]; d2 = data[dataOff+2]; d3 = data[dataOff+3]; d4 = data[dataOff+4]; d5 = data[dataOff+5]; d6 = data[dataOff+6]; d7 = data[dataOff+7]; var tmp0 = d0 + d7; var tmp7 = d0 - d7; var tmp1 = d1 + d6; var tmp6 = d1 - d6; var tmp2 = d2 + d5; var tmp5 = d2 - d5; var tmp3 = d3 + d4; var tmp4 = d3 - d4; /* Even part */ var tmp10 = tmp0 + tmp3; /* phase 2 */ var tmp13 = tmp0 - tmp3; var tmp11 = tmp1 + tmp2; var tmp12 = tmp1 - tmp2; data[dataOff] = tmp10 + tmp11; /* phase 3 */ data[dataOff+4] = tmp10 - tmp11; var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */ data[dataOff+2] = tmp13 + z1; /* phase 5 */ data[dataOff+6] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */ var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */ var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */ var z3 = tmp11 * 0.707106781; /* c4 */ var z11 = tmp7 + z3; /* phase 5 */ var z13 = tmp7 - z3; data[dataOff+5] = z13 + z2; /* phase 6 */ data[dataOff+3] = z13 - z2; data[dataOff+1] = z11 + z4; data[dataOff+7] = z11 - z4; dataOff += 8; /* advance pointer to next row */ } /* Pass 2: process columns. */ dataOff = 0; for (i=0; i<I8; ++i) { d0 = data[dataOff]; d1 = data[dataOff + 8]; d2 = data[dataOff + 16]; d3 = data[dataOff + 24]; d4 = data[dataOff + 32]; d5 = data[dataOff + 40]; d6 = data[dataOff + 48]; d7 = data[dataOff + 56]; var tmp0p2 = d0 + d7; var tmp7p2 = d0 - d7; var tmp1p2 = d1 + d6; var tmp6p2 = d1 - d6; var tmp2p2 = d2 + d5; var tmp5p2 = d2 - d5; var tmp3p2 = d3 + d4; var tmp4p2 = d3 - d4; /* Even part */ var tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */ var tmp13p2 = tmp0p2 - tmp3p2; var tmp11p2 = tmp1p2 + tmp2p2; var tmp12p2 = tmp1p2 - tmp2p2; data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */ data[dataOff+32] = tmp10p2 - tmp11p2; var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */ data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */ data[dataOff+48] = tmp13p2 - z1p2; /* Odd part */ tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */ tmp11p2 = tmp5p2 + tmp6p2; tmp12p2 = tmp6p2 + tmp7p2; /* The rotator is modified from fig 4-8 to avoid extra negations. */ var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */ var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */ var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */ var z3p2 = tmp11p2 * 0.707106781; /* c4 */ var z11p2 = tmp7p2 + z3p2; /* phase 5 */ var z13p2 = tmp7p2 - z3p2; data[dataOff+40] = z13p2 + z2p2; /* phase 6 */ data[dataOff+24] = z13p2 - z2p2; data[dataOff+ 8] = z11p2 + z4p2; data[dataOff+56] = z11p2 - z4p2; dataOff++; /* advance pointer to next column */ } // Quantize/descale the coefficients var fDCTQuant; for (i=0; i<I64; ++i) { // Apply the quantization and scaling factor & Round to nearest integer fDCTQuant = data[i]*fdtbl[i]; outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0); //outputfDCTQuant[i] = fround(fDCTQuant); } return outputfDCTQuant; } function writeAPP0() { writeWord(0xFFE0); // marker writeWord(16); // length writeByte(0x4A); // J writeByte(0x46); // F writeByte(0x49); // I writeByte(0x46); // F writeByte(0); // = "JFIF",'\0' writeByte(1); // versionhi writeByte(1); // versionlo writeByte(0); // xyunits writeWord(1); // xdensity writeWord(1); // ydensity writeByte(0); // thumbnwidth writeByte(0); // thumbnheight } function writeSOF0(width, height) { writeWord(0xFFC0); // marker writeWord(17); // length, truecolor YUV JPG writeByte(8); // precision writeWord(height); writeWord(width); writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0x11); // HVY writeByte(0); // QTY writeByte(2); // IdU writeByte(0x11); // HVU writeByte(1); // QTU writeByte(3); // IdV writeByte(0x11); // HVV writeByte(1); // QTV } function writeDQT() { writeWord(0xFFDB); // marker writeWord(132); // length writeByte(0); for (var i=0; i<64; i++) { writeByte(YTable[i]); } writeByte(1); for (var j=0; j<64; j++) { writeByte(UVTable[j]); } } function writeDHT() { writeWord(0xFFC4); // marker writeWord(0x01A2); // length writeByte(0); // HTYDCinfo for (var i=0; i<16; i++) { writeByte(std_dc_luminance_nrcodes[i+1]); } for (var j=0; j<=11; j++) { writeByte(std_dc_luminance_values[j]); } writeByte(0x10); // HTYACinfo for (var k=0; k<16; k++) { writeByte(std_ac_luminance_nrcodes[k+1]); } for (var l=0; l<=161; l++) { writeByte(std_ac_luminance_values[l]); } writeByte(1); // HTUDCinfo for (var m=0; m<16; m++) { writeByte(std_dc_chrominance_nrcodes[m+1]); } for (var n=0; n<=11; n++) { writeByte(std_dc_chrominance_values[n]); } writeByte(0x11); // HTUACinfo for (var o=0; o<16; o++) { writeByte(std_ac_chrominance_nrcodes[o+1]); } for (var p=0; p<=161; p++) { writeByte(std_ac_chrominance_values[p]); } } function writeSOS() { writeWord(0xFFDA); // marker writeWord(12); // length writeByte(3); // nrofcomponents writeByte(1); // IdY writeByte(0); // HTY writeByte(2); // IdU writeByte(0x11); // HTU writeByte(3); // IdV writeByte(0x11); // HTV writeByte(0); // Ss writeByte(0x3f); // Se writeByte(0); // Bf } function processDU(CDU, fdtbl, DC, HTDC, HTAC){ var EOB = HTAC[0x00]; var M16zeroes = HTAC[0xF0]; var pos; var I16 = 16; var I63 = 63; var I64 = 64; var DU_DCT = fDCTQuant(CDU, fdtbl); //ZigZag reorder for (var j=0;j<I64;++j) { DU[ZigZag[j]]=DU_DCT[j]; } var Diff = DU[0] - DC; DC = DU[0]; //Encode DC if (Diff==0) { writeBits(HTDC[0]); // Diff might be 0 } else { pos = 32767+Diff; writeBits(HTDC[category[pos]]); writeBits(bitcode[pos]); } //Encode ACs var end0pos = 63; // was const... which is crazy for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {}; //end0pos = first element in reverse order !=0 if ( end0pos == 0) { writeBits(EOB); return DC; } var i = 1; var lng; while ( i <= end0pos ) { var startpos = i; for (; (DU[i]==0) && (i<=end0pos); ++i) {} var nrzeroes = i-startpos; if ( nrzeroes >= I16 ) { lng = nrzeroes>>4; for (var nrmarker=1; nrmarker <= lng; ++nrmarker) writeBits(M16zeroes); nrzeroes = nrzeroes&0xF; } pos = 32767+DU[i]; writeBits(HTAC[(nrzeroes<<4)+category[pos]]); writeBits(bitcode[pos]); i++; } if ( end0pos != I63 ) { writeBits(EOB); } return DC; } function initCharLookupTable(){ var sfcc = String.fromCharCode; for(var i=0; i < 256; i++){ ///// ACHTUNG // 255 clt[i] = sfcc(i); } } this.encode = function(image,quality) // image data object { // var time_start = new Date().getTime(); if(quality) setQuality(quality); // Initialize bit writer byteout = new Array(); bytenew=0; bytepos=7; // Add JPEG headers writeWord(0xFFD8); // SOI writeAPP0(); writeDQT(); writeSOF0(image.width,image.height); writeDHT(); writeSOS(); // Encode 8x8 macroblocks var DCY=0; var DCU=0; var DCV=0; bytenew=0; bytepos=7; this.encode.displayName = "_encode_"; var imageData = image.data; var width = image.width; var height = image.height; var quadWidth = width*4; var tripleWidth = width*3; var x, y = 0; var r, g, b; var start,p, col,row,pos; while(y < height){ x = 0; while(x < quadWidth){ start = quadWidth * y + x; p = start; col = -1; row = 0; for(pos=0; pos < 64; pos++){ row = pos >> 3;// /8 col = ( pos & 7 ) * 4; // %8 p = start + ( row * quadWidth ) + col; if(y+row >= height){ // padding bottom p-= (quadWidth*(y+1+row-height)); } if(x+col >= quadWidth){ // padding right p-= ((x+col) - quadWidth +4) } r = imageData[ p++ ]; g = imageData[ p++ ]; b = imageData[ p++ ]; /* // calculate YUV values dynamically YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80 UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b)); VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b)); */ // use lookup table (slightly faster) YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128; UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128; VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128; } DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); x+=32; } y+=8; } //////////////////////////////////////////////////////////////// // Do the bit alignment of the EOI marker if ( bytepos >= 0 ) { var fillbits = []; fillbits[1] = bytepos+1; fillbits[0] = (1<<(bytepos+1))-1; writeBits(fillbits); } writeWord(0xFFD9); //EOI var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join('')); byteout = []; // benchmarking // var duration = new Date().getTime() - time_start; // console.log('Encoding time: '+ currentQuality + 'ms'); // return jpegDataUri } function setQuality(quality){ if (quality <= 0) { quality = 1; } if (quality > 100) { quality = 100; } if(currentQuality == quality) return // don't recalc if unchanged var sf = 0; if (quality < 50) { sf = Math.floor(5000 / quality); } else { sf = Math.floor(200 - quality*2); } initQuantTables(sf); currentQuality = quality; // console.log('Quality set to: '+quality +'%'); } function init(){ // var time_start = new Date().getTime(); if(!quality) quality = 50; // Create tables initCharLookupTable() initHuffmanTbl(); initCategoryNumber(); initRGBYUVTable(); setQuality(quality); // var duration = new Date().getTime() - time_start; // console.log('Initialization '+ duration + 'ms'); } init(); }; JPEGEncoder.encode = function( data, quality ) { var encoder = new JPEGEncoder( quality ); return encoder.encode( data ); } return JPEGEncoder; }); /** * @fileOverview Fix android canvas.toDataUrl bug. */ define('runtime/html5/androidpatch',[ 'runtime/html5/util', 'runtime/html5/jpegencoder', 'base' ], function( Util, encoder, Base ) { var origin = Util.canvasToDataUrl, supportJpeg; Util.canvasToDataUrl = function( canvas, type, quality ) { var ctx, w, h, fragement, parts; // éžandroid手机直接跳过。 if ( !Base.os.android ) { return origin.apply( null, arguments ); } // 检测是å¦canvas支æŒjpegå¯¼å‡ºï¼Œæ ¹æ®æ•°æ®æ ¼å¼æ¥åˆ¤æ–。 // JPEG å‰ä¸¤ä½åˆ†åˆ«æ˜¯ï¼š255, 216 if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) { fragement = origin.apply( null, arguments ); parts = fragement.split(','); if ( ~parts[ 0 ].indexOf('base64') ) { fragement = atob( parts[ 1 ] ); } else { fragement = decodeURIComponent( parts[ 1 ] ); } fragement = fragement.substring( 0, 2 ); supportJpeg = fragement.charCodeAt( 0 ) === 255 && fragement.charCodeAt( 1 ) === 216; } // åªæœ‰åœ¨android环境下æ‰ä¿®å¤ if ( type === 'image/jpeg' && !supportJpeg ) { w = canvas.width; h = canvas.height; ctx = canvas.getContext('2d'); return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality ); } return origin.apply( null, arguments ); }; }); /** * @fileOverview Image */ define('runtime/html5/image',[ 'base', 'runtime/html5/runtime', 'runtime/html5/util' ], function( Base, Html5Runtime, Util ) { var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'; return Html5Runtime.register( 'Image', { // flag: æ ‡è®°æ˜¯å¦è¢«ä¿®æ”¹è¿‡ã€‚ modified: false, init: function() { var me = this, img = new Image(); img.onload = function() { me._info = { type: me.type, width: this.width, height: this.height }; // 读å–metaä¿¡æ¯ã€‚ if ( !me._metas && 'image/jpeg' === me.type ) { Util.parseMeta( me._blob, function( error, ret ) { me._metas = ret; me.owner.trigger('load'); }); } else { me.owner.trigger('load'); } }; img.onerror = function() { me.owner.trigger('error'); }; me._img = img; }, loadFromBlob: function( blob ) { var me = this, img = me._img; me._blob = blob; me.type = blob.type; img.src = Util.createObjectURL( blob.getSource() ); me.owner.once( 'load', function() { Util.revokeObjectURL( img.src ); }); }, resize: function( width, height ) { var canvas = this._canvas || (this._canvas = document.createElement('canvas')); this._resize( this._img, canvas, width, height ); this._blob = null; // 没用了,å¯ä»¥åˆ 掉了。 this.modified = true; this.owner.trigger( 'complete', 'resize' ); }, crop: function( x, y, w, h, s ) { var cvs = this._canvas || (this._canvas = document.createElement('canvas')), opts = this.options, img = this._img, iw = img.naturalWidth, ih = img.naturalHeight, orientation = this.getOrientation(); s = s || 1; // todo 解决 orientation 的问题。 // values that require 90 degree rotation // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // switch ( orientation ) { // case 6: // tmp = x; // x = y; // y = iw * s - tmp - w; // console.log(ih * s, tmp, w) // break; // } // (w ^= h, h ^= w, w ^= h); // } cvs.width = w; cvs.height = h; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s ); this._blob = null; // 没用了,å¯ä»¥åˆ 掉了。 this.modified = true; this.owner.trigger( 'complete', 'crop' ); }, getAsBlob: function( type ) { var blob = this._blob, opts = this.options, canvas; type = type || this.type; // blob需è¦é‡æ–°ç”Ÿæˆã€‚ if ( this.modified || this.type !== type ) { canvas = this._canvas; if ( type === 'image/jpeg' ) { blob = Util.canvasToDataUrl( canvas, type, opts.quality ); if ( opts.preserveHeaders && this._metas && this._metas.imageHead ) { blob = Util.dataURL2ArrayBuffer( blob ); blob = Util.updateImageHead( blob, this._metas.imageHead ); blob = Util.arrayBufferToBlob( blob, type ); return blob; } } else { blob = Util.canvasToDataUrl( canvas, type ); } blob = Util.dataURL2Blob( blob ); } return blob; }, getAsDataUrl: function( type ) { var opts = this.options; type = type || this.type; if ( type === 'image/jpeg' ) { return Util.canvasToDataUrl( this._canvas, type, opts.quality ); } else { return this._canvas.toDataURL( type ); } }, getOrientation: function() { return this._metas && this._metas.exif && this._metas.exif.get('Orientation') || 1; }, 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; }, destroy: function() { var canvas = this._canvas; this._img.onload = null; if ( canvas ) { canvas.getContext('2d') .clearRect( 0, 0, canvas.width, canvas.height ); canvas.width = canvas.height = 0; this._canvas = null; } // 释放内å˜ã€‚éžå¸¸é‡è¦ï¼Œå¦åˆ™é‡Šæ”¾ä¸äº†image的内å˜ã€‚ this._img.src = BLANK; this._img = this._blob = null; }, _resize: function( img, cvs, width, height ) { var opts = this.options, naturalWidth = img.width, naturalHeight = img.height, orientation = this.getOrientation(), scale, w, h, x, y; // values that require 90 degree rotation if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) { // 交æ¢width, height的值。 width ^= height; height ^= width; width ^= height; } scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth, height / naturalHeight ); // ä¸å…许放大。 opts.allowMagnify || (scale = Math.min( 1, scale )); w = naturalWidth * scale; h = naturalHeight * scale; if ( opts.crop ) { cvs.width = width; cvs.height = height; } else { cvs.width = w; cvs.height = h; } x = (cvs.width - w) / 2; y = (cvs.height - h) / 2; opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation ); this._renderImageToCanvas( cvs, img, x, y, w, h ); }, _rotate2Orientaion: function( canvas, orientation ) { var width = canvas.width, height = canvas.height, ctx = canvas.getContext('2d'); switch ( orientation ) { case 5: case 6: case 7: case 8: canvas.width = height; canvas.height = width; break; } switch ( orientation ) { case 2: // horizontal flip ctx.translate( width, 0 ); ctx.scale( -1, 1 ); break; case 3: // 180 rotate left ctx.translate( width, height ); ctx.rotate( Math.PI ); break; case 4: // vertical flip ctx.translate( 0, height ); ctx.scale( 1, -1 ); break; case 5: // vertical flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.scale( 1, -1 ); break; case 6: // 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( 0, -height ); break; case 7: // horizontal flip + 90 rotate right ctx.rotate( 0.5 * Math.PI ); ctx.translate( width, -height ); ctx.scale( -1, 1 ); break; case 8: // 90 rotate left ctx.rotate( -0.5 * Math.PI ); ctx.translate( -width, 0 ); break; } }, // https://github.com/stomita/ios-imagefile-megapixel/ // blob/master/src/megapix-image.js _renderImageToCanvas: (function() { // å¦‚æžœä¸æ˜¯ios, ä¸éœ€è¦è¿™ä¹ˆå¤æ‚ï¼ if ( !Base.os.ios ) { return function( canvas ) { var args = Base.slice( arguments, 1 ), ctx = canvas.getContext('2d'); ctx.drawImage.apply( ctx, args ); }; } /** * Detecting vertical squash in loaded image. * Fixes a bug which squash image vertically while drawing into * canvas for some images. */ function detectVerticalSquash( img, iw, ih ) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), sy = 0, ey = ih, py = ih, data, alpha, ratio; canvas.width = 1; canvas.height = ih; ctx.drawImage( img, 0, 0 ); data = ctx.getImageData( 0, 0, 1, ih ).data; // search image edge pixel position in case // it is squashed vertically. while ( py > sy ) { alpha = data[ (py - 1) * 4 + 3 ]; if ( alpha === 0 ) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } ratio = (py / ih); return (ratio === 0) ? 1 : ratio; } // fix ie7 bug // http://stackoverflow.com/questions/11929099/ // html5-canvas-drawimage-ratio-bug-ios if ( Base.os.ios >= 7 ) { return function( canvas, img, x, y, w, h ) { var iw = img.naturalWidth, ih = img.naturalHeight, vertSquashRatio = detectVerticalSquash( img, iw, ih ); return canvas.getContext('2d').drawImage( img, 0, 0, iw * vertSquashRatio, ih * vertSquashRatio, x, y, w, h ); }; } /** * Detect subsampling in loaded image. * In iOS, larger images than 2M pixels may be * subsampled in rendering. */ function detectSubsampling( img ) { var iw = img.naturalWidth, ih = img.naturalHeight, canvas, ctx; // subsampling may happen overmegapixel image if ( iw * ih > 1024 * 1024 ) { canvas = document.createElement('canvas'); canvas.width = canvas.height = 1; ctx = canvas.getContext('2d'); ctx.drawImage( img, -iw + 1, 0 ); // subsampled image becomes half smaller in rendering size. // check alpha channel value to confirm image is covering // edge pixel or not. if alpha value is 0 // image is not covering, hence subsampled. return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0; } else { return false; } } return function( canvas, img, x, y, width, height ) { var iw = img.naturalWidth, ih = img.naturalHeight, ctx = canvas.getContext('2d'), subsampled = detectSubsampling( img ), doSquash = this.type === 'image/jpeg', d = 1024, sy = 0, dy = 0, tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx; if ( subsampled ) { iw /= 2; ih /= 2; } ctx.save(); tmpCanvas = document.createElement('canvas'); tmpCanvas.width = tmpCanvas.height = d; tmpCtx = tmpCanvas.getContext('2d'); vertSquashRatio = doSquash ? detectVerticalSquash( img, iw, ih ) : 1; dw = Math.ceil( d * width / iw ); dh = Math.ceil( d * height / ih / vertSquashRatio ); while ( sy < ih ) { sx = 0; dx = 0; while ( sx < iw ) { tmpCtx.clearRect( 0, 0, d, d ); tmpCtx.drawImage( img, -sx, -sy ); ctx.drawImage( tmpCanvas, 0, 0, d, d, x + dx, y + dy, dw, dh ); sx += d; dx += dw; } sy += d; dy += dh; } ctx.restore(); tmpCanvas = tmpCtx = null; }; })() }); }); /** * @fileOverview Transport * @todo 支æŒchunkedä¼ è¾“ï¼Œä¼˜åŠ¿ï¼š * å¯ä»¥å°†å¤§æ–‡ä»¶åˆ†æˆå°å—ï¼ŒæŒ¨ä¸ªä¼ è¾“ï¼Œå¯ä»¥æé«˜å¤§æ–‡ä»¶æˆåŠŸçŽ‡ï¼Œå½“å¤±è´¥çš„æ—¶å€™ï¼Œä¹Ÿåªéœ€è¦é‡ä¼ é‚£å°éƒ¨åˆ†ï¼Œ * 而ä¸éœ€è¦é‡å¤´å†ä¼ 一次。å¦å¤–æ–点ç»ä¼ 也需è¦ç”¨chunkedæ–¹å¼ã€‚ */ define('runtime/html5/transport',[ 'base', 'runtime/html5/runtime' ], function( Base, Html5Runtime ) { var noop = Base.noop, $ = Base.$; return Html5Runtime.register( 'Transport', { init: function() { this._status = 0; this._response = null; }, send: function() { var owner = this.owner, opts = this.options, xhr = this._initAjax(), blob = owner._blob, server = opts.server, formData, binary, fr; if ( opts.sendAsBinary ) { server += (/\?/.test( server ) ? '&' : '?') + $.param( owner._formData ); binary = blob.getSource(); } else { formData = new FormData(); $.each( owner._formData, function( k, v ) { formData.append( k, v ); }); formData.append( opts.fileVal, blob.getSource(), opts.filename || owner._formData.name || '' ); } if ( opts.withCredentials && 'withCredentials' in xhr ) { xhr.open( opts.method, server, true ); xhr.withCredentials = true; } else { xhr.open( opts.method, server ); } this._setRequestHeader( xhr, opts.headers ); if ( binary ) { // å¼ºåˆ¶è®¾ç½®æˆ content-type 为文件æµã€‚ xhr.overrideMimeType && xhr.overrideMimeType('application/octet-stream'); // android直接å‘é€blob会导致æœåŠ¡ç«¯æŽ¥æ”¶åˆ°çš„æ˜¯ç©ºæ–‡ä»¶ã€‚ // bug详情。 // https://code.google.com/p/android/issues/detail?id=39882 // 所以先用fileReader读å–出æ¥å†é€šè¿‡arraybuffer的方å¼å‘é€ã€‚ if ( Base.os.android ) { fr = new FileReader(); fr.onload = function() { xhr.send( this.result ); fr = fr.onload = null; }; fr.readAsArrayBuffer( binary ); } else { xhr.send( binary ); } } else { xhr.send( formData ); } }, getResponse: function() { return this._response; }, getResponseAsJson: function() { return this._parseJson( this._response ); }, getStatus: function() { return this._status; }, abort: function() { var xhr = this._xhr; if ( xhr ) { xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; xhr.abort(); this._xhr = xhr = null; } }, destroy: function() { this.abort(); }, _initAjax: function() { var me = this, xhr = new XMLHttpRequest(), opts = this.options; if ( opts.withCredentials && !('withCredentials' in xhr) && typeof XDomainRequest !== 'undefined' ) { xhr = new XDomainRequest(); } xhr.upload.onprogress = function( e ) { var percentage = 0; if ( e.lengthComputable ) { percentage = e.loaded / e.total; } return me.trigger( 'progress', percentage ); }; xhr.onreadystatechange = function() { if ( xhr.readyState !== 4 ) { return; } xhr.upload.onprogress = noop; xhr.onreadystatechange = noop; me._xhr = null; me._status = xhr.status; if ( xhr.status >= 200 && xhr.status < 300 ) { me._response = xhr.responseText; return me.trigger('load'); } else if ( xhr.status >= 500 && xhr.status < 600 ) { me._response = xhr.responseText; return me.trigger( 'error', 'server' ); } return me.trigger( 'error', me._status ? 'http' : 'abort' ); }; me._xhr = xhr; return xhr; }, _setRequestHeader: function( xhr, headers ) { $.each( headers, function( key, val ) { xhr.setRequestHeader( key, val ); }); }, _parseJson: function( str ) { var json; try { json = JSON.parse( str ); } catch ( ex ) { json = {}; } return json; } }); }); /** * @fileOverview Transport flash实现 */ define('runtime/html5/md5',[ 'runtime/html5/runtime' ], function( FlashRuntime ) { /* * Fastest md5 implementation around (JKM md5) * Credits: Joseph Myers * * @see http://www.myersdaily.org/joseph/javascript/md5-text.html * @see http://jsperf.com/md5-shootout/7 */ /* this function is much faster, so if possible we use it. Some IEs are the only ones I know of that need the idiotic second function, generated by an if clause. */ var add32 = function (a, b) { return (a + b) & 0xFFFFFFFF; }, cmn = function (q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }, ff = function (a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }, gg = function (a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }, hh = function (a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }, ii = function (a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }, md5cycle = function (x, k) { var a = x[0], b = x[1], c = x[2], d = x[3]; a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); }, /* there needs to be support for Unicode here, * unless we pretend that we can redefine the MD-5 * algorithm for multi-byte characters (perhaps * by adding every four 16-bit characters and * shortening the sum to 32 bits). Otherwise * I suggest performing MD-5 as if every character * was two bytes--e.g., 0040 0025 = @%--but then * how will an ordinary MD-5 sum be matched? * There is no way to standardize text to something * like UTF-8 before transformation; speed cost is * utterly prohibitive. The JavaScript standard * itself needs to look at this: it should start * providing access to strings as preformed UTF-8 * 8-bit unsigned value arrays. */ md5blk = function (s) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } return md5blks; }, md5blk_array = function (a) { var md5blks = [], i; /* Andy King said do it this way. */ for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); } return md5blks; }, md51 = function (s) { var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } s = s.substring(i - 64); length = s.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, md51_array = function (a) { var n = a.length, state = [1732584193, -271733879, -1732584194, 271733878], i, length, tail, tmp, lo, hi; for (i = 64; i <= n; i += 64) { md5cycle(state, md5blk_array(a.subarray(i - 64, i))); } // Not sure if it is a bug, however IE10 will always produce a sub array of length 1 // containing the last element of the parent array if the sub array specified starts // beyond the length of the parent array - weird. // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); length = a.length; tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (i = 0; i < length; i += 1) { tail[i >> 2] |= a[i] << ((i % 4) << 3); } tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Beware that the final length might not fit in 32 bits so we take care of that tmp = n * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(state, tail); return state; }, hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], rhex = function (n) { var s = '', j; for (j = 0; j < 4; j += 1) { s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; } return s; }, hex = function (x) { var i; for (i = 0; i < x.length; i += 1) { x[i] = rhex(x[i]); } return x.join(''); }, md5 = function (s) { return hex(md51(s)); }, //////////////////////////////////////////////////////////////////////////// /** * SparkMD5 OOP implementation. * * Use this class to perform an incremental md5, otherwise use the * static methods instead. */ SparkMD5 = function () { // call reset to init the instance this.reset(); }; // In some cases the fast add32 function cannot be used.. if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') { add32 = function (x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF), msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); }; } /** * Appends a string. * A conversion will be applied if an utf8 string is detected. * * @param {String} str The string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.append = function (str) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } // then append as binary this.appendBinary(str); return this; }; /** * Appends a binary string. * * @param {String} contents The binary string to be appended * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.appendBinary = function (contents) { this._buff += contents; this._length += contents.length; var length = this._buff.length, i; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk(this._buff.substring(i - 64, i))); } this._buff = this._buff.substr(i - 64); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.prototype.end = function (raw) { var buff = this._buff, length = buff.length, i, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; /** * Finish the final calculation based on the tail. * * @param {Array} tail The tail (will be modified) * @param {Number} length The length of the remaining buffer */ SparkMD5.prototype._finish = function (tail, length) { var i = length, tmp, lo, hi; tail[i >> 2] |= 0x80 << ((i % 4) << 3); if (i > 55) { md5cycle(this._state, tail); for (i = 0; i < 16; i += 1) { tail[i] = 0; } } // Do the final computation based on the tail and length // Beware that the final length may not fit in 32 bits so we take care of that tmp = this._length * 8; tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); lo = parseInt(tmp[2], 16); hi = parseInt(tmp[1], 16) || 0; tail[14] = lo; tail[15] = hi; md5cycle(this._state, tail); }; /** * Resets the internal state of the computation. * * @return {SparkMD5} The instance itself */ SparkMD5.prototype.reset = function () { this._buff = ""; this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.prototype.destroy = function () { delete this._state; delete this._buff; delete this._length; }; /** * Performs the md5 hash on a string. * A conversion will be applied if utf8 string is detected. * * @param {String} str The string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hash = function (str, raw) { // converts the string to utf8 bytes if necessary if (/[\u0080-\uFFFF]/.test(str)) { str = unescape(encodeURIComponent(str)); } var hash = md51(str); return !!raw ? hash : hex(hash); }; /** * Performs the md5 hash on a binary string. * * @param {String} content The binary string * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.hashBinary = function (content, raw) { var hash = md51(content); return !!raw ? hash : hex(hash); }; /** * SparkMD5 OOP implementation for array buffers. * * Use this class to perform an incremental md5 ONLY for array buffers. */ SparkMD5.ArrayBuffer = function () { // call reset to init the instance this.reset(); }; //////////////////////////////////////////////////////////////////////////// /** * Appends an array buffer. * * @param {ArrayBuffer} arr The array to be appended * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.append = function (arr) { // TODO: we could avoid the concatenation here but the algorithm would be more complex // if you find yourself needing extra performance, please make a PR. var buff = this._concatArrayBuffer(this._buff, arr), length = buff.length, i; this._length += arr.byteLength; for (i = 64; i <= length; i += 64) { md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i))); } // Avoids IE10 weirdness (documented above) this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0); return this; }; /** * Finishes the incremental computation, reseting the internal state and * returning the result. * Use the raw parameter to obtain the raw result instead of the hex one. * * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.prototype.end = function (raw) { var buff = this._buff, length = buff.length, tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i, ret; for (i = 0; i < length; i += 1) { tail[i >> 2] |= buff[i] << ((i % 4) << 3); } this._finish(tail, length); ret = !!raw ? this._state : hex(this._state); this.reset(); return ret; }; SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; /** * Resets the internal state of the computation. * * @return {SparkMD5.ArrayBuffer} The instance itself */ SparkMD5.ArrayBuffer.prototype.reset = function () { this._buff = new Uint8Array(0); this._length = 0; this._state = [1732584193, -271733879, -1732584194, 271733878]; return this; }; /** * Releases memory used by the incremental buffer and other aditional * resources. If you plan to use the instance again, use reset instead. */ SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; /** * Concats two array buffers, returning a new one. * * @param {ArrayBuffer} first The first array buffer * @param {ArrayBuffer} second The second array buffer * * @return {ArrayBuffer} The new array buffer */ SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) { var firstLength = first.length, result = new Uint8Array(firstLength + second.byteLength); result.set(first); result.set(new Uint8Array(second), firstLength); return result; }; /** * Performs the md5 hash on an array buffer. * * @param {ArrayBuffer} arr The array buffer * @param {Boolean} raw True to get the raw result, false to get the hex result * * @return {String|Array} The result */ SparkMD5.ArrayBuffer.hash = function (arr, raw) { var hash = md51_array(new Uint8Array(arr)); return !!raw ? hash : hex(hash); }; return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( file ) { var blob = file.getSource(), chunkSize = 2 * 1024 * 1024, chunks = Math.ceil( blob.size / chunkSize ), chunk = 0, owner = this.owner, spark = new SparkMD5.ArrayBuffer(), me = this, blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice, loadNext, fr; fr = new FileReader(); loadNext = function() { var start, end; start = chunk * chunkSize; end = Math.min( start + chunkSize, blob.size ); fr.onload = function( e ) { spark.append( e.target.result ); owner.trigger( 'progress', { total: file.size, loaded: end }); }; fr.onloadend = function() { fr.onloadend = fr.onload = null; if ( ++chunk < chunks ) { setTimeout( loadNext, 1 ); } else { setTimeout(function(){ owner.trigger('load'); me.result = spark.end(); loadNext = file = blob = spark = null; owner.trigger('complete'); }, 50 ); } }; fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) ); }; loadNext(); }, getResult: function() { return this.result; } }); }); /** * @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 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 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 Md5 flash实现 */ define('runtime/flash/md5',[ 'runtime/flash/runtime' ], function( FlashRuntime ) { return FlashRuntime.register( 'Md5', { init: function() { // do nothing. }, loadFromBlob: function( blob ) { return this.flashExec( 'Md5', 'loadFromBlob', blob.uid ); } }); }); /** * @fileOverview 完全版本。 */ define('preset/all',[ 'base', // widgets 'widgets/filednd', 'widgets/filepaste', 'widgets/filepicker', 'widgets/image', 'widgets/queue', 'widgets/runtime', 'widgets/upload', 'widgets/validator', 'widgets/md5', // runtimes // html5 'runtime/html5/blob', 'runtime/html5/dnd', 'runtime/html5/filepaste', 'runtime/html5/filepicker', 'runtime/html5/imagemeta/exif', 'runtime/html5/androidpatch', 'runtime/html5/image', 'runtime/html5/transport', 'runtime/html5/md5', // flash 'runtime/flash/filepicker', 'runtime/flash/image', 'runtime/flash/transport', 'runtime/flash/blob', 'runtime/flash/md5' ], function( Base ) { return Base; }); /** * @fileOverview 日志组件,主è¦ç”¨æ¥æ”¶é›†é”™è¯¯ä¿¡æ¯ï¼Œå¯ä»¥å¸®åŠ© webuploader 更好的定ä½é—®é¢˜å’Œå‘展。 * * å¦‚æžœæ‚¨ä¸æƒ³è¦å¯ç”¨æ¤åŠŸèƒ½ï¼Œè¯·åœ¨æ‰“åŒ…çš„æ—¶å€™åŽ»æŽ‰ log 模å—。 * * 或者å¯ä»¥åœ¨åˆå§‹åŒ–的时候通过 options.disableWidgets 属性ç¦ç”¨ã€‚ * * 如: * WebUploader.create({ * ... * * disableWidgets: 'log', * * ... * }) */ define('widgets/log',[ 'base', 'uploader', 'widgets/widget' ], function( Base, Uploader ) { var $ = Base.$, logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??', product = (location.hostname || location.host || 'protected').toLowerCase(), // åªé’ˆå¯¹ baidu 内部产å“用户åšç»Ÿè®¡åŠŸèƒ½ã€‚ enable = product && /baidu/i.exec(product), base; if (!enable) { return; } base = { dv: 3, master: 'webuploader', online: /test/.exec(product) ? 0 : 1, module: '', product: product, type: 0 }; function send(data) { var obj = $.extend({}, base, data), url = logUrl.replace(/^(.*)\?/, '$1' + $.param( obj )), image = new Image(); image.src = url; } return Uploader.register({ name: 'log', init: function() { var owner = this.owner, count = 0, size = 0; owner .on('error', function(code) { send({ type: 2, c_error_code: code }); }) .on('uploadError', function(file, reason) { send({ type: 2, c_error_code: 'UPLOAD_ERROR', c_reason: '' + reason }); }) .on('uploadComplete', function(file) { count++; size += file.size; }). on('uploadFinished', function() { send({ c_count: count, c_size: size }); count = size = 0; }); send({ c_usage: 1 }); } }); }); /** * @fileOverview Uploaderä¸Šä¼ ç±» */ define('webuploader',[ 'preset/all', 'widgets/log' ], function( preset ) { return preset; }); return require('webuploader'); });