[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.idea\n/pages\n/node_modules\n/doc\nserver/*\n!server/preview.php\n!server/fileupload.php\n!server/fileupload2.php\n!server/crossdomain.xml\n.tags\n.tags_sorted_by_file\n/flash/bin-debug\n/flash/html-template\n/.grunt\n/jekyll/_site/\n/dist/*.zip\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n    \"maxerr\"        : 50,\n    \"bitwise\"       : false,\n    \"camelcase\"     : true,\n    \"curly\"         : true,\n    \"eqeqeq\"        : true,\n    \"forin\"         : false,\n    \"immed\"         : false,\n    \"indent\"        : 4,\n    \"latedef\"       : false,\n    \"newcap\"        : false,\n    \"noarg\"         : false,\n    \"noempty\"       : true,\n    \"nonew\"         : false,\n    \"plusplus\"      : false,\n    \"quotmark\"      : \"single\",\n\n    \"undef\"         : true,\n    \"unused\"        : true,\n    \"strict\"        : false,\n    \"trailing\"      : false,\n    \"maxparams\"     : false,\n    \"maxdepth\"      : false,\n    \"maxstatements\" : false,\n    \"maxcomplexity\" : false,\n    \"maxlen\"        : 80,\n    \"asi\"           : false,\n    \"boss\"          : false,\n    \"debug\"         : false,\n    \"eqnull\"        : true,\n    \"es5\"           : false,\n    \"esnext\"        : false,\n\n\n    \"evil\"          : false,\n    \"expr\"          : true,\n    \"funcscope\"     : false,\n    \"globalstrict\"  : false,\n    \"iterator\"      : false,\n    \"lastsemic\"     : false,\n    \"laxbreak\"      : false,\n    \"laxcomma\"      : false,\n    \"loopfunc\"      : false,\n    \"multistr\"      : false,\n    \"proto\"         : false,\n    \"scripturl\"     : false,\n    \"smarttabs\"     : false,\n    \"shadow\"        : false,\n    \"sub\"           : false,\n    \"supernew\"      : false,\n    \"validthis\"     : false,\n    \"browser\"       : true,\n    \"couch\"         : false,\n    \"devel\"         : true,\n    \"dojo\"          : false,\n    \"jquery\"        : false,\n    \"mootools\"      : false,\n    \"node\"          : false,\n    \"nonstandard\"   : false,\n    \"prototypejs\"   : false,\n    \"rhino\"         : false,\n    \"worker\"        : false,\n    \"wsh\"           : false,\n    \"yui\"           : false,\n    \"nomen\"         : false,\n    \"onevar\"        : true,\n    \"passfail\"      : false,\n    \"white\"         : true,\n    \"predef\"        : [ \"require\", \"define\", \"module\", \"URL\", \"XDomainRequest\", \"ActiveXObject\" ],\n    \"withstmt\"    : false,\n    \"zepto\" : true,\n    \"assignvarfirst\" : true,\n    \"varnewline\": true,\n    \"strictchain\": true,\n    \"strictindent\": true,\n    \"operatorend\": true,\n    \"strictlinebreak\": false,\n    \"assignthisto\"    : false,\n    \"assignexceptionto\"    : false,\n    \"strictcomment\"    : true,\n    \"fileoverview\"    : true,\n    \"novoid\"    : true\n}"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\n\nnode_js:\n  - 0.10\n\nbefore_script:\n    - npm install -g grunt-cli\n\nscript:\n    - grunt test"
  },
  {
    "path": "Gruntfile.js",
    "content": "module.exports = function(grunt) {\n    'use strict';\n\n    grunt.initConfig({\n        pkg: grunt.file.readJSON('package.json'),\n\n        build: {\n            options: {\n                banner: '/*! WebUploader <%= pkg.version %> */\\n'\n            },\n\n            all: {\n                name: 'webuploader',\n                dest: \"dist/webuploader.js\",\n\n                // 在没有jquery类似的库的前提下可以设置builtin,去除强行依赖。\n                builtin: {\n                    dollar: false,\n                    promise: false\n                }\n            },\n\n            nolog: {\n                preset: 'all',\n                dest: \"dist/webuploader.nolog.js\",\n\n                // 在没有jquery类似的库的前提下可以设置builtin,去除强行依赖。\n                builtin: {\n                    dollar: false,\n                    promise: false\n                }\n            },\n\n            flashonly: {\n                preset: 'flashonly',\n                dest: \"dist/webuploader.flashonly.js\",\n            },\n\n            html5only: {\n                preset: 'html5only',\n                dest: \"dist/webuploader.html5only.js\",\n            },\n\n            html5nodepend: {\n                preset: 'html5only',\n                dest: \"dist/webuploader.html5nodepend.js\",\n                builtin: {\n                    dollar: true,\n                    promise: true\n                },\n            },\n\n            withoutimage: {\n                preset: 'withoutimage',\n                dest: \"dist/webuploader.noimage.js\",\n            },\n\n            // 自己配置的实例\n            // glob语法。\n            // 移动端方案，请搭配 zepto 使用。\n            custom: {\n                preset: \"custom\",\n                cwd: \"src\",\n                builtin: {\n                    promise: true\n                },\n                src: [\n                    'widgets/filepicker.js',\n                    'widgets/image.js',\n                    'widgets/queue.js',\n                    'widgets/runtime.js',\n                    'widgets/upload.js',\n                    'widgets/log.js',\n\n                    'runtime/html5/blob.js',\n                    'runtime/html5/filepicker.js',\n                    'runtime/html5/imagemeta/exif.js',\n                    'runtime/html5/image.js',\n                    'runtime/html5/androidpatch.js',\n                    'runtime/html5/transport.js'\n                ],\n                dest: \"dist/webuploader.custom.js\"\n            },\n\n            fis: {\n                name: 'webuploader',\n                dest: \"dist/webuploader.fis.js\",\n\n                fis: true,\n\n                // 在没有jquery类似的库的前提下可以设置builtin,去除强行依赖。\n                builtin: {\n                    dollar: false,\n                    promise: false\n                }\n            }\n        },\n\n        uglify: {\n            options: {\n                mangle: true,\n                banner: '/* WebUploader <%= pkg.version %> */'\n            },\n\n            static_mapping: {\n                files: [\n                    {\n                        src: 'dist/webuploader.js',\n                        dest: 'dist/webuploader.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.nolog.js',\n                        dest: 'dist/webuploader.nolog.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.flashonly.js',\n                        dest: 'dist/webuploader.flashonly.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.html5only.js',\n                        dest: 'dist/webuploader.html5only.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.html5nodepend.js',\n                        dest: 'dist/webuploader.html5nodepend.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.noimage.js',\n                        dest: 'dist/webuploader.noimage.min.js'\n                    },\n\n                    {\n                        src: 'dist/webuploader.custom.js',\n                        dest: 'dist/webuploader.custom.min.js'\n                    }\n                ]\n            }\n        },\n\n        copy: {\n            jekyll: {\n                src: 'dist/webuploader.js',\n                dest: 'jekyll/js/webuploader.js',\n            },\n\n            css: {\n                src: 'css/webuploader.css',\n                dest: 'dist/webuploader.css'\n            },\n\n            css2: {\n                src: 'css/webuploader.css',\n                dest: 'jekyll/css/webuploader.css'\n            }\n        },\n\n        watch: {\n            options: {\n                debounceDelay: 250\n            },\n\n            all: {\n                files: ['src/**/*.js', 'Gruntfile.js'],\n                tasks: ['default'],\n            },\n\n            dist: {\n                files: ['src/**/*.js', 'Gruntfile.js'],\n                tasks: ['dist'],\n            },\n\n            doc: {\n                files: ['src/**/*.js', 'Gruntfile.js', 'build/docTpl/**/*'],\n                tasks: ['doc'],\n            },\n\n\n            dev: {\n                files: 'src/**/*.js',\n                tasks: 'build:all'\n            },\n\n            flashonly: {\n                files: 'src/**/*.js',\n                tasks: 'build:flashonly'\n            }\n        },\n\n        jsbint: {\n            options: {\n                jshintrc: '.jshintrc'\n            },\n\n            all: [\n                'src/**/*.js',\n                '!src/runtime/html5/jpegencoder.js'\n            ]\n        },\n\n        size: {\n            dist: {\n                cwd: 'dist/',\n                src: '*.js'\n            },\n\n            src: {\n                src: 'src/**/*.js'\n            }\n        },\n\n        doc: {\n            options: {\n                cwd: './src/',\n                files: [\n                    'uploader.js',\n                    'base.js',\n                    'mediator.js',\n                    '**/*.js'\n                ],\n                tplDir: './build/docTpl',\n                theme: 'gmu',\n                outputDir: './jekyll/doc',\n                title: 'WebUploader API文档'\n            }\n        },\n\n        jekyll: {\n            options: { // Universal options\n                src: 'jekyll'\n            },\n            dist: { // Target\n                options: { // Target options\n                    dest: 'pages',\n                    config: 'jekyll/_config.yml'\n                }\n            }\n        },\n\n        'gh-pages': {\n            options: {\n                message: '程序自动提交，源码请查看tree/master/jekyll目录',\n                base: 'pages',\n                repo: 'https://github.com/fex-team/webuploader.git'\n            },\n            src: ['**/*']\n        },\n\n        qunit: {\n            all: {\n                options: {\n                    urls: [\n                        'http://0.0.0.0:8000/test/index.html'\n                    ]\n                }\n            }\n        },\n\n        connect: {\n            server: {\n                options: {\n                    port: 8000,\n                    base: '.'\n                }\n            }\n            /*,\n\n            keepalive: {\n                options: {\n                    port: 8000,\n                    base: '.',\n                    keepalive: true\n                }\n            }*/\n        },\n    });\n\n    require('load-grunt-tasks')(grunt);\n    grunt.loadTasks('build/tasks');    // 加载build目录下的所有task\n\n    // Default task(s).\n    grunt.registerTask('default', ['jsbint:all', 'dist']);\n    grunt.registerTask('dist', ['build', 'uglify', 'copy']);\n    grunt.registerTask('deploy', ['doc', 'jekyll', 'gh-pages']);\n    grunt.registerTask('test', ['connect', 'qunit']);\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014, FEX@Baidu Inc.\nAll rights reserved.\n\nRedistribution and use of this software in source and binary forms, with or\nwithout modification, are permitted provided that the following conditions\nare met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\nNeither the name of Baidu Inc. nor the names of its contributors may be used\nto endorse or promote products derived from this software without specific\nprior written permission of Baidu Inc.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "README.md",
    "content": "不再对使用问题进行答复，如果有希望的功能改进或者bugfix可以提交PR\n==================================================\n\n\n# WebUploader 文件上传 [![Build Status](https://secure.travis-ci.org/fex-team/webuploader.png?branch=master)](http://travis-ci.org/fex-team/webuploader) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)\n\nWebUploader是一个简单的以HTML5为主，FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势，同时又不摒弃主流IE浏览器，延用原来的FLASH运行时，兼容IE6+，Andorid 4+，IOS 6+。两套运行时，同样的调用方式，可供用户任意选用。\n\n支持大文件分片并发上传，极大的提高了文件上传效率。\n\n- 官网： http://fex.baidu.com/webuploader/\n- ISSUES：https://github.com/fex-team/webuploader/issues\n\n## 支持\n\n**代码肯定存在很多不足和需要优化的地方，欢迎大家提交 [pr](https://help.github.com/articles/using-pull-requests)。**感谢**以下代码贡献者, 排名不分先后。**\n\n[@zensh](https://github.com/zensh)，[@ushelp](https://github.com/ushelp)，[@duanlixin](https://github.com/duanlixin)。\n\n## 特性\n\n### 分片、并发\n分片与并发结合，将一个大文件分割成多块，并发上传，极大地提高大文件的上传速度。\n\n当网络问题导致传输错误时，只需要重传出错分片，而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。\n\n### 预览、压缩\n\n支持常用图片格式jpg,jpeg,gif,bmp,png预览与压缩，节省网络数据传输。\n\n解析jpeg中的meta信息，对于各种orientation做了正确的处理，同时压缩后上传保留图片的所有原始meta数据。\n\n### 多途径添加文件\n支持文件多选，类型过滤，拖拽(文件&文件夹)，图片粘贴功能。\n\n粘贴功能主要体现在当有图片数据在剪切板中时（截屏工具如QQ(Ctrl + ALT + A), 网页中右击图片点击复制），Ctrl + V便可添加此图片文件。\n\n### HTML5 & FLASH\n兼容主流浏览器，接口一致，实现了两套运行时支持，用户无需关心内部用了什么内核。\n\n同时Flash部分没有做任何UI相关的工作，方便不关心flash的用户扩展和自定义业务需求。\n\n### MD5秒传\n当文件体积大、量比较多时，支持上传前做文件md5值验证，一致则可直接跳过。\n\n如果服务端与前端统一修改算法，取段md5，可大大提升验证性能，耗时在20ms左右。\n\n### 易扩展、可拆分\n采用可拆分机制, 将各个功能独立成了小组件，可自由搭配。\n\n采用AMD规范组织代码，清晰明了，方便高级玩家扩展。\n"
  },
  {
    "path": "_draft/demo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示 - 秒传</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../css/webuploader.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div id=\"container\">\n            <!--头部，相册选择和格式选择-->\n\n            <div id=\"uploader\">\n                <div class=\"queueList\">\n                    <div id=\"dndArea\" class=\"placeholder\">\n                        <div id=\"filePicker\">点击选择文件</div>\n                        <p>或将照片拖到这里，单次最多可选300张</p>\n                    </div>\n                </div>\n                <div class=\"statusBar\" style=\"display:none;\">\n                    <div class=\"progress\">\n                        <span class=\"text\">0%</span>\n                        <span class=\"percentage\"></span>\n                    </div><div class=\"info\"></div>\n                    <div class=\"btns\">\n                        <div id=\"filePicker2\">继续添加</div><div class=\"uploadBtn\">开始上传</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script type=\"text/javascript\" src=\"./jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../dist/webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./demo.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "_draft/demo.js",
    "content": "(function( $ ){\n    // 当domReady的时候开始初始化\n    $(function() {\n        var $wrap = $('#uploader'),\n\n            // 图片容器\n            $queue = $( '<ul class=\"filelist\"></ul>' )\n                .appendTo( $wrap.find( '.queueList' ) ),\n\n            // 状态栏，包括进度和控制按钮\n            $statusBar = $wrap.find( '.statusBar' ),\n\n            // 文件总体选择信息。\n            $info = $statusBar.find( '.info' ),\n\n            // 上传按钮\n            $upload = $wrap.find( '.uploadBtn' ),\n\n            // 没选择文件之前的内容。\n            $placeHolder = $wrap.find( '.placeholder' ),\n\n            $progress = $statusBar.find( '.progress' ).hide(),\n\n            // 添加的文件数量\n            fileCount = 0,\n\n            // 添加的文件总大小\n            fileSize = 0,\n\n            // 优化retina, 在retina下这个值是2\n            ratio = window.devicePixelRatio || 1,\n\n            // 缩略图大小\n            thumbnailWidth = 110 * ratio,\n            thumbnailHeight = 110 * ratio,\n\n            // 可能有pedding, ready, uploading, confirm, done.\n            state = 'pedding',\n\n            // 所有文件的进度信息，key为file id\n            percentages = {},\n\n            // WebUploader实例\n            uploader;\n\n        // 实例化\n        uploader = WebUploader.create({\n            pick: '#filePicker',\n            dnd: '#dndArea',\n            paste: '#uploader',\n            server: '../server/fileupload.php',\n            accept: '',\n            fileNumLimit: 300,\n            fileSizeLimit: 0,\n            fileSingleSizeLimit: 512 * 1024 * 1024    // 512 M\n        });\n\n        // 添加“添加文件”的按钮，\n        uploader.addButton('#filePicker2');\n\n        // 当有文件添加进来时执行，负责view的创建\n        function addFile( file ) {\n            var $li = $( '<li id=\"' + file.id + '\">' +\n                    '<p class=\"title\">' + file.name + '</p>' +\n                    '<p class=\"imgWrap\"></p>'+\n                    '<p class=\"progress\"><span></span></p>' +\n                    '</li>' ),\n\n                $btns = $('<div class=\"file-panel\">' +\n                    '<span class=\"cancel\">删除</span>' +\n                    '<span class=\"rotateRight\">向右旋转</span>' +\n                    '<span class=\"rotateLeft\">向左旋转</span></div>').appendTo( $li ),\n                $prgress = $li.find('p.progress span'),\n                $wrap = $li.find( 'p.imgWrap' ),\n                $info = $('<p class=\"error\"></p>'),\n\n                showError = function( code ) {\n                    switch( code ) {\n                        case 'exceed_size':\n                            text = '文件大小超出';\n                            break;\n\n                        default:\n                            text = '上传失败，请重试';\n                            break;\n                    }\n\n                    $info.text( text ).appendTo( $li );\n                };\n\n            if ( file.getStatus() === 'invalid' ) {\n                showError( file.statusText );\n            } else {\n                // @todo lazyload\n                $wrap.text( '预览中' );\n                uploader.makeThumb( file, function( error, src ) {\n                    if ( error ) {\n                        $wrap.text( '不能预览' );\n                        return;\n                    }\n\n                    var img = $('<img src=\"'+src+'\">');\n                    $wrap.empty().append( img );\n                }, thumbnailWidth, thumbnailHeight );\n\n                percentages[ file.id ] = [ file.size, 0 ];\n                file.ratation = 0;\n            }\n\n            file.on('statuschange', function( cur, prev ) {\n                if ( prev === 'progress' ) {\n                    $prgress.hide().width(0);\n                } else if ( prev === 'queued' ) {\n                    $li.off( 'mouseenter mouseleave' );\n                    $btns.remove();\n                }\n\n                // 成功\n                if ( cur === 'error' || cur === 'invalid' ) {\n                    showError( file.statusText );\n                    percentages[ file.id ][ 1 ] = 1;\n                } else if ( cur === 'queued' ) {\n                    percentages[ file.id ][ 1 ] = 0;\n                } else if ( cur === 'progress' ) {\n                    $info.remove();\n                    $prgress.css('display', 'block');\n                }\n\n                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );\n            });\n\n            $li.on( 'mouseenter', function() {\n                $btns.stop().animate({height: 30});\n            });\n\n            $li.on( 'mouseleave', function() {\n                $btns.stop().animate({height: 0});\n            });\n\n            $btns.on( 'click', 'span', function() {\n                var index = $(this).index(),\n                    deg;\n\n                switch ( index ) {\n                    case 0:\n                        uploader.removeFile( file );\n                        return;\n\n                    case 1:\n                        file.ratation += 90;\n                        break;\n\n                    case 2:\n                        file.ratation -= 90;\n                        break;\n                }\n\n                // -webkit-transform: rotate(90deg);\n                index && (deg = 'rotate(' + file.ratation + 'deg)', $wrap.css({\n                    '-webkit-transform': deg,\n                    '-mos-transform': deg,\n                    '-o-transform': deg,\n                    'transform': deg\n                }));\n            });\n\n            $li.appendTo( $queue );\n        }\n\n        // 负责view的销毁\n        function removeFile( file ) {\n            var $li = $('#'+file.id);\n\n            delete percentages[ file.id ];\n            updateTotalProgress();\n            $li.off().find('.file-panel').off().end().remove();\n        }\n\n        function updateTotalProgress() {\n            var loaded = 0,\n                total = 0,\n                spans = $progress.children(),\n                percent;\n\n            $.each( percentages, function( k, v ) {\n                total += v[ 0 ];\n                loaded += v[ 0 ] * v[ 1 ];\n            } );\n\n            percent = total ? loaded / total : 0;\n\n            spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );\n            spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );\n            updateStatus();\n        }\n\n        function updateStatus() {\n            var text = '', stats;\n\n            if ( state === 'ready' ) {\n                text = '选中' + fileCount + '张图片，共' +\n                        uploader.formatSize( fileSize ) + '。';\n            } else if ( state === 'confirm' ) {\n                stats = uploader.getStats();\n                if ( stats.uploadFailNum ) {\n                    text = '已成功上传' + stats.successNum+ '张照片至XX相册，'+\n                        stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n                }\n\n            } else {\n                stats = uploader.getStats();\n                text = '共' + fileCount + '张（' +\n                        uploader.formatSize( fileSize )  +\n                        '），已上传' + stats.successNum + '张';\n\n                if ( stats.uploadFailNum ) {\n                    text += '，失败' + stats.uploadFailNum + '张';\n                }\n            }\n\n            $info.html( text );\n        }\n\n        function setState( val ) {\n            var file, stats;\n\n            if ( val === state ) {\n                return;\n            }\n\n            $upload.removeClass( 'state-' + state );\n            $upload.addClass( 'state-' + val );\n            state = val;\n\n            switch ( state ) {\n                case 'pedding':\n                    $placeHolder.show();\n                    $queue.hide();\n                    $statusBar.hide();\n                    break;\n\n                case 'ready':\n                    $placeHolder.hide();\n                    $( '#filePicker2' ).show();\n                    $queue.show();\n                    $statusBar.show();\n                    break;\n\n                case 'uploading':\n                    $( '#filePicker2' ).hide();\n                    $progress.show();\n                    $upload.text( '暂停上传' );\n                    break;\n\n                case 'paused':\n                    $progress.show();\n                    $upload.text( '继续上传' );\n                    break;\n\n                case 'confirm':\n                    $progress.hide();\n                    $upload.text( '开始上传' ).addClass( 'disabled' );\n\n                    stats = uploader.getStats();\n                    if ( stats.successNum && !stats.uploadFailNum ) {\n                        setState( 'finish' );\n                        return;\n                    }\n                    break;\n                case 'finish':\n                    stats = uploader.getStats();\n                    if ( stats.successNum ) {\n                        alert( '上传成功' );\n                    } else {\n                        // 没有成功的图片，重设\n                        state = 'done';\n                        location.reload();\n                    }\n                    break;\n            }\n\n            updateStatus();\n        }\n\n        uploader.onUploadBeforeSend = function( file, data ) {\n            data.md5 = file.md5 || '';\n        };\n\n        uploader.onUploadProgress = function( file, percentage ) {\n            var $li = $('#'+file.id),\n                $percent = $li.find('.progress span');\n\n            $percent.css( 'width', percentage * 100 + '%' );\n            percentages[ file.id ][ 1 ] = percentage;\n            updateTotalProgress();\n        };\n\n        uploader.onFileQueued = function( file ) {\n            var start = Date.now();\n\n            fileCount++;\n            fileSize += file.size;\n\n            if ( fileCount === 1 ) {\n                $placeHolder.hide();\n                $statusBar.show();\n            }\n\n            Md5File( file, function( value ) {\n                var $li = $('#'+file.id);\n                file.md5 = value;\n\n                $li.append( '<p class=\"log\">md5:' + ((Date.now() - start)/1000).toFixed(2) + '秒</p>')\n            });\n\n            addFile( file );\n            setState( 'ready' );\n            updateTotalProgress();\n        };\n\n        uploader.onFileDequeued = function( file ) {\n            fileCount--;\n            fileSize -= file.size;\n\n            if ( !fileCount ) {\n                setState( 'pedding' );\n            }\n\n            removeFile( file );\n            updateTotalProgress();\n\n        };\n\n        uploader.on( 'all', function( type ) {\n            var stats;\n            switch( type ) {\n                case 'uploadFinished':\n                    setState( 'confirm' );\n                    break;\n\n                case 'startUpload':\n                    setState( 'uploading' );\n                    break;\n\n                case 'stopUpload':\n                    setState( 'paused' );\n                    break;\n\n            }\n        });\n\n        uploader.onError = function( code ) {\n            alert( 'Eroor: ' + code );\n        };\n\n        uploader.onUploadChunkcontinue = function( file, ret ) {\n            if ( ret.exist ) {\n                var $li = $( '#'+file.id );\n\n                $li.append( '<p class=\"log\">跳过' + uploader.formatSize( file.size - file.loaded ) + '</p>' );\n                return false;\n            }\n        };\n\n        $upload.on('click', function() {\n            if ( $(this).hasClass( 'disabled' ) ) {\n                return false;\n            }\n\n            if ( state === 'ready' ) {\n                uploader.upload();\n            } else if ( state === 'paused' ) {\n                uploader.upload();\n            } else if ( state === 'uploading' ) {\n                uploader.stop();\n            }\n        });\n\n        $info.on( 'click', '.retry', function() {\n            uploader.retry();\n        } );\n\n        $info.on( 'click', '.ignore', function() {\n            alert( 'todo' );\n        } );\n\n        $upload.addClass( 'state-' + state );\n        updateTotalProgress();\n\n        var Md5File = (function(){\n\n\n            return function( file, cb ) {\n                var worker = new Worker( 'md5File.js' );\n                worker.onmessage = function( e ) {\n                    cb( e.data, file );\n                    worker.terminate();\n                };\n\n                worker.postMessage( file.source );\n            }\n        })();\n    });\n\n})( jQuery );"
  },
  {
    "path": "_draft/demo2.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示 - 秒传</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../css/webuploader.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div id=\"container\">\n            <!--头部，相册选择和格式选择-->\n\n            <div id=\"uploader\">\n                <div class=\"queueList\">\n                    <div id=\"dndArea\" class=\"placeholder\">\n                        <div id=\"filePicker\">点击选择文件</div>\n                        <p>或将照片拖到这里，单次最多可选300张</p>\n                    </div>\n                </div>\n                <div class=\"statusBar\" style=\"display:none;\">\n                    <div class=\"progress\">\n                        <span class=\"text\">0%</span>\n                        <span class=\"percentage\"></span>\n                    </div><div class=\"info\"></div>\n                    <div class=\"btns\">\n                        <div id=\"filePicker2\">继续添加</div><div class=\"uploadBtn\">开始上传</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script type=\"text/javascript\" src=\"./jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../dist/webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./demo2.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "_draft/demo2.js",
    "content": "(function( $ ){\n    // 当domReady的时候开始初始化\n    $(function() {\n        var $wrap = $('#uploader'),\n\n            // 图片容器\n            $queue = $( '<ul class=\"filelist\"></ul>' )\n                .appendTo( $wrap.find( '.queueList' ) ),\n\n            // 状态栏，包括进度和控制按钮\n            $statusBar = $wrap.find( '.statusBar' ),\n\n            // 文件总体选择信息。\n            $info = $statusBar.find( '.info' ),\n\n            // 上传按钮\n            $upload = $wrap.find( '.uploadBtn' ),\n\n            // 没选择文件之前的内容。\n            $placeHolder = $wrap.find( '.placeholder' ),\n\n            $progress = $statusBar.find( '.progress' ).hide(),\n\n            // 添加的文件数量\n            fileCount = 0,\n\n            // 添加的文件总大小\n            fileSize = 0,\n\n            // 优化retina, 在retina下这个值是2\n            ratio = window.devicePixelRatio || 1,\n\n            // 缩略图大小\n            thumbnailWidth = 110 * ratio,\n            thumbnailHeight = 110 * ratio,\n\n            // 可能有pedding, ready, uploading, confirm, done.\n            state = 'pedding',\n\n            // 所有文件的进度信息，key为file id\n            percentages = {},\n\n            // WebUploader实例\n            uploader;\n\n        // 实例化\n        uploader = WebUploader.create({\n            pick: '#filePicker',\n            dnd: '#dndArea',\n            paste: '#uploader',\n            server: '../server/fileupload2.php',\n            accept: '',\n            fileNumLimit: 300,\n            fileSizeLimit: 0,\n            fileSingleSizeLimit: 512 * 1024 * 1024    // 512 M\n        });\n\n        // 添加“添加文件”的按钮，\n        uploader.addButton('#filePicker2');\n\n        // 当有文件添加进来时执行，负责view的创建\n        function addFile( file ) {\n            var $li = $( '<li id=\"' + file.id + '\">' +\n                    '<p class=\"title\">' + file.name + '</p>' +\n                    '<p class=\"imgWrap\"></p>'+\n                    '<p class=\"progress\"><span></span></p>' +\n                    '</li>' ),\n\n                $btns = $('<div class=\"file-panel\">' +\n                    '<span class=\"cancel\">删除</span>' +\n                    '<span class=\"rotateRight\">向右旋转</span>' +\n                    '<span class=\"rotateLeft\">向左旋转</span></div>').appendTo( $li ),\n                $prgress = $li.find('p.progress span'),\n                $wrap = $li.find( 'p.imgWrap' ),\n                $info = $('<p class=\"error\"></p>'),\n\n                showError = function( code ) {\n                    switch( code ) {\n                        case 'exceed_size':\n                            text = '文件大小超出';\n                            break;\n\n                        default:\n                            text = '上传失败，请重试';\n                            break;\n                    }\n\n                    $info.text( text ).appendTo( $li );\n                };\n\n            if ( file.getStatus() === 'invalid' ) {\n                showError( file.statusText );\n            } else {\n                // @todo lazyload\n                $wrap.text( '预览中' );\n                uploader.makeThumb( file, function( error, src ) {\n                    if ( error ) {\n                        $wrap.text( '不能预览' );\n                        return;\n                    }\n\n                    var img = $('<img src=\"'+src+'\">');\n                    $wrap.empty().append( img );\n                }, thumbnailWidth, thumbnailHeight );\n\n                percentages[ file.id ] = [ file.size, 0 ];\n                file.ratation = 0;\n            }\n\n            file.on('statuschange', function( cur, prev ) {\n                if ( prev === 'progress' ) {\n                    $prgress.hide().width(0);\n                } else if ( prev === 'queued' ) {\n                    $li.off( 'mouseenter mouseleave' );\n                    $btns.remove();\n                }\n\n                // 成功\n                if ( cur === 'error' || cur === 'invalid' ) {\n                    showError( file.statusText );\n                    percentages[ file.id ][ 1 ] = 1;\n                } else if ( cur === 'queued' ) {\n                    percentages[ file.id ][ 1 ] = 0;\n                } else if ( cur === 'progress' ) {\n                    $info.remove();\n                    $prgress.css('display', 'block');\n                }\n\n                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );\n            });\n\n            $li.on( 'mouseenter', function() {\n                $btns.stop().animate({height: 30});\n            });\n\n            $li.on( 'mouseleave', function() {\n                $btns.stop().animate({height: 0});\n            });\n\n            $btns.on( 'click', 'span', function() {\n                var index = $(this).index(),\n                    deg;\n\n                switch ( index ) {\n                    case 0:\n                        uploader.removeFile( file );\n                        return;\n\n                    case 1:\n                        file.ratation += 90;\n                        break;\n\n                    case 2:\n                        file.ratation -= 90;\n                        break;\n                }\n\n                // -webkit-transform: rotate(90deg);\n                index && (deg = 'rotate(' + file.ratation + 'deg)', $wrap.css({\n                    '-webkit-transform': deg,\n                    '-mos-transform': deg,\n                    '-o-transform': deg,\n                    'transform': deg\n                }));\n            });\n\n            $li.appendTo( $queue );\n        }\n\n        // 负责view的销毁\n        function removeFile( file ) {\n            var $li = $('#'+file.id);\n\n            delete percentages[ file.id ];\n            updateTotalProgress();\n            $li.off().find('.file-panel').off().end().remove();\n        }\n\n        function updateTotalProgress() {\n            var loaded = 0,\n                total = 0,\n                spans = $progress.children(),\n                percent;\n\n            $.each( percentages, function( k, v ) {\n                total += v[ 0 ];\n                loaded += v[ 0 ] * v[ 1 ];\n            } );\n\n            percent = total ? loaded / total : 0;\n\n            spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );\n            spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );\n            updateStatus();\n        }\n\n        function updateStatus() {\n            var text = '', stats;\n\n            if ( state === 'ready' ) {\n                text = '选中' + fileCount + '张图片，共' +\n                        uploader.formatSize( fileSize ) + '。';\n            } else if ( state === 'confirm' ) {\n                stats = uploader.getStats();\n                if ( stats.uploadFailNum ) {\n                    text = '已成功上传' + stats.successNum+ '张照片至XX相册，'+\n                        stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n                }\n\n            } else {\n                stats = uploader.getStats();\n                text = '共' + fileCount + '张（' +\n                        uploader.formatSize( fileSize )  +\n                        '），已上传' + stats.successNum + '张';\n\n                if ( stats.uploadFailNum ) {\n                    text += '，失败' + stats.uploadFailNum + '张';\n                }\n            }\n\n            $info.html( text );\n        }\n\n        function setState( val ) {\n            var file, stats;\n\n            if ( val === state ) {\n                return;\n            }\n\n            $upload.removeClass( 'state-' + state );\n            $upload.addClass( 'state-' + val );\n            state = val;\n\n            switch ( state ) {\n                case 'pedding':\n                    $placeHolder.show();\n                    $queue.hide();\n                    $statusBar.hide();\n                    break;\n\n                case 'ready':\n                    $placeHolder.hide();\n                    $( '#filePicker2' ).show();\n                    $queue.show();\n                    $statusBar.show();\n                    break;\n\n                case 'uploading':\n                    $( '#filePicker2' ).hide();\n                    $progress.show();\n                    $upload.text( '暂停上传' );\n                    break;\n\n                case 'paused':\n                    $progress.show();\n                    $upload.text( '继续上传' );\n                    break;\n\n                case 'confirm':\n                    $progress.hide();\n                    $upload.text( '开始上传' ).addClass( 'disabled' );\n\n                    stats = uploader.getStats();\n                    if ( stats.successNum && !stats.uploadFailNum ) {\n                        setState( 'finish' );\n                        return;\n                    }\n                    break;\n                case 'finish':\n                    stats = uploader.getStats();\n                    if ( stats.successNum ) {\n                        alert( '上传成功' );\n                    } else {\n                        // 没有成功的图片，重设\n                        state = 'done';\n                        location.reload();\n                    }\n                    break;\n            }\n\n            updateStatus();\n        }\n\n        uploader.onUploadBeforeSend = function( file, data ) {\n            data.md5 = file.md5 || '';\n        };\n\n        uploader.onUploadProgress = function( file, percentage ) {\n            var $li = $('#'+file.id),\n                $percent = $li.find('.progress span');\n\n            $percent.css( 'width', percentage * 100 + '%' );\n            percentages[ file.id ][ 1 ] = percentage;\n            updateTotalProgress();\n        };\n\n        uploader.onFileQueued = function( file ) {\n            var start = Date.now();\n\n            fileCount++;\n            fileSize += file.size;\n\n            if ( fileCount === 1 ) {\n                $placeHolder.hide();\n                $statusBar.show();\n            }\n\n            Md5File( file, function( value ) {\n                var $li = $('#'+file.id);\n                file.md5 = value;\n\n                console.log( value );\n\n                $li.append( '<p class=\"log\">md5:' + ((Date.now() - start)/1000).toFixed(2) + '秒</p>')\n            });\n\n            addFile( file );\n            setState( 'ready' );\n            updateTotalProgress();\n        };\n\n        uploader.onFileDequeued = function( file ) {\n            fileCount--;\n            fileSize -= file.size;\n\n            if ( !fileCount ) {\n                setState( 'pedding' );\n            }\n\n            removeFile( file );\n            updateTotalProgress();\n\n        };\n\n        uploader.on( 'all', function( type ) {\n            var stats;\n            switch( type ) {\n                case 'uploadFinished':\n                    setState( 'confirm' );\n                    break;\n\n                case 'startUpload':\n                    setState( 'uploading' );\n                    break;\n\n                case 'stopUpload':\n                    setState( 'paused' );\n                    break;\n\n            }\n        });\n\n        uploader.onError = function( code ) {\n            alert( 'Eroor: ' + code );\n        };\n\n        uploader.onUploadChunkcontinue = function( file, ret ) {\n            if ( ret.exist ) {\n                var $li = $( '#'+file.id );\n\n                $li.append( '<p class=\"log\">跳过' + uploader.formatSize( file.size - file.loaded ) + '</p>' );\n                return false;\n            }\n        };\n\n        $upload.on('click', function() {\n            if ( $(this).hasClass( 'disabled' ) ) {\n                return false;\n            }\n\n            if ( state === 'ready' ) {\n                uploader.upload();\n            } else if ( state === 'paused' ) {\n                uploader.upload();\n            } else if ( state === 'uploading' ) {\n                uploader.stop();\n            }\n        });\n\n        $info.on( 'click', '.retry', function() {\n            uploader.retry();\n        } );\n\n        $info.on( 'click', '.ignore', function() {\n            alert( 'todo' );\n        } );\n\n        $upload.addClass( 'state-' + state );\n        updateTotalProgress();\n\n        var Md5File = (function(){\n\n\n            return function( file, cb ) {\n                var worker = new Worker( 'hashFile.js' );\n                worker.onmessage = function( e ) {\n                    cb( e.data, file );\n                    worker.terminate();\n                };\n\n                worker.postMessage( file.source );\n            }\n        })();\n    });\n\n})( jQuery );"
  },
  {
    "path": "_draft/flash_cpp/as3api.cpp",
    "content": "#include <stdlib.h>\n#include <string>\n#include \"AS3/AS3.h\"\n\n#include \"hashlib/hl_hashwrapper.h\"\n#include \"hashlib/hl_md5wrapper.h\"\n\nvoid md5String() __attribute__((used,\n    annotate(\"as3sig:public function md5String(input:String):String\"),\n    annotate(\"as3package:com.webuploader\")));\n\n\n/**\n * MD5 (\"\") = d41d8cd98f00b204e9800998ecf8427e\n * MD5 (\"a\") = 0cc175b9c0f1b6a831c399e269772661\n * MD5 (\"abc\") = 900150983cd24fb0d6963f7d28e17f72\n * MD5 (\"message digest\") = f96b697d7cb7938d525a2f31aaf161d0\n * MD5 (\"abcdefghijklmnopqrstuvwxyz\") = c3fcd3d76192e4007dfb496cca67e13b\n * MD5 (\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\") =\n * d174ab98d277d9f5a5611c2c9f419d9f\n * MD5 (\"123456789012345678901234567890123456789012345678901234567890123456\n * 78901234567890\") = 57edf4a22be3c955ac49da2e2107b67a\n */\nvoid md5String(){\n    const char *src = NULL;\n    AS3_MallocString(src, input);\n\n    std::string srcString(src);\n\n    const char *result;\n\n    hashwrapper *wrapper;\n\n    wrapper = new md5wrapper();\n    std::string md5 = wrapper->getHashFromString(srcString);\n\n    result = md5.c_str();\n\n    delete wrapper;\n\n    // We can't just call AS3_Return(s) because s is not a scalar.\n    // Instead we need to marshall the C string into AS3 and use\n    // AS3_ReturnAS3Var().\n\n    AS3_DeclareVar(myString, String);\n    AS3_CopyCStringToVar(myString, result, 32);\n    AS3_ReturnAS3Var(myString);\n}"
  },
  {
    "path": "_draft/flash_cpp/exports.txt",
    "content": "# built in symbols that must always be preserved\n_start1\nmalloc\nfree\nmemcpy\nmemmove\nflascc_uiTickProc\n_sync_synchronize\n\n# symbols for C++ exception handling\n_Unwind_SjLj_Register\n_Unwind_SjLj_Resume\n_Unwind_SjLj_Unregister\n_Unwind_SjLj_RaiseException\n\n# symbols from libm.a\n__muldi3\n\n# symbols for the GLUT based examples\n_avm2_glut_keyhandler\nglutMainLoopBody"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/all-wcprops",
    "content": "K 25\nsvn:wc:ra_dav:version-url\nV 43\n/svnroot/hashlib2plus/!svn/ver/77/trunk/src\nEND\nhl_sha384wrapper.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 64\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha384wrapper.cpp\nEND\nhl_sha256.h\nK 25\nsvn:wc:ra_dav:version-url\nV 55\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha256.h\nEND\nhl_wrapperfactory.h\nK 25\nsvn:wc:ra_dav:version-url\nV 63\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/hl_wrapperfactory.h\nEND\nhl_sha2ext.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 58\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha2ext.cpp\nEND\nhl_sha256wrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 62\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha256wrapper.h\nEND\nhl_sha384wrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 62\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha384wrapper.h\nEND\nhl_sha2mac.h\nK 25\nsvn:wc:ra_dav:version-url\nV 56\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha2mac.h\nEND\nhl_sha2ext.h\nK 25\nsvn:wc:ra_dav:version-url\nV 56\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha2ext.h\nEND\nhl_md5.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 54\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_md5.cpp\nEND\nhl_md5wrapper.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 61\n/svnroot/hashlib2plus/!svn/ver/71/trunk/src/hl_md5wrapper.cpp\nEND\nhl_hashwrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 60\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/hl_hashwrapper.h\nEND\nhl_md5.h\nK 25\nsvn:wc:ra_dav:version-url\nV 52\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_md5.h\nEND\nhl_md5wrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 59\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_md5wrapper.h\nEND\nhashlibpp.h\nK 25\nsvn:wc:ra_dav:version-url\nV 55\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/hashlibpp.h\nEND\nhl_sha1.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 55\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha1.cpp\nEND\nhl_sha1wrapper.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 62\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha1wrapper.cpp\nEND\nhl_sha1.h\nK 25\nsvn:wc:ra_dav:version-url\nV 53\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha1.h\nEND\nhl_sha1wrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 60\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha1wrapper.h\nEND\nhl_exception.h\nK 25\nsvn:wc:ra_dav:version-url\nV 58\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/hl_exception.h\nEND\nhl_sha512wrapper.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 64\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha512wrapper.cpp\nEND\nhl_sha256.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 57\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha256.cpp\nEND\nhl_wrapperfactory.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 65\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/hl_wrapperfactory.cpp\nEND\nhl_sha256wrapper.cpp\nK 25\nsvn:wc:ra_dav:version-url\nV 64\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha256wrapper.cpp\nEND\nMakefile\nK 25\nsvn:wc:ra_dav:version-url\nV 52\n/svnroot/hashlib2plus/!svn/ver/78/trunk/src/Makefile\nEND\nhl_sha512wrapper.h\nK 25\nsvn:wc:ra_dav:version-url\nV 62\n/svnroot/hashlib2plus/!svn/ver/70/trunk/src/hl_sha512wrapper.h\nEND\nhl_types.h\nK 25\nsvn:wc:ra_dav:version-url\nV 54\n/svnroot/hashlib2plus/!svn/ver/71/trunk/src/hl_types.h\nEND\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/entries",
    "content": "10\n\ndir\n77\nhttps://hashlib2plus.svn.sourceforge.net/svnroot/hashlib2plus/trunk/src\nhttps://hashlib2plus.svn.sourceforge.net/svnroot/hashlib2plus\n\n\n\n2010-06-21T16:29:04.476273Z\n77\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n9fabf1d2-8a3a-0410-866c-cb411183c2a1\n\f\nhl_sha384wrapper.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n17f226c2a3e6270168d48504cd33e4c5\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3872\n\f\nhl_wrapperfactory.h\nfile\n78\n\n\n\n2011-10-13T17:38:25.000000Z\n6c168d2d628100549c5f86aea00b5eb2\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3081\n\f\nhl_sha256.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\nac167db3cab5eb2e27a8d41bd76f379e\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n6439\n\f\nhl_sha256wrapper.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\ncff339a15b7b14a3ef48314245d91c2b\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4306\n\f\nhl_sha2ext.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n4cec161a0d4f59437da86e5f1f765bc4\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n17404\n\f\nhl_sha384wrapper.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\nf005457935d9c2a049baa9fadd021c6d\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4328\n\f\nhl_sha2ext.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\na4e096bb8f97a7e5a0660b869f9610da\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n8066\n\f\nhl_sha2mac.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n28d51c6e7a66d1f27061b371e3faaae4\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n7978\n\f\nhl_md5.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n51cc23699f77568796b7b999fc97bcb7\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n13296\n\f\nhl_md5wrapper.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n15590646af0fbe840a7a97fccc341481\n2010-02-21T19:02:17.752873Z\n71\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4135\n\f\nhl_hashwrapper.h\nfile\n78\n\n\n\n2011-10-13T17:39:24.000000Z\n1ea63f2f3d508eda466826372d671d8c\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n8770\n\f\nhl_md5.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n28696d2f48d25b659f02c2ce961732ba\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n6703\n\f\nhl_md5wrapper.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n1a195c7d60cc839cf3231467a2abbcd2\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4197\n\f\nhl_sha1.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\nd71e2ae594ad8ac3282d982729ae9c6a\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n11049\n\f\nhashlibpp.h\nfile\n78\n\n\n\n2011-10-13T17:01:47.000000Z\n286599faa35183c3ebe43a8d6cb9a6e8\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2353\n\f\nhl_sha1wrapper.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\nedb423e4747fa15007de22433493813d\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4008\n\f\nhl_sha1.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n6b43fa03d52be7252b40d2a394a8ef92\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n7520\n\f\nhl_sha1wrapper.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n30060841e872c93ba4060d1ba3747501\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4169\n\f\nhl_exception.h\nfile\n78\n\n\n\n2011-10-13T16:21:12.000000Z\nd47e62e2482d07775926927f69b22c71\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3370\n\f\nhl_sha512wrapper.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n29019e1424ec4752f4451616dcd6c90b\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3905\n\f\nhl_sha256.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\nf1cb5fa1517094d1c02f8fa85a424241\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n14722\n\f\nhl_wrapperfactory.cpp\nfile\n78\n\n\n\n2011-10-13T17:29:23.000000Z\ncb43b8485fa9189e7ca21e52262d25a8\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3251\n\f\nhl_sha256wrapper.cpp\nfile\n\n\n\n\n2011-10-13T17:29:23.000000Z\n19acf3f7d3c25d24b0633cb4a45bc8a9\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n3948\n\f\nMakefile\nfile\n78\n\n\n\n2011-10-13T15:46:46.000000Z\na6f58648331aa4856bfe65b6d5d66b60\n2011-10-13T17:45:28.472824Z\n78\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n5644\n\f\nhl_sha512wrapper.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n629350ff78bd95a50fdd8eb563126da0\n2010-01-24T14:59:38.128638Z\n70\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n4327\n\f\nhl_types.h\nfile\n\n\n\n\n2011-10-13T15:33:07.000000Z\n5d03e90911fa02c00fcbcda47c885794\n2010-02-21T19:02:17.752873Z\n71\nbennygr\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n2584\n\f\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/Makefile.svn-base",
    "content": "# \n# hashlib++ - a simple hash library for C++\n# \n# Copyright (c) 2007-2010 Benjamin Grdelbach\n# \n# Redistribution and use in source and binary forms, with or without modification,\n# are permitted provided that the following conditions are met:\n# \n# \t1)     Redistributions of source code must retain the above copyright\n# \t       notice, this list of conditions and the following disclaimer.\n# \n# \t2)     Redistributions in binary form must reproduce the above copyright\n# \t       notice, this list of conditions and the following disclaimer in\n# \t       the documentation and/or other materials provided with the\n# \t       distribution.\n# \t     \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n#----------------------------------------------------------------------- \n#\n# This is the hashlib++ makefile. It builds and installs hashlib++ as a\n# static library. See the documentation for more information.\n#\n# Type 'make' to build the lib.\n# Type 'make install' to build and install the lib.\n# Type 'make clean' to clean the object files\n#\n# Note for FreeBSD users:\n# use gmake from \"/usr/ports/devel/gmake\"\n#\n# Benjamin Grdelbach\n# Mi 10 Okt 2007\n#\n#----------------------------------------------------------------------- \n# Edit the following lines to meet your needs\n\n# Path to install the headerfiles\nINCLUDE_PATH = /usr/include/hashlib++\n\n# Path to install the library\nLIB_PATH = /usr/local/lib/\n\n# Compiler to use\nCOMPILER = g++\n\n# Global options for the compiler\nCOPTIONS = -ansi -Wall -Wextra\n\n#----------------------------------------------------------------------- \n# DON'T CHANGE ANYTHING BELOW\n#----------------------------------------------------------------------- \n\nifdef DEBUG\n\tCOPTIONS += -g\nelse\n\tCOPTIONS += -O3 -fomit-frame-pointer\nendif\n\nGCC = $(COMPILER) $(COPTIONS)\n\n#----------------------------------------------------------------------- \n#Main-Target\nall:\t\tMD5 SHA1 SHA256 SHA2EXT CORE LIB\n\n#----------------------------------------------------------------------- \n#all header-files\n\n\t\t\nHEADER = \thl_hashwrapper.h \\\n\t\thl_wrapperfactory.h \\\n\t\thl_exception.h \\\n\t\thl_md5.h hl_md5wrapper.h \\\n\t\thl_sha1.h hl_sha1wrapper.h \\\n\t\thl_sha2mac.h \\\n\t\thl_sha256.h hl_sha256wrapper.h \\\n\t\thl_sha2ext.h hl_sha384wrapper.h  hl_sha512wrapper.h \\\n\t\thl_types.h \\\n\t\thashlibpp.h\n\n#----------------------------------------------------------------------- \nCORE = \t\thl_wrapperfactory.o\nCORE:\t\thl_wrapperfactory.o\n\nhl_wrapperfactory.o:\thl_wrapperfactory.cpp hl_wrapperfactory.h\n\t\t\t$(GCC) -c hl_wrapperfactory.cpp\n\t\t\n#----------------------------------------------------------------------- \n# MD5 Targets\n\nMD5 = \t\thl_md5.o \\\n      \t\thl_md5wrapper.o\n\nMD5:\t\thl_md5.o hl_md5wrapper.o\n\nhl_md5.o:\thl_md5.cpp hl_md5.h\n\t\t$(GCC) -c hl_md5.cpp\n\nhl_md5wrapper.o:\thl_md5wrapper.cpp hl_md5wrapper.h\n\t\t\t$(GCC) -c hl_md5wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA1 Targets\n\nSHA1 = \t\thl_sha1.o \\\n       \t\thl_sha1wrapper.o\n\nSHA1:\t\thl_sha1.o hl_sha1wrapper.o\n\nhl_sha1.o:\thl_sha1.cpp hl_sha1.h\n\t\t$(GCC) -c hl_sha1.cpp\n\nhl_sha1wrapper.o:\thl_sha1wrapper.cpp hl_sha1wrapper.h\n\t\t\t$(GCC) -c hl_sha1wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA256 Targets\n\nSHA256 = \thl_sha256.o \\\n\t \thl_sha256wrapper.o\n\nSHA256:\t\thl_sha256.o hl_sha256wrapper.o\n\nhl_sha256.o:\thl_sha256.cpp hl_sha256.h hl_sha2mac.h\n\t\t$(GCC) -c hl_sha256.cpp\n\nhl_sha256wrapper.o:\thl_sha256wrapper.cpp hl_sha256wrapper.h\n\t\t\t$(GCC) -c hl_sha256wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA2-ext Targets\n\nSHA2EXT = \thl_sha2ext.o \\\n\t\thl_sha384wrapper.o \\\n\t\thl_sha512wrapper.o \n\n\t\t\nSHA2EXT:\thl_sha2ext.o hl_sha384wrapper.o hl_sha512wrapper.o\n\nhl_sha2ext.o:\thl_sha2ext.cpp hl_sha2ext.h hl_sha2mac.h\n\t\t$(GCC) -c hl_sha2ext.cpp\n\nhl_sha384wrapper.o:\thl_sha384wrapper.cpp hl_sha384wrapper.h\n\t\t\t$(GCC) -c hl_sha384wrapper.cpp\n\nhl_sha512wrapper.o:\thl_sha512wrapper.cpp hl_sha512wrapper.h\n\t\t\t$(GCC) -c hl_sha512wrapper.cpp\n\n#----------------------------------------------------------------------- \n# Creating a static lib using ar\n\nLIB:\t\tMD5 SHA1 SHA256\t\t\t\n\t\tar rs libhl++.a $(MD5) $(SHA1) $(SHA256) $(SHA2EXT) $(CORE)\n\n#----------------------------------------------------------------------- \n#Installing the lib\ninstall:\tall \n\t\tmkdir -p $(INCLUDE_PATH); \\\n\t\tmkdir -p $(LIB_PATH); \\\n\t\tcp libhl++.a $(LIB_PATH) && \\\n\t\tcp $(HEADER) $(INCLUDE_PATH)\n\t\t-@ echo \"\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"------------------------------\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"hashlib++ has been installed to:\"\n\t\t-@ echo \"include files: $(INCLUDE_PATH)\"\n\t\t-@ echo \"library files: $(LIB_PATH)\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"------------------------------\"\n\n#----------------------------------------------------------------------- \n# Cleaning object-files\n\nclean:\n\t\trm *.o\n\t\t-@ echo \"cleaned up \"\n\t\t-@ echo \"\"\n\n#----------------------------------------------------------------------- \n#EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hashlibpp.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\n\n/**\n *  @file \thashlibpp.h\n *  @brief\tThis file is just an include wrapper\n *  @date \tSa  2 Feb\n */  \n\n//----------------------------------------------------------------------\n#ifndef HASHLIBPP_H\n#define HASHLIBPP_H\n\n//----------------------------------------------------------------------\n//current version of hashilb++\n#define _HASHLIBPP_VERSION_ \"0.3.4\"\n\n//----------------------------------------------------------------------\n\n#include \"hl_exception.h\"\n#include \"hl_wrapperfactory.h\"\n#include \"hl_hashwrapper.h\"\n#include \"hl_md5wrapper.h\"\n#include \"hl_sha1wrapper.h\"\n#include \"hl_sha256wrapper.h\"\n#include \"hl_sha384wrapper.h\"\n#include \"hl_sha512wrapper.h\"\n\n\n//----------------------------------------------------------------------\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_exception.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_exception.h\n *  @brief\tThis file contains the hashlib++ exception class\n *  @date \tSa 24 Nov 2007\n */  \n\n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef HL_EXCEPTION_H\n#define HL_EXCEPTION_H\n\n//---------------------------------------------------------------------- \n//STL\n#include <string>\n\n//----------------------------------------------------------------------\n\n/**\n * definition of hashlib++ errornumbers\n */\ntypedef enum hlerrors\n{\n\tHL_NO_ERROR = 0,\n\tHL_FILE_READ_ERROR,\n\tHL_VERIFY_TEST_FAILED,\n\tHL_UNKNOWN_SEE_MSG,\n\tHL_UNKNOWN_HASH_TYPE\n} hlerror;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief\tThis class represents a exception within the hashlib++\n *  \t\tproject\n */  \nclass hlException\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t * Error Number\n\t\t\t */\n\t\t\thlerror iError;\n\n\t\t\t/**\n\t\t\t * Error message as string\n\t\t\t */\n\t\t\tstd::string strMessge;\n\t\t\t\n\n\tpublic:\n\t\t\t/**\n\t\t\t *  @brief \tconstructor\n\t\t\t *  @param\ter\tError number\n\t\t\t *  @param\tm\tError message\n\t\t\t */  \n\t\t\thlException(hlerror er, std::string m)\n\t\t\t{\n\t\t\t\tthis->iError = er;\n\t\t\t\tthis->strMessge = m;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \tconstructor\n\t\t\t *  @param\tm\tError Message\n\t\t\t */  \n\t\t\thlException(std::string m)\n\t\t\t{\n\t\t\t\tthis->iError = HL_UNKNOWN_SEE_MSG;\n\t\t\t\tthis->strMessge = m;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \treturns the error message\n\t\t\t *  @return\tthe error message\n\t\t\t */  \n\t\t\tstd::string error_message(void)\n\t\t\t{\n\t\t\t\treturn strMessge;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \treturns the error number\n\t\t\t *  @return\tthe error number\n\t\t\t */  \n\t\t\thlerror error_number(void)\n\t\t\t{\n\t\t\t\treturn iError;\n\t\t\t}\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_hashwrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2011 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n//doxygen mainpage\n\n/**\n * @mainpage  hashlib++ source documentation\n *\n * \t      <div align=\"center\"><b>Version 0.3.4</b></div>\n * \t      \n *\n * \t      @section intro Introduction\n * \t      hashlib++ a simple hash library for C++  \\n\n * \t      Copyright (c) 2007-2011 Benjamin Gr&uuml;delbach\n *\n *\n *\n * \t      hashlib++ is a simple and very easy to use library to create a\n * \t      cryptographic checksum called \"hash\". hashlib++ is written in\n * \t      plain C++ and should work with every compiler and platform.\n * \t      hashlib++ is released under the BSD-license and\n * \t      therefore free software.\n *\n * \t      @section about About this document\n *\n * \t      This is the documentation about the hashlib++ sourcecode.\n * \t      Since it contains some internal information it its helpfull\n * \t      but not necessary to read for the average user.\n * \t      If you are new to hashlib++ you should start with reading\n * \t      the README.TXT file which contains all relevant information\n * \t      to start using this library.\n *\n */\n\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_hashwrapper.h\n *  @brief\tThis file contains the hashwrapper base class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef HASHWRAPPER_H\n#define HASHWRAPPER_H\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//C includes\n//#include <stdio.h>\n#include <fstream>\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_exception.h\"\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the baseclass for all subwrappers\n *\n *  hashwrapper is the abstract base class of all subwrappers like md5wrapper\n *  or sha1wrapper. This class implements two simple and easy to use\n *  memberfunctions to create a hash based on a string or a file.\n *  ( getHashFromString() and getHashFromFile() )\n *\n *  getHashFromString() calls resetContext(), updateContext() and hashIt()\n *  in this order. These three memberfunctions are pure virtual and have to\n *  be implemented by the subclasses.\n *\n *  getHashFromFile() calls resetContext() before reading the specified file\n *  in 1024 byte blocks which are forwarded to the hash context by calling\n *  updateContext(). Finaly hashIt() is called to return the hash.\n */  \nclass hashwrapper\n{\n\tprivate:\n\t\tconst std::string teststring;\n\n\tprotected:\n\n\t\t/**\n\t\t *  @brief \tThis method finalizes the hash process\n\t\t *  \t\tand returns the hash as std::string\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @return \tthe created hash as std::string\n\t\t */  \n\t\tvirtual std::string hashIt(void) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis internal member-function\n\t\t *  \t\tconvertes the hash-data to a\n\t\t *  \t\tstd::string (HEX)\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t *  @return\tThe converted data as std::string\n\t\t */  \n\t\tvirtual std::string convToString(unsigned char *data) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis method adds the given data to the \n\t\t *  \t\tcurrent hash context\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @param \tdata The data to add to the current context\n\t\t *  @param \tlen The length of the data to add\n\t\t */  \n\t\tvirtual void updateContext(unsigned char *data, unsigned int len) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis method resets the current hash context.\n\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t */  \n\t\tvirtual void resetContext(void) = 0;\n\n\n\t\t/**\n\t\t * @brief \tThis method should return the hash of the\n\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t * \t\tdog\"\n\t\t */\n\t\tvirtual std::string getTestHash(void) = 0;\n\n\tpublic:\n\n\t\t/**\n\t\t * @brief Default Konstruktor\n\t\t */\n\t\thashwrapper( void ) \n\t\t\t: teststring(\"The quick brown fox jumps over the lazy dog\")\n\t\t{\n\t\t}\n\n\t\t/**\n\t\t *  @brief \tDefault destructor\n\t\t */  \n\t\tvirtual ~hashwrapper ( void ) { };\n\n\t\t/**\n\t\t * @brief Method for testing the concrete implementation\n\t\t */\n\t\tvirtual void test( void )\n\t\t{\n\t\t\tstd::string hash = this->getHashFromString(teststring);\n\t\t\tstd::string verify = this->getTestHash();\n\t\t\tif(hash != verify)\n\t\t\t{\n\t\t\t\tthrow hlException(HL_VERIFY_TEST_FAILED,\n\t\t\t\t\t\t  \"hashlib test-error: \\\"\" + \n\t\t\t\t\t\t  hash +\n\t\t\t\t\t\t  \"\\\" is not \\\"\" + \n\t\t\t\t\t\t  verify + \n\t\t\t\t\t\t  \"\\\" as supposed to be.\");\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t *  @brief \tThis method creates a hash based on the\n\t\t *  \t\tgiven string\n\t\t *\n\t\t *  \t\tThis memberfunctions calls resetContext(),\n\t\t *  \t\tupdateContext() and hashIt() in this order.\n\t\t *  \t\tThese three memberfunctions are pure virtual and have to\n\t\t *  \t\tbe implemented by the subclasses.\n\t\t *\n\t\t *  @param \ttext The text to create a hash from. This\n\t\t *  \t\tparameter is forwarded to updateContext()\n\t\t *  @return \tthe created hash as std::string\n\t\t */  \n\t\tvirtual std::string getHashFromString(std::string text)\n\t\t{\n\t\t\t/*\n\t\t\t * reset the context so that we can start\n\t\t\t * with a new hash process\n\t\t\t */\n\t\t\tresetContext();\n\n\t\t\t/*\n\t\t\t * we update the context with the given text\n\t\t\t */\n\t\t\tupdateContext((unsigned char*) text.c_str(),text.length());\n\n\t\t\t/*\n\t\t\t * now we can close the hash process \n\t\t\t * and return the created hash\n\t\t\t */\n\t\t\treturn this->hashIt(); \n\t\t}\n\n\t\t/**\n\t\t *  @brief \tThis method creates a hash from a given file\n\t\t *\n\t\t *  \t\tFirst of all resetContext() is called before reading the\n\t\t *  \t\tspecified file\tin 1024 byte blocks which are forwarded\n\t\t *  \t\tto the hash context by calling updateContext().\n\t\t *  \t\tFinaly hashIt() is called to return the hash.\n\t\t *  \t\tThese three memberfunctions are pure virtual and have to\n\t\t *  \t\tbe implemented by the subclasses.\n\t\t *\n\t\t *  @param \tfilename The file to created a hash from\n\t\t *\n\t\t *  @return\tThe created hash of the file or \"-1\" in case\n\t\t *  \t\tthe file could not be opened\n\t\t *  @throw\tThrows a hlException if the specified file could not\n\t\t *  \t\tbe opened.\n\t\t */  \n\t\tvirtual std::string getHashFromFile(std::string filename)\n\t\t{\n\t\t\tFILE *file;\n\t\t\tint len;\n\t\t\tunsigned char buffer[1024];\n\n\t\t\t/*\n\t\t\t * reset the current hash context\n\t\t\t */\n\t\t\tresetContext();\n\n\t\t\t/*\n\t\t\t * open the specified file\n\t\t\t */\n\t\t\tif((file = fopen(filename.c_str(), \"rb\")) == NULL)\n\t\t\t{\n\t\t\t\tthrow hlException(HL_FILE_READ_ERROR,\n\t\t\t\t\t\t  \"Cannot read file \\\"\" + \n\t\t\t\t\t\t  filename + \n\t\t\t\t\t\t  \"\\\".\");\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * read the file in 1024b blocks and\n\t\t\t * update the context for every block\n\t\t\t */\n\t\t\twhile( (len = fread(buffer,1,1024,file)) )\n\t\t\t{\n\t\t\t\tupdateContext(buffer, len);\n\t\t\t}\n\n\t\t\t//close the file and create the hash\n\t\t\tfclose(file);\n\t\t\treturn(hashIt());\n\t\t}\n}; \n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_md5.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/*\n * The hashlib++ MD5 implementation is derivative from the sourcecode\n * published in RFC 1321 \n * \n * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\n * rights reserved.\n * \n * License to copy and use this software is granted provided that it\n * is identified as the \"RSA Data Security, Inc. MD5 Message-Digest\n * Algorithm\" in all material mentioning or referencing this software\n * or this function.\n * \n * License is also granted to make and use derivative works provided\n * that such works are identified as \"derived from the RSA Data\n * Security, Inc. MD5 Message-Digest Algorithm\" in all material\n * mentioning or referencing the derived work.\n * \n * RSA Data Security, Inc. makes no representations concerning either\n * the merchantability of this software or the suitability of this\n * software for any particular purpose. It is provided \"as is\"\n * without express or implied warranty of any kind.\n * \n * These notices must be retained in any copies of any part of this\n * documentation and/or software.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5.cpp\n *  @brief\tThis file contains the implementation of the MD5 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\n//hashlib++ includes\n#include \"hl_md5.h\"\n\n//----------------------------------------------------------------------\n// defines\n\n// Constants for MD5Transform routine.\n#define S11 7\n#define S12 12\n#define S13 17\n#define S14 22\n#define S21 5\n#define S22 9\n#define S23 14\n#define S24 20\n#define S31 4\n#define S32 11\n#define S33 16\n#define S34 23\n#define S41 6\n#define S42 10\n#define S43 15\n#define S44 21\n\nstatic unsigned char PADDING[64] = {\n  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/* F, G, H and I are basic MD5 functions. */\n#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))\n#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))\n#define H(x, y, z) ((x) ^ (y) ^ (z))\n#define I(x, y, z) ((y) ^ ((x) | (~z)))\n\n/*\n * ROTATE_LEFT rotates x left n bits. \n * cast to unsigned int to guarantee support for 64Bit System\n */\n#define ROTATE_LEFT(x, n) (((x) << (n)) | (( (unsigned int) x) >> (32-(n))))\n\n/*\nFF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.\nRotation is separate from addition to prevent recomputation.\n*/\n#define FF(a, b, c, d, x, s, ac) { \\\n (a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n\n#define GG(a, b, c, d, x, s, ac) { \\\n (a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define HH(a, b, c, d, x, s, ac) { \\\n (a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define II(a, b, c, d, x, s, ac) { \\\n (a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n\n//----------------------------------------------------------------------\t\n//private member-functions\n\n/**\n *  @brief \tBasic transformation. Transforms state based on block.\n *  @param\tstate\tstate to transform\n *  @param\tblock\tblock to transform\n */  \nvoid MD5::MD5Transform (unsigned long int state[4], unsigned char block[64])\n{\n\tunsigned long int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n\n\tDecode (x, block, 64);\n\n\t/* Round 1 */\n\tFF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */\n\tFF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */\n\tFF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */\n\tFF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */\n\tFF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */\n\tFF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */\n\tFF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */\n\tFF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */\n\tFF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */\n\tFF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */\n\tFF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */\n\tFF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */\n\tFF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */\n\tFF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */\n\tFF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */\n\tFF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */\n\n\t/* Round 2 */\n\tGG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */\n\tGG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */\n\tGG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */\n\tGG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */\n\tGG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */\n\tGG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */\n\tGG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */\n\tGG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */\n\tGG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */\n\tGG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */\n\tGG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */\n\n\tGG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */\n\tGG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */\n\tGG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */\n\tGG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */\n\tGG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */\n\n\t/* Round 3 */\n\tHH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */\n\tHH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */\n\tHH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */\n\tHH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */\n\tHH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */\n\tHH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */\n\tHH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */\n\tHH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */\n\tHH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */\n\tHH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */\n\tHH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */\n\tHH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */\n\tHH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */\n\tHH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */\n\tHH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */\n\tHH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */\n\n\t/* Round 4 */\n\tII (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */\n\tII (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */\n\tII (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */\n\tII (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */\n\tII (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */\n\tII (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */\n\tII (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */\n\tII (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */\n\tII (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */\n\tII (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */\n\tII (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */\n\tII (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */\n\tII (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */\n\tII (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */\n\tII (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */\n\tII (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */\n\n\tstate[0] += a;\n\tstate[1] += b;\n\tstate[2] += c;\n\tstate[3] += d;\n\n\t/* \n\t * Zeroize sensitive information.\n\t */\n\tMD5_memset ((POINTER)x, 0, sizeof (x));\n}\n\n/**\n *  @brief \tEncodes input data\n *  @param\toutput Encoded data as OUT parameter\n *  @param\tinput Input data\n *  @param\tlen The length of the input assuming it is a\n *  \t\tmultiple of 4\n */  \nvoid MD5::Encode (unsigned char *output, unsigned long int *input, unsigned int len)\n{\n\tunsigned int i, j;\n\n\tfor (i = 0, j = 0; j < len; i++, j += 4) {\n\t\toutput[j] = (unsigned char)(input[i] & 0xff);\n\t\toutput[j+1] = (unsigned char)((input[i] >> 8) & 0xff);\n\t\toutput[j+2] = (unsigned char)((input[i] >> 16) & 0xff);\n\t\toutput[j+3] = (unsigned char)((input[i] >> 24) & 0xff);\n\t}\n}\n\n/**\n *  @brief \tDecodes input data into output\n *  @param\toutput Decoded data as OUT parameter\n *  @param\tinput Input data\n *  @param\tlen The length of the input assuming it is a\n *  \t\tmultiple of 4\n */  \nvoid MD5::Decode (unsigned long int *output, unsigned char *input, unsigned int len)\n{\n\t  unsigned int i, j;\n\n\t  for (i = 0, j = 0; j < len; i++, j += 4)\n\t\t output[i] = ((unsigned long int)input[j]) | \n\t\t\t     (((unsigned long int)input[j+1]) << 8) |\n\t\t\t     (((unsigned long int)input[j+2]) << 16) |\n\t\t\t     (((unsigned long int)input[j+3]) << 24);\n}\n\n/**\n *  @brief \tinternal memory management\n *  @param\toutput OUT parameter where POINTER is a unsigned\n *  \t\tchar*\n *  @param\tinput Data to copy where POINTER is a unsigned char*\n *  @param\tlen The length of the data\n */  \nvoid MD5::MD5_memcpy (POINTER output, POINTER input, unsigned int len)\n{\n\t/*\n\t * TODO-Note: Replace \"for loop\" with standard memcpy if possible.\n\t */\n\tunsigned int i;\n\n\tfor (i = 0; i < len; i++)\n\t\toutput[i] = input[i];\n}\n\n/**\n *  @brief \tinternal memory management\n *  @param \toutput OUT parameter where POINTER is an unsigned\n *  \t\tchar*\n *  @param\tvalue Value to fill the memory with\n *  @param\tlen The length of the data\n *  \n */  \nvoid MD5::MD5_memset (POINTER output,int value,unsigned int len)\n{\n\t/*\n\t * TODO-Note: Replace \"for loop\" with standard memset if possible.\n\t */\n\tunsigned int i;\n\tfor (i = 0; i < len; i++)\n\t\t((char *)output)[i] = (char)value;\n}\n\n//----------------------------------------------------------------------\t\n//public member-functions\n\n/**\n *  @brief \tInitialization begins an operation,\n *  \t\twriting a new context\n *  @param \tcontext\tThe HL_MD5_CTX context to initialize\n */  \nvoid MD5::MD5Init (HL_MD5_CTX *context)\n{\n\t  context->count[0] = context->count[1] = 0;\n\t  context->state[0] = 0x67452301;\n\t  context->state[1] = 0xefcdab89;\n\t  context->state[2] = 0x98badcfe;\n\t  context->state[3] = 0x10325476;\n}\n\n/**\n *  @brief \tBlock update operation. Continues an md5\n *  \t\tmessage-digest operation, processing another\n *  \t\tmessage block, and updating the context.\n *  @param\tcontext The HL_MD5_CTX context to update\n *  @param\tinput The data to write into the context\n *  @param\tinputLen The length of the input data\n */  \nvoid MD5::MD5Update (HL_MD5_CTX *context, unsigned char *input, unsigned int inputLen)\n{\n\t  unsigned int i, index, partLen;\n\n\t  /* Compute number of bytes mod 64 */\n\t  index = (unsigned int)((context->count[0] >> 3) & 0x3F);\n\n\t  /* Update number of bits */\n\t  if ( (context->count[0] += ((unsigned long int)inputLen << 3))\n\t       < ((unsigned long int)inputLen << 3))\n\t\tcontext->count[1]++;\n\n\t  context->count[1] += ((unsigned long int)inputLen >> 29);\n\t  partLen = 64 - index;\n\n\t  /*\n\t   * Transform as many times as possible.\n\t   */\n\t  if (inputLen >= partLen) \n\t  {\n\t\t MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);\n\t\t MD5Transform (context->state, context->buffer);\n\n\t\t for (i = partLen; i + 63 < inputLen; i += 64)\n\t\t   MD5Transform (context->state, &input[i]);\n\n\t\t index = 0;\n\t  }\n\t  else \n\t \ti = 0;\n\n\t  /* Buffer remaining input */\n\t  MD5_memcpy ((POINTER)&context->buffer[index],\n\t              (POINTER)&input[i],\n\t\t      inputLen-i);\n}\n\n/**\n *  @brief \tFinalization ends the md5 message-digest \n *  \t\toperation, writing the the message digest and\n *  \t\tzeroizing the context.\n *  @param\tdigest This is an OUT parameter which contains\n *  \t\tthe created hash after the method returns\n *  @param\tcontext The context to finalize\n */  \nvoid MD5::MD5Final (unsigned char digest[16], HL_MD5_CTX *context)\n{\n\tunsigned char bits[8];\n\tunsigned int index, padLen;\n\n\t/* Save number of bits */\n\tEncode (bits, context->count, 8);\n\n\t/* \n\t * Pad out to 56 mod 64.\n\t */\n\tindex = (unsigned int)((context->count[0] >> 3) & 0x3f);\n\tpadLen = (index < 56) ? (56 - index) : (120 - index);\n\tMD5Update (context, PADDING, padLen);\n\n\t/* Append length (before padding) */\n\tMD5Update (context, bits, 8);\n\n\t/* Store state in digest */\n\tEncode (digest, context->state, 16);\n\n\t/*\n\t * Zeroize sensitive information.\n\t */\n\tMD5_memset ((POINTER)context, 0, sizeof (*context));\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_md5.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/*\n * The hashlib++ MD5 implementation is derivative from the sourcecode\n * published in RFC 1321 \n * \n * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\n * rights reserved.\n * \n * License to copy and use this software is granted provided that it\n * is identified as the \"RSA Data Security, Inc. MD5 Message-Digest\n * Algorithm\" in all material mentioning or referencing this software\n * or this function.\n * \n * License is also granted to make and use derivative works provided\n * that such works are identified as \"derived from the RSA Data\n * Security, Inc. MD5 Message-Digest Algorithm\" in all material\n * mentioning or referencing the derived work.\n * \n * RSA Data Security, Inc. makes no representations concerning either\n * the merchantability of this software or the suitability of this\n * software for any particular purpose. It is provided \"as is\"\n * without express or implied warranty of any kind.\n * \n * These notices must be retained in any copies of any part of this\n * documentation and/or software.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5.h\n *  @brief\tThis file contains the declaration of the MD5 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef MD5_H\n#define MD5_H\n\n//---------------------------------------------------------------------- \n//STL includes\n#include <string>\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\ntypedef hl_uint8 *POINTER;\n\n/**\n * @brief this struct represents a MD5-hash context.\n */\ntypedef struct \n{\n\t/** state (ABCD) */\n\tunsigned long int state[4];   \t      \n\n\t/** number of bits, modulo 2^64 (lsb first) */\n\tunsigned long int count[2];\n\n\t/** input buffer */\n\tunsigned char buffer[64];\n} HL_MD5_CTX;\n\n//---------------------------------------------------------------------- \n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe md5 message digest algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  MD5Init(), MD5Update() and MD5Final().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the md5wrapper class instead of MD5.\n */  \nclass MD5\n{\n\n\tprivate:\n\n\t\t/**\n\t\t *  @brief \tBasic transformation. Transforms state based on block.\n\t\t *  @param\tstate\tstate to transform\n\t\t *  @param\tblock\tblock to transform\n\t\t */  \n\t\tvoid MD5Transform (unsigned long int state[4], unsigned char block[64]);\n\n\t\t/**\n\t\t *  @brief \tEncodes input data\n\t\t *  @param\toutput Encoded data as OUT parameter\n\t\t *  @param\tinput Input data\n\t\t *  @param\tlen The length of the input assuming it is a\n\t\t *  \t\tmultiple of 4\n\t\t */  \n\t\tvoid Encode (unsigned char* output,\n\t\t\t     unsigned long int *input,\n\t\t\t     unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tDecodes input data into output\n\t\t *  @param\toutput Decoded data as OUT parameter\n\t\t *  @param\tinput Input data\n\t\t *  @param\tlen The length of the input assuming it is a\n\t\t *  \t\tmultiple of 4\n\t\t */  \n\t\tvoid Decode (unsigned long int *output,\n\t\t\t     unsigned char *input,\n\t\t\t     unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tinternal memory management\n\t\t *  @param\toutput OUT parameter where POINTER is an unsigned\n\t\t *  \t\tchar*\n\t\t *  @param\tinput Data to copy where POINTER is a unsigned char*\n\t\t *  @param\tlen The length of the data\n\t\t */  \n\t\tvoid MD5_memcpy (POINTER output, POINTER input, unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tinternal memory management\n\t\t *  @param \toutput OUT parameter where POINTER is an unsigned\n\t\t *  \t\tchar*\n\t\t *  @param\tvalue Value to fill the memory with\n\t\t *  @param\tlen The length of the data\n\t\t *  \n\t\t */  \n\t\tvoid MD5_memset (POINTER output, int value, unsigned int len);\n\n\tpublic:\n\t\n\t\t/**\n\t\t *  @brief \tInitialization begins an operation,\n\t\t *  \t\twriting a new context\n\t\t *  @param \tcontext\tThe HL_MD5_CTX context to initialize\n\t\t */  \n\t\tvoid MD5Init (HL_MD5_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tBlock update operation. Continues an md5\n\t\t *  \t\tmessage-digest operation, processing another\n\t\t *  \t\tmessage block, and updating the context.\n\t\t *  @param\tcontext The HL_MD5_CTX context to update\n\t\t *  @param\tinput The data to write into the context\n\t\t *  @param\tinputLen The length of the input data\n\t\t */  \n\t\tvoid MD5Update (HL_MD5_CTX* context,\n\t\t\t       \tunsigned char *input,\n\t\t\t       \tunsigned int inputLen);\n\n\t\t/**\n\t\t *  @brief \tFinalization ends the md5 message-digest \n\t\t *  \t\toperation, writing the the message digest and\n\t\t *  \t\tzeroizing the context.\n\t\t *  @param\tdigest This is an OUT parameter which contains\n\t\t *  \t\tthe created hash after the method returns\n\t\t *  @param\tcontext The context to finalize\n\t\t */  \n\t\tvoid MD5Final (unsigned char digest[16], HL_MD5_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tdefault constructor\n\t\t */  \n\t\tMD5(){};\n};\n\n//---------------------------------------------------------------------- \n//End of include protection\n#endif\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_md5wrapper.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5wrapper.cpp\n *  @brief\tThis file contains the implementation of the \n *  \t\tmd5wrapper class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//STL includes\n#include <string>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_md5wrapper.h\"\n\n//---------------------------------------------------------------------- \n//private member functions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \tthe hash as std::string\n */  \nstd::string md5wrapper::hashIt(void)\n{\n\t//create the hash\n\tunsigned char buff[16] = \"\";\t\n\tmd5->MD5Final((unsigned char*)buff,&ctx);\n\n\t//converte the hash to a string and return it\n\treturn convToString(buff);\t\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string md5wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * using a ostringstream to convert the hash in a\n\t * hex string\n\t */\n\tstd::ostringstream os;\n\tfor(int i=0; i<16; ++i)\n\t{\n\t\t/*\n\t\t * set the width to 2\n\t\t */\n\t\tos.width(2);\n\n\t\t/*\n\t\t * fill with 0\n\t\t */\n\t\tos.fill('0');\n\n\t\t/*\n\t\t * conv to hex\n\t\t */\n\t\tos << std::hex << static_cast<unsigned int>(data[i]);\n\t}\n\n\t/*\n\t * return as std::string\n\t */\n\treturn os.str();\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context.\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid md5wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\t//update \n\tmd5->MD5Update(&ctx, data, len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid md5wrapper::resetContext(void)\n{\n\t//init md5\n\tmd5->MD5Init(&ctx);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string md5wrapper::getTestHash(void)\n{\n\treturn \"9e107d9d372bb6826bd81d3542a419d6\";\n}\n\n//---------------------------------------------------------------------- \n//public member functions\n\n/**\n *  @brief \tdefault constructor\n */  \nmd5wrapper::md5wrapper()\n{\n\tmd5 = new MD5();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nmd5wrapper::~md5wrapper()\n{\n\tdelete md5;\n}\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_md5wrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5wrapper.h\n *  @brief\tThis file contains the definition of the md5wrapper\n *  \t\tclass.\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef MD5WRAPPER_H\n#define MD5WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_md5.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the MD5 wrapper-class\n *\n *  \t\tYou can use this class to easily create a md5 hash.\n *  \t\tJust create an instance of md5wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tmd5example.cpp\n *\n *  \t\tmd5wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass md5wrapper : public hashwrapper\n{\n\tprotected:\n\n\t\t/**\n\t\t * MD5 access\n\t\t */\n\t\tMD5 *md5;\n\n\t\t/**\n\t\t * MD5 context\n\t\t */\n\t\tHL_MD5_CTX ctx;\n\t\n\t\t/**\n\t\t *  @brief \tThis method ends the hash process\n\t\t *  \t\tand returns the hash as string.\n\t\t *\n\t\t *  @return \tthe hash as std::string\n\t\t */  \n\t\tvirtual std::string hashIt(void);\n\n\t\t/**\n\t\t *  @brief \tThis internal member-function\n\t\t *  \t\tconvertes the hash-data to a\n\t\t *  \t\tstd::string (HEX).\n\t\t *\n\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t *  @return\tthe converted data as std::string\n\t\t */  \n\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t/**\n\t\t *  @brief \tThis method adds the given data to the \n\t\t *  \t\tcurrent hash context.\n\t\t *\n\t\t *  @param \tdata The data to add to the current context\n\t\t *  @param \tlen The length of the data to add\n\t\t */  \n\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tThis method resets the current hash context.\n\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t */  \n\t\tvirtual void resetContext(void);\n\n\t\t/**\n\t\t * @brief \tThis method should return the hash of the\n\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t * \t\tdog\"\n\t\t */\n\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tdefault constructor\n\t\t */  \n\t\tmd5wrapper();\n\n\t\t/**\n\t\t *  @brief \tdefault destructor\n\t\t */  \n\t\tvirtual ~md5wrapper();\n};\n\n//----------------------------------------------------------------------\n//include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha1.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA1 implementation is derivative from the sourcecode\n * published in RFC 3174  \n *\n * Copyright (C) The Internet Society (2001).  All Rights Reserved.\n * \n * This document and translations of it may be copied and furnished to\n * others, and derivative works that comment on or otherwise explain it\n * or assist in its implementation may be prepared, copied, published\n * and distributed, in whole or in part, without restriction of any\n * kind, provided that the above copyright notice and this paragraph are\n * included on all such copies and derivative works.  However, this\n * document itself may not be modified in any way, such as by removing\n * the copyright notice or references to the Internet Society or other\n * Internet organizations, except as needed for the purpose of\n * developing Internet standards in which case the procedures for\n * copyrights defined in the Internet Standards process must be\n * followed, or as required to translate it into languages other than\n * English.\n * \n * The limited permissions granted above are perpetual and will not be\n * revoked by the Internet Society or its successors or assigns.\n * \n * This document and the information contained herein is provided on an\n * \"AS IS\" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING\n * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING\n * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION\n * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF\n * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha1.cpp\n *  @brief\tThis file contains the implementation of the SHA1 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha1.h\"\n\n\n//---------------------------------------------------------------------- \n//defines\n\n/*\n *  Define the SHA1 circular left shift macro\n */\n#define SHA1CircularShift(bits,word) \\\n                (((word) << (bits)) | ((word) >> (32-(bits))))\n\n//----------------------------------------------------------------------\n//private member-functions\n\n/**\n *  @brief \tInternal method to padd the message\n *\n *      \tAccording to the standard, the message must\n *      \tbe padded to an even 512 bits. The first \n *      \tpadding bit must be a '1'.  The last 64\tbits \n *      \trepresent the length of the original message.\n *      \tAll bits in between should be 0.\n *      \tThis function will pad the message according \n *      \tto those rules by filling the Message_Block array\n *      \taccordingly.  It will also call the \n *      \tProcessMessageBlock function provided appropriately.\n *      \tWhen it returns, it can be assumed that the message\n *      \tdigest has been computed.\n *\n *  @param\tcontext The context to padd\n *\n */  \nvoid SHA1::SHA1PadMessage(HL_SHA1_CTX *context)\n{\n\t/*\n\t *  Check to see if the current message block is too small to hold\n\t *  the initial padding bits and length.  If so, we will pad the\n\t *  block, process it, and then continue padding into a second\n\t *  block.\n\t */\n\tif (context->Message_Block_Index > 55)\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] = 0x80;\n\t\twhile(context->Message_Block_Index < 64)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\n\t\tSHA1ProcessMessageBlock(context);\n\n\t\twhile(context->Message_Block_Index < 56)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] = 0x80;\n\t\twhile(context->Message_Block_Index < 56)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\t}\n\n\t/*\n\t *  Store the message length as the last 8 octets\n\t */\n\tcontext->Message_Block[56] = context->Length_High >> 24;\n\tcontext->Message_Block[57] = context->Length_High >> 16;\n\tcontext->Message_Block[58] = context->Length_High >> 8;\n\tcontext->Message_Block[59] = context->Length_High;\n\tcontext->Message_Block[60] = context->Length_Low >> 24;\n\tcontext->Message_Block[61] = context->Length_Low >> 16;\n\tcontext->Message_Block[62] = context->Length_Low >> 8;\n\tcontext->Message_Block[63] = context->Length_Low;\n\n\tSHA1ProcessMessageBlock(context);\n}\n\n/**\n *  @brief      This member-function will process the next 512 bits of the\n *  \t\tmessage stored in the Message_Block array.\n *\n *      \tMany of the variable names in this code, especially the\n *      \tsingle character names, were used because those were the\n *      \tnames used in the publication.\n *\n *  @param\tcontext The context to process\n */  \nvoid SHA1::SHA1ProcessMessageBlock(HL_SHA1_CTX *context)\n{\n\tconst hl_uint32 K[] =    {       /* Constants defined in SHA-1   */\n\t\t0x5A827999,\n\t\t0x6ED9EBA1,\n\t\t0x8F1BBCDC,\n\t\t0xCA62C1D6\n\t};\n\tint           t;                 /* Loop counter                */\n\thl_uint32      temp;              /* Temporary word value        */\n\thl_uint32      W[80];             /* Word sequence               */\n\thl_uint32      A, B, C, D, E;     /* Word buffers                */\n\n\t/*\n\t *  Initialize the first 16 words in the array W\n\t */\n\tfor(t = 0; t < 16; t++)\n\t{\n\t\tW[t] = context->Message_Block[t * 4] << 24;\n\t\tW[t] |= context->Message_Block[t * 4 + 1] << 16;\n\t\tW[t] |= context->Message_Block[t * 4 + 2] << 8;\n\t\tW[t] |= context->Message_Block[t * 4 + 3];\n\t}\n\n\tfor(t = 16; t < 80; t++)\n\t{\n\t\tW[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);\n\t}\n\n\tA = context->Intermediate_Hash[0];\n\tB = context->Intermediate_Hash[1];\n\tC = context->Intermediate_Hash[2];\n\tD = context->Intermediate_Hash[3];\n\tE = context->Intermediate_Hash[4];\n\n\tfor(t = 0; t < 20; t++)\n\t{\n\t\ttemp =  SHA1CircularShift(5,A) +\n\t\t\t((B & C) | ((~B) & D)) + E + W[t] + K[0];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 20; t < 40; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 40; t < 60; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) +\n\t\t\t((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 60; t < 80; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tcontext->Intermediate_Hash[0] += A;\n\tcontext->Intermediate_Hash[1] += B;\n\tcontext->Intermediate_Hash[2] += C;\n\tcontext->Intermediate_Hash[3] += D;\n\tcontext->Intermediate_Hash[4] += E;\n\n\tcontext->Message_Block_Index = 0;\n}\n\n//----------------------------------------------------------------------\n//public member-functions\n\n/**\n *  @brief \tResets the sha1 context and starts a new\n *  \t\thashprocess\n *  @param\tcontext The context to reset\n *  @return\t0 on succes an error number otherwise\n */  \nint SHA1::SHA1Reset(HL_SHA1_CTX *context)\n{\n\tif (!context)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tcontext->Length_Low             = 0;\n\tcontext->Length_High            = 0;\n\tcontext->Message_Block_Index    = 0;\n\n\tcontext->Intermediate_Hash[0]   = 0x67452301;\n\tcontext->Intermediate_Hash[1]   = 0xEFCDAB89;\n\tcontext->Intermediate_Hash[2]   = 0x98BADCFE;\n\tcontext->Intermediate_Hash[3]   = 0x10325476;\n\tcontext->Intermediate_Hash[4]   = 0xC3D2E1F0;\n\n\tcontext->Computed   = 0;\n\tcontext->Corrupted  = 0;\n\n\treturn shaSuccess;\n}\n\n/**\n *  @brief \tData input.\n *\n *  \t\tThis memberfunction add data to the specified\n *  \t\tcontext.\n *\n *  @param\tcontext The context to add data to\n *  @param\tmessage_array The data to add\n *  @param\tlength The length of the data to add\n */  \nint SHA1::SHA1Input(    HL_SHA1_CTX    *context,\n\t\t\tconst hl_uint8  *message_array,\n\t\t\tunsigned int   length)\n{\n\tif (!length)\n\t{\n\t\treturn shaSuccess;\n\t}\n\n\tif (!context || !message_array)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tif (context->Computed)\n\t{\n\t\tcontext->Corrupted = shaStateError;\n\t\treturn shaStateError;\n\t}\n\n\tif (context->Corrupted)\n\t{\n\t\treturn context->Corrupted;\n\t}\n\twhile(length-- && !context->Corrupted)\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] =\n\t\t\t(*message_array & 0xFF);\n\n\t\tcontext->Length_Low += 8;\n\t\tif (context->Length_Low == 0)\n\t\t{\n\t\t\tcontext->Length_High++;\n\t\t\tif (context->Length_High == 0)\n\t\t\t{\n\t\t\t\t/* Message is too long */\n\t\t\t\tcontext->Corrupted = 1;\n\t\t\t}\n\t\t}\n\n\t\tif (context->Message_Block_Index == 64)\n\t\t{\n\t\t\tSHA1ProcessMessageBlock(context);\n\t\t}\n\n\t\tmessage_array++;\n\t}\n\n\treturn shaSuccess;\n}\n\n/**\n *  @brief \tThis ends the sha operation, zeroizing the context\n *  \t\tand returning the computed hash.\n *\n *  @param\tcontext The context to get the hash from\n *  @param\tMessage_Digest This is an OUT parameter which\n *  \t\tcontains the hash after the menberfunction returns\n *  @return\t0 on succes, an error-code otherwise\n */  \nint SHA1::SHA1Result( HL_SHA1_CTX *context,\n\t\t      hl_uint8 \t  Message_Digest[SHA1HashSize])\n{\n\tint i;\n\n\tif (!context || !Message_Digest)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tif (context->Corrupted)\n\t{\n\t\treturn context->Corrupted;\n\t}\n\n\tif (!context->Computed)\n\t{\n\t\tSHA1PadMessage(context);\n\t\tfor(i=0; i<64; ++i)\n\t\t{\n\t\t\t/* message may be sensitive, clear it out */\n\t\t\tcontext->Message_Block[i] = 0;\n\t\t}\n\t\tcontext->Length_Low = 0;    /* and clear length */\n\t\tcontext->Length_High = 0;\n\t\tcontext->Computed = 1;\n\t}\n\n\tfor(i = 0; i < SHA1HashSize; ++i)\n\t{\n\t\tMessage_Digest[i] = context->Intermediate_Hash[i>>2]\n\t\t\t>> 8 * ( 3 - ( i & 0x03 ) );\n\t}\n\n\treturn shaSuccess;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha1.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA1 implementation is derivative from the sourcecode\n * published in RFC 3174  \n *\n * Copyright (C) The Internet Society (2001).  All Rights Reserved.\n * \n * This document and translations of it may be copied and furnished to\n * others, and derivative works that comment on or otherwise explain it\n * or assist in its implementation may be prepared, copied, published\n * and distributed, in whole or in part, without restriction of any\n * kind, provided that the above copyright notice and this paragraph are\n * included on all such copies and derivative works.  However, this\n * document itself may not be modified in any way, such as by removing\n * the copyright notice or references to the Internet Society or other\n * Internet organizations, except as needed for the purpose of\n * developing Internet standards in which case the procedures for\n * copyrights defined in the Internet Standards process must be\n * followed, or as required to translate it into languages other than\n * English.\n * \n * The limited permissions granted above are perpetual and will not be\n * revoked by the Internet Society or its successors or assigns.\n * \n * This document and the information contained herein is provided on an\n * \"AS IS\" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING\n * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING\n * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION\n * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF\n * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha1.h\n *  @brief\tThis file contains the declaration of the SHA1 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA1_H\n#define SHA1_H\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//enums\n\n#ifndef _SHA_enum_\n#define _SHA_enum_\nenum\n{\n    shaSuccess = 0,\n    shaNull,            /* Null pointer parameter */\n    shaInputTooLong,    /* input data too long */\n    shaStateError       /* called Input after Result */\n};\n#endif\n\n//---------------------------------------------------------------------- \n//defines\n#define SHA1HashSize 20\n\n//---------------------------------------------------------------------- \n//structs\n\n/**\n * @brief this struct represents a SHA1-hash context.\n */\ntypedef struct HL_SHA1_CTX\n{\n\t/** Message Digest */\n\thl_uint32 Intermediate_Hash[SHA1HashSize/4];\n\n\t/** Message length in bits */\n\thl_uint32 Length_Low;            \n\n\t/** Message length in bits */\n\thl_uint32 Length_High;\n\n\t/** Index into message block array */\n\thl_uint16 Message_Block_Index;\n\n\t/** 512-bit message blocks */\n\thl_uint8 Message_Block[64];      \n\n\t/** Is the digest computed? */\n\tint Computed;\n\n\t/** Is the message digest corrupted? */\n\tint Corrupted;\n\n} HL_SHA1_CTX;\n\n//---------------------------------------------------------------------- \n//class definition\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe sha1 algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  SHA1Reset(), SHA1Input() and SHA1Result().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha1wrapper class instead of SHA1.\n */  \nclass SHA1\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t *  @brief \tInternal method to padd the message\n\t\t\t *\n\t\t\t *      \tAccording to the standard, the message must\n\t\t\t *      \tbe padded to an even 512 bits. The first \n\t\t\t *      \tpadding bit must be a '1'.  The last 64\tbits \n\t\t\t *      \trepresent the length of the original message.\n\t\t\t *      \tAll bits in between should be 0.\n\t\t\t *      \tThis function will pad the message according \n\t\t\t *      \tto those rules by filling the Message_Block array\n\t\t\t *      \taccordingly.  It will also call the \n\t\t\t *      \tProcessMessageBlock function provided appropriately.\n\t\t\t *      \tWhen it returns, it can be assumed that the message\n\t\t\t *      \tdigest has been computed.\n\t\t\t *\n\t\t\t *  @param\tcontext The context to padd\n\t\t\t *\n\t\t\t */  \n\t\t\tvoid SHA1PadMessage(HL_SHA1_CTX *context);\n\n\t\t\t/**\n\t\t\t *  @brief      This member-function will process the next 512 bits of the\n\t\t\t *  \t\tmessage stored in the Message_Block array.\n\t\t\t *\n\t\t\t *      \tMany of the variable names in this code, especially the\n\t\t\t *      \tsingle character names, were used because those were the\n\t\t\t *      \tnames used in the publication.\n\t\t\t *\n\t\t\t *  @param\tcontext The context to process\n\t\t\t */  \n\t\t\tvoid SHA1ProcessMessageBlock(HL_SHA1_CTX *context);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tResets the sha1 context and starts a new\n\t\t *  \t\thashprocess\n\t\t *  @param\tcontext The context to reset\n\t\t *  @return\t0 on succes an error number otherwise\n\t\t */  \n\t\tint SHA1Reset(  HL_SHA1_CTX *context);\n\n\t\t/**\n\t\t *  @brief \tData input.\n\t\t *\n\t\t *  \t\tThis memberfunction add data to the specified\n\t\t *  \t\tcontext.\n\t\t *\n\t\t *  @param\tcontext The context to add data to\n\t\t *  @param\tmessage_array The data to add\n\t\t *  @param\tlength The length of the data to add\n\t\t */  \n\t\tint SHA1Input(  HL_SHA1_CTX   *context,\n\t\t\t\tconst hl_uint8 *message_array,\n\t\t\t\tunsigned int  length);\n\n\t\t/**\n\t\t *  @brief \tThis ends the sha operation, zeroizing the context\n\t\t *  \t\tand returning the computed hash.\n\t\t *\n\t\t *  @param\tcontext The context to get the hash from\n\t\t *  @param\tMessage_Digest This is an OUT parameter which\n\t\t *  \t\tcontains the hash after the menberfunction returns\n\t\t *  @return\t0 on succes, an error-code otherwise\n\t\t */  \n\t\tint SHA1Result( HL_SHA1_CTX *context,\n\t\t\t\thl_uint8     Message_Digest[SHA1HashSize]);\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha1wrapper.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha1wrapper.cpp\n *  @brief\tThis file contains the implementation of the \n *  \t\tsha1wrapper class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha1wrapper.h\"\n#include \"hl_sha1.h\"\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//STL includes\n#include <sstream>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha1wrapper::hashIt(void)\n{\n\thl_uint8 Message_Digest[20];\n\tsha1->SHA1Result(&context, Message_Digest);\n\n\treturn convToString(Message_Digest);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha1wrapper::convToString(unsigned char *data)\n{\n\tstd::ostringstream os;\n\tfor(int i=0; i<20; ++i)\n\t{\n\t\t/*\n\t\t * set the width to 2\n\t\t */\n\t\tos.width(2);\n\n\t\t/*\n\t\t * fill with 0\n\t\t */\n\t\tos.fill('0');\n\n\t\t/*\n\t\t * conv to hex\n\t\t */\n\t\tos << std::hex << static_cast<unsigned int>(data[i]);\n\t}\n\n\t/*\n\t * return as std::string\n\t */\n\treturn os.str();\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha1wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tsha1->SHA1Input(&context, data, len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha1wrapper::resetContext(void)\n{\n\tsha1->SHA1Reset(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha1wrapper::getTestHash(void)\n{\n\treturn \"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha1wrapper::sha1wrapper()\n{\n\tthis->sha1 = new SHA1();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha1wrapper::~sha1wrapper()\n{\n\tdelete this->sha1;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha1wrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha1wrapper.h\n *  @brief\tThis file contains the definition of the sha1wrapper \n *  \t\tclass.\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA1WRAPPER_H\n#define SHA1WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha1.h\"\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA1 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha1 hash.\n *  \t\tJust create an instance of sha1wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha1example.cpp\n *\n *  \t\tsha1wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha1wrapper : public hashwrapper\n{\n\tprotected:\n\t\t\t/**\n\t\t\t * SHA1 access\n\t\t\t */\n\t\t\tSHA1 *sha1;\n\t\t\t\n\t\t\t/*\n\t\t\t * SHA1 context\n\t\t\t */\n\t\t\tHL_SHA1_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha1wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha1wrapper();\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha256.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA256 implementation is derivative from the sourcecode\n * published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha256.cpp\n *  @brief\tThis file contains the implementation of the SHA256 class\n *  @date \tDi 25 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha256.h\"\n\n//---------------------------------------------------------------------- \n/*\n * Standard C includes\n */\n#include <string.h>\t\n#include <assert.h>\n\n//---------------------------------------------------------------------- \n#include \"hl_sha2mac.h\"\n\n//---------------------------------------------------------------------- \n\n/*\n * Hash constant words K for SHA-256: \n */ \nconst static sha2_word32 K256[64] = {\n\t0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,\n\t0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,\n\t0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,\n\t0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,\n\t0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,\n\t0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,\n\t0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,\n\t0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,\n\t0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,\n\t0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,\n\t0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,\n\t0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,\n\t0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,\n\t0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,\n\t0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,\n\t0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL\n};\n\n/*\n * Initial hash value H for SHA-256:\n */\nconst static sha2_word32 sha256_initial_hash_value[8] = {\n\t0x6a09e667UL,\n\t0xbb67ae85UL,\n\t0x3c6ef372UL,\n\t0xa54ff53aUL,\n\t0x510e527fUL,\n\t0x9b05688cUL,\n\t0x1f83d9abUL,\n\t0x5be0cd19UL\n};\n\n\n/*\n * Constant used by End() functions for converting the\n * digest to a readable hexadecimal character string:\n */\nstatic const char *sha2_hex_digits = \"0123456789abcdef\";\n\n/*** SHA-256: *********************************************************/\n\n/**\n *  @brief \tInitialize the context\n *  @param\tcontext The context to init.\n */  \nvoid SHA256::SHA256_Init(HL_SHA256_CTX* context) {\n\tif (context == (HL_SHA256_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);\n\tcontext->bitcount = 0;\n}\n\n#ifdef SHA2_UNROLL_TRANSFORM\n\n/* Unrolled SHA-256 round macros: */\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n\n#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tREVERSE32(*data++, W256[j]); \\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \\\n             K256[j] + W256[j]; \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \\\n\t     K256[j] + (W256[j] = *data++); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND256(a,b,c,d,e,f,g,h)\t\\\n\ts0 = W256[(j+1)&0x0f]; \\\n\ts0 = sigma0_256(s0); \\\n\ts1 = W256[(j+14)&0x0f]; \\\n\ts1 = sigma1_256(s1); \\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \\\n\t     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA256::SHA256_Transform(HL_SHA256_CTX* context, const sha2_word32* data) {\n\tsha2_word32\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word32\tT1, *W256;\n\tint\t\tj;\n\n\tW256 = (sha2_word32*)context->buffer;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n\t\t/* Rounds 0 to 15 (unrolled): */\n\t\tROUND256_0_TO_15(a,b,c,d,e,f,g,h);\n\t\tROUND256_0_TO_15(h,a,b,c,d,e,f,g);\n\t\tROUND256_0_TO_15(g,h,a,b,c,d,e,f);\n\t\tROUND256_0_TO_15(f,g,h,a,b,c,d,e);\n\t\tROUND256_0_TO_15(e,f,g,h,a,b,c,d);\n\t\tROUND256_0_TO_15(d,e,f,g,h,a,b,c);\n\t\tROUND256_0_TO_15(c,d,e,f,g,h,a,b);\n\t\tROUND256_0_TO_15(b,c,d,e,f,g,h,a);\n\t} while (j < 16);\n\n\t/* Now for the remaining rounds to 64: */\n\tdo {\n\t\tROUND256(a,b,c,d,e,f,g,h);\n\t\tROUND256(h,a,b,c,d,e,f,g);\n\t\tROUND256(g,h,a,b,c,d,e,f);\n\t\tROUND256(f,g,h,a,b,c,d,e);\n\t\tROUND256(e,f,g,h,a,b,c,d);\n\t\tROUND256(d,e,f,g,h,a,b,c);\n\t\tROUND256(c,d,e,f,g,h,a,b);\n\t\tROUND256(b,c,d,e,f,g,h,a);\n\t} while (j < 64);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = 0;\n}\n\n#else /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA256::SHA256_Transform(HL_SHA256_CTX* context, const sha2_word32* data) {\n\tsha2_word32\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word32\tT1, T2, *W256;\n\tint\t\tj;\n\n\tW256 = (sha2_word32*)context->buffer;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Copy data while converting to host byte order */\n\t\tREVERSE32(*data++,W256[j]);\n\t\t/* Apply the SHA-256 compression function to update a..h */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\t/* Apply the SHA-256 compression function to update a..h with copy */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\tT2 = Sigma0_256(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 16);\n\n\tdo {\n\t\t/* Part of the message block expansion: */\n\t\ts0 = W256[(j+1)&0x0f];\n\t\ts0 = sigma0_256(s0);\n\t\ts1 = W256[(j+14)&0x0f];\t\n\t\ts1 = sigma1_256(s1);\n\n\t\t/* Apply the SHA-256 compression function to update a..h */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + \n\t\t     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);\n\t\tT2 = Sigma0_256(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 64);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = T2 = 0;\n}\n\n#endif /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief\tUpdates the context \n *  @param\tcontext The context to update.\n *  @param\tdata The data for updating the context.\n *  @param\tlen The length of the given data.\n */  \nvoid SHA256::SHA256_Update(HL_SHA256_CTX* context, const sha2_byte *data, unsigned int len) {\n\tunsigned int\tfreespace, usedspace;\n\n\tif (len == 0) {\n\t\t/* Calling with no data is valid - we do nothing */\n\t\treturn;\n\t}\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0 && data != (sha2_byte*)0);\n\n\tusedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;\n\tif (usedspace > 0) {\n\t\t/* Calculate how much free space is available in the buffer */\n\t\tfreespace = SHA256_BLOCK_LENGTH - usedspace;\n\n\t\tif (len >= freespace) {\n\t\t\t/* Fill the buffer completely and process it */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);\n\t\t\tcontext->bitcount += freespace << 3;\n\t\t\tlen -= freespace;\n\t\t\tdata += freespace;\n\t\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\t\t} else {\n\t\t\t/* The buffer is not yet full */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, len);\n\t\t\tcontext->bitcount += len << 3;\n\t\t\t/* Clean up: */\n\t\t\tusedspace = freespace = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\twhile (len >= SHA256_BLOCK_LENGTH) {\n\t\t/* Process as many complete blocks as we can */\n\t\tSHA256_Transform(context, (sha2_word32*)data);\n\t\tcontext->bitcount += SHA256_BLOCK_LENGTH << 3;\n\t\tlen -= SHA256_BLOCK_LENGTH;\n\t\tdata += SHA256_BLOCK_LENGTH;\n\t}\n\tif (len > 0) {\n\t\t/* There's left-overs, so save 'em */\n\t\tMEMCPY_BCOPY(context->buffer, data, len);\n\t\tcontext->bitcount += len << 3;\n\t}\n\t/* Clean up: */\n\tusedspace = freespace = 0;\n}\n\n/**\n *  @brief \tFinalize the sha256 operation\n *  @param\tdigest The digest to finalize the operation with.\n *  @param\tcontext The context to finalize.\n */  \nvoid SHA256::SHA256_Final(sha2_byte digest[], HL_SHA256_CTX* context) {\n\tsha2_word32\t*d = (sha2_word32*)digest;\n\tunsigned int\tusedspace;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tusedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Convert FROM host byte order */\n\t\tREVERSE64(context->bitcount,context->bitcount);\n#endif\n\t\tif (usedspace > 0) {\n\t\t\t/* Begin padding with a 1 bit: */\n\t\t\tcontext->buffer[usedspace++] = 0x80;\n\n\t\t\tif (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {\n\t\t\t\t/* Set-up for the last transform: */\n\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);\n\t\t\t} else {\n\t\t\t\tif (usedspace < SHA256_BLOCK_LENGTH) {\n\t\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);\n\t\t\t\t}\n\t\t\t\t/* Do second-to-last transform: */\n\t\t\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\n\t\t\t\t/* And set-up for the last transform: */\n\t\t\t\tMEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);\n\t\t\t}\n\t\t} else {\n\t\t\t/* Set-up for the last transform: */\n\t\t\tMEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);\n\n\t\t\t/* Begin padding with a 1 bit: */\n\t\t\t*context->buffer = 0x80;\n\t\t}\n\t\t/* Set the bit count: */\n\t\t*(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;\n\n\t\t/* Final transform: */\n\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 8; j++) {\n\t\t\t\tREVERSE32(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Clean up state data: */\n\tMEMSET_BZERO(context, sizeof(context));\n\tusedspace = 0;\n}\n\n/**\n *  @brief \tEnds the sha256 operation and return the\n *  \t\tcreated hash in the given buffer.\n *  @param\tcontext The context to end.\n *  @param\tbuffer This OUT-Parameter contains the created\n *  \t\thash after ending the operation.\n */  \nchar* SHA256::SHA256_End(HL_SHA256_CTX* context, char buffer[]) {\n\tsha2_byte\tdigest[SHA256_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA256_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA256_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);\n\treturn buffer;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha256.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA256 implementation is derivative from the sourcecode\n * published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha256.h\n *  @brief\tThis file contains the declaration of the SHA256 class\n *  @date \tDi 25 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA256_H\n#define SHA256_H\n\n//---------------------------------------------------------------------- \n//lenght defines\n#define SHA256_BLOCK_LENGTH\t\t64\n#define SHA256_SHORT_BLOCK_LENGTH\t(SHA256_BLOCK_LENGTH - 8)\n#define SHA256_DIGEST_LENGTH\t\t32\n#define SHA256_DIGEST_STRING_LENGTH\t(SHA256_DIGEST_LENGTH * 2 + 1)\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\n\n/**\n * Exactly 1 byte \n */ \ntypedef hl_uint8  sha2_byte;\t\n\n/**\n * Exactly 4 bytes \n */\ntypedef hl_uint32 sha2_word32;\t\n\n/**\n * Exactly 8 bytes \n */ \ntypedef hl_uint64 sha2_word64;\t\n\n/**\n * @brief This struct represents a SHA256-hash context\n */\ntypedef struct HL_SHA256_CTX \n{\n\t/**\n\t * state \n\t */\n\thl_uint32\t\tstate[8];\n\n\t/**\n\t * bitcount\n\t */\n\thl_uint64\t\tbitcount;\n\n\t/**\n\t * message buffer\n\t */\n\thl_uint8\t\tbuffer[SHA256_BLOCK_LENGTH];\n} HL_SHA256_CTX;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe sha256 algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  SHA256_Init(), SHA256_Update() and SHA256_End().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha256wrapper class instead of SHA256.\n */  \nclass SHA256\n{\n\tprivate:\n\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha256 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA256_Final(hl_uint8 digest[SHA256_DIGEST_LENGTH],\n\t\t\t          HL_SHA256_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal data transformation\n\t\t *  @param\tcontext The context to use\n\t\t *  @param\tdata The data to transform\t\n\t\t */  \n\t\tvoid SHA256_Transform(HL_SHA256_CTX* context,\n\t\t\t              const sha2_word32* data);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tInitialize the context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA256_Init(HL_SHA256_CTX *context);\n\n\t\t/**\n\t\t *  @brief\tUpdates the context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA256_Update(HL_SHA256_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tEnds the sha256 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA256_End(HL_SHA256_CTX* context,\n\t\t\t         char buffer[SHA256_DIGEST_STRING_LENGTH]);\n\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha256wrapper.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha256wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha256wrapper \n *  \t\tclass.\n *  @date \tDi 25 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha256wrapper.h\"\n#include \"hl_sha256.h\"\n\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha256wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA256_DIGEST_STRING_LENGTH];\n\tsha256->SHA256_End(&context,(char*)buff);\n\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha256wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha256 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha256wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha256->SHA256_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha256wrapper::resetContext(void)\n{\n\tsha256->SHA256_Init(&context);\n}\n\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha256wrapper::getTestHash(void)\n{\n\treturn \"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha256wrapper::sha256wrapper()\n{\n\tthis->sha256 = new SHA256();\t\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha256wrapper::~sha256wrapper()\n{\n\tdelete sha256;\n}\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha256wrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha256wrapper.h\n *  @brief\tThis file contains the definition of the sha256wrapper \n *  \t\tclass.\n *  @date \tDi 25 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA256WRAPPER_H\n#define SHA256WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha256.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n\n/**\n *  @brief \tThis class represents the SHA256 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha256 hash.\n *  \t\tJust create an instance of sha256wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha256example.cpp\n *\n *  \t\tsha256wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha256wrapper : public hashwrapper\n{\n\tprivate:\n\t\t\t/**\n\t\t\t * SHA256 access\n\t\t\t */\n\t\t\tSHA256 *sha256;\n\n\t\t\t/**\n\t\t\t * SHA256 context\n\t\t\t */\n\t\t\tHL_SHA256_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\t\t\t\t\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha256wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha256wrapper();\n\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha2ext.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from \n * the sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n//---------------------------------------------------------------------- \n\n/**\n *  @file \tsha2ext.cpp\n *  @brief\tThis file contains the implementation of the SHA2Ext class\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n// Standard C includes\n#include <string.h>\t\n#include <assert.h>\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha2ext.h\"\n#include \"hl_sha2mac.h\"\n\n//---------------------------------------------------------------------- \n\n/* Hash constant words K for SHA-384 and SHA-512: */\nconst static sha2_word64 K512[80] = {\n\t0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,\n\t0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,\n\t0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,\n\t0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,\n\t0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,\n\t0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,\n\t0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,\n\t0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,\n\t0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,\n\t0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,\n\t0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,\n\t0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,\n\t0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,\n\t0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,\n\t0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,\n\t0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,\n\t0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,\n\t0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,\n\t0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,\n\t0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,\n\t0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,\n\t0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,\n\t0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,\n\t0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,\n\t0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,\n\t0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,\n\t0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,\n\t0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,\n\t0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,\n\t0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,\n\t0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,\n\t0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,\n\t0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,\n\t0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,\n\t0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,\n\t0x113f9804bef90daeULL, 0x1b710b35131c471bULL,\n\t0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,\n\t0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,\n\t0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,\n\t0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL\n};\n\n/* Initial hash value H for SHA-384 */\nconst static sha2_word64 sha384_initial_hash_value[8] = {\n\t0xcbbb9d5dc1059ed8ULL,\n\t0x629a292a367cd507ULL,\n\t0x9159015a3070dd17ULL,\n\t0x152fecd8f70e5939ULL,\n\t0x67332667ffc00b31ULL,\n\t0x8eb44a8768581511ULL,\n\t0xdb0c2e0d64f98fa7ULL,\n\t0x47b5481dbefa4fa4ULL\n};\n\n/* Initial hash value H for SHA-512 */\nconst static sha2_word64 sha512_initial_hash_value[8] = {\n\t0x6a09e667f3bcc908ULL,\n\t0xbb67ae8584caa73bULL,\n\t0x3c6ef372fe94f82bULL,\n\t0xa54ff53a5f1d36f1ULL,\n\t0x510e527fade682d1ULL,\n\t0x9b05688c2b3e6c1fULL,\n\t0x1f83d9abfb41bd6bULL,\n\t0x5be0cd19137e2179ULL\n};\n\n/*\n *  * Constant used by SHA256/384/512_End() functions for converting the\n *   * digest to a readable hexadecimal character string:\n *    */\nstatic const char *sha2_hex_digits = \"0123456789abcdef\";\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tInitialize the SHA512 context\n *  @param\tcontext The context to init.\n */  \nvoid SHA2ext::SHA512_Init(HL_SHA512_CTX* context) \n{\n\tif (context == (HL_SHA512_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);\n\tcontext->bitcount[0] = context->bitcount[1] =  0;\n}\n\n#ifdef SHA2_UNROLL_TRANSFORM\n\n/* Unrolled SHA-512 round macros: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\n#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tREVERSE64(*data++, W512[j]); \\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \\\n             K512[j] + W512[j]; \\\n\t(d) += T1, \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \\\n\tj++\n\n\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \\\n             K512[j] + (W512[j] = *data++); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \\\n\tj++\n\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND512(a,b,c,d,e,f,g,h)\t\\\n\ts0 = W512[(j+1)&0x0f]; \\\n\ts0 = sigma0_512(s0); \\\n\ts1 = W512[(j+14)&0x0f]; \\\n\ts1 = sigma1_512(s1); \\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \\\n             (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \\\n\tj++\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA2ext::SHA512_Transform(HL_SHA512_CTX* context, const sha2_word64* data) {\n\tsha2_word64\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word64\tT1, *W512 = (sha2_word64*)context->buffer;\n\tint\t\tj;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n\t\tROUND512_0_TO_15(a,b,c,d,e,f,g,h);\n\t\tROUND512_0_TO_15(h,a,b,c,d,e,f,g);\n\t\tROUND512_0_TO_15(g,h,a,b,c,d,e,f);\n\t\tROUND512_0_TO_15(f,g,h,a,b,c,d,e);\n\t\tROUND512_0_TO_15(e,f,g,h,a,b,c,d);\n\t\tROUND512_0_TO_15(d,e,f,g,h,a,b,c);\n\t\tROUND512_0_TO_15(c,d,e,f,g,h,a,b);\n\t\tROUND512_0_TO_15(b,c,d,e,f,g,h,a);\n\t} while (j < 16);\n\n\t/* Now for the remaining rounds up to 79: */\n\tdo {\n\t\tROUND512(a,b,c,d,e,f,g,h);\n\t\tROUND512(h,a,b,c,d,e,f,g);\n\t\tROUND512(g,h,a,b,c,d,e,f);\n\t\tROUND512(f,g,h,a,b,c,d,e);\n\t\tROUND512(e,f,g,h,a,b,c,d);\n\t\tROUND512(d,e,f,g,h,a,b,c);\n\t\tROUND512(c,d,e,f,g,h,a,b);\n\t\tROUND512(b,c,d,e,f,g,h,a);\n\t} while (j < 80);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = 0;\n}\n\n#else /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA2ext::SHA512_Transform(HL_SHA512_CTX* context, const sha2_word64* data) {\n\tsha2_word64\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word64\tT1, T2, *W512 = (sha2_word64*)context->buffer;\n\tint\t\tj;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Convert TO host byte order */\n\t\tREVERSE64(*data++, W512[j]);\n\t\t/* Apply the SHA-512 compression function to update a..h */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\t/* Apply the SHA-512 compression function to update a..h with copy */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\tT2 = Sigma0_512(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 16);\n\n\tdo {\n\t\t/* Part of the message block expansion: */\n\t\ts0 = W512[(j+1)&0x0f];\n\t\ts0 = sigma0_512(s0);\n\t\ts1 = W512[(j+14)&0x0f];\n\t\ts1 =  sigma1_512(s1);\n\n\t\t/* Apply the SHA-512 compression function to update a..h */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +\n\t\t     (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);\n\t\tT2 = Sigma0_512(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 80);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = T2 = 0;\n}\n#endif /* SHA2_UNROLL_TRANSFORM */\n\nvoid SHA2ext::SHA512_Update(HL_SHA512_CTX* context, const sha2_byte *data, unsigned int len) {\n\tunsigned int\tfreespace, usedspace;\n\n\tif (len == 0) {\n\t\t/* Calling with no data is valid - we do nothing */\n\t\treturn;\n\t}\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0 && data != (sha2_byte*)0);\n\n\tusedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;\n\tif (usedspace > 0) {\n\t\t/* Calculate how much free space is available in the buffer */\n\t\tfreespace = SHA512_BLOCK_LENGTH - usedspace;\n\n\t\tif (len >= freespace) {\n\t\t\t/* Fill the buffer completely and process it */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);\n\t\t\tADDINC128(context->bitcount, freespace << 3);\n\t\t\tlen -= freespace;\n\t\t\tdata += freespace;\n\t\t\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n\t\t} else {\n\t\t\t/* The buffer is not yet full */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, len);\n\t\t\tADDINC128(context->bitcount, len << 3);\n\t\t\t/* Clean up: */\n\t\t\tusedspace = freespace = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\twhile (len >= SHA512_BLOCK_LENGTH) {\n\t\t/* Process as many complete blocks as we can */\n\t\tSHA512_Transform(context, (sha2_word64*)data);\n\t\tADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);\n\t\tlen -= SHA512_BLOCK_LENGTH;\n\t\tdata += SHA512_BLOCK_LENGTH;\n\t}\n\tif (len > 0) {\n\t\t/* There's left-overs, so save 'em */\n\t\tMEMCPY_BCOPY(context->buffer, data, len);\n\t\tADDINC128(context->bitcount, len << 3);\n\t}\n\t/* Clean up: */\n\tusedspace = freespace = 0;\n}\n\nvoid SHA2ext::SHA512_Last(HL_SHA512_CTX* context) \n{\n\tunsigned int\tusedspace;\n\n\tusedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t/* Convert FROM host byte order */\n\tREVERSE64(context->bitcount[0],context->bitcount[0]);\n\tREVERSE64(context->bitcount[1],context->bitcount[1]);\n#endif\n\tif (usedspace > 0) {\n\t\t/* Begin padding with a 1 bit: */\n\t\tcontext->buffer[usedspace++] = 0x80;\n\n\t\tif (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {\n\t\t\t/* Set-up for the last transform: */\n\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);\n\t\t} else {\n\t\t\tif (usedspace < SHA512_BLOCK_LENGTH) {\n\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);\n\t\t\t}\n\t\t\t/* Do second-to-last transform: */\n\t\t\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n\n\t\t\t/* And set-up for the last transform: */\n\t\t\tMEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);\n\t\t}\n\t} else {\n\t\t/* Prepare for final transform: */\n\t\tMEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);\n\n\t\t/* Begin padding with a 1 bit: */\n\t\t*context->buffer = 0x80;\n\t}\n\t/* Store the length of input data (in bits): */\n\t*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];\n\t*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];\n\n\t/* Final transform: */\n\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n}\n\nvoid SHA2ext::SHA512_Final(sha2_byte digest[], HL_SHA512_CTX* context) \n{\n\tsha2_word64\t*d = (sha2_word64*)digest;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tSHA512_Last(context);\n\n\t\t/* Save the hash data for output: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 8; j++) {\n\t\t\t\tREVERSE64(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Zero out state data */\n\tMEMSET_BZERO(context, sizeof(context));\n}\n\nchar* SHA2ext::SHA512_End(HL_SHA512_CTX* context, char buffer[]) \n{\n\tsha2_byte\tdigest[SHA512_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA512_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA512_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);\n\treturn buffer;\n}\n\nvoid SHA2ext::SHA384_Init(HL_SHA_384_CTX* context) \n{\n\tif (context == (HL_SHA_384_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);\n\tcontext->bitcount[0] = context->bitcount[1] = 0;\n}\n\nvoid SHA2ext::SHA384_Update(HL_SHA_384_CTX* context, const sha2_byte* data, unsigned int len) \n{\n\tSHA512_Update((HL_SHA512_CTX*)context, data, len);\n}\n\nvoid SHA2ext::SHA384_Final(sha2_byte digest[], HL_SHA_384_CTX* context) \n{\n\tsha2_word64\t*d = (sha2_word64*)digest;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA_384_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tSHA512_Last((HL_SHA512_CTX*)context);\n\n\t\t/* Save the hash data for output: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 6; j++) {\n\t\t\t\tREVERSE64(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Zero out state data */\n\tMEMSET_BZERO(context, sizeof(context));\n}\n\nchar* SHA2ext::SHA384_End(HL_SHA_384_CTX* context, char buffer[]) \n{\n\tsha2_byte\tdigest[SHA384_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA_384_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA384_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA384_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);\n\treturn buffer;\n}\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha2ext.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from the\n * sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha2ext.h\n *  @brief\tThis file contains the declaration of the SHA384 and \n *  \t\tSHA512 classes\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA2ext_H\n#define SHA2ext_H\n\n//----------------------------------------------------------------------\n//lenght defines\n#define SHA384_BLOCK_LENGTH             128\n#define SHA384_DIGEST_LENGTH            48\n#define SHA384_DIGEST_STRING_LENGTH     (SHA384_DIGEST_LENGTH * 2 + 1)\n#define SHA512_BLOCK_LENGTH             128\n#define SHA512_DIGEST_LENGTH            64\n#define SHA512_DIGEST_STRING_LENGTH     (SHA512_DIGEST_LENGTH * 2 + 1)\n#define SHA512_SHORT_BLOCK_LENGTH\t(SHA512_BLOCK_LENGTH - 16)\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\n\n/**\n * Exactly 1 byte \n */ \ntypedef hl_uint8  sha2_byte;\t\n\n/**\n * Exactly 4 bytes \n */\ntypedef hl_uint32 sha2_word32;\t\n\n/**\n * Exactly 8 bytes \n */ \ntypedef hl_uint64 sha2_word64;\t\n\n/**\n * @brief This struct represents a SHA512-hash context\n */\ntypedef struct HL_SHA512_CTX \n{\n\thl_uint64       state[8];\n\thl_uint64       bitcount[2];\n\thl_uint8        buffer[SHA512_BLOCK_LENGTH];\n} HL_SHA512_CTX;\n\n\n/**\n * @brief This struct represents a SHA384-hash context\n */\ntypedef HL_SHA512_CTX HL_SHA_384_CTX;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe SHA384 and SHA512 algorithm.\n *\n *   \t\tBasically the class provides six public member-functions\n *   \t\tto create a hash:  SHA384_Init(), SHA384_Update(), SHA384_End(),\n *\t\tSHA512_Init(), SHA512_Update() and SHA512_End().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha384wrapper or sha512wrapper classes.\n */  \nclass SHA2ext\n{\n\tprivate:\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha384 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA384_Final(hl_uint8 digest[SHA384_DIGEST_LENGTH],\n\t\t\t          HL_SHA_384_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha512 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA512_Final(hl_uint8 digest[SHA512_DIGEST_LENGTH],\n\t\t\t       \t  HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal method\n\t\t *\n\t\t *  \t\tused by SHA512 and SHA384\n\t\t *  @author\tBenjamin Grdelbach\n\t\t *  @param\tcontext The context of the operation\n\t\t */  \n\t\tvoid SHA512_Last(HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal data transformation\n\t\t *  @param\tcontext The context to use\n\t\t *  @param\tdata The data to transform\t\n\t\t */  \n\t\tvoid SHA512_Transform(HL_SHA512_CTX* context,\n\t\t\t              const sha2_word64* data);\n\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tInitialize the SHA384 context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA384_Init(HL_SHA_384_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInitialize the SHA512 context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA512_Init(HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief\tUpdates the SHA512 context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA384_Update(HL_SHA_384_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief\tUpdates the SHA284 context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA512_Update(HL_SHA512_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tEnds the SHA384 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA384_End(HL_SHA_384_CTX* context,\n\t\t\t       \t char buffer[SHA384_DIGEST_STRING_LENGTH]);\n\n\t\t/**\n\t\t *  @brief \tEnds the SHA512 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA512_End(HL_SHA512_CTX* context,\n\t\t\t       \t char buffer[SHA512_DIGEST_STRING_LENGTH]);\n\n};\n\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha2mac.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from\n * the sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha2mac.h\n *  @brief\tThis file contains useful macros for use with SHA384\n *  \t\tand SHA512\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n/*\n* SHA-256/384/512 Machine Architecture Definitions \n* BYTE_ORDER NOTE:\n*\n* Please make sure that your system defines BYTE_ORDER.  If your\n* architecture is little-endian, make sure it also defines\n* LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are\n* equivilent.\n*\n* If your system does not define the above, then you can do so by\n* hand like this:\n*\n*   #define LITTLE_ENDIAN 1234\n*   #define BIG_ENDIAN    4321\n*\n* And for little-endian machines, add:\n*\n*   #define BYTE_ORDER LITTLE_ENDIAN \n*\n* Or for big-endian machines:\n*\n*   #define BYTE_ORDER BIG_ENDIAN\n*\n* The FreeBSD machine this was written on defines\n* BYTE_ORDER  appropriately by including <sys/types.h> \n* (which in  turn includes <machine/endian.h> where the appropriate\n* definitions are actually made).\n*/\n#define LITTLE_ENDIAN 1234\n#define BYTE_ORDER LITTLE_ENDIAN \n#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)\n#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN\n#endif\n\n/*** ENDIAN REVERSAL MACROS *******************************************/\n#if BYTE_ORDER == LITTLE_ENDIAN\n#define REVERSE32(w,x)  { \\\n\tsha2_word32 tmp = (w); \\\n\ttmp = (tmp >> 16) | (tmp << 16); \\\n\t(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \\\n}\n#define REVERSE64(w,x)  { \\\n\tsha2_word64 tmp = (w); \\\n\ttmp = (tmp >> 32) | (tmp << 32); \\\n\ttmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \\\n\t((tmp & 0x00ff00ff00ff00ffULL) << 8); \\\n\t(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \\\n\t((tmp & 0x0000ffff0000ffffULL) << 16); \\\n}\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n/*\n * Macro for incrementally adding the unsigned 64-bit integer n to the\n * unsigned 128-bit integer (represented using a two-element array of\n * 64-bit words):\n */\n#define ADDINC128(w,n)  { \\\n\t(w)[0] += (sha2_word64)(n); \\\n\tif ((w)[0] < (n)) { \\\n\t\t(w)[1]++; \\\n\t} \\\n}\n\n/*\n * Macros for copying blocks of memory and for zeroing out ranges\n * of memory.  Using these macros makes it easy to switch from\n * using memset()/memcpy() and using bzero()/bcopy().\n *\n * Please define either SHA2_USE_MEMSET_MEMCPY or define\n * SHA2_USE_BZERO_BCOPY depending on which function set you\n * choose to use:\n */\n#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)\n/* Default to memset()/memcpy() if no option is specified */\n#define SHA2_USE_MEMSET_MEMCPY  1\n#endif\n#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)\n/* Abort with an error if BOTH options are defined */\n#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!\n#endif\n\n#ifdef SHA2_USE_MEMSET_MEMCPY\n#define MEMSET_BZERO(p,l)       memset((p), 0, (l))\n#define MEMCPY_BCOPY(d,s,l)     memcpy((d), (s), (l))\n#endif\n#ifdef SHA2_USE_BZERO_BCOPY\n#define MEMSET_BZERO(p,l)       bzero((p), (l))\n#define MEMCPY_BCOPY(d,s,l)     bcopy((s), (d), (l))\n#endif\n\n/*** THE SIX LOGICAL FUNCTIONS ****************************************\n *\n * Bit shifting and rotation (used by the six SHA-XYZ logical functions:\n *\n *   NOTE:  The naming of R and S appears backwards here (R is a SHIFT and\n *   S is a ROTATION) because the SHA-256/384/512 description document\n *   (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this\n *   same \"backwards\" definition.\n */\n/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */\n#define R(b,x)          ((x) >> (b))\n/* 32-bit Rotate-right (used in SHA-256): */\n#define S32(b,x)        (((x) >> (b)) | ((x) << (32 - (b))))\n/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */\n#define S64(b,x)        (((x) >> (b)) | ((x) << (64 - (b))))\n\n/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */\n#define Ch(x,y,z)       (((x) & (y)) ^ ((~(x)) & (z)))\n#define Maj(x,y,z)      (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n\n/* Four of six logical functions used in SHA-256: */\n#define Sigma0_256(x)   (S32(2,  (x)) ^ S32(13, (x)) ^ S32(22, (x)))\n#define Sigma1_256(x)   (S32(6,  (x)) ^ S32(11, (x)) ^ S32(25, (x)))\n#define sigma0_256(x)   (S32(7,  (x)) ^ S32(18, (x)) ^ R(3 ,   (x)))\n#define sigma1_256(x)   (S32(17, (x)) ^ S32(19, (x)) ^ R(10,   (x)))\n\n/* Four of six logical functions used in SHA-384 and SHA-512: */\n#define Sigma0_512(x)   (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))\n#define Sigma1_512(x)   (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))\n#define sigma0_512(x)   (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7,   (x)))\n#define sigma1_512(x)   (S64(19, (x)) ^ S64(61, (x)) ^ R( 6,   (x)))\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha384wrapper.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n/**\n *  @file \thl_sha384wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha384wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha384wrapper.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha384wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA384_DIGEST_STRING_LENGTH];\n\tsha384->SHA384_End(&context,(char*)buff);\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha384wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha384 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha384wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha384->SHA384_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha384wrapper::resetContext(void)\n{\n\tsha384->SHA384_Init(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha384wrapper::getTestHash(void)\n{ \n\treturn \"ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha384wrapper::sha384wrapper()\n{\n\tthis->sha384 = new SHA2ext();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha384wrapper::~sha384wrapper()\n{\n\tdelete sha384;\n}\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha384wrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha384wrapper.h\n *  @brief\tThis file contains the definition of the sha384wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA384WRAPPER_H\n#define SHA384WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha2ext.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA384 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha384 hash.\n *  \t\tJust create an instance of sha384wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha384example.cpp\n *\n *  \t\tsha384wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha384wrapper : public hashwrapper\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t * SHA384 access\n\t\t\t * via extended SHA2\n\t\t\t */\n\t\t\tSHA2ext *sha384;\n\n\t\t\t/**\n\t\t\t * SHA384 context\n\t\t\t */\n\t\t\tHL_SHA_384_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha384wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha384wrapper();\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha512wrapper.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha512wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha512wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha512wrapper.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha512wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA512_DIGEST_STRING_LENGTH];\n\tsha512->SHA512_End(&context,(char*)buff);\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha512wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha512 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha512wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha512->SHA512_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha512wrapper::resetContext(void)\n{\n\tsha512->SHA512_Init(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha512wrapper::getTestHash(void)\n{\n\treturn \"07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha512wrapper::sha512wrapper()\n{\n\tthis->sha512 = new SHA2ext();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha512wrapper::~sha512wrapper()\n{\n\tdelete sha512;\n}\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_sha512wrapper.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha512wrapper.h\n *  @brief\tThis file contains the definition of the sha512wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA512WRAPPER_H\n#define SHA512WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha2ext.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA512 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha512 hash.\n *  \t\tJust create an instance of sha512wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha512example.cpp\n *\n *  \t\tsha512wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha512wrapper : public hashwrapper\n{\n\tprivate:\n\t\t\t/**\n\t\t\t * SHA512 access\n\t\t\t * via extended SHA2\n\t\t\t */\n\t\t\tSHA2ext *sha512;\n\n\t\t\t/**\n\t\t\t * SHA512 context\n\t\t\t */\n\t\t\tHL_SHA512_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha512wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha512wrapper();\n\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_types.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_types.h\n *  @brief\tThis file defines some global types\n *  @date \tSo 13 Jan 2008\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef HLTYPES_H\n#define HLTYPES_H\n\n//----------------------------------------------------------------------\t\n\n/**\n * exactly 1 Byte\n */\ntypedef unsigned char \thl_uint8;\n\n/**\n * at least 2 Byte\n */\ntypedef unsigned short int \thl_uint16;\n\n/**\n * at least 4 Byte\n */\ntypedef unsigned int hl_uint32;\n\n/**\n* at least 8 Byte\n*/\n#ifdef __GNUC__\n\ttypedef unsigned long long int\thl_uint64;\n#elif __MINGW32__\n\ttypedef unsigned long long int\thl_uint64;\n#elif _MSC_VER\n\ttypedef unsigned __int64 hl_uint64;\n#else\n\t#error \"Unsuppported compiler.\" \\\n               \"Please use GCC,MINGW,MSVC \" \\\n\t       \" or define hl_uint64 for your compiler in hl_types.h line 62\"\n#endif\n\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_wrapperfactory.cpp.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grüdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_wrapperfactory.cpp\n *  @brief\tThis file contains the implementation of the \n *\t\twrapperfactory class\n *  @date \tDO 13 Oct 2011\n */  \n\n//---------------------------------------------------------------------- \n#include <algorithm>\n#include \"hl_wrapperfactory.h\"\n#include \"hashlibpp.h\"\n\n\n\n//---------------------------------------------------------------------- \n//public member functions\n\n/**\n * @brief\tSimple factory-method to create a hashwrapper\n * \n * @param\ttype The type of the hash algorithm to create a wrapper for\n * @return\tA hashwrapper for the fiven type\n */\nhashwrapper* wrapperfactory::create(HL_Wrappertype type)\n{\n\tif(type == HL_MD5)\n\t{\n\t\treturn new md5wrapper();\n\t}\n\telse if(type == HL_SHA1)\n\t{\n\t\treturn new sha1wrapper();\n\t}\n\telse if(type == HL_SHA256)\n\t{\n\t\treturn new sha256wrapper();\n\t}\n\telse if(type == HL_SHA384)\n\t{\n\t\treturn new sha384wrapper();\n\t}\n\telse if(type == HL_SHA512)\n\t{\n\t\treturn new sha512wrapper();\n\t}\n\n\tthrow hlException(HL_UNKNOWN_HASH_TYPE,\"Unknown hashtype\");\n}\n\n/**\n * @brief\tSimple factory-method to create a hashwrapper\n * \n * @param\ttype The type of the hash algorithm to create a wrapper for\n * @return\tA hashwrapper for the fiven type\n */\nhashwrapper* wrapperfactory::create(std::string type)\n{\n \tstd::transform(type.begin(), type.end(), type.begin(), ::toupper);\n\tif(type == \"MD5\")\n\t{\n\t\treturn new md5wrapper();\n\t}\n\telse if(type == \"SHA1\")\n\t{\n\t\treturn new sha1wrapper();\n\t}\n\telse if(type == \"SHA256\")\n\t{\n\t\treturn new sha256wrapper();\n\t}\n\telse if(type == \"SHA384\")\n\t{\n\t\treturn new sha384wrapper();\n\t}\n\telse if(type == \"SHA512\")\n\t{\n\t\treturn new sha512wrapper();\n\t}\n\treturn NULL;\n}\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/.svn/text-base/hl_wrapperfactory.h.svn-base",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grüdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_wrapperfactory.h\n *  @brief\tThis file contains a simple hashwrapper factory\n *  @date \tDO 13 Oct 2011\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef WRAPPERFACTORY_H\n#define WRAPPERFACTORY_H\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n\n//----------------------------------------------------------------------\t\n//enumeration\n\n/*\n * definition of the supported hashtypes \n */\nenum HL_Wrappertype { HL_MD5, HL_SHA1, HL_SHA256, HL_SHA384, HL_SHA512 };\n\n//---------------------------------------------------------------------- \n\n/**\n *  @brief\tThis class represents a simple factory for creating wrappers.\n *\n *\t\tU can use this class for dynamicly create wrappers of a given\n * \t\ttype at runtime. \n */\nclass wrapperfactory\n{\n\tpublic:\n\n\t\t/**\n\t\t * @brief\tSimple factory-method to create a hashwrapper\n\t\t * \n\t\t * @param\ttype The type of the hash algorithm to create a wrapper for\n\t\t * @return\tA hashwrapper for the fiven type\n\t\t */\n\t\thashwrapper* create(HL_Wrappertype type);\n\n\t\t/**\n\t\t * @brief\tSimple factory-method to create a hashwrapper\n\t\t * \n\t\t * @param\ttype the simple name of the type for example \"md5\"\n\t\t * @return\tA hashwrapper for the fiven type\n\t\t */\n\t\thashwrapper* create(std::string type);\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/Makefile",
    "content": "# \n# hashlib++ - a simple hash library for C++\n# \n# Copyright (c) 2007-2010 Benjamin Grdelbach\n# \n# Redistribution and use in source and binary forms, with or without modification,\n# are permitted provided that the following conditions are met:\n# \n# \t1)     Redistributions of source code must retain the above copyright\n# \t       notice, this list of conditions and the following disclaimer.\n# \n# \t2)     Redistributions in binary form must reproduce the above copyright\n# \t       notice, this list of conditions and the following disclaimer in\n# \t       the documentation and/or other materials provided with the\n# \t       distribution.\n# \t     \n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n#----------------------------------------------------------------------- \n#\n# This is the hashlib++ makefile. It builds and installs hashlib++ as a\n# static library. See the documentation for more information.\n#\n# Type 'make' to build the lib.\n# Type 'make install' to build and install the lib.\n# Type 'make clean' to clean the object files\n#\n# Note for FreeBSD users:\n# use gmake from \"/usr/ports/devel/gmake\"\n#\n# Benjamin Grdelbach\n# Mi 10 Okt 2007\n#\n#----------------------------------------------------------------------- \n# Edit the following lines to meet your needs\n\n# Path to install the headerfiles\nINCLUDE_PATH = /usr/include/hashlib++\n\n# Path to install the library\nLIB_PATH = /usr/local/lib/\n\n# Compiler to use\nCOMPILER = g++\n\n# Global options for the compiler\nCOPTIONS = -ansi -Wall -Wextra\n\n#----------------------------------------------------------------------- \n# DON'T CHANGE ANYTHING BELOW\n#----------------------------------------------------------------------- \n\nifdef DEBUG\n\tCOPTIONS += -g\nelse\n\tCOPTIONS += -O3 -fomit-frame-pointer\nendif\n\nGCC = $(COMPILER) $(COPTIONS)\n\n#----------------------------------------------------------------------- \n#Main-Target\nall:\t\tMD5 SHA1 SHA256 SHA2EXT CORE LIB\n\n#----------------------------------------------------------------------- \n#all header-files\n\n\t\t\nHEADER = \thl_hashwrapper.h \\\n\t\thl_wrapperfactory.h \\\n\t\thl_exception.h \\\n\t\thl_md5.h hl_md5wrapper.h \\\n\t\thl_sha1.h hl_sha1wrapper.h \\\n\t\thl_sha2mac.h \\\n\t\thl_sha256.h hl_sha256wrapper.h \\\n\t\thl_sha2ext.h hl_sha384wrapper.h  hl_sha512wrapper.h \\\n\t\thl_types.h \\\n\t\thashlibpp.h\n\n#----------------------------------------------------------------------- \nCORE = \t\thl_wrapperfactory.o\nCORE:\t\thl_wrapperfactory.o\n\nhl_wrapperfactory.o:\thl_wrapperfactory.cpp hl_wrapperfactory.h\n\t\t\t$(GCC) -c hl_wrapperfactory.cpp\n\t\t\n#----------------------------------------------------------------------- \n# MD5 Targets\n\nMD5 = \t\thl_md5.o \\\n      \t\thl_md5wrapper.o\n\nMD5:\t\thl_md5.o hl_md5wrapper.o\n\nhl_md5.o:\thl_md5.cpp hl_md5.h\n\t\t$(GCC) -c hl_md5.cpp\n\nhl_md5wrapper.o:\thl_md5wrapper.cpp hl_md5wrapper.h\n\t\t\t$(GCC) -c hl_md5wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA1 Targets\n\nSHA1 = \t\thl_sha1.o \\\n       \t\thl_sha1wrapper.o\n\nSHA1:\t\thl_sha1.o hl_sha1wrapper.o\n\nhl_sha1.o:\thl_sha1.cpp hl_sha1.h\n\t\t$(GCC) -c hl_sha1.cpp\n\nhl_sha1wrapper.o:\thl_sha1wrapper.cpp hl_sha1wrapper.h\n\t\t\t$(GCC) -c hl_sha1wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA256 Targets\n\nSHA256 = \thl_sha256.o \\\n\t \thl_sha256wrapper.o\n\nSHA256:\t\thl_sha256.o hl_sha256wrapper.o\n\nhl_sha256.o:\thl_sha256.cpp hl_sha256.h hl_sha2mac.h\n\t\t$(GCC) -c hl_sha256.cpp\n\nhl_sha256wrapper.o:\thl_sha256wrapper.cpp hl_sha256wrapper.h\n\t\t\t$(GCC) -c hl_sha256wrapper.cpp\n\n#----------------------------------------------------------------------- \n# SHA2-ext Targets\n\nSHA2EXT = \thl_sha2ext.o \\\n\t\thl_sha384wrapper.o \\\n\t\thl_sha512wrapper.o \n\n\t\t\nSHA2EXT:\thl_sha2ext.o hl_sha384wrapper.o hl_sha512wrapper.o\n\nhl_sha2ext.o:\thl_sha2ext.cpp hl_sha2ext.h hl_sha2mac.h\n\t\t$(GCC) -c hl_sha2ext.cpp\n\nhl_sha384wrapper.o:\thl_sha384wrapper.cpp hl_sha384wrapper.h\n\t\t\t$(GCC) -c hl_sha384wrapper.cpp\n\nhl_sha512wrapper.o:\thl_sha512wrapper.cpp hl_sha512wrapper.h\n\t\t\t$(GCC) -c hl_sha512wrapper.cpp\n\n#----------------------------------------------------------------------- \n# Creating a static lib using ar\n\nLIB:\t\tMD5 SHA1 SHA256\t\t\t\n\t\tar rs libhl++.a $(MD5) $(SHA1) $(SHA256) $(SHA2EXT) $(CORE)\n\n#----------------------------------------------------------------------- \n#Installing the lib\ninstall:\tall \n\t\tmkdir -p $(INCLUDE_PATH); \\\n\t\tmkdir -p $(LIB_PATH); \\\n\t\tcp libhl++.a $(LIB_PATH) && \\\n\t\tcp $(HEADER) $(INCLUDE_PATH)\n\t\t-@ echo \"\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"------------------------------\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"hashlib++ has been installed to:\"\n\t\t-@ echo \"include files: $(INCLUDE_PATH)\"\n\t\t-@ echo \"library files: $(LIB_PATH)\"\n\t\t-@ echo \"\"\n\t\t-@ echo \"------------------------------\"\n\n#----------------------------------------------------------------------- \n# Cleaning object-files\n\nclean:\n\t\trm *.o\n\t\t-@ echo \"cleaned up \"\n\t\t-@ echo \"\"\n\n#----------------------------------------------------------------------- \n#EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hashlibpp.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\n\n/**\n *  @file \thashlibpp.h\n *  @brief\tThis file is just an include wrapper\n *  @date \tSa  2 Feb\n */  \n\n//----------------------------------------------------------------------\n#ifndef HASHLIBPP_H\n#define HASHLIBPP_H\n\n//----------------------------------------------------------------------\n//current version of hashilb++\n#define _HASHLIBPP_VERSION_ \"0.3.4\"\n\n//----------------------------------------------------------------------\n\n#include \"hl_exception.h\"\n#include \"hl_wrapperfactory.h\"\n#include \"hl_hashwrapper.h\"\n#include \"hl_md5wrapper.h\"\n#include \"hl_sha1wrapper.h\"\n#include \"hl_sha256wrapper.h\"\n#include \"hl_sha384wrapper.h\"\n#include \"hl_sha512wrapper.h\"\n\n\n//----------------------------------------------------------------------\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_exception.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_exception.h\n *  @brief\tThis file contains the hashlib++ exception class\n *  @date \tSa 24 Nov 2007\n */  \n\n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef HL_EXCEPTION_H\n#define HL_EXCEPTION_H\n\n//---------------------------------------------------------------------- \n//STL\n#include <string>\n\n//----------------------------------------------------------------------\n\n/**\n * definition of hashlib++ errornumbers\n */\ntypedef enum hlerrors\n{\n\tHL_NO_ERROR = 0,\n\tHL_FILE_READ_ERROR,\n\tHL_VERIFY_TEST_FAILED,\n\tHL_UNKNOWN_SEE_MSG,\n\tHL_UNKNOWN_HASH_TYPE\n} hlerror;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief\tThis class represents a exception within the hashlib++\n *  \t\tproject\n */  \nclass hlException\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t * Error Number\n\t\t\t */\n\t\t\thlerror iError;\n\n\t\t\t/**\n\t\t\t * Error message as string\n\t\t\t */\n\t\t\tstd::string strMessge;\n\t\t\t\n\n\tpublic:\n\t\t\t/**\n\t\t\t *  @brief \tconstructor\n\t\t\t *  @param\ter\tError number\n\t\t\t *  @param\tm\tError message\n\t\t\t */  \n\t\t\thlException(hlerror er, std::string m)\n\t\t\t{\n\t\t\t\tthis->iError = er;\n\t\t\t\tthis->strMessge = m;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \tconstructor\n\t\t\t *  @param\tm\tError Message\n\t\t\t */  \n\t\t\thlException(std::string m)\n\t\t\t{\n\t\t\t\tthis->iError = HL_UNKNOWN_SEE_MSG;\n\t\t\t\tthis->strMessge = m;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \treturns the error message\n\t\t\t *  @return\tthe error message\n\t\t\t */  \n\t\t\tstd::string error_message(void)\n\t\t\t{\n\t\t\t\treturn strMessge;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t *  @brief \treturns the error number\n\t\t\t *  @return\tthe error number\n\t\t\t */  \n\t\t\thlerror error_number(void)\n\t\t\t{\n\t\t\t\treturn iError;\n\t\t\t}\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_hashwrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2011 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n//doxygen mainpage\n\n/**\n * @mainpage  hashlib++ source documentation\n *\n * \t      <div align=\"center\"><b>Version 0.3.4</b></div>\n * \t      \n *\n * \t      @section intro Introduction\n * \t      hashlib++ a simple hash library for C++  \\n\n * \t      Copyright (c) 2007-2011 Benjamin Gr&uuml;delbach\n *\n *\n *\n * \t      hashlib++ is a simple and very easy to use library to create a\n * \t      cryptographic checksum called \"hash\". hashlib++ is written in\n * \t      plain C++ and should work with every compiler and platform.\n * \t      hashlib++ is released under the BSD-license and\n * \t      therefore free software.\n *\n * \t      @section about About this document\n *\n * \t      This is the documentation about the hashlib++ sourcecode.\n * \t      Since it contains some internal information it its helpfull\n * \t      but not necessary to read for the average user.\n * \t      If you are new to hashlib++ you should start with reading\n * \t      the README.TXT file which contains all relevant information\n * \t      to start using this library.\n *\n */\n\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_hashwrapper.h\n *  @brief\tThis file contains the hashwrapper base class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef HASHWRAPPER_H\n#define HASHWRAPPER_H\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//C includes\n//#include <stdio.h>\n#include <fstream>\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_exception.h\"\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the baseclass for all subwrappers\n *\n *  hashwrapper is the abstract base class of all subwrappers like md5wrapper\n *  or sha1wrapper. This class implements two simple and easy to use\n *  memberfunctions to create a hash based on a string or a file.\n *  ( getHashFromString() and getHashFromFile() )\n *\n *  getHashFromString() calls resetContext(), updateContext() and hashIt()\n *  in this order. These three memberfunctions are pure virtual and have to\n *  be implemented by the subclasses.\n *\n *  getHashFromFile() calls resetContext() before reading the specified file\n *  in 1024 byte blocks which are forwarded to the hash context by calling\n *  updateContext(). Finaly hashIt() is called to return the hash.\n */  \nclass hashwrapper\n{\n\tprivate:\n\t\tconst std::string teststring;\n\n\tprotected:\n\n\t\t/**\n\t\t *  @brief \tThis method finalizes the hash process\n\t\t *  \t\tand returns the hash as std::string\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @return \tthe created hash as std::string\n\t\t */  \n\t\tvirtual std::string hashIt(void) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis internal member-function\n\t\t *  \t\tconvertes the hash-data to a\n\t\t *  \t\tstd::string (HEX)\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t *  @return\tThe converted data as std::string\n\t\t */  \n\t\tvirtual std::string convToString(unsigned char *data) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis method adds the given data to the \n\t\t *  \t\tcurrent hash context\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t *\n\t\t *  @param \tdata The data to add to the current context\n\t\t *  @param \tlen The length of the data to add\n\t\t */  \n\t\tvirtual void updateContext(unsigned char *data, unsigned int len) = 0;\n\n\t\t/**\n\t\t *  @brief \tThis method resets the current hash context.\n\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t *\n\t\t *  \t\tThis memberfunction is pure virtual and\n\t\t *  \t\thas to be implemented by the subclass\n\t\t */  \n\t\tvirtual void resetContext(void) = 0;\n\n\n\t\t/**\n\t\t * @brief \tThis method should return the hash of the\n\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t * \t\tdog\"\n\t\t */\n\t\tvirtual std::string getTestHash(void) = 0;\n\n\tpublic:\n\n\t\t/**\n\t\t * @brief Default Konstruktor\n\t\t */\n\t\thashwrapper( void ) \n\t\t\t: teststring(\"The quick brown fox jumps over the lazy dog\")\n\t\t{\n\t\t}\n\n\t\t/**\n\t\t *  @brief \tDefault destructor\n\t\t */  \n\t\tvirtual ~hashwrapper ( void ) { };\n\n\t\t/**\n\t\t * @brief Method for testing the concrete implementation\n\t\t */\n\t\tvirtual void test( void )\n\t\t{\n\t\t\tstd::string hash = this->getHashFromString(teststring);\n\t\t\tstd::string verify = this->getTestHash();\n\t\t\tif(hash != verify)\n\t\t\t{\n\t\t\t\tthrow hlException(HL_VERIFY_TEST_FAILED,\n\t\t\t\t\t\t  \"hashlib test-error: \\\"\" + \n\t\t\t\t\t\t  hash +\n\t\t\t\t\t\t  \"\\\" is not \\\"\" + \n\t\t\t\t\t\t  verify + \n\t\t\t\t\t\t  \"\\\" as supposed to be.\");\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t *  @brief \tThis method creates a hash based on the\n\t\t *  \t\tgiven string\n\t\t *\n\t\t *  \t\tThis memberfunctions calls resetContext(),\n\t\t *  \t\tupdateContext() and hashIt() in this order.\n\t\t *  \t\tThese three memberfunctions are pure virtual and have to\n\t\t *  \t\tbe implemented by the subclasses.\n\t\t *\n\t\t *  @param \ttext The text to create a hash from. This\n\t\t *  \t\tparameter is forwarded to updateContext()\n\t\t *  @return \tthe created hash as std::string\n\t\t */  \n\t\tvirtual std::string getHashFromString(std::string text)\n\t\t{\n\t\t\t/*\n\t\t\t * reset the context so that we can start\n\t\t\t * with a new hash process\n\t\t\t */\n\t\t\tresetContext();\n\n\t\t\t/*\n\t\t\t * we update the context with the given text\n\t\t\t */\n\t\t\tupdateContext((unsigned char*) text.c_str(),text.length());\n\n\t\t\t/*\n\t\t\t * now we can close the hash process \n\t\t\t * and return the created hash\n\t\t\t */\n\t\t\treturn this->hashIt(); \n\t\t}\n\n\t\t/**\n\t\t *  @brief \tThis method creates a hash from a given file\n\t\t *\n\t\t *  \t\tFirst of all resetContext() is called before reading the\n\t\t *  \t\tspecified file\tin 1024 byte blocks which are forwarded\n\t\t *  \t\tto the hash context by calling updateContext().\n\t\t *  \t\tFinaly hashIt() is called to return the hash.\n\t\t *  \t\tThese three memberfunctions are pure virtual and have to\n\t\t *  \t\tbe implemented by the subclasses.\n\t\t *\n\t\t *  @param \tfilename The file to created a hash from\n\t\t *\n\t\t *  @return\tThe created hash of the file or \"-1\" in case\n\t\t *  \t\tthe file could not be opened\n\t\t *  @throw\tThrows a hlException if the specified file could not\n\t\t *  \t\tbe opened.\n\t\t */  \n\t\tvirtual std::string getHashFromFile(std::string filename)\n\t\t{\n\t\t\tFILE *file;\n\t\t\tint len;\n\t\t\tunsigned char buffer[1024];\n\n\t\t\t/*\n\t\t\t * reset the current hash context\n\t\t\t */\n\t\t\tresetContext();\n\n\t\t\t/*\n\t\t\t * open the specified file\n\t\t\t */\n\t\t\tif((file = fopen(filename.c_str(), \"rb\")) == NULL)\n\t\t\t{\n\t\t\t\tthrow hlException(HL_FILE_READ_ERROR,\n\t\t\t\t\t\t  \"Cannot read file \\\"\" + \n\t\t\t\t\t\t  filename + \n\t\t\t\t\t\t  \"\\\".\");\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * read the file in 1024b blocks and\n\t\t\t * update the context for every block\n\t\t\t */\n\t\t\twhile( (len = fread(buffer,1,1024,file)) )\n\t\t\t{\n\t\t\t\tupdateContext(buffer, len);\n\t\t\t}\n\n\t\t\t//close the file and create the hash\n\t\t\tfclose(file);\n\t\t\treturn(hashIt());\n\t\t}\n}; \n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_md5.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/*\n * The hashlib++ MD5 implementation is derivative from the sourcecode\n * published in RFC 1321 \n * \n * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\n * rights reserved.\n * \n * License to copy and use this software is granted provided that it\n * is identified as the \"RSA Data Security, Inc. MD5 Message-Digest\n * Algorithm\" in all material mentioning or referencing this software\n * or this function.\n * \n * License is also granted to make and use derivative works provided\n * that such works are identified as \"derived from the RSA Data\n * Security, Inc. MD5 Message-Digest Algorithm\" in all material\n * mentioning or referencing the derived work.\n * \n * RSA Data Security, Inc. makes no representations concerning either\n * the merchantability of this software or the suitability of this\n * software for any particular purpose. It is provided \"as is\"\n * without express or implied warranty of any kind.\n * \n * These notices must be retained in any copies of any part of this\n * documentation and/or software.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5.cpp\n *  @brief\tThis file contains the implementation of the MD5 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\n//hashlib++ includes\n#include \"hl_md5.h\"\n\n//----------------------------------------------------------------------\n// defines\n\n// Constants for MD5Transform routine.\n#define S11 7\n#define S12 12\n#define S13 17\n#define S14 22\n#define S21 5\n#define S22 9\n#define S23 14\n#define S24 20\n#define S31 4\n#define S32 11\n#define S33 16\n#define S34 23\n#define S41 6\n#define S42 10\n#define S43 15\n#define S44 21\n\nstatic unsigned char PADDING[64] = {\n  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/* F, G, H and I are basic MD5 functions. */\n#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))\n#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))\n#define H(x, y, z) ((x) ^ (y) ^ (z))\n#define I(x, y, z) ((y) ^ ((x) | (~z)))\n\n/*\n * ROTATE_LEFT rotates x left n bits. \n * cast to unsigned int to guarantee support for 64Bit System\n */\n#define ROTATE_LEFT(x, n) (((x) << (n)) | (( (unsigned int) x) >> (32-(n))))\n\n/*\nFF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.\nRotation is separate from addition to prevent recomputation.\n*/\n#define FF(a, b, c, d, x, s, ac) { \\\n (a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n\n#define GG(a, b, c, d, x, s, ac) { \\\n (a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define HH(a, b, c, d, x, s, ac) { \\\n (a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define II(a, b, c, d, x, s, ac) { \\\n (a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n\n//----------------------------------------------------------------------\t\n//private member-functions\n\n/**\n *  @brief \tBasic transformation. Transforms state based on block.\n *  @param\tstate\tstate to transform\n *  @param\tblock\tblock to transform\n */  \nvoid MD5::MD5Transform (unsigned long int state[4], unsigned char block[64])\n{\n\tunsigned long int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n\n\tDecode (x, block, 64);\n\n\t/* Round 1 */\n\tFF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */\n\tFF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */\n\tFF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */\n\tFF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */\n\tFF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */\n\tFF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */\n\tFF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */\n\tFF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */\n\tFF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */\n\tFF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */\n\tFF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */\n\tFF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */\n\tFF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */\n\tFF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */\n\tFF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */\n\tFF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */\n\n\t/* Round 2 */\n\tGG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */\n\tGG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */\n\tGG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */\n\tGG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */\n\tGG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */\n\tGG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */\n\tGG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */\n\tGG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */\n\tGG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */\n\tGG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */\n\tGG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */\n\n\tGG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */\n\tGG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */\n\tGG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */\n\tGG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */\n\tGG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */\n\n\t/* Round 3 */\n\tHH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */\n\tHH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */\n\tHH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */\n\tHH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */\n\tHH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */\n\tHH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */\n\tHH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */\n\tHH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */\n\tHH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */\n\tHH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */\n\tHH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */\n\tHH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */\n\tHH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */\n\tHH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */\n\tHH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */\n\tHH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */\n\n\t/* Round 4 */\n\tII (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */\n\tII (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */\n\tII (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */\n\tII (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */\n\tII (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */\n\tII (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */\n\tII (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */\n\tII (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */\n\tII (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */\n\tII (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */\n\tII (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */\n\tII (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */\n\tII (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */\n\tII (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */\n\tII (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */\n\tII (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */\n\n\tstate[0] += a;\n\tstate[1] += b;\n\tstate[2] += c;\n\tstate[3] += d;\n\n\t/* \n\t * Zeroize sensitive information.\n\t */\n\tMD5_memset ((POINTER)x, 0, sizeof (x));\n}\n\n/**\n *  @brief \tEncodes input data\n *  @param\toutput Encoded data as OUT parameter\n *  @param\tinput Input data\n *  @param\tlen The length of the input assuming it is a\n *  \t\tmultiple of 4\n */  \nvoid MD5::Encode (unsigned char *output, unsigned long int *input, unsigned int len)\n{\n\tunsigned int i, j;\n\n\tfor (i = 0, j = 0; j < len; i++, j += 4) {\n\t\toutput[j] = (unsigned char)(input[i] & 0xff);\n\t\toutput[j+1] = (unsigned char)((input[i] >> 8) & 0xff);\n\t\toutput[j+2] = (unsigned char)((input[i] >> 16) & 0xff);\n\t\toutput[j+3] = (unsigned char)((input[i] >> 24) & 0xff);\n\t}\n}\n\n/**\n *  @brief \tDecodes input data into output\n *  @param\toutput Decoded data as OUT parameter\n *  @param\tinput Input data\n *  @param\tlen The length of the input assuming it is a\n *  \t\tmultiple of 4\n */  \nvoid MD5::Decode (unsigned long int *output, unsigned char *input, unsigned int len)\n{\n\t  unsigned int i, j;\n\n\t  for (i = 0, j = 0; j < len; i++, j += 4)\n\t\t output[i] = ((unsigned long int)input[j]) | \n\t\t\t     (((unsigned long int)input[j+1]) << 8) |\n\t\t\t     (((unsigned long int)input[j+2]) << 16) |\n\t\t\t     (((unsigned long int)input[j+3]) << 24);\n}\n\n/**\n *  @brief \tinternal memory management\n *  @param\toutput OUT parameter where POINTER is a unsigned\n *  \t\tchar*\n *  @param\tinput Data to copy where POINTER is a unsigned char*\n *  @param\tlen The length of the data\n */  \nvoid MD5::MD5_memcpy (POINTER output, POINTER input, unsigned int len)\n{\n\t/*\n\t * TODO-Note: Replace \"for loop\" with standard memcpy if possible.\n\t */\n\tunsigned int i;\n\n\tfor (i = 0; i < len; i++)\n\t\toutput[i] = input[i];\n}\n\n/**\n *  @brief \tinternal memory management\n *  @param \toutput OUT parameter where POINTER is an unsigned\n *  \t\tchar*\n *  @param\tvalue Value to fill the memory with\n *  @param\tlen The length of the data\n *  \n */  \nvoid MD5::MD5_memset (POINTER output,int value,unsigned int len)\n{\n\t/*\n\t * TODO-Note: Replace \"for loop\" with standard memset if possible.\n\t */\n\tunsigned int i;\n\tfor (i = 0; i < len; i++)\n\t\t((char *)output)[i] = (char)value;\n}\n\n//----------------------------------------------------------------------\t\n//public member-functions\n\n/**\n *  @brief \tInitialization begins an operation,\n *  \t\twriting a new context\n *  @param \tcontext\tThe HL_MD5_CTX context to initialize\n */  \nvoid MD5::MD5Init (HL_MD5_CTX *context)\n{\n\t  context->count[0] = context->count[1] = 0;\n\t  context->state[0] = 0x67452301;\n\t  context->state[1] = 0xefcdab89;\n\t  context->state[2] = 0x98badcfe;\n\t  context->state[3] = 0x10325476;\n}\n\n/**\n *  @brief \tBlock update operation. Continues an md5\n *  \t\tmessage-digest operation, processing another\n *  \t\tmessage block, and updating the context.\n *  @param\tcontext The HL_MD5_CTX context to update\n *  @param\tinput The data to write into the context\n *  @param\tinputLen The length of the input data\n */  \nvoid MD5::MD5Update (HL_MD5_CTX *context, unsigned char *input, unsigned int inputLen)\n{\n\t  unsigned int i, index, partLen;\n\n\t  /* Compute number of bytes mod 64 */\n\t  index = (unsigned int)((context->count[0] >> 3) & 0x3F);\n\n\t  /* Update number of bits */\n\t  if ( (context->count[0] += ((unsigned long int)inputLen << 3))\n\t       < ((unsigned long int)inputLen << 3))\n\t\tcontext->count[1]++;\n\n\t  context->count[1] += ((unsigned long int)inputLen >> 29);\n\t  partLen = 64 - index;\n\n\t  /*\n\t   * Transform as many times as possible.\n\t   */\n\t  if (inputLen >= partLen) \n\t  {\n\t\t MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);\n\t\t MD5Transform (context->state, context->buffer);\n\n\t\t for (i = partLen; i + 63 < inputLen; i += 64)\n\t\t   MD5Transform (context->state, &input[i]);\n\n\t\t index = 0;\n\t  }\n\t  else \n\t \ti = 0;\n\n\t  /* Buffer remaining input */\n\t  MD5_memcpy ((POINTER)&context->buffer[index],\n\t              (POINTER)&input[i],\n\t\t      inputLen-i);\n}\n\n/**\n *  @brief \tFinalization ends the md5 message-digest \n *  \t\toperation, writing the the message digest and\n *  \t\tzeroizing the context.\n *  @param\tdigest This is an OUT parameter which contains\n *  \t\tthe created hash after the method returns\n *  @param\tcontext The context to finalize\n */  \nvoid MD5::MD5Final (unsigned char digest[16], HL_MD5_CTX *context)\n{\n\tunsigned char bits[8];\n\tunsigned int index, padLen;\n\n\t/* Save number of bits */\n\tEncode (bits, context->count, 8);\n\n\t/* \n\t * Pad out to 56 mod 64.\n\t */\n\tindex = (unsigned int)((context->count[0] >> 3) & 0x3f);\n\tpadLen = (index < 56) ? (56 - index) : (120 - index);\n\tMD5Update (context, PADDING, padLen);\n\n\t/* Append length (before padding) */\n\tMD5Update (context, bits, 8);\n\n\t/* Store state in digest */\n\tEncode (digest, context->state, 16);\n\n\t/*\n\t * Zeroize sensitive information.\n\t */\n\tMD5_memset ((POINTER)context, 0, sizeof (*context));\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_md5.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/*\n * The hashlib++ MD5 implementation is derivative from the sourcecode\n * published in RFC 1321 \n * \n * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\n * rights reserved.\n * \n * License to copy and use this software is granted provided that it\n * is identified as the \"RSA Data Security, Inc. MD5 Message-Digest\n * Algorithm\" in all material mentioning or referencing this software\n * or this function.\n * \n * License is also granted to make and use derivative works provided\n * that such works are identified as \"derived from the RSA Data\n * Security, Inc. MD5 Message-Digest Algorithm\" in all material\n * mentioning or referencing the derived work.\n * \n * RSA Data Security, Inc. makes no representations concerning either\n * the merchantability of this software or the suitability of this\n * software for any particular purpose. It is provided \"as is\"\n * without express or implied warranty of any kind.\n * \n * These notices must be retained in any copies of any part of this\n * documentation and/or software.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5.h\n *  @brief\tThis file contains the declaration of the MD5 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef MD5_H\n#define MD5_H\n\n//---------------------------------------------------------------------- \n//STL includes\n#include <string>\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\ntypedef hl_uint8 *POINTER;\n\n/**\n * @brief this struct represents a MD5-hash context.\n */\ntypedef struct \n{\n\t/** state (ABCD) */\n\tunsigned long int state[4];   \t      \n\n\t/** number of bits, modulo 2^64 (lsb first) */\n\tunsigned long int count[2];\n\n\t/** input buffer */\n\tunsigned char buffer[64];\n} HL_MD5_CTX;\n\n//---------------------------------------------------------------------- \n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe md5 message digest algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  MD5Init(), MD5Update() and MD5Final().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the md5wrapper class instead of MD5.\n */  \nclass MD5\n{\n\n\tprivate:\n\n\t\t/**\n\t\t *  @brief \tBasic transformation. Transforms state based on block.\n\t\t *  @param\tstate\tstate to transform\n\t\t *  @param\tblock\tblock to transform\n\t\t */  \n\t\tvoid MD5Transform (unsigned long int state[4], unsigned char block[64]);\n\n\t\t/**\n\t\t *  @brief \tEncodes input data\n\t\t *  @param\toutput Encoded data as OUT parameter\n\t\t *  @param\tinput Input data\n\t\t *  @param\tlen The length of the input assuming it is a\n\t\t *  \t\tmultiple of 4\n\t\t */  \n\t\tvoid Encode (unsigned char* output,\n\t\t\t     unsigned long int *input,\n\t\t\t     unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tDecodes input data into output\n\t\t *  @param\toutput Decoded data as OUT parameter\n\t\t *  @param\tinput Input data\n\t\t *  @param\tlen The length of the input assuming it is a\n\t\t *  \t\tmultiple of 4\n\t\t */  \n\t\tvoid Decode (unsigned long int *output,\n\t\t\t     unsigned char *input,\n\t\t\t     unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tinternal memory management\n\t\t *  @param\toutput OUT parameter where POINTER is an unsigned\n\t\t *  \t\tchar*\n\t\t *  @param\tinput Data to copy where POINTER is a unsigned char*\n\t\t *  @param\tlen The length of the data\n\t\t */  \n\t\tvoid MD5_memcpy (POINTER output, POINTER input, unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tinternal memory management\n\t\t *  @param \toutput OUT parameter where POINTER is an unsigned\n\t\t *  \t\tchar*\n\t\t *  @param\tvalue Value to fill the memory with\n\t\t *  @param\tlen The length of the data\n\t\t *  \n\t\t */  \n\t\tvoid MD5_memset (POINTER output, int value, unsigned int len);\n\n\tpublic:\n\t\n\t\t/**\n\t\t *  @brief \tInitialization begins an operation,\n\t\t *  \t\twriting a new context\n\t\t *  @param \tcontext\tThe HL_MD5_CTX context to initialize\n\t\t */  \n\t\tvoid MD5Init (HL_MD5_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tBlock update operation. Continues an md5\n\t\t *  \t\tmessage-digest operation, processing another\n\t\t *  \t\tmessage block, and updating the context.\n\t\t *  @param\tcontext The HL_MD5_CTX context to update\n\t\t *  @param\tinput The data to write into the context\n\t\t *  @param\tinputLen The length of the input data\n\t\t */  \n\t\tvoid MD5Update (HL_MD5_CTX* context,\n\t\t\t       \tunsigned char *input,\n\t\t\t       \tunsigned int inputLen);\n\n\t\t/**\n\t\t *  @brief \tFinalization ends the md5 message-digest \n\t\t *  \t\toperation, writing the the message digest and\n\t\t *  \t\tzeroizing the context.\n\t\t *  @param\tdigest This is an OUT parameter which contains\n\t\t *  \t\tthe created hash after the method returns\n\t\t *  @param\tcontext The context to finalize\n\t\t */  \n\t\tvoid MD5Final (unsigned char digest[16], HL_MD5_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tdefault constructor\n\t\t */  \n\t\tMD5(){};\n};\n\n//---------------------------------------------------------------------- \n//End of include protection\n#endif\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_md5wrapper.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5wrapper.cpp\n *  @brief\tThis file contains the implementation of the \n *  \t\tmd5wrapper class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//STL includes\n#include <string>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_md5wrapper.h\"\n\n//---------------------------------------------------------------------- \n//private member functions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \tthe hash as std::string\n */  \nstd::string md5wrapper::hashIt(void)\n{\n\t//create the hash\n\tunsigned char buff[16] = \"\";\t\n\tmd5->MD5Final((unsigned char*)buff,&ctx);\n\n\t//converte the hash to a string and return it\n\treturn convToString(buff);\t\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string md5wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * using a ostringstream to convert the hash in a\n\t * hex string\n\t */\n\tstd::ostringstream os;\n\tfor(int i=0; i<16; ++i)\n\t{\n\t\t/*\n\t\t * set the width to 2\n\t\t */\n\t\tos.width(2);\n\n\t\t/*\n\t\t * fill with 0\n\t\t */\n\t\tos.fill('0');\n\n\t\t/*\n\t\t * conv to hex\n\t\t */\n\t\tos << std::hex << static_cast<unsigned int>(data[i]);\n\t}\n\n\t/*\n\t * return as std::string\n\t */\n\treturn os.str();\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context.\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid md5wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\t//update \n\tmd5->MD5Update(&ctx, data, len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid md5wrapper::resetContext(void)\n{\n\t//init md5\n\tmd5->MD5Init(&ctx);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string md5wrapper::getTestHash(void)\n{\n\treturn \"9e107d9d372bb6826bd81d3542a419d6\";\n}\n\n//---------------------------------------------------------------------- \n//public member functions\n\n/**\n *  @brief \tdefault constructor\n */  \nmd5wrapper::md5wrapper()\n{\n\tmd5 = new MD5();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nmd5wrapper::~md5wrapper()\n{\n\tdelete md5;\n}\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_md5wrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_md5wrapper.h\n *  @brief\tThis file contains the definition of the md5wrapper\n *  \t\tclass.\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef MD5WRAPPER_H\n#define MD5WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_md5.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the MD5 wrapper-class\n *\n *  \t\tYou can use this class to easily create a md5 hash.\n *  \t\tJust create an instance of md5wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tmd5example.cpp\n *\n *  \t\tmd5wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass md5wrapper : public hashwrapper\n{\n\tprotected:\n\n\t\t/**\n\t\t * MD5 access\n\t\t */\n\t\tMD5 *md5;\n\n\t\t/**\n\t\t * MD5 context\n\t\t */\n\t\tHL_MD5_CTX ctx;\n\t\n\t\t/**\n\t\t *  @brief \tThis method ends the hash process\n\t\t *  \t\tand returns the hash as string.\n\t\t *\n\t\t *  @return \tthe hash as std::string\n\t\t */  \n\t\tvirtual std::string hashIt(void);\n\n\t\t/**\n\t\t *  @brief \tThis internal member-function\n\t\t *  \t\tconvertes the hash-data to a\n\t\t *  \t\tstd::string (HEX).\n\t\t *\n\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t *  @return\tthe converted data as std::string\n\t\t */  \n\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t/**\n\t\t *  @brief \tThis method adds the given data to the \n\t\t *  \t\tcurrent hash context.\n\t\t *\n\t\t *  @param \tdata The data to add to the current context\n\t\t *  @param \tlen The length of the data to add\n\t\t */  \n\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tThis method resets the current hash context.\n\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t */  \n\t\tvirtual void resetContext(void);\n\n\t\t/**\n\t\t * @brief \tThis method should return the hash of the\n\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t * \t\tdog\"\n\t\t */\n\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tdefault constructor\n\t\t */  \n\t\tmd5wrapper();\n\n\t\t/**\n\t\t *  @brief \tdefault destructor\n\t\t */  \n\t\tvirtual ~md5wrapper();\n};\n\n//----------------------------------------------------------------------\n//include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha1.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA1 implementation is derivative from the sourcecode\n * published in RFC 3174  \n *\n * Copyright (C) The Internet Society (2001).  All Rights Reserved.\n * \n * This document and translations of it may be copied and furnished to\n * others, and derivative works that comment on or otherwise explain it\n * or assist in its implementation may be prepared, copied, published\n * and distributed, in whole or in part, without restriction of any\n * kind, provided that the above copyright notice and this paragraph are\n * included on all such copies and derivative works.  However, this\n * document itself may not be modified in any way, such as by removing\n * the copyright notice or references to the Internet Society or other\n * Internet organizations, except as needed for the purpose of\n * developing Internet standards in which case the procedures for\n * copyrights defined in the Internet Standards process must be\n * followed, or as required to translate it into languages other than\n * English.\n * \n * The limited permissions granted above are perpetual and will not be\n * revoked by the Internet Society or its successors or assigns.\n * \n * This document and the information contained herein is provided on an\n * \"AS IS\" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING\n * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING\n * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION\n * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF\n * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha1.cpp\n *  @brief\tThis file contains the implementation of the SHA1 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha1.h\"\n\n\n//---------------------------------------------------------------------- \n//defines\n\n/*\n *  Define the SHA1 circular left shift macro\n */\n#define SHA1CircularShift(bits,word) \\\n                (((word) << (bits)) | ((word) >> (32-(bits))))\n\n//----------------------------------------------------------------------\n//private member-functions\n\n/**\n *  @brief \tInternal method to padd the message\n *\n *      \tAccording to the standard, the message must\n *      \tbe padded to an even 512 bits. The first \n *      \tpadding bit must be a '1'.  The last 64\tbits \n *      \trepresent the length of the original message.\n *      \tAll bits in between should be 0.\n *      \tThis function will pad the message according \n *      \tto those rules by filling the Message_Block array\n *      \taccordingly.  It will also call the \n *      \tProcessMessageBlock function provided appropriately.\n *      \tWhen it returns, it can be assumed that the message\n *      \tdigest has been computed.\n *\n *  @param\tcontext The context to padd\n *\n */  \nvoid SHA1::SHA1PadMessage(HL_SHA1_CTX *context)\n{\n\t/*\n\t *  Check to see if the current message block is too small to hold\n\t *  the initial padding bits and length.  If so, we will pad the\n\t *  block, process it, and then continue padding into a second\n\t *  block.\n\t */\n\tif (context->Message_Block_Index > 55)\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] = 0x80;\n\t\twhile(context->Message_Block_Index < 64)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\n\t\tSHA1ProcessMessageBlock(context);\n\n\t\twhile(context->Message_Block_Index < 56)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] = 0x80;\n\t\twhile(context->Message_Block_Index < 56)\n\t\t{\n\t\t\tcontext->Message_Block[context->Message_Block_Index++] = 0;\n\t\t}\n\t}\n\n\t/*\n\t *  Store the message length as the last 8 octets\n\t */\n\tcontext->Message_Block[56] = context->Length_High >> 24;\n\tcontext->Message_Block[57] = context->Length_High >> 16;\n\tcontext->Message_Block[58] = context->Length_High >> 8;\n\tcontext->Message_Block[59] = context->Length_High;\n\tcontext->Message_Block[60] = context->Length_Low >> 24;\n\tcontext->Message_Block[61] = context->Length_Low >> 16;\n\tcontext->Message_Block[62] = context->Length_Low >> 8;\n\tcontext->Message_Block[63] = context->Length_Low;\n\n\tSHA1ProcessMessageBlock(context);\n}\n\n/**\n *  @brief      This member-function will process the next 512 bits of the\n *  \t\tmessage stored in the Message_Block array.\n *\n *      \tMany of the variable names in this code, especially the\n *      \tsingle character names, were used because those were the\n *      \tnames used in the publication.\n *\n *  @param\tcontext The context to process\n */  \nvoid SHA1::SHA1ProcessMessageBlock(HL_SHA1_CTX *context)\n{\n\tconst hl_uint32 K[] =    {       /* Constants defined in SHA-1   */\n\t\t0x5A827999,\n\t\t0x6ED9EBA1,\n\t\t0x8F1BBCDC,\n\t\t0xCA62C1D6\n\t};\n\tint           t;                 /* Loop counter                */\n\thl_uint32      temp;              /* Temporary word value        */\n\thl_uint32      W[80];             /* Word sequence               */\n\thl_uint32      A, B, C, D, E;     /* Word buffers                */\n\n\t/*\n\t *  Initialize the first 16 words in the array W\n\t */\n\tfor(t = 0; t < 16; t++)\n\t{\n\t\tW[t] = context->Message_Block[t * 4] << 24;\n\t\tW[t] |= context->Message_Block[t * 4 + 1] << 16;\n\t\tW[t] |= context->Message_Block[t * 4 + 2] << 8;\n\t\tW[t] |= context->Message_Block[t * 4 + 3];\n\t}\n\n\tfor(t = 16; t < 80; t++)\n\t{\n\t\tW[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);\n\t}\n\n\tA = context->Intermediate_Hash[0];\n\tB = context->Intermediate_Hash[1];\n\tC = context->Intermediate_Hash[2];\n\tD = context->Intermediate_Hash[3];\n\tE = context->Intermediate_Hash[4];\n\n\tfor(t = 0; t < 20; t++)\n\t{\n\t\ttemp =  SHA1CircularShift(5,A) +\n\t\t\t((B & C) | ((~B) & D)) + E + W[t] + K[0];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 20; t < 40; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 40; t < 60; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) +\n\t\t\t((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tfor(t = 60; t < 80; t++)\n\t{\n\t\ttemp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];\n\t\tE = D;\n\t\tD = C;\n\t\tC = SHA1CircularShift(30,B);\n\t\tB = A;\n\t\tA = temp;\n\t}\n\n\tcontext->Intermediate_Hash[0] += A;\n\tcontext->Intermediate_Hash[1] += B;\n\tcontext->Intermediate_Hash[2] += C;\n\tcontext->Intermediate_Hash[3] += D;\n\tcontext->Intermediate_Hash[4] += E;\n\n\tcontext->Message_Block_Index = 0;\n}\n\n//----------------------------------------------------------------------\n//public member-functions\n\n/**\n *  @brief \tResets the sha1 context and starts a new\n *  \t\thashprocess\n *  @param\tcontext The context to reset\n *  @return\t0 on succes an error number otherwise\n */  \nint SHA1::SHA1Reset(HL_SHA1_CTX *context)\n{\n\tif (!context)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tcontext->Length_Low             = 0;\n\tcontext->Length_High            = 0;\n\tcontext->Message_Block_Index    = 0;\n\n\tcontext->Intermediate_Hash[0]   = 0x67452301;\n\tcontext->Intermediate_Hash[1]   = 0xEFCDAB89;\n\tcontext->Intermediate_Hash[2]   = 0x98BADCFE;\n\tcontext->Intermediate_Hash[3]   = 0x10325476;\n\tcontext->Intermediate_Hash[4]   = 0xC3D2E1F0;\n\n\tcontext->Computed   = 0;\n\tcontext->Corrupted  = 0;\n\n\treturn shaSuccess;\n}\n\n/**\n *  @brief \tData input.\n *\n *  \t\tThis memberfunction add data to the specified\n *  \t\tcontext.\n *\n *  @param\tcontext The context to add data to\n *  @param\tmessage_array The data to add\n *  @param\tlength The length of the data to add\n */  \nint SHA1::SHA1Input(    HL_SHA1_CTX    *context,\n\t\t\tconst hl_uint8  *message_array,\n\t\t\tunsigned int   length)\n{\n\tif (!length)\n\t{\n\t\treturn shaSuccess;\n\t}\n\n\tif (!context || !message_array)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tif (context->Computed)\n\t{\n\t\tcontext->Corrupted = shaStateError;\n\t\treturn shaStateError;\n\t}\n\n\tif (context->Corrupted)\n\t{\n\t\treturn context->Corrupted;\n\t}\n\twhile(length-- && !context->Corrupted)\n\t{\n\t\tcontext->Message_Block[context->Message_Block_Index++] =\n\t\t\t(*message_array & 0xFF);\n\n\t\tcontext->Length_Low += 8;\n\t\tif (context->Length_Low == 0)\n\t\t{\n\t\t\tcontext->Length_High++;\n\t\t\tif (context->Length_High == 0)\n\t\t\t{\n\t\t\t\t/* Message is too long */\n\t\t\t\tcontext->Corrupted = 1;\n\t\t\t}\n\t\t}\n\n\t\tif (context->Message_Block_Index == 64)\n\t\t{\n\t\t\tSHA1ProcessMessageBlock(context);\n\t\t}\n\n\t\tmessage_array++;\n\t}\n\n\treturn shaSuccess;\n}\n\n/**\n *  @brief \tThis ends the sha operation, zeroizing the context\n *  \t\tand returning the computed hash.\n *\n *  @param\tcontext The context to get the hash from\n *  @param\tMessage_Digest This is an OUT parameter which\n *  \t\tcontains the hash after the menberfunction returns\n *  @return\t0 on succes, an error-code otherwise\n */  \nint SHA1::SHA1Result( HL_SHA1_CTX *context,\n\t\t      hl_uint8 \t  Message_Digest[SHA1HashSize])\n{\n\tint i;\n\n\tif (!context || !Message_Digest)\n\t{\n\t\treturn shaNull;\n\t}\n\n\tif (context->Corrupted)\n\t{\n\t\treturn context->Corrupted;\n\t}\n\n\tif (!context->Computed)\n\t{\n\t\tSHA1PadMessage(context);\n\t\tfor(i=0; i<64; ++i)\n\t\t{\n\t\t\t/* message may be sensitive, clear it out */\n\t\t\tcontext->Message_Block[i] = 0;\n\t\t}\n\t\tcontext->Length_Low = 0;    /* and clear length */\n\t\tcontext->Length_High = 0;\n\t\tcontext->Computed = 1;\n\t}\n\n\tfor(i = 0; i < SHA1HashSize; ++i)\n\t{\n\t\tMessage_Digest[i] = context->Intermediate_Hash[i>>2]\n\t\t\t>> 8 * ( 3 - ( i & 0x03 ) );\n\t}\n\n\treturn shaSuccess;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha1.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA1 implementation is derivative from the sourcecode\n * published in RFC 3174  \n *\n * Copyright (C) The Internet Society (2001).  All Rights Reserved.\n * \n * This document and translations of it may be copied and furnished to\n * others, and derivative works that comment on or otherwise explain it\n * or assist in its implementation may be prepared, copied, published\n * and distributed, in whole or in part, without restriction of any\n * kind, provided that the above copyright notice and this paragraph are\n * included on all such copies and derivative works.  However, this\n * document itself may not be modified in any way, such as by removing\n * the copyright notice or references to the Internet Society or other\n * Internet organizations, except as needed for the purpose of\n * developing Internet standards in which case the procedures for\n * copyrights defined in the Internet Standards process must be\n * followed, or as required to translate it into languages other than\n * English.\n * \n * The limited permissions granted above are perpetual and will not be\n * revoked by the Internet Society or its successors or assigns.\n * \n * This document and the information contained herein is provided on an\n * \"AS IS\" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING\n * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING\n * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION\n * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF\n * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha1.h\n *  @brief\tThis file contains the declaration of the SHA1 class\n *  @date \tMo 17 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA1_H\n#define SHA1_H\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//enums\n\n#ifndef _SHA_enum_\n#define _SHA_enum_\nenum\n{\n    shaSuccess = 0,\n    shaNull,            /* Null pointer parameter */\n    shaInputTooLong,    /* input data too long */\n    shaStateError       /* called Input after Result */\n};\n#endif\n\n//---------------------------------------------------------------------- \n//defines\n#define SHA1HashSize 20\n\n//---------------------------------------------------------------------- \n//structs\n\n/**\n * @brief this struct represents a SHA1-hash context.\n */\ntypedef struct HL_SHA1_CTX\n{\n\t/** Message Digest */\n\thl_uint32 Intermediate_Hash[SHA1HashSize/4];\n\n\t/** Message length in bits */\n\thl_uint32 Length_Low;            \n\n\t/** Message length in bits */\n\thl_uint32 Length_High;\n\n\t/** Index into message block array */\n\thl_uint16 Message_Block_Index;\n\n\t/** 512-bit message blocks */\n\thl_uint8 Message_Block[64];      \n\n\t/** Is the digest computed? */\n\tint Computed;\n\n\t/** Is the message digest corrupted? */\n\tint Corrupted;\n\n} HL_SHA1_CTX;\n\n//---------------------------------------------------------------------- \n//class definition\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe sha1 algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  SHA1Reset(), SHA1Input() and SHA1Result().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha1wrapper class instead of SHA1.\n */  \nclass SHA1\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t *  @brief \tInternal method to padd the message\n\t\t\t *\n\t\t\t *      \tAccording to the standard, the message must\n\t\t\t *      \tbe padded to an even 512 bits. The first \n\t\t\t *      \tpadding bit must be a '1'.  The last 64\tbits \n\t\t\t *      \trepresent the length of the original message.\n\t\t\t *      \tAll bits in between should be 0.\n\t\t\t *      \tThis function will pad the message according \n\t\t\t *      \tto those rules by filling the Message_Block array\n\t\t\t *      \taccordingly.  It will also call the \n\t\t\t *      \tProcessMessageBlock function provided appropriately.\n\t\t\t *      \tWhen it returns, it can be assumed that the message\n\t\t\t *      \tdigest has been computed.\n\t\t\t *\n\t\t\t *  @param\tcontext The context to padd\n\t\t\t *\n\t\t\t */  \n\t\t\tvoid SHA1PadMessage(HL_SHA1_CTX *context);\n\n\t\t\t/**\n\t\t\t *  @brief      This member-function will process the next 512 bits of the\n\t\t\t *  \t\tmessage stored in the Message_Block array.\n\t\t\t *\n\t\t\t *      \tMany of the variable names in this code, especially the\n\t\t\t *      \tsingle character names, were used because those were the\n\t\t\t *      \tnames used in the publication.\n\t\t\t *\n\t\t\t *  @param\tcontext The context to process\n\t\t\t */  \n\t\t\tvoid SHA1ProcessMessageBlock(HL_SHA1_CTX *context);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tResets the sha1 context and starts a new\n\t\t *  \t\thashprocess\n\t\t *  @param\tcontext The context to reset\n\t\t *  @return\t0 on succes an error number otherwise\n\t\t */  \n\t\tint SHA1Reset(  HL_SHA1_CTX *context);\n\n\t\t/**\n\t\t *  @brief \tData input.\n\t\t *\n\t\t *  \t\tThis memberfunction add data to the specified\n\t\t *  \t\tcontext.\n\t\t *\n\t\t *  @param\tcontext The context to add data to\n\t\t *  @param\tmessage_array The data to add\n\t\t *  @param\tlength The length of the data to add\n\t\t */  \n\t\tint SHA1Input(  HL_SHA1_CTX   *context,\n\t\t\t\tconst hl_uint8 *message_array,\n\t\t\t\tunsigned int  length);\n\n\t\t/**\n\t\t *  @brief \tThis ends the sha operation, zeroizing the context\n\t\t *  \t\tand returning the computed hash.\n\t\t *\n\t\t *  @param\tcontext The context to get the hash from\n\t\t *  @param\tMessage_Digest This is an OUT parameter which\n\t\t *  \t\tcontains the hash after the menberfunction returns\n\t\t *  @return\t0 on succes, an error-code otherwise\n\t\t */  \n\t\tint SHA1Result( HL_SHA1_CTX *context,\n\t\t\t\thl_uint8     Message_Digest[SHA1HashSize]);\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha1wrapper.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha1wrapper.cpp\n *  @brief\tThis file contains the implementation of the \n *  \t\tsha1wrapper class\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha1wrapper.h\"\n#include \"hl_sha1.h\"\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//STL includes\n#include <sstream>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha1wrapper::hashIt(void)\n{\n\thl_uint8 Message_Digest[20];\n\tsha1->SHA1Result(&context, Message_Digest);\n\n\treturn convToString(Message_Digest);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha1wrapper::convToString(unsigned char *data)\n{\n\tstd::ostringstream os;\n\tfor(int i=0; i<20; ++i)\n\t{\n\t\t/*\n\t\t * set the width to 2\n\t\t */\n\t\tos.width(2);\n\n\t\t/*\n\t\t * fill with 0\n\t\t */\n\t\tos.fill('0');\n\n\t\t/*\n\t\t * conv to hex\n\t\t */\n\t\tos << std::hex << static_cast<unsigned int>(data[i]);\n\t}\n\n\t/*\n\t * return as std::string\n\t */\n\treturn os.str();\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha1wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tsha1->SHA1Input(&context, data, len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha1wrapper::resetContext(void)\n{\n\tsha1->SHA1Reset(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha1wrapper::getTestHash(void)\n{\n\treturn \"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha1wrapper::sha1wrapper()\n{\n\tthis->sha1 = new SHA1();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha1wrapper::~sha1wrapper()\n{\n\tdelete this->sha1;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha1wrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha1wrapper.h\n *  @brief\tThis file contains the definition of the sha1wrapper \n *  \t\tclass.\n *  @date \tMo 17 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA1WRAPPER_H\n#define SHA1WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha1.h\"\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA1 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha1 hash.\n *  \t\tJust create an instance of sha1wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha1example.cpp\n *\n *  \t\tsha1wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha1wrapper : public hashwrapper\n{\n\tprotected:\n\t\t\t/**\n\t\t\t * SHA1 access\n\t\t\t */\n\t\t\tSHA1 *sha1;\n\t\t\t\n\t\t\t/*\n\t\t\t * SHA1 context\n\t\t\t */\n\t\t\tHL_SHA1_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha1wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha1wrapper();\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha256.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA256 implementation is derivative from the sourcecode\n * published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha256.cpp\n *  @brief\tThis file contains the implementation of the SHA256 class\n *  @date \tDi 25 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha256.h\"\n\n//---------------------------------------------------------------------- \n/*\n * Standard C includes\n */\n#include <string.h>\t\n#include <assert.h>\n\n//---------------------------------------------------------------------- \n#include \"hl_sha2mac.h\"\n\n//---------------------------------------------------------------------- \n\n/*\n * Hash constant words K for SHA-256: \n */ \nconst static sha2_word32 K256[64] = {\n\t0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,\n\t0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,\n\t0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,\n\t0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,\n\t0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,\n\t0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,\n\t0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,\n\t0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,\n\t0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,\n\t0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,\n\t0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,\n\t0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,\n\t0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,\n\t0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,\n\t0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,\n\t0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL\n};\n\n/*\n * Initial hash value H for SHA-256:\n */\nconst static sha2_word32 sha256_initial_hash_value[8] = {\n\t0x6a09e667UL,\n\t0xbb67ae85UL,\n\t0x3c6ef372UL,\n\t0xa54ff53aUL,\n\t0x510e527fUL,\n\t0x9b05688cUL,\n\t0x1f83d9abUL,\n\t0x5be0cd19UL\n};\n\n\n/*\n * Constant used by End() functions for converting the\n * digest to a readable hexadecimal character string:\n */\nstatic const char *sha2_hex_digits = \"0123456789abcdef\";\n\n/*** SHA-256: *********************************************************/\n\n/**\n *  @brief \tInitialize the context\n *  @param\tcontext The context to init.\n */  \nvoid SHA256::SHA256_Init(HL_SHA256_CTX* context) {\n\tif (context == (HL_SHA256_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);\n\tcontext->bitcount = 0;\n}\n\n#ifdef SHA2_UNROLL_TRANSFORM\n\n/* Unrolled SHA-256 round macros: */\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n\n#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tREVERSE32(*data++, W256[j]); \\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \\\n             K256[j] + W256[j]; \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \\\n\t     K256[j] + (W256[j] = *data++); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND256(a,b,c,d,e,f,g,h)\t\\\n\ts0 = W256[(j+1)&0x0f]; \\\n\ts0 = sigma0_256(s0); \\\n\ts1 = W256[(j+14)&0x0f]; \\\n\ts1 = sigma1_256(s1); \\\n\tT1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \\\n\t     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \\\n\tj++\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA256::SHA256_Transform(HL_SHA256_CTX* context, const sha2_word32* data) {\n\tsha2_word32\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word32\tT1, *W256;\n\tint\t\tj;\n\n\tW256 = (sha2_word32*)context->buffer;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n\t\t/* Rounds 0 to 15 (unrolled): */\n\t\tROUND256_0_TO_15(a,b,c,d,e,f,g,h);\n\t\tROUND256_0_TO_15(h,a,b,c,d,e,f,g);\n\t\tROUND256_0_TO_15(g,h,a,b,c,d,e,f);\n\t\tROUND256_0_TO_15(f,g,h,a,b,c,d,e);\n\t\tROUND256_0_TO_15(e,f,g,h,a,b,c,d);\n\t\tROUND256_0_TO_15(d,e,f,g,h,a,b,c);\n\t\tROUND256_0_TO_15(c,d,e,f,g,h,a,b);\n\t\tROUND256_0_TO_15(b,c,d,e,f,g,h,a);\n\t} while (j < 16);\n\n\t/* Now for the remaining rounds to 64: */\n\tdo {\n\t\tROUND256(a,b,c,d,e,f,g,h);\n\t\tROUND256(h,a,b,c,d,e,f,g);\n\t\tROUND256(g,h,a,b,c,d,e,f);\n\t\tROUND256(f,g,h,a,b,c,d,e);\n\t\tROUND256(e,f,g,h,a,b,c,d);\n\t\tROUND256(d,e,f,g,h,a,b,c);\n\t\tROUND256(c,d,e,f,g,h,a,b);\n\t\tROUND256(b,c,d,e,f,g,h,a);\n\t} while (j < 64);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = 0;\n}\n\n#else /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA256::SHA256_Transform(HL_SHA256_CTX* context, const sha2_word32* data) {\n\tsha2_word32\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word32\tT1, T2, *W256;\n\tint\t\tj;\n\n\tW256 = (sha2_word32*)context->buffer;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Copy data while converting to host byte order */\n\t\tREVERSE32(*data++,W256[j]);\n\t\t/* Apply the SHA-256 compression function to update a..h */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\t/* Apply the SHA-256 compression function to update a..h with copy */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\tT2 = Sigma0_256(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 16);\n\n\tdo {\n\t\t/* Part of the message block expansion: */\n\t\ts0 = W256[(j+1)&0x0f];\n\t\ts0 = sigma0_256(s0);\n\t\ts1 = W256[(j+14)&0x0f];\t\n\t\ts1 = sigma1_256(s1);\n\n\t\t/* Apply the SHA-256 compression function to update a..h */\n\t\tT1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + \n\t\t     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);\n\t\tT2 = Sigma0_256(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 64);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = T2 = 0;\n}\n\n#endif /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief\tUpdates the context \n *  @param\tcontext The context to update.\n *  @param\tdata The data for updating the context.\n *  @param\tlen The length of the given data.\n */  \nvoid SHA256::SHA256_Update(HL_SHA256_CTX* context, const sha2_byte *data, unsigned int len) {\n\tunsigned int\tfreespace, usedspace;\n\n\tif (len == 0) {\n\t\t/* Calling with no data is valid - we do nothing */\n\t\treturn;\n\t}\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0 && data != (sha2_byte*)0);\n\n\tusedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;\n\tif (usedspace > 0) {\n\t\t/* Calculate how much free space is available in the buffer */\n\t\tfreespace = SHA256_BLOCK_LENGTH - usedspace;\n\n\t\tif (len >= freespace) {\n\t\t\t/* Fill the buffer completely and process it */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);\n\t\t\tcontext->bitcount += freespace << 3;\n\t\t\tlen -= freespace;\n\t\t\tdata += freespace;\n\t\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\t\t} else {\n\t\t\t/* The buffer is not yet full */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, len);\n\t\t\tcontext->bitcount += len << 3;\n\t\t\t/* Clean up: */\n\t\t\tusedspace = freespace = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\twhile (len >= SHA256_BLOCK_LENGTH) {\n\t\t/* Process as many complete blocks as we can */\n\t\tSHA256_Transform(context, (sha2_word32*)data);\n\t\tcontext->bitcount += SHA256_BLOCK_LENGTH << 3;\n\t\tlen -= SHA256_BLOCK_LENGTH;\n\t\tdata += SHA256_BLOCK_LENGTH;\n\t}\n\tif (len > 0) {\n\t\t/* There's left-overs, so save 'em */\n\t\tMEMCPY_BCOPY(context->buffer, data, len);\n\t\tcontext->bitcount += len << 3;\n\t}\n\t/* Clean up: */\n\tusedspace = freespace = 0;\n}\n\n/**\n *  @brief \tFinalize the sha256 operation\n *  @param\tdigest The digest to finalize the operation with.\n *  @param\tcontext The context to finalize.\n */  \nvoid SHA256::SHA256_Final(sha2_byte digest[], HL_SHA256_CTX* context) {\n\tsha2_word32\t*d = (sha2_word32*)digest;\n\tunsigned int\tusedspace;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tusedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Convert FROM host byte order */\n\t\tREVERSE64(context->bitcount,context->bitcount);\n#endif\n\t\tif (usedspace > 0) {\n\t\t\t/* Begin padding with a 1 bit: */\n\t\t\tcontext->buffer[usedspace++] = 0x80;\n\n\t\t\tif (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {\n\t\t\t\t/* Set-up for the last transform: */\n\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);\n\t\t\t} else {\n\t\t\t\tif (usedspace < SHA256_BLOCK_LENGTH) {\n\t\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);\n\t\t\t\t}\n\t\t\t\t/* Do second-to-last transform: */\n\t\t\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\n\t\t\t\t/* And set-up for the last transform: */\n\t\t\t\tMEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);\n\t\t\t}\n\t\t} else {\n\t\t\t/* Set-up for the last transform: */\n\t\t\tMEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);\n\n\t\t\t/* Begin padding with a 1 bit: */\n\t\t\t*context->buffer = 0x80;\n\t\t}\n\t\t/* Set the bit count: */\n\t\t*(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;\n\n\t\t/* Final transform: */\n\t\tSHA256_Transform(context, (sha2_word32*)context->buffer);\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 8; j++) {\n\t\t\t\tREVERSE32(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Clean up state data: */\n\tMEMSET_BZERO(context, sizeof(context));\n\tusedspace = 0;\n}\n\n/**\n *  @brief \tEnds the sha256 operation and return the\n *  \t\tcreated hash in the given buffer.\n *  @param\tcontext The context to end.\n *  @param\tbuffer This OUT-Parameter contains the created\n *  \t\thash after ending the operation.\n */  \nchar* SHA256::SHA256_End(HL_SHA256_CTX* context, char buffer[]) {\n\tsha2_byte\tdigest[SHA256_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA256_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA256_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA256_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);\n\treturn buffer;\n}\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha256.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA256 implementation is derivative from the sourcecode\n * published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha256.h\n *  @brief\tThis file contains the declaration of the SHA256 class\n *  @date \tDi 25 Sep 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA256_H\n#define SHA256_H\n\n//---------------------------------------------------------------------- \n//lenght defines\n#define SHA256_BLOCK_LENGTH\t\t64\n#define SHA256_SHORT_BLOCK_LENGTH\t(SHA256_BLOCK_LENGTH - 8)\n#define SHA256_DIGEST_LENGTH\t\t32\n#define SHA256_DIGEST_STRING_LENGTH\t(SHA256_DIGEST_LENGTH * 2 + 1)\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\n\n/**\n * Exactly 1 byte \n */ \ntypedef hl_uint8  sha2_byte;\t\n\n/**\n * Exactly 4 bytes \n */\ntypedef hl_uint32 sha2_word32;\t\n\n/**\n * Exactly 8 bytes \n */ \ntypedef hl_uint64 sha2_word64;\t\n\n/**\n * @brief This struct represents a SHA256-hash context\n */\ntypedef struct HL_SHA256_CTX \n{\n\t/**\n\t * state \n\t */\n\thl_uint32\t\tstate[8];\n\n\t/**\n\t * bitcount\n\t */\n\thl_uint64\t\tbitcount;\n\n\t/**\n\t * message buffer\n\t */\n\thl_uint8\t\tbuffer[SHA256_BLOCK_LENGTH];\n} HL_SHA256_CTX;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe sha256 algorithm.\n *\n *   \t\tBasically the class provides three public member-functions\n *   \t\tto create a hash:  SHA256_Init(), SHA256_Update() and SHA256_End().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha256wrapper class instead of SHA256.\n */  \nclass SHA256\n{\n\tprivate:\n\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha256 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA256_Final(hl_uint8 digest[SHA256_DIGEST_LENGTH],\n\t\t\t          HL_SHA256_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal data transformation\n\t\t *  @param\tcontext The context to use\n\t\t *  @param\tdata The data to transform\t\n\t\t */  \n\t\tvoid SHA256_Transform(HL_SHA256_CTX* context,\n\t\t\t              const sha2_word32* data);\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tInitialize the context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA256_Init(HL_SHA256_CTX *context);\n\n\t\t/**\n\t\t *  @brief\tUpdates the context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA256_Update(HL_SHA256_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tEnds the sha256 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA256_End(HL_SHA256_CTX* context,\n\t\t\t         char buffer[SHA256_DIGEST_STRING_LENGTH]);\n\n};\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha256wrapper.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha256wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha256wrapper \n *  \t\tclass.\n *  @date \tDi 25 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha256wrapper.h\"\n#include \"hl_sha256.h\"\n\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha256wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA256_DIGEST_STRING_LENGTH];\n\tsha256->SHA256_End(&context,(char*)buff);\n\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha256wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha256 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha256wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha256->SHA256_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha256wrapper::resetContext(void)\n{\n\tsha256->SHA256_Init(&context);\n}\n\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha256wrapper::getTestHash(void)\n{\n\treturn \"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha256wrapper::sha256wrapper()\n{\n\tthis->sha256 = new SHA256();\t\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha256wrapper::~sha256wrapper()\n{\n\tdelete sha256;\n}\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha256wrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha256wrapper.h\n *  @brief\tThis file contains the definition of the sha256wrapper \n *  \t\tclass.\n *  @date \tDi 25 Sep 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA256WRAPPER_H\n#define SHA256WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha256.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n\n/**\n *  @brief \tThis class represents the SHA256 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha256 hash.\n *  \t\tJust create an instance of sha256wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha256example.cpp\n *\n *  \t\tsha256wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha256wrapper : public hashwrapper\n{\n\tprivate:\n\t\t\t/**\n\t\t\t * SHA256 access\n\t\t\t */\n\t\t\tSHA256 *sha256;\n\n\t\t\t/**\n\t\t\t * SHA256 context\n\t\t\t */\n\t\t\tHL_SHA256_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\t\t\t\t\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha256wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha256wrapper();\n\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha2ext.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from \n * the sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n//---------------------------------------------------------------------- \n\n/**\n *  @file \tsha2ext.cpp\n *  @brief\tThis file contains the implementation of the SHA2Ext class\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n// Standard C includes\n#include <string.h>\t\n#include <assert.h>\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_sha2ext.h\"\n#include \"hl_sha2mac.h\"\n\n//---------------------------------------------------------------------- \n\n/* Hash constant words K for SHA-384 and SHA-512: */\nconst static sha2_word64 K512[80] = {\n\t0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,\n\t0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,\n\t0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,\n\t0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,\n\t0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,\n\t0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,\n\t0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,\n\t0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,\n\t0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,\n\t0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,\n\t0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,\n\t0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,\n\t0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,\n\t0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,\n\t0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,\n\t0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,\n\t0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,\n\t0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,\n\t0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,\n\t0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,\n\t0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,\n\t0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,\n\t0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,\n\t0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,\n\t0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,\n\t0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,\n\t0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,\n\t0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,\n\t0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,\n\t0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,\n\t0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,\n\t0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,\n\t0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,\n\t0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,\n\t0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,\n\t0x113f9804bef90daeULL, 0x1b710b35131c471bULL,\n\t0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,\n\t0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,\n\t0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,\n\t0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL\n};\n\n/* Initial hash value H for SHA-384 */\nconst static sha2_word64 sha384_initial_hash_value[8] = {\n\t0xcbbb9d5dc1059ed8ULL,\n\t0x629a292a367cd507ULL,\n\t0x9159015a3070dd17ULL,\n\t0x152fecd8f70e5939ULL,\n\t0x67332667ffc00b31ULL,\n\t0x8eb44a8768581511ULL,\n\t0xdb0c2e0d64f98fa7ULL,\n\t0x47b5481dbefa4fa4ULL\n};\n\n/* Initial hash value H for SHA-512 */\nconst static sha2_word64 sha512_initial_hash_value[8] = {\n\t0x6a09e667f3bcc908ULL,\n\t0xbb67ae8584caa73bULL,\n\t0x3c6ef372fe94f82bULL,\n\t0xa54ff53a5f1d36f1ULL,\n\t0x510e527fade682d1ULL,\n\t0x9b05688c2b3e6c1fULL,\n\t0x1f83d9abfb41bd6bULL,\n\t0x5be0cd19137e2179ULL\n};\n\n/*\n *  * Constant used by SHA256/384/512_End() functions for converting the\n *   * digest to a readable hexadecimal character string:\n *    */\nstatic const char *sha2_hex_digits = \"0123456789abcdef\";\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tInitialize the SHA512 context\n *  @param\tcontext The context to init.\n */  \nvoid SHA2ext::SHA512_Init(HL_SHA512_CTX* context) \n{\n\tif (context == (HL_SHA512_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);\n\tcontext->bitcount[0] = context->bitcount[1] =  0;\n}\n\n#ifdef SHA2_UNROLL_TRANSFORM\n\n/* Unrolled SHA-512 round macros: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\n#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tREVERSE64(*data++, W512[j]); \\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \\\n             K512[j] + W512[j]; \\\n\t(d) += T1, \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \\\n\tj++\n\n\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h)\t\\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \\\n             K512[j] + (W512[j] = *data++); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \\\n\tj++\n\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n#define ROUND512(a,b,c,d,e,f,g,h)\t\\\n\ts0 = W512[(j+1)&0x0f]; \\\n\ts0 = sigma0_512(s0); \\\n\ts1 = W512[(j+14)&0x0f]; \\\n\ts1 = sigma1_512(s1); \\\n\tT1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \\\n             (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \\\n\t(d) += T1; \\\n\t(h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \\\n\tj++\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA2ext::SHA512_Transform(HL_SHA512_CTX* context, const sha2_word64* data) {\n\tsha2_word64\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word64\tT1, *W512 = (sha2_word64*)context->buffer;\n\tint\t\tj;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n\t\tROUND512_0_TO_15(a,b,c,d,e,f,g,h);\n\t\tROUND512_0_TO_15(h,a,b,c,d,e,f,g);\n\t\tROUND512_0_TO_15(g,h,a,b,c,d,e,f);\n\t\tROUND512_0_TO_15(f,g,h,a,b,c,d,e);\n\t\tROUND512_0_TO_15(e,f,g,h,a,b,c,d);\n\t\tROUND512_0_TO_15(d,e,f,g,h,a,b,c);\n\t\tROUND512_0_TO_15(c,d,e,f,g,h,a,b);\n\t\tROUND512_0_TO_15(b,c,d,e,f,g,h,a);\n\t} while (j < 16);\n\n\t/* Now for the remaining rounds up to 79: */\n\tdo {\n\t\tROUND512(a,b,c,d,e,f,g,h);\n\t\tROUND512(h,a,b,c,d,e,f,g);\n\t\tROUND512(g,h,a,b,c,d,e,f);\n\t\tROUND512(f,g,h,a,b,c,d,e);\n\t\tROUND512(e,f,g,h,a,b,c,d);\n\t\tROUND512(d,e,f,g,h,a,b,c);\n\t\tROUND512(c,d,e,f,g,h,a,b);\n\t\tROUND512(b,c,d,e,f,g,h,a);\n\t} while (j < 80);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = 0;\n}\n\n#else /* SHA2_UNROLL_TRANSFORM */\n\n/**\n *  @brief \tInternal data transformation\n *  @param\tcontext The context to use\n *  @param\tdata The data to transform\t\n */  \nvoid SHA2ext::SHA512_Transform(HL_SHA512_CTX* context, const sha2_word64* data) {\n\tsha2_word64\ta, b, c, d, e, f, g, h, s0, s1;\n\tsha2_word64\tT1, T2, *W512 = (sha2_word64*)context->buffer;\n\tint\t\tj;\n\n\t/* Initialize registers with the prev. intermediate value */\n\ta = context->state[0];\n\tb = context->state[1];\n\tc = context->state[2];\n\td = context->state[3];\n\te = context->state[4];\n\tf = context->state[5];\n\tg = context->state[6];\n\th = context->state[7];\n\n\tj = 0;\n\tdo {\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t/* Convert TO host byte order */\n\t\tREVERSE64(*data++, W512[j]);\n\t\t/* Apply the SHA-512 compression function to update a..h */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];\n#else /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\t/* Apply the SHA-512 compression function to update a..h with copy */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\t\tT2 = Sigma0_512(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 16);\n\n\tdo {\n\t\t/* Part of the message block expansion: */\n\t\ts0 = W512[(j+1)&0x0f];\n\t\ts0 = sigma0_512(s0);\n\t\ts1 = W512[(j+14)&0x0f];\n\t\ts1 =  sigma1_512(s1);\n\n\t\t/* Apply the SHA-512 compression function to update a..h */\n\t\tT1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +\n\t\t     (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);\n\t\tT2 = Sigma0_512(a) + Maj(a, b, c);\n\t\th = g;\n\t\tg = f;\n\t\tf = e;\n\t\te = d + T1;\n\t\td = c;\n\t\tc = b;\n\t\tb = a;\n\t\ta = T1 + T2;\n\n\t\tj++;\n\t} while (j < 80);\n\n\t/* Compute the current intermediate hash value */\n\tcontext->state[0] += a;\n\tcontext->state[1] += b;\n\tcontext->state[2] += c;\n\tcontext->state[3] += d;\n\tcontext->state[4] += e;\n\tcontext->state[5] += f;\n\tcontext->state[6] += g;\n\tcontext->state[7] += h;\n\n\t/* Clean up */\n\ta = b = c = d = e = f = g = h = T1 = T2 = 0;\n}\n#endif /* SHA2_UNROLL_TRANSFORM */\n\nvoid SHA2ext::SHA512_Update(HL_SHA512_CTX* context, const sha2_byte *data, unsigned int len) {\n\tunsigned int\tfreespace, usedspace;\n\n\tif (len == 0) {\n\t\t/* Calling with no data is valid - we do nothing */\n\t\treturn;\n\t}\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0 && data != (sha2_byte*)0);\n\n\tusedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;\n\tif (usedspace > 0) {\n\t\t/* Calculate how much free space is available in the buffer */\n\t\tfreespace = SHA512_BLOCK_LENGTH - usedspace;\n\n\t\tif (len >= freespace) {\n\t\t\t/* Fill the buffer completely and process it */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);\n\t\t\tADDINC128(context->bitcount, freespace << 3);\n\t\t\tlen -= freespace;\n\t\t\tdata += freespace;\n\t\t\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n\t\t} else {\n\t\t\t/* The buffer is not yet full */\n\t\t\tMEMCPY_BCOPY(&context->buffer[usedspace], data, len);\n\t\t\tADDINC128(context->bitcount, len << 3);\n\t\t\t/* Clean up: */\n\t\t\tusedspace = freespace = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\twhile (len >= SHA512_BLOCK_LENGTH) {\n\t\t/* Process as many complete blocks as we can */\n\t\tSHA512_Transform(context, (sha2_word64*)data);\n\t\tADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);\n\t\tlen -= SHA512_BLOCK_LENGTH;\n\t\tdata += SHA512_BLOCK_LENGTH;\n\t}\n\tif (len > 0) {\n\t\t/* There's left-overs, so save 'em */\n\t\tMEMCPY_BCOPY(context->buffer, data, len);\n\t\tADDINC128(context->bitcount, len << 3);\n\t}\n\t/* Clean up: */\n\tusedspace = freespace = 0;\n}\n\nvoid SHA2ext::SHA512_Last(HL_SHA512_CTX* context) \n{\n\tunsigned int\tusedspace;\n\n\tusedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t/* Convert FROM host byte order */\n\tREVERSE64(context->bitcount[0],context->bitcount[0]);\n\tREVERSE64(context->bitcount[1],context->bitcount[1]);\n#endif\n\tif (usedspace > 0) {\n\t\t/* Begin padding with a 1 bit: */\n\t\tcontext->buffer[usedspace++] = 0x80;\n\n\t\tif (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {\n\t\t\t/* Set-up for the last transform: */\n\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);\n\t\t} else {\n\t\t\tif (usedspace < SHA512_BLOCK_LENGTH) {\n\t\t\t\tMEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);\n\t\t\t}\n\t\t\t/* Do second-to-last transform: */\n\t\t\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n\n\t\t\t/* And set-up for the last transform: */\n\t\t\tMEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);\n\t\t}\n\t} else {\n\t\t/* Prepare for final transform: */\n\t\tMEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);\n\n\t\t/* Begin padding with a 1 bit: */\n\t\t*context->buffer = 0x80;\n\t}\n\t/* Store the length of input data (in bits): */\n\t*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];\n\t*(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];\n\n\t/* Final transform: */\n\tSHA512_Transform(context, (sha2_word64*)context->buffer);\n}\n\nvoid SHA2ext::SHA512_Final(sha2_byte digest[], HL_SHA512_CTX* context) \n{\n\tsha2_word64\t*d = (sha2_word64*)digest;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tSHA512_Last(context);\n\n\t\t/* Save the hash data for output: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 8; j++) {\n\t\t\t\tREVERSE64(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Zero out state data */\n\tMEMSET_BZERO(context, sizeof(context));\n}\n\nchar* SHA2ext::SHA512_End(HL_SHA512_CTX* context, char buffer[]) \n{\n\tsha2_byte\tdigest[SHA512_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA512_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA512_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA512_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);\n\treturn buffer;\n}\n\nvoid SHA2ext::SHA384_Init(HL_SHA_384_CTX* context) \n{\n\tif (context == (HL_SHA_384_CTX*)0) {\n\t\treturn;\n\t}\n\tMEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);\n\tMEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);\n\tcontext->bitcount[0] = context->bitcount[1] = 0;\n}\n\nvoid SHA2ext::SHA384_Update(HL_SHA_384_CTX* context, const sha2_byte* data, unsigned int len) \n{\n\tSHA512_Update((HL_SHA512_CTX*)context, data, len);\n}\n\nvoid SHA2ext::SHA384_Final(sha2_byte digest[], HL_SHA_384_CTX* context) \n{\n\tsha2_word64\t*d = (sha2_word64*)digest;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA_384_CTX*)0);\n\n\t/* If no digest buffer is passed, we don't bother doing this: */\n\tif (digest != (sha2_byte*)0) {\n\t\tSHA512_Last((HL_SHA512_CTX*)context);\n\n\t\t/* Save the hash data for output: */\n#if BYTE_ORDER == LITTLE_ENDIAN\n\t\t{\n\t\t\t/* Convert TO host byte order */\n\t\t\tint\tj;\n\t\t\tfor (j = 0; j < 6; j++) {\n\t\t\t\tREVERSE64(context->state[j],context->state[j]);\n\t\t\t\t*d++ = context->state[j];\n\t\t\t}\n\t\t}\n#else\n\t\tMEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);\n#endif\n\t}\n\n\t/* Zero out state data */\n\tMEMSET_BZERO(context, sizeof(context));\n}\n\nchar* SHA2ext::SHA384_End(HL_SHA_384_CTX* context, char buffer[]) \n{\n\tsha2_byte\tdigest[SHA384_DIGEST_LENGTH], *d = digest;\n\tint\t\ti;\n\n\t/* Sanity check: */\n\tassert(context != (HL_SHA_384_CTX*)0);\n\n\tif (buffer != (char*)0) {\n\t\tSHA384_Final(digest, context);\n\n\t\tfor (i = 0; i < SHA384_DIGEST_LENGTH; i++) {\n\t\t\t*buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];\n\t\t\t*buffer++ = sha2_hex_digits[*d & 0x0f];\n\t\t\td++;\n\t\t}\n\t\t*buffer = (char)0;\n\t} else {\n\t\tMEMSET_BZERO(context, sizeof(context));\n\t}\n\tMEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);\n\treturn buffer;\n}\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha2ext.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n *\n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from the\n * sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha2ext.h\n *  @brief\tThis file contains the declaration of the SHA384 and \n *  \t\tSHA512 classes\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n//include protection\n#ifndef SHA2ext_H\n#define SHA2ext_H\n\n//----------------------------------------------------------------------\n//lenght defines\n#define SHA384_BLOCK_LENGTH             128\n#define SHA384_DIGEST_LENGTH            48\n#define SHA384_DIGEST_STRING_LENGTH     (SHA384_DIGEST_LENGTH * 2 + 1)\n#define SHA512_BLOCK_LENGTH             128\n#define SHA512_DIGEST_LENGTH            64\n#define SHA512_DIGEST_STRING_LENGTH     (SHA512_DIGEST_LENGTH * 2 + 1)\n#define SHA512_SHORT_BLOCK_LENGTH\t(SHA512_BLOCK_LENGTH - 16)\n\n//---------------------------------------------------------------------- \n//hl includes\n#include \"hl_types.h\"\n\n//---------------------------------------------------------------------- \n//typedefs\n\n/**\n * Exactly 1 byte \n */ \ntypedef hl_uint8  sha2_byte;\t\n\n/**\n * Exactly 4 bytes \n */\ntypedef hl_uint32 sha2_word32;\t\n\n/**\n * Exactly 8 bytes \n */ \ntypedef hl_uint64 sha2_word64;\t\n\n/**\n * @brief This struct represents a SHA512-hash context\n */\ntypedef struct HL_SHA512_CTX \n{\n\thl_uint64       state[8];\n\thl_uint64       bitcount[2];\n\thl_uint8        buffer[SHA512_BLOCK_LENGTH];\n} HL_SHA512_CTX;\n\n\n/**\n * @brief This struct represents a SHA384-hash context\n */\ntypedef HL_SHA512_CTX HL_SHA_384_CTX;\n\n//----------------------------------------------------------------------\n\n/**\n *  @brief \tThis class represents the implementation of \n *   \t\tthe SHA384 and SHA512 algorithm.\n *\n *   \t\tBasically the class provides six public member-functions\n *   \t\tto create a hash:  SHA384_Init(), SHA384_Update(), SHA384_End(),\n *\t\tSHA512_Init(), SHA512_Update() and SHA512_End().\n *   \t\tIf you want to create a hash based on a string or file quickly\n *   \t\tyou should use the sha384wrapper or sha512wrapper classes.\n */  \nclass SHA2ext\n{\n\tprivate:\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha384 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA384_Final(hl_uint8 digest[SHA384_DIGEST_LENGTH],\n\t\t\t          HL_SHA_384_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tFinalize the sha512 operation\n\t\t *  @param\tdigest The digest to finalize the operation with.\n\t\t *  @param\tcontext The context to finalize.\n\t\t */  \n\t\tvoid SHA512_Final(hl_uint8 digest[SHA512_DIGEST_LENGTH],\n\t\t\t       \t  HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal method\n\t\t *\n\t\t *  \t\tused by SHA512 and SHA384\n\t\t *  @author\tBenjamin Grdelbach\n\t\t *  @param\tcontext The context of the operation\n\t\t */  \n\t\tvoid SHA512_Last(HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInternal data transformation\n\t\t *  @param\tcontext The context to use\n\t\t *  @param\tdata The data to transform\t\n\t\t */  \n\t\tvoid SHA512_Transform(HL_SHA512_CTX* context,\n\t\t\t              const sha2_word64* data);\n\n\n\tpublic:\n\n\t\t/**\n\t\t *  @brief \tInitialize the SHA384 context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA384_Init(HL_SHA_384_CTX* context);\n\n\t\t/**\n\t\t *  @brief \tInitialize the SHA512 context\n\t\t *  @param\tcontext The context to init.\n\t\t */  \n\t\tvoid SHA512_Init(HL_SHA512_CTX* context);\n\n\t\t/**\n\t\t *  @brief\tUpdates the SHA512 context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA384_Update(HL_SHA_384_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief\tUpdates the SHA284 context \n\t\t *  @param\tcontext The context to update.\n\t\t *  @param\tdata The data for updating the context.\n\t\t *  @param\tlen The length of the given data.\n\t\t */  \n\t\tvoid SHA512_Update(HL_SHA512_CTX* context,\n\t\t\t           const hl_uint8* data,\n\t\t\t\t   unsigned int len);\n\n\t\t/**\n\t\t *  @brief \tEnds the SHA384 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA384_End(HL_SHA_384_CTX* context,\n\t\t\t       \t char buffer[SHA384_DIGEST_STRING_LENGTH]);\n\n\t\t/**\n\t\t *  @brief \tEnds the SHA512 operation and return the\n\t\t *  \t\tcreated hash in the given buffer.\n\t\t *  @param\tcontext The context to end.\n\t\t *  @param\tbuffer This OUT-Parameter contains the created\n\t\t *  \t\thash after ending the operation.\n\t\t */  \n\t\tchar* SHA512_End(HL_SHA512_CTX* context,\n\t\t\t       \t char buffer[SHA512_DIGEST_STRING_LENGTH]);\n\n};\n\n\n//----------------------------------------------------------------------\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha2mac.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//---------------------------------------------------------------------- \n\n/*\n * The hashlib++ SHA384 and SHA512 implementations are derivative from\n * the sourcecode published by Aaron D. Gifford\n *\n * Copyright (c) 2000-2001, Aaron D. Gifford\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n */\n\n//---------------------------------------------------------------------- \n\n/**\n *  @file \thl_sha2mac.h\n *  @brief\tThis file contains useful macros for use with SHA384\n *  \t\tand SHA512\n *  @date \tMo 12 Nov 2007\n */  \n\n//---------------------------------------------------------------------- \n/*\n* SHA-256/384/512 Machine Architecture Definitions \n* BYTE_ORDER NOTE:\n*\n* Please make sure that your system defines BYTE_ORDER.  If your\n* architecture is little-endian, make sure it also defines\n* LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are\n* equivilent.\n*\n* If your system does not define the above, then you can do so by\n* hand like this:\n*\n*   #define LITTLE_ENDIAN 1234\n*   #define BIG_ENDIAN    4321\n*\n* And for little-endian machines, add:\n*\n*   #define BYTE_ORDER LITTLE_ENDIAN \n*\n* Or for big-endian machines:\n*\n*   #define BYTE_ORDER BIG_ENDIAN\n*\n* The FreeBSD machine this was written on defines\n* BYTE_ORDER  appropriately by including <sys/types.h> \n* (which in  turn includes <machine/endian.h> where the appropriate\n* definitions are actually made).\n*/\n#define LITTLE_ENDIAN 1234\n#define BYTE_ORDER LITTLE_ENDIAN \n#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)\n#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN\n#endif\n\n/*** ENDIAN REVERSAL MACROS *******************************************/\n#if BYTE_ORDER == LITTLE_ENDIAN\n#define REVERSE32(w,x)  { \\\n\tsha2_word32 tmp = (w); \\\n\ttmp = (tmp >> 16) | (tmp << 16); \\\n\t(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \\\n}\n#define REVERSE64(w,x)  { \\\n\tsha2_word64 tmp = (w); \\\n\ttmp = (tmp >> 32) | (tmp << 32); \\\n\ttmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \\\n\t((tmp & 0x00ff00ff00ff00ffULL) << 8); \\\n\t(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \\\n\t((tmp & 0x0000ffff0000ffffULL) << 16); \\\n}\n#endif /* BYTE_ORDER == LITTLE_ENDIAN */\n\n/*\n * Macro for incrementally adding the unsigned 64-bit integer n to the\n * unsigned 128-bit integer (represented using a two-element array of\n * 64-bit words):\n */\n#define ADDINC128(w,n)  { \\\n\t(w)[0] += (sha2_word64)(n); \\\n\tif ((w)[0] < (n)) { \\\n\t\t(w)[1]++; \\\n\t} \\\n}\n\n/*\n * Macros for copying blocks of memory and for zeroing out ranges\n * of memory.  Using these macros makes it easy to switch from\n * using memset()/memcpy() and using bzero()/bcopy().\n *\n * Please define either SHA2_USE_MEMSET_MEMCPY or define\n * SHA2_USE_BZERO_BCOPY depending on which function set you\n * choose to use:\n */\n#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)\n/* Default to memset()/memcpy() if no option is specified */\n#define SHA2_USE_MEMSET_MEMCPY  1\n#endif\n#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)\n/* Abort with an error if BOTH options are defined */\n#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!\n#endif\n\n#ifdef SHA2_USE_MEMSET_MEMCPY\n#define MEMSET_BZERO(p,l)       memset((p), 0, (l))\n#define MEMCPY_BCOPY(d,s,l)     memcpy((d), (s), (l))\n#endif\n#ifdef SHA2_USE_BZERO_BCOPY\n#define MEMSET_BZERO(p,l)       bzero((p), (l))\n#define MEMCPY_BCOPY(d,s,l)     bcopy((s), (d), (l))\n#endif\n\n/*** THE SIX LOGICAL FUNCTIONS ****************************************\n *\n * Bit shifting and rotation (used by the six SHA-XYZ logical functions:\n *\n *   NOTE:  The naming of R and S appears backwards here (R is a SHIFT and\n *   S is a ROTATION) because the SHA-256/384/512 description document\n *   (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this\n *   same \"backwards\" definition.\n */\n/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */\n#define R(b,x)          ((x) >> (b))\n/* 32-bit Rotate-right (used in SHA-256): */\n#define S32(b,x)        (((x) >> (b)) | ((x) << (32 - (b))))\n/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */\n#define S64(b,x)        (((x) >> (b)) | ((x) << (64 - (b))))\n\n/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */\n#define Ch(x,y,z)       (((x) & (y)) ^ ((~(x)) & (z)))\n#define Maj(x,y,z)      (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n\n/* Four of six logical functions used in SHA-256: */\n#define Sigma0_256(x)   (S32(2,  (x)) ^ S32(13, (x)) ^ S32(22, (x)))\n#define Sigma1_256(x)   (S32(6,  (x)) ^ S32(11, (x)) ^ S32(25, (x)))\n#define sigma0_256(x)   (S32(7,  (x)) ^ S32(18, (x)) ^ R(3 ,   (x)))\n#define sigma1_256(x)   (S32(17, (x)) ^ S32(19, (x)) ^ R(10,   (x)))\n\n/* Four of six logical functions used in SHA-384 and SHA-512: */\n#define Sigma0_512(x)   (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))\n#define Sigma1_512(x)   (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))\n#define sigma0_512(x)   (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7,   (x)))\n#define sigma1_512(x)   (S64(19, (x)) ^ S64(61, (x)) ^ R( 6,   (x)))\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha384wrapper.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n/**\n *  @file \thl_sha384wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha384wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha384wrapper.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha384wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA384_DIGEST_STRING_LENGTH];\n\tsha384->SHA384_End(&context,(char*)buff);\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha384wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha384 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha384wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha384->SHA384_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha384wrapper::resetContext(void)\n{\n\tsha384->SHA384_Init(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha384wrapper::getTestHash(void)\n{ \n\treturn \"ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha384wrapper::sha384wrapper()\n{\n\tthis->sha384 = new SHA2ext();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha384wrapper::~sha384wrapper()\n{\n\tdelete sha384;\n}\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha384wrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha384wrapper.h\n *  @brief\tThis file contains the definition of the sha384wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA384WRAPPER_H\n#define SHA384WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha2ext.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA384 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha384 hash.\n *  \t\tJust create an instance of sha384wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha384example.cpp\n *\n *  \t\tsha384wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha384wrapper : public hashwrapper\n{\n\tprivate:\n\n\t\t\t/**\n\t\t\t * SHA384 access\n\t\t\t * via extended SHA2\n\t\t\t */\n\t\t\tSHA2ext *sha384;\n\n\t\t\t/**\n\t\t\t * SHA384 context\n\t\t\t */\n\t\t\tHL_SHA_384_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha384wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha384wrapper();\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha512wrapper.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha512wrapper.cpp\n *  @brief\tThis file contains the implementation of the sha512wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_sha512wrapper.h\"\n\n//----------------------------------------------------------------------\t\n//STL includes\n#include <string>\n\n//----------------------------------------------------------------------\t\n//private memberfunctions\n\n/**\n *  @brief \tThis method ends the hash process\n *  \t\tand returns the hash as string.\n *\n *  @return \ta hash as std::string\n */  \nstd::string sha512wrapper::hashIt(void)\n{\n\tsha2_byte buff[SHA512_DIGEST_STRING_LENGTH];\n\tsha512->SHA512_End(&context,(char*)buff);\n\treturn convToString(buff);\n}\n\n/**\n *  @brief \tThis internal member-function\n *  \t\tconvertes the hash-data to a\n *  \t\tstd::string (HEX).\n *\n *  @param \tdata The hash-data to covert into HEX\n *  @return\tthe converted data as std::string\n */  \nstd::string sha512wrapper::convToString(unsigned char *data)\n{\n\t/*\n\t * we can just copy data to a string, because \n\t * the transforming to hash is already done\n\t * within the sha512 implementation\n\t */\n\treturn std::string((const char*)data);\n}\n\n/**\n *  @brief \tThis method adds the given data to the \n *  \t\tcurrent hash context\n *\n *  @param \tdata The data to add to the current context\n *  @param \tlen The length of the data to add\n */  \nvoid sha512wrapper::updateContext(unsigned char *data, unsigned int len)\n{\n\tthis->sha512->SHA512_Update(&context,data,len);\n}\n\n/**\n *  @brief \tThis method resets the current hash context.\n *  \t\tIn other words: It starts a new hash process.\n */  \nvoid sha512wrapper::resetContext(void)\n{\n\tsha512->SHA512_Init(&context);\n}\n\n/**\n * @brief \tThis method should return the hash of the\n * \t\ttest-string \"The quick brown fox jumps over the lazy\n * \t\tdog\"\n */\nstd::string sha512wrapper::getTestHash(void)\n{\n\treturn \"07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6\";\n}\n\n//----------------------------------------------------------------------\t\n//public memberfunctions\n\n/**\n *  @brief \tdefault constructor\n */  \nsha512wrapper::sha512wrapper()\n{\n\tthis->sha512 = new SHA2ext();\n}\n\n/**\n *  @brief \tdefault destructor\n */  \nsha512wrapper::~sha512wrapper()\n{\n\tdelete sha512;\n}\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_sha512wrapper.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_sha512wrapper.h\n *  @brief\tThis file contains the definition of the sha512wrapper \n *  \t\tclass.\n *  @date \tMo 12 Nov 2007\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef SHA512WRAPPER_H\n#define SHA512WRAPPER_H\n\n//----------------------------------------------------------------------\t\n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n#include \"hl_sha2ext.h\"\n\n//----------------------------------------------------------------------\t\n//STL\n#include <string>\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @brief \tThis class represents the SHA512 wrapper-class\n *\n *  \t\tYou can use this class to easily create a sha512 hash.\n *  \t\tJust create an instance of sha512wrapper and call the\n *  \t\tinherited memberfunctions getHashFromString()\n *  \t\tand getHashFromFile() to create a hash based on a\n *  \t\tstring or a file. \n *\n *  \t\tHave a look at the following example:\n *\n *  @include \tsha512example.cpp\n *\n *  \t\tsha512wrapper implements resetContext(), updateContext()\n *  \t\tand hashIt() to create a hash.\n */  \nclass sha512wrapper : public hashwrapper\n{\n\tprivate:\n\t\t\t/**\n\t\t\t * SHA512 access\n\t\t\t * via extended SHA2\n\t\t\t */\n\t\t\tSHA2ext *sha512;\n\n\t\t\t/**\n\t\t\t * SHA512 context\n\t\t\t */\n\t\t\tHL_SHA512_CTX context;\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method ends the hash process\n\t\t\t *  \t\tand returns the hash as string.\n\t\t\t *\n\t\t\t *  @return \ta hash as std::string\n\t\t\t */  \n\t\t\tvirtual std::string hashIt(void);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis internal member-function\n\t\t\t *  \t\tconvertes the hash-data to a\n\t\t\t *  \t\tstd::string (HEX).\n\t\t\t *\n\t\t\t *  @param \tdata The hash-data to covert into HEX\n\t\t\t *  @return\tthe converted data as std::string\n\t\t\t */  \n\t\t\tvirtual std::string convToString(unsigned char *data);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method adds the given data to the \n\t\t\t *  \t\tcurrent hash context\n\t\t\t *\n\t\t\t *  @param \tdata The data to add to the current context\n\t\t\t *  @param \tlen The length of the data to add\n\t\t\t */  \n\t\t\tvirtual void updateContext(unsigned char *data, unsigned int len);\n\n\t\t\t/**\n\t\t\t *  @brief \tThis method resets the current hash context.\n\t\t\t *  \t\tIn other words: It starts a new hash process.\n\t\t\t */  \n\t\t\tvirtual void resetContext(void);\n\n\t\t\t/**\n\t\t\t * @brief \tThis method should return the hash of the\n\t\t\t * \t\ttest-string \"The quick brown fox jumps over the lazy\n\t\t\t * \t\tdog\"\n\t\t\t */\n\t\t\tvirtual std::string getTestHash(void);\n\n\tpublic:\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault constructor\n\t\t\t */  \n\t\t\tsha512wrapper();\n\n\t\t\t/**\n\t\t\t *  @brief \tdefault destructor\n\t\t\t */  \n\t\t\tvirtual ~sha512wrapper();\n\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_types.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_types.h\n *  @brief\tThis file defines some global types\n *  @date \tSo 13 Jan 2008\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef HLTYPES_H\n#define HLTYPES_H\n\n//----------------------------------------------------------------------\t\n\n/**\n * exactly 1 Byte\n */\ntypedef unsigned char \thl_uint8;\n\n/**\n * at least 2 Byte\n */\ntypedef unsigned short int \thl_uint16;\n\n/**\n * at least 4 Byte\n */\ntypedef unsigned int hl_uint32;\n\n/**\n* at least 8 Byte\n*/\n#ifdef __GNUC__\n\ttypedef unsigned long long int\thl_uint64;\n#elif __MINGW32__\n\ttypedef unsigned long long int\thl_uint64;\n#elif _MSC_VER\n\ttypedef unsigned __int64 hl_uint64;\n#else\n\t#error \"Unsuppported compiler.\" \\\n               \"Please use GCC,MINGW,MSVC \" \\\n\t       \" or define hl_uint64 for your compiler in hl_types.h line 62\"\n#endif\n\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_wrapperfactory.cpp",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grüdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_wrapperfactory.cpp\n *  @brief\tThis file contains the implementation of the \n *\t\twrapperfactory class\n *  @date \tDO 13 Oct 2011\n */  \n\n//---------------------------------------------------------------------- \n#include <algorithm>\n#include \"hl_wrapperfactory.h\"\n#include \"hashlibpp.h\"\n\n\n\n//---------------------------------------------------------------------- \n//public member functions\n\n/**\n * @brief\tSimple factory-method to create a hashwrapper\n * \n * @param\ttype The type of the hash algorithm to create a wrapper for\n * @return\tA hashwrapper for the fiven type\n */\nhashwrapper* wrapperfactory::create(HL_Wrappertype type)\n{\n\tif(type == HL_MD5)\n\t{\n\t\treturn new md5wrapper();\n\t}\n\telse if(type == HL_SHA1)\n\t{\n\t\treturn new sha1wrapper();\n\t}\n\telse if(type == HL_SHA256)\n\t{\n\t\treturn new sha256wrapper();\n\t}\n\telse if(type == HL_SHA384)\n\t{\n\t\treturn new sha384wrapper();\n\t}\n\telse if(type == HL_SHA512)\n\t{\n\t\treturn new sha512wrapper();\n\t}\n\n\tthrow hlException(HL_UNKNOWN_HASH_TYPE,\"Unknown hashtype\");\n}\n\n/**\n * @brief\tSimple factory-method to create a hashwrapper\n * \n * @param\ttype The type of the hash algorithm to create a wrapper for\n * @return\tA hashwrapper for the fiven type\n */\nhashwrapper* wrapperfactory::create(std::string type)\n{\n \tstd::transform(type.begin(), type.end(), type.begin(), ::toupper);\n\tif(type == \"MD5\")\n\t{\n\t\treturn new md5wrapper();\n\t}\n\telse if(type == \"SHA1\")\n\t{\n\t\treturn new sha1wrapper();\n\t}\n\telse if(type == \"SHA256\")\n\t{\n\t\treturn new sha256wrapper();\n\t}\n\telse if(type == \"SHA384\")\n\t{\n\t\treturn new sha384wrapper();\n\t}\n\telse if(type == \"SHA512\")\n\t{\n\t\treturn new sha512wrapper();\n\t}\n\treturn NULL;\n}\n\n//---------------------------------------------------------------------- \n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/hashlib/hl_wrapperfactory.h",
    "content": "/* \n * hashlib++ - a simple hash library for C++\n * \n * Copyright (c) 2007-2010 Benjamin Grüdelbach\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * \t1)     Redistributions of source code must retain the above copyright\n * \t       notice, this list of conditions and the following disclaimer.\n * \n * \t2)     Redistributions in binary form must reproduce the above copyright\n * \t       notice, this list of conditions and the following disclaimer in\n * \t       the documentation and/or other materials provided with the\n * \t       distribution.\n * \t     \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n//----------------------------------------------------------------------\t\n\n/**\n *  @file \thl_wrapperfactory.h\n *  @brief\tThis file contains a simple hashwrapper factory\n *  @date \tDO 13 Oct 2011\n */  \n\n//----------------------------------------------------------------------\t\n//include protection\n#ifndef WRAPPERFACTORY_H\n#define WRAPPERFACTORY_H\n\n//---------------------------------------------------------------------- \n//hashlib++ includes\n#include \"hl_hashwrapper.h\"\n\n//----------------------------------------------------------------------\t\n//enumeration\n\n/*\n * definition of the supported hashtypes \n */\nenum HL_Wrappertype { HL_MD5, HL_SHA1, HL_SHA256, HL_SHA384, HL_SHA512 };\n\n//---------------------------------------------------------------------- \n\n/**\n *  @brief\tThis class represents a simple factory for creating wrappers.\n *\n *\t\tU can use this class for dynamicly create wrappers of a given\n * \t\ttype at runtime. \n */\nclass wrapperfactory\n{\n\tpublic:\n\n\t\t/**\n\t\t * @brief\tSimple factory-method to create a hashwrapper\n\t\t * \n\t\t * @param\ttype The type of the hash algorithm to create a wrapper for\n\t\t * @return\tA hashwrapper for the fiven type\n\t\t */\n\t\thashwrapper* create(HL_Wrappertype type);\n\n\t\t/**\n\t\t * @brief\tSimple factory-method to create a hashwrapper\n\t\t * \n\t\t * @param\ttype the simple name of the type for example \"md5\"\n\t\t * @return\tA hashwrapper for the fiven type\n\t\t */\n\t\thashwrapper* create(std::string type);\n};\n\n//----------------------------------------------------------------------\t\n//end of include protection\n#endif\n\n//----------------------------------------------------------------------\t\n//EOF\n"
  },
  {
    "path": "_draft/flash_cpp/main.cpp",
    "content": "#include <stdlib.h>\n#include \"AS3/AS3.h\"\n\n\nint main() {\n    AS3_GoAsync();\n}"
  },
  {
    "path": "_draft/flash_cpp/makefile",
    "content": "T05: check\n\t@echo \"-------- Sample 5 --------\"\n\n\t@echo && echo \"Now compile a SWC and demo SWF\"\n\t\"$(FLASCC)/usr/bin/g++\" $(BASE_CFLAGS) \\\n\t\t\tmain.cpp \\\n\t\t\tas3api.cpp \\\n\t\t\thashlib/hl_md5.cpp \\\n\t\t\thashlib/hl_md5wrapper.cpp \\\n\t\t-O4 -disable-telemetry -flto-api=exports.txt\\\n\t\t-emit-swc=com.webuploader -o Test.swc\n\t# \"$(FLEX)/bin/mxmlc\" -static-link-runtime-shared-libraries -compiler.omit-trace-statements=false -library-path=MurmurHash.swc -debug=false swcdemo.as -o swcdemo.swf\n\nFLASCC:=X\nFLEX:=X\nAS3COMPILER:=asc2.jar\nBASE_CFLAGS:=-Werror -Wno-write-strings -Wno-trigraphs\n\n$?UNAME=$(shell uname -s)\nifneq (,$(findstring CYGWIN,$(UNAME)))\n\t$?nativepath=$(shell cygpath -at mixed $(1))\n\t$?unixpath=$(shell cygpath -at unix $(1))\nelse\n\t$?nativepath=$(abspath $(1))\n\t$?unixpath=$(abspath $(1))\nendif\n\nifneq (,$(findstring \"asc2.jar\",\"$(AS3COMPILER)\"))\n\t$?AS3COMPILERARGS=java $(JVMARGS) -jar $(call nativepath,$(FLASCC)/usr/lib/$(AS3COMPILER)) -merge -md\nelse\n\techo \"ASC is no longer supported\" ; exit 1 ;\nendif\n\ncheck:\n\t@if [ -d $(FLASCC)/usr/bin ] ; then true ; \\\n\telse echo \"Couldn't locate FLASCC sdk directory, please invoke make with \\\"make FLASCC=/path/to/FLASCC/sdk ...\\\"\" ; exit 1 ; \\\n\tfi\n\n\t@if [ -d \"$(FLEX)/bin\" ] ; then true ; \\\n\telse echo \"Couldn't locate Flex sdk directory, please invoke make with \\\"make FLEX=/path/to/flex  ...\\\"\" ; exit 1 ; \\\n\tfi\n\n\nclean:\n\trm -f *.swf *.swc *.bc *.exe\n"
  },
  {
    "path": "_draft/hashFile.js",
    "content": "// 此文件在worker环境下运行。\nimportScripts('md5.js');\n\nvar hashMe = function(file, callbackFunction) {\n\n  var thisObj = this,\n    _binStart = \"\",\n    _binEnd = \"\",\n    callback = \"\",\n    fileManager1 = new FileReader,\n    fileManager2 = new FileReader;\n\n  thisObj.setBinAndHash = function(startOrEnd, binData) {\n\n    switch (startOrEnd) {\n      case 0:\n        thisObj._binStart = binData;\n        break;\n      case 1:\n        thisObj._binEnd = binData;\n    }\n\n    thisObj._binStart && thisObj._binEnd && thisObj.md5sum(thisObj._binStart, thisObj._binEnd)\n  };\n\n  thisObj.md5sum = function(start, end) {\n    thisObj._hash = md5(start + end);\n    callback(thisObj._hash);\n  };\n\n  thisObj.getHash = function() {\n    return thisObj._hash;\n  };\n\n  thisObj.calculateHashOfFile = function(file) {\n\n    fileManager1.onload = function(f) {\n      thisObj.setBinAndHash(0, f.target.result);\n    };\n\n    fileManager2.onload = function(f) {\n      thisObj.setBinAndHash(1, f.target.result);\n    };\n\n    var start = file.slice(0, 65536);\n    var end = file.slice(file.size - 65536, file.size);\n\n    fileManager1.readAsBinaryString(start);\n    fileManager2.readAsBinaryString(end);\n  };\n\n  thisObj.calculateHashOfFile(file);\n  callback = callbackFunction;\n\n};\n\nonmessage = function( e ) {\n    var file = e.data;\n    hashMe( file, function( ret ) {\n        postMessage( ret );\n    });\n}"
  },
  {
    "path": "_draft/md5.js",
    "content": "(function( window ){\n    function md5cycle(x, k) {\n        var a = x[0],\n            b = x[1],\n            c = x[2],\n            d = x[3];\n\n        a = ff(a, b, c, d, k[0], 7, -680876936);\n        d = ff(d, a, b, c, k[1], 12, -389564586);\n        c = ff(c, d, a, b, k[2], 17, 606105819);\n        b = ff(b, c, d, a, k[3], 22, -1044525330);\n        a = ff(a, b, c, d, k[4], 7, -176418897);\n        d = ff(d, a, b, c, k[5], 12, 1200080426);\n        c = ff(c, d, a, b, k[6], 17, -1473231341);\n        b = ff(b, c, d, a, k[7], 22, -45705983);\n        a = ff(a, b, c, d, k[8], 7, 1770035416);\n        d = ff(d, a, b, c, k[9], 12, -1958414417);\n        c = ff(c, d, a, b, k[10], 17, -42063);\n        b = ff(b, c, d, a, k[11], 22, -1990404162);\n        a = ff(a, b, c, d, k[12], 7, 1804603682);\n        d = ff(d, a, b, c, k[13], 12, -40341101);\n        c = ff(c, d, a, b, k[14], 17, -1502002290);\n        b = ff(b, c, d, a, k[15], 22, 1236535329);\n\n        a = gg(a, b, c, d, k[1], 5, -165796510);\n        d = gg(d, a, b, c, k[6], 9, -1069501632);\n        c = gg(c, d, a, b, k[11], 14, 643717713);\n        b = gg(b, c, d, a, k[0], 20, -373897302);\n        a = gg(a, b, c, d, k[5], 5, -701558691);\n        d = gg(d, a, b, c, k[10], 9, 38016083);\n        c = gg(c, d, a, b, k[15], 14, -660478335);\n        b = gg(b, c, d, a, k[4], 20, -405537848);\n        a = gg(a, b, c, d, k[9], 5, 568446438);\n        d = gg(d, a, b, c, k[14], 9, -1019803690);\n        c = gg(c, d, a, b, k[3], 14, -187363961);\n        b = gg(b, c, d, a, k[8], 20, 1163531501);\n        a = gg(a, b, c, d, k[13], 5, -1444681467);\n        d = gg(d, a, b, c, k[2], 9, -51403784);\n        c = gg(c, d, a, b, k[7], 14, 1735328473);\n        b = gg(b, c, d, a, k[12], 20, -1926607734);\n\n        a = hh(a, b, c, d, k[5], 4, -378558);\n        d = hh(d, a, b, c, k[8], 11, -2022574463);\n        c = hh(c, d, a, b, k[11], 16, 1839030562);\n        b = hh(b, c, d, a, k[14], 23, -35309556);\n        a = hh(a, b, c, d, k[1], 4, -1530992060);\n        d = hh(d, a, b, c, k[4], 11, 1272893353);\n        c = hh(c, d, a, b, k[7], 16, -155497632);\n        b = hh(b, c, d, a, k[10], 23, -1094730640);\n        a = hh(a, b, c, d, k[13], 4, 681279174);\n        d = hh(d, a, b, c, k[0], 11, -358537222);\n        c = hh(c, d, a, b, k[3], 16, -722521979);\n        b = hh(b, c, d, a, k[6], 23, 76029189);\n        a = hh(a, b, c, d, k[9], 4, -640364487);\n        d = hh(d, a, b, c, k[12], 11, -421815835);\n        c = hh(c, d, a, b, k[15], 16, 530742520);\n        b = hh(b, c, d, a, k[2], 23, -995338651);\n\n        a = ii(a, b, c, d, k[0], 6, -198630844);\n        d = ii(d, a, b, c, k[7], 10, 1126891415);\n        c = ii(c, d, a, b, k[14], 15, -1416354905);\n        b = ii(b, c, d, a, k[5], 21, -57434055);\n        a = ii(a, b, c, d, k[12], 6, 1700485571);\n        d = ii(d, a, b, c, k[3], 10, -1894986606);\n        c = ii(c, d, a, b, k[10], 15, -1051523);\n        b = ii(b, c, d, a, k[1], 21, -2054922799);\n        a = ii(a, b, c, d, k[8], 6, 1873313359);\n        d = ii(d, a, b, c, k[15], 10, -30611744);\n        c = ii(c, d, a, b, k[6], 15, -1560198380);\n        b = ii(b, c, d, a, k[13], 21, 1309151649);\n        a = ii(a, b, c, d, k[4], 6, -145523070);\n        d = ii(d, a, b, c, k[11], 10, -1120210379);\n        c = ii(c, d, a, b, k[2], 15, 718787259);\n        b = ii(b, c, d, a, k[9], 21, -343485551);\n\n        x[0] = add32(a, x[0]);\n        x[1] = add32(b, x[1]);\n        x[2] = add32(c, x[2]);\n        x[3] = add32(d, x[3]);\n\n    }\n\n    function cmn(q, a, b, x, s, t) {\n        a = add32(add32(a, q), add32(x, t));\n        return add32((a << s) | (a >>> (32 - s)), b);\n    }\n\n    function ff(a, b, c, d, x, s, t) {\n        return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n    }\n\n    function gg(a, b, c, d, x, s, t) {\n        return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n    }\n\n    function hh(a, b, c, d, x, s, t) {\n        return cmn(b ^ c ^ d, a, b, x, s, t);\n    }\n\n    function ii(a, b, c, d, x, s, t) {\n        return cmn(c ^ (b | (~d)), a, b, x, s, t);\n    }\n\n    function md51(s) {\n        txt = '';\n        var n = s.length,\n            state = [1732584193, -271733879, -1732584194, 271733878],\n            i;\n        for (i = 64; i <= s.length; i += 64) {\n            md5cycle(state, md5blk(s.substring(i - 64, i)));\n        }\n        s = s.substring(i - 64);\n        var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n        for (i = 0; i < s.length; i++)\n            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n        tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n        if (i > 55) {\n            md5cycle(state, tail);\n            for (i = 0; i < 16; i++) tail[i] = 0;\n        }\n        tail[14] = n * 8;\n        md5cycle(state, tail);\n        return state;\n    }\n\n    /* there needs to be support for Unicode here,\n     * unless we pretend that we can redefine the MD-5\n     * algorithm for multi-byte characters (perhaps\n     * by adding every four 16-bit characters and\n     * shortening the sum to 32 bits). Otherwise\n     * I suggest performing MD-5 as if every character\n     * was two bytes--e.g., 0040 0025 = @%--but then\n     * how will an ordinary MD-5 sum be matched?\n     * There is no way to standardize text to something\n     * like UTF-8 before transformation; speed cost is\n     * utterly prohibitive. The JavaScript standard\n     * itself needs to look at this: it should start\n     * providing access to strings as preformed UTF-8\n     * 8-bit unsigned value arrays.\n     */\n\n    function md5blk(s) { /* I figured global was faster.   */\n        var md5blks = [],\n            i; /* Andy King said do it this way. */\n        for (i = 0; i < 64; i += 4) {\n            md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n        }\n        return md5blks;\n    }\n\n    var hex_chr = '0123456789abcdef'.split('');\n\n    function rhex(n) {\n        var s = '',\n            j = 0;\n        for (; j < 4; j++)\n            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n        return s;\n    }\n\n    function hex(x) {\n        for (var i = 0; i < x.length; i++)\n            x[i] = rhex(x[i]);\n        return x.join('');\n    }\n\n    function md5(s) {\n        return hex(md51(s));\n    }\n\n    /* this function is much faster,\nso if possible we use it. Some IEs\nare the only ones I know of that\nneed the idiotic second function,\ngenerated by an if clause.  */\n\n    var add32 = function(a, b) {\n        return (a + b) & 0xFFFFFFFF;\n    }\n\n    if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {\n        add32 = function(x, y) {\n            var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n            return (msw << 16) | (lsw & 0xFFFF);\n        }\n    }\n\n    window.md5 = md5;\n})(this);"
  },
  {
    "path": "_draft/md5File.js",
    "content": "// 此文件在worker环境下运行。\nimportScripts('md5.js');\n\nvar fr = new FileReader();\n\nvar Md5File = (function() {\n    /*var throttle = 3,\n        pool = [],\n        wating = [];\n\n    function _tick() {\n        var avaibles = [],\n            i, fr, cb;\n\n        for ( i = 0; i < throttle; i++ ) {\n            fr = pool[ i ];\n            fr && fr.readyState === 2 && avaibles.push( fr );\n        }\n\n        while ( avaibles.length && wating.length ) {\n            fr = avaibles.shift();\n            cb = wating.shift();\n            fr.onload = fr.onerror = null;\n            cb( fr );\n            fr.onloadend = _tick;\n        }\n    }\n\n    function getReader( cb ) {\n        var fr;\n\n        if ( pool.length < throttle ) {\n            fr = new FileReader();\n            pool.push( fr );\n            cb( fr );\n            fr.onloadend = _tick;\n            return;\n        }\n\n        wating.push( cb );\n        _tick();\n    }*/\n\n    return function( file, cb ) {\n        var reader = new FileReader();\n\n        reader.onload = function() {\n            cb( md5( this.result ) );\n            reader.onload = reader.onerror = null;\n\n            reader.readAsBinaryString( new Blob() );\n            reader = null;\n        };\n\n        reader.onerror = function( e ) {\n            reader = reader.onload = reader.onerror = null;\n        };\n\n        reader.readAsBinaryString( file );\n    }\n})();\n\nonmessage = function( e ) {\n    var file = e.data;\n    Md5File( file, function( ret ) {\n        postMessage( ret );\n    });\n}"
  },
  {
    "path": "_draft/music/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div id=\"container\">\n            <!--头部，相册选择和格式选择-->\n\n            <div id=\"uploader\">\n                <div class=\"queueList\">\n                    <div id=\"dndArea\" class=\"placeholder\">\n                        <div id=\"filePicker\"></div>\n                        <p>或将文件拖到这里</p>\n                    </div>\n                </div>\n                <div class=\"statusBar\" style=\"display:none;\">\n                    <div class=\"progress\">\n                        <span class=\"text\">0%</span>\n                        <span class=\"percentage\"></span>\n                    </div><div class=\"info\"></div>\n                    <div class=\"btns\">\n                        <div id=\"filePicker2\"></div><div class=\"uploadBtn\">开始上传</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script type=\"text/javascript\" src=\"./jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"./webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./md5.js\"></script>\n    <script type=\"text/javascript\" src=\"./upload.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "_draft/music/jquery.js",
    "content": "/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license\n*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p=\"1.9.1\",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,w=/\\S+/g,T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,N=/^(?:(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/,C=/^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/,k=/^[\\],:{}\\s]*$/,E=/(?:^|:|,)(?:\\s*\\[)+/g,S=/\\\\(?:[\"\\\\\\/bfnrt]|u[\\da-fA-F]{4})/g,A=/\"[^\"\\\\\\r\\n]*\"|true|false|null|-?(?:\\d+\\.|)\\d+(?:[eE][+-]?\\d+|)/g,j=/^-ms-/,D=/-([\\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||\"load\"===e.type||\"complete\"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener(\"DOMContentLoaded\",H,!1),e.removeEventListener(\"load\",H,!1)):(o.detachEvent(\"onreadystatechange\",H),e.detachEvent(\"onload\",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if(\"string\"==typeof e){if(i=\"<\"===e.charAt(0)&&\">\"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:\"\",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for(\"boolean\"==typeof s&&(c=s,s=arguments[1]||{},u=2),\"object\"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger(\"ready\").off(\"ready\"))}},isFunction:function(e){return\"function\"===b.type(e)},isArray:Array.isArray||function(e){return\"array\"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[m.call(e)]||\"object\":typeof e},isPlainObject:function(e){if(!e||\"object\"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,\"constructor\")&&!y.call(e.constructor.prototype,\"isPrototypeOf\"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||\"string\"!=typeof e)return null;\"boolean\"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:\"string\"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,\"@\").replace(A,\"]\").replace(E,\"\")))?Function(\"return \"+n)():(b.error(\"Invalid JSON: \"+n),t)},parseXML:function(n){var r,i;if(!n||\"string\"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,\"text/xml\")):(r=new ActiveXObject(\"Microsoft.XMLDOM\"),r.async=\"false\",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName(\"parsererror\").length||b.error(\"Invalid XML: \"+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,\"ms-\").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call(\"\\ufeff\\u00a0\")?function(e){return null==e?\"\":v.call(e)}:function(e){return null==e?\"\":(e+\"\").replace(T,\"\")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,\"string\"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if(\"number\"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return\"string\"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if(\"object\"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),\"complete\"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener(\"DOMContentLoaded\",H,!1),e.addEventListener(\"load\",H,!1);else{o.attachEvent(\"onreadystatechange\",H),e.attachEvent(\"onload\",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll(\"left\")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each(\"Boolean Number String Function Array Date RegExp Object Error\".split(\" \"),function(e,t){l[\"[object \"+t+\"]\"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:\"array\"===n||\"function\"!==n&&(0===t||\"number\"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e=\"string\"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);\"function\"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&\"string\"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[[\"resolve\",\"done\",b.Callbacks(\"once memory\"),\"resolved\"],[\"reject\",\"fail\",b.Callbacks(\"once memory\"),\"rejected\"],[\"notify\",\"progress\",b.Callbacks(\"memory\")]],n=\"pending\",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+\"With\"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+\"With\"](this===i?r:this,arguments),this},i[o[0]+\"With\"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement(\"div\");if(d.setAttribute(\"className\",\"t\"),d.innerHTML=\"  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>\",n=d.getElementsByTagName(\"*\"),r=d.getElementsByTagName(\"a\")[0],!n||!r||!n.length)return{};s=o.createElement(\"select\"),l=s.appendChild(o.createElement(\"option\")),a=d.getElementsByTagName(\"input\")[0],r.style.cssText=\"top:1px;float:left;opacity:.5\",t={getSetAttribute:\"t\"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName(\"tbody\").length,htmlSerialize:!!d.getElementsByTagName(\"link\").length,style:/top/.test(r.getAttribute(\"style\")),hrefNormalized:\"/a\"===r.getAttribute(\"href\"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement(\"form\").enctype,html5Clone:\"<:nav></:nav>\"!==o.createElement(\"nav\").cloneNode(!0).outerHTML,boxModel:\"CSS1Compat\"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement(\"input\"),a.setAttribute(\"value\",\"\"),t.input=\"\"===a.getAttribute(\"value\"),a.value=\"t\",a.setAttribute(\"type\",\"radio\"),t.radioValue=\"t\"===a.value,a.setAttribute(\"checked\",\"t\"),a.setAttribute(\"name\",\"t\"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent(\"onclick\",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c=\"on\"+f,\"t\"),t[f+\"Bubbles\"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip=\"content-box\",d.cloneNode(!0).style.backgroundClip=\"\",t.clearCloneStyle=\"content-box\"===d.style.backgroundClip,b(function(){var n,r,a,s=\"padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;\",u=o.getElementsByTagName(\"body\")[0];u&&(n=o.createElement(\"div\"),n.style.cssText=\"border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px\",u.appendChild(n).appendChild(d),d.innerHTML=\"<table><tr><td></td><td>t</td></tr></table>\",a=d.getElementsByTagName(\"td\"),a[0].style.cssText=\"padding:0;margin:0;border:0;display:none\",p=0===a[0].offsetHeight,a[0].style.display=\"\",a[1].style.display=\"none\",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML=\"\",d.style.cssText=\"box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;\",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition=\"1%\"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable=\"4px\"===(e.getComputedStyle(d,null)||{width:\"4px\"}).width,r=d.appendChild(o.createElement(\"div\")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width=\"0\",d.style.width=\"1px\",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML=\"\",d.style.cssText=s+\"width:1px;padding:1px;display:inline;zoom:1\",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display=\"block\",d.innerHTML=\"<div></div>\",d.firstChild.style.width=\"5px\",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u=\"string\"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),(\"object\"==typeof n||\"function\"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(\" \"));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:\"jQuery\"+(p+Math.random()).replace(/\\D/g,\"\"),noData:{embed:!0,object:\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute(\"classid\")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,\"parsedAttrs\"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf(\"data-\")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,\"parsedAttrs\",!0)}return s}return\"object\"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i=\"data-\"+n.replace(B,\"-$1\").toLowerCase();if(r=e.getAttribute(i),\"string\"==typeof r){try{r=\"true\"===r?!0:\"false\"===r?!1:\"null\"===r?null:+r+\"\"===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if((\"data\"!==t||!b.isEmptyObject(e[t]))&&\"toJSON\"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||\"fx\")+\"queue\",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||\"fx\";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};\"inprogress\"===i&&(i=n.shift(),r--),o.cur=i,i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks(\"once memory\").add(function(){b._removeData(e,t+\"queue\"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return\"string\"!=typeof e&&(n=e,e=\"fx\",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),\"fx\"===e&&\"inprogress\"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||\"fx\",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};\"string\"!=typeof e&&(n=e,e=t),e=e||\"fx\";while(s--)r=b._data(a[s],e+\"queueHooks\"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\\t\\r\\n]/g,U=/\\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\" \")){o=0;while(i=t[o++])0>r.indexOf(\" \"+i+\" \")&&(r+=i+\" \");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\"\")){o=0;while(i=t[o++])while(r.indexOf(\" \"+i+\" \")>=0)r=r.replace(\" \"+i+\" \",\" \");n.className=e?b.trim(r):\"\"}return this},toggleClass:function(e,t){var n=typeof e,r=\"boolean\"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(\"string\"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?\"addClass\":\"removeClass\"](o)}else(n===i||\"boolean\"===n)&&(this.className&&b._data(this,\"__className__\",this.className),this.className=this.className||e===!1?\"\":b._data(this,\"__className__\")||\"\")})},hasClass:function(e){var t=\" \"+e+\" \",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(\" \"+this[n].className+\" \").replace(X,\" \").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o=\"\":\"number\"==typeof o?o+=\"\":b.isArray(o)&&(o=b.map(o,function(e){return null==e?\"\":e+\"\"})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&\"set\"in r&&r.set(this,o,\"value\")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&\"get\"in r&&(n=r.get(o,\"value\"))!==t?n:(n=o.value,\"string\"==typeof n?n.replace(U,\"\"):null==n?\"\":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o=\"select-one\"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute(\"disabled\"))||n.parentNode.disabled&&b.nodeName(n.parentNode,\"optgroup\"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find(\"option\").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&\"get\"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&\"set\"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+\"\"),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase(\"default-\"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,\"\"),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&\"radio\"===t&&b.nodeName(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},propFix:{tabindex:\"tabIndex\",readonly:\"readOnly\",\"for\":\"htmlFor\",\"class\":\"className\",maxlength:\"maxLength\",cellspacing:\"cellSpacing\",cellpadding:\"cellPadding\",rowspan:\"rowSpan\",colspan:\"colSpan\",usemap:\"useMap\",frameborder:\"frameBorder\",contenteditable:\"contentEditable\"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&\"set\"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&\"get\"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode(\"tabindex\");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i=\"boolean\"==typeof r&&e.getAttribute(n),o=\"boolean\"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase(\"default-\"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase(\"default-\"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,\"input\")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,\"input\")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&(\"id\"===n||\"name\"===n||\"coords\"===n?\"\"!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+=\"\",\"value\"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,\"\"===t?!1:t,n)}},b.each([\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return\"\"===r?(e.setAttribute(n,\"auto\"),r):t}})})),b.support.hrefNormalized||(b.each([\"href\",\"src\",\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each([\"href\",\"src\"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+\"\"}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype=\"encoding\"),b.support.checkOn||b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute(\"value\")?\"on\":e.value}}}),b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||\"\").match(w)||[\"\"],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||\"\").split(\".\").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(\".\")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent(\"on\"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||\"\").match(w)||[\"\"],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||\"\").split(\".\").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&(\"**\"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,\"events\"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,\"type\")?n.type:n,m=y.call(n,\"namespace\")?n.namespace.split(\".\"):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(\".\")>=0&&(m=g.split(\".\"),g=m.shift(),m.sort()),u=0>g.indexOf(\":\")&&\"on\"+g,n=n[b.expando]?n:new b.Event(g,\"object\"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join(\".\"),n.namespace_re=n.namespace?RegExp(\"(^|\\\\.)\"+m.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,\"events\")||{})[n.type]&&b._data(l,\"handle\"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||\"click\"===g&&b.nodeName(i,\"a\")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,\"events\")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||\"click\"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||\"click\"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+\" \",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:\"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),fixHooks:{},keyHooks:{props:\"char charCode key keyCode\".split(\" \"),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:\"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,\"input\")&&\"checkbox\"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:\"focusin\"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:\"focusout\"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r=\"on\"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;\nreturn(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,\"form\")?!1:(b.event.add(this,\"click._submit keypress._submit\",function(e){var n=e.target,r=b.nodeName(n,\"input\")||b.nodeName(n,\"button\")?n.form:t;r&&!b._data(r,\"submitBubbles\")&&(b.event.add(r,\"submit._submit\",function(e){e._submit_bubble=!0}),b._data(r,\"submitBubbles\",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate(\"submit\",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,\"form\")?!1:(b.event.remove(this,\"._submit\"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?((\"checkbox\"===this.type||\"radio\"===this.type)&&(b.event.add(this,\"propertychange._change\",function(e){\"checked\"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,\"click._change\",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate(\"change\",this,e,!0)})),!1):(b.event.add(this,\"beforeactivate._change\",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,\"changeBubbles\")&&(b.event.add(t,\"change._change\",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate(\"change\",this.parentNode,e,!0)}),b._data(t,\"changeBubbles\",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||\"radio\"!==n.type&&\"checkbox\"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,\"._change\"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if(\"object\"==typeof e){\"string\"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&(\"string\"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+\".\"+i.namespace:i.origType,i.selector,i.handler),this;if(\"object\"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||\"function\"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x=\"sizzle\"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",F=\"(?:\\\\\\\\.|[\\\\w-]|[^\\\\x00-\\\\xa0])+\",O=F.replace(\"w\",\"w#\"),B=\"([*^$|!~]?=)\",P=\"\\\\[\"+_+\"*(\"+F+\")\"+_+\"*(?:\"+B+_+\"*(?:(['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|(\"+O+\")|)|)\"+_+\"*\\\\]\",R=\":(\"+F+\")(?:\\\\(((['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+P.replace(3,8)+\")*)|.*)\\\\)|)\",W=RegExp(\"^\"+_+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+_+\"+$\",\"g\"),$=RegExp(\"^\"+_+\"*,\"+_+\"*\"),I=RegExp(\"^\"+_+\"*([\\\\x20\\\\t\\\\r\\\\n\\\\f>+~])\"+_+\"*\"),z=RegExp(R),X=RegExp(\"^\"+O+\"$\"),U={ID:RegExp(\"^#(\"+F+\")\"),CLASS:RegExp(\"^\\\\.(\"+F+\")\"),NAME:RegExp(\"^\\\\[name=['\\\"]?(\"+F+\")['\\\"]?\\\\]\"),TAG:RegExp(\"^(\"+F.replace(\"w\",\"w*\")+\")\"),ATTR:RegExp(\"^\"+P),PSEUDO:RegExp(\"^\"+R),CHILD:RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+_+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+_+\"*(?:([+-]|)\"+_+\"*(\\\\d+)|))\"+_+\"*\\\\)|)\",\"i\"),needsContext:RegExp(\"^\"+_+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+_+\"*((?:-\\\\d)?\\\\d*)\"+_+\"*\\\\)|)(?=[^-]|$)\",\"i\")},V=/[\\x20\\t\\r\\n\\f]*[+~]/,Y=/^[^{]+\\{\\s*\\[native code/,J=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\\d$/i,K=/'|\\\\/g,Z=/\\=[\\x20\\t\\r\\n\\f]*([^'\"\\]]*)[\\x20\\t\\r\\n\\f]*\\]/g,et=/\\\\([\\da-fA-F]{1,6}[\\x20\\t\\r\\n\\f]?|.)/g,tt=function(e,t){var n=\"0x\"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+\"\")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=\" \")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement(\"div\");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||\"string\"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&\"object\"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute(\"id\"))?g=f.replace(K,\"\\\\$&\"):t.setAttribute(\"id\",g),g=\"[id='\"+g+\"'] \",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(\",\")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute(\"id\")}}}return wt(e.replace(W,\"$1\"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?\"HTML\"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),T.attributes=at(function(e){e.innerHTML=\"<select></select>\";var t=typeof e.lastChild.getAttribute(\"multiple\");return\"boolean\"!==t&&\"string\"!==t}),T.getByClassName=at(function(e){return e.innerHTML=\"<div class='hidden e'></div><div class='hidden'></div>\",e.getElementsByClassName&&e.getElementsByClassName(\"e\").length?(e.lastChild.className=\"e\",2===e.getElementsByClassName(\"e\").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML=\"<a name='\"+x+\"'></a><div name='\"+x+\"'></div>\",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML=\"<a href='#'></a>\",e.firstChild&&typeof e.firstChild.getAttribute!==A&&\"#\"===e.firstChild.getAttribute(\"href\")})?{}:{href:function(e){return e.getAttribute(\"href\",2)},type:function(e){return e.getAttribute(\"type\")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute(\"id\")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode(\"id\").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode(\"id\");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[\":focus\"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML=\"<select><option selected=''></option></select>\",e.querySelectorAll(\"[selected]\").length||h.push(\"\\\\[\"+_+\"*(?:checked|disabled|ismap|multiple|readonly|selected|value)\"),e.querySelectorAll(\":checked\").length||h.push(\":checked\")}),at(function(e){e.innerHTML=\"<input type='hidden' i=''/>\",e.querySelectorAll(\"[i^='']\").length&&h.push(\"[*^$]=\"+_+\"*(?:\\\"\\\"|'')\"),e.querySelectorAll(\":enabled\").length||h.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),h.push(\",.*:\")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,\"div\"),m.call(e,\"[s!='']:x\"),g.push(\"!=\",R)}),h=RegExp(h.join(\"|\")),g=RegExp(g.join(\"|\")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,\"='$1']\"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error(\"Syntax error, unrecognized expression: \"+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return\"input\"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return(\"input\"===n||\"button\"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||\"\").replace(et,tt),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return\"*\"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+\" \"];return t||(t=RegExp(\"(^|\"+_+\")\"+e+\"(\"+_+\"|$)\"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute(\"class\")||\"\")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?\"!=\"===t:t?(i+=\"\",\"=\"===t?i===n:\"!=\"===t?i!==n:\"^=\"===t?n&&0===i.indexOf(n):\"*=\"===t?n&&i.indexOf(n)>-1:\"$=\"===t?n&&i.slice(-n.length)===n:\"~=\"===t?(\" \"+i+\" \").indexOf(n)>-1:\"|=\"===t?i===n||i.slice(0,n.length+1)===n+\"-\":!1):!0}},CHILD:function(e,t,n,r,i){var o=\"nth\"!==e.slice(0,3),a=\"last\"!==e.slice(-4),s=\"of-type\"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?\"nextSibling\":\"previousSibling\",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g=\"only\"===e&&!h&&\"nextSibling\"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error(\"unsupported pseudo: \"+e);return r[x]?r(t):r.length>1?(n=[e,e,\"\",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,\"$1\"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||\"\")||st.error(\"unsupported lang: \"+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute(\"xml:lang\")||t.getAttribute(\"lang\"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+\"-\");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>\"@\"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+\" \"];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W,\" \")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r=\"\";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&\"parentNode\"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+\" \"+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||\"*\",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[\" \"],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&&gt(f),u>1&&dt(e.slice(0,u-1)).replace(W,\"$1\"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b=\"0\",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG(\"*\",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+\" \"];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&\"ID\"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[\":\"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\\[\\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if(\"string\"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+\" \":\"\")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&(\"string\"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||\"string\"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?\"string\"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n=\"string\"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,\"parentNode\")},parentsUntil:function(e,t,n){return b.dir(e,\"parentNode\",n)},next:function(e){return pt(e,\"nextSibling\")},prev:function(e){return pt(e,\"previousSibling\")},nextAll:function(e){return b.dir(e,\"nextSibling\")},prevAll:function(e){return b.dir(e,\"previousSibling\")},nextUntil:function(e,t,n){return b.dir(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return b.dir(e,\"previousSibling\",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,\"iframe\")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&\"string\"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=\":not(\"+e+\")\"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if(\"string\"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split(\"|\"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht=\"abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video\",gt=/ jQuery\\d+=\"(?:null|\\d+)\"/g,mt=RegExp(\"<(?:\"+ht+\")[\\\\s/>]\",\"i\"),yt=/^\\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi,bt=/<([\\w:]+)/,xt=/<tbody/i,wt=/<|&#?\\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\\s*(?:[^=]|=\\s*.checked.)/i,kt=/^$|\\/(?:java|ecma)script/i,Et=/^true\\/(.*)/,St=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g,At={option:[1,\"<select multiple='multiple'>\",\"</select>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],area:[1,\"<map>\",\"</map>\"],param:[1,\"<object>\",\"</object>\"],thead:[1,\"<table>\",\"</table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:b.support.htmlSerialize?[0,\"\",\"\"]:[1,\"X<div>\",\"</div>\"]},jt=dt(o),Dt=jt.appendChild(o.createElement(\"div\"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,\"body\")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,\"script\")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,\"select\")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,\"\"):t;if(!(\"string\"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||[\"\",\"\"])[1].toLowerCase()])){e=e.replace(vt,\"<$1></$2>\");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||\"string\"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||\"string\"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,\"tr\"),s=b.map(Ot(l,\"script\"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,\"script\"))),r.call(n&&b.nodeName(this[c],\"table\")?Lt(this[c],\"tbody\"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||\"\")&&!b._data(o,\"globalEval\")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:\"GET\",dataType:\"script\",async:!1,global:!1,\"throws\":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||\"\").replace(St,\"\")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode(\"type\");return e.type=(t&&t.specified)+\"/\"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute(\"type\"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,\"globalEval\",!t||b._data(t[r],\"globalEval\"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}\"script\"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):\"object\"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):\"input\"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):\"option\"===n?t.defaultSelected=t.selected=e.defaultSelected:(\"input\"===n||\"textarea\"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||\"*\"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||\"*\"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test(\"<\"+e.nodeName+\">\")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,\"script\"),r.length>0&&Mt(r,!u&&Ot(e,\"script\")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if(\"object\"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement(\"div\")),u=(bt.exec(o)||[\"\",\"\"])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,\"<$1></$2>\")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o=\"table\"!==u||xt.test(o)?\"<table>\"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],\"tbody\")&&!l.childNodes.length&&o.removeChild(l)\n}b.merge(d,s.childNodes),s.textContent=\"\";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,\"input\"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),\"script\"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||\"\")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\\([^)]*\\)/i,It=/opacity\\s*=\\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp(\"^(\"+x+\")(.*)$\",\"i\"),Yt=RegExp(\"^(\"+x+\")(?!px)[a-z%]+$\",\"i\"),Jt=RegExp(\"^([+-])=(\"+x+\")\",\"i\"),Gt={BODY:\"block\"},Qt={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Kt={letterSpacing:0,fontWeight:400},Zt=[\"Top\",\"Right\",\"Bottom\",\"Left\"],en=[\"Webkit\",\"O\",\"Moz\",\"ms\"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,\"none\"===b.css(e,\"display\")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,\"olddisplay\"),n=r.style.display,t?(o[a]||\"none\"!==n||(r.style.display=\"\"),\"\"===r.style.display&&nn(r)&&(o[a]=b._data(r,\"olddisplay\",un(r.nodeName)))):o[a]||(i=nn(r),(n&&\"none\"!==n||!i)&&b._data(r,\"olddisplay\",i?n:b.css(r,\"display\"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&\"none\"!==r.style.display&&\"\"!==r.style.display||(r.style.display=t?o[a]||\"\":\"none\"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t=\"boolean\"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{\"float\":b.support.cssFloat?\"cssFloat\":\"styleFloat\"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&\"get\"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,\"string\"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a=\"number\"),!(null==r||\"number\"===a&&isNaN(r)||(\"number\"!==a||b.cssNumber[u]||(r+=\"px\"),b.support.clearCloneStyle||\"\"!==r||0!==n.indexOf(\"background\")||(l[n]=\"inherit\"),s&&\"set\"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&\"get\"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),\"normal\"===a&&n in Kt&&(a=Kt[n]),\"\"===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(\"\"!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left=\"fontSize\"===n?\"1em\":u,u=l.pixelLeft+\"px\",l.left=i,a&&(o.left=a)),\"\"===u?\"auto\":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||\"px\"):t}function an(e,t,n,r,i){var o=n===(r?\"border\":\"content\")?4:\"width\"===t?1:0,a=0;for(;4>o;o+=2)\"margin\"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?(\"content\"===n&&(a-=b.css(e,\"padding\"+Zt[o],!0,i)),\"margin\"!==n&&(a-=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i))):(a+=b.css(e,\"padding\"+Zt[o],!0,i),\"padding\"!==n&&(a+=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i)));return a}function sn(e,t,n){var r=!0,i=\"width\"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?\"border\":\"content\"),r,o)+\"px\"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),\"none\"!==n&&n||(Pt=(Pt||b(\"<iframe frameborder='0' width='0' height='0'/>\").css(\"cssText\",\"display:block !important\")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(\"<!doctype html><html><body>\"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],\"display\");return n.remove(),r}b.each([\"height\",\"width\"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,\"display\"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||\"\")?.01*parseFloat(RegExp.$1)+\"\":t?\"1\":\"\"},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?\"alpha(opacity=\"+100*t+\")\":\"\",o=r&&r.filter||n.filter||\"\";n.zoom=1,(t>=1||\"\"===t)&&\"\"===b.trim(o.replace($t,\"\"))&&n.removeAttribute&&(n.removeAttribute(\"filter\"),\"\"===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+\" \"+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:\"inline-block\"},Wt,[e,\"marginRight\"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each([\"top\",\"left\"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+\"px\":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&\"none\"===(e.style&&e.style.display||b.css(e,\"display\"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:\"\",padding:\"\",border:\"Width\"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o=\"string\"==typeof n?n.split(\" \"):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\\[\\]$/,fn=/\\r?\\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,\"elements\");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(\":disabled\")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,\"\\r\\n\")}}):{name:t.name,value:n.replace(fn,\"\\r\\n\")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?\"\":t,i[i.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join(\"&\").replace(cn,\"+\")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+\"[\"+(\"object\"==typeof i?t:\"\")+\"]\",i,n,r)});else if(n||\"object\"!==b.type(t))r(e,t);else for(i in t)gn(e+\"[\"+i+\"]\",t[i],n,r)}b.each(\"blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu\".split(\" \"),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\\/\\//,En=/^([\\w.+-]+:)(?:\\/\\/([^\\/?#:]*)(?::(\\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn=\"*/\".concat(\"*\");try{yn=a.href}catch(Ln){yn=o.createElement(\"a\"),yn.href=\"\",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return\"string\"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o[\"*\"]&&s(\"*\")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if(\"string\"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(\" \");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&\"object\"==typeof n&&(a=\"POST\"),s.length>0&&b.ajax({url:e,type:a,dataType:\"html\",data:n}).done(function(e){o=arguments,s.html(i?b(\"<div>\").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each([\"get\",\"post\"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:\"GET\",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":Dn,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:\"responseXML\",text:\"responseText\"},converters:{\"* text\":e.String,\"text html\":!0,\"text json\":b.parseJSON,\"text xml\":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){\"object\"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks(\"once memory\"),m=p.statusCode||{},y={},v={},x=0,T=\"canceled\",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+\"\").replace(xn,\"\").replace(kn,mn[1]+\"//\"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||\"*\").toLowerCase().match(w)||[\"\"],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||(\"http:\"===r[1]?80:443))==(mn[3]||(\"http:\"===mn[1]?80:443)))),p.data&&p.processData&&\"string\"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger(\"ajaxStart\"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?\"&\":\"?\")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,\"$1_=\"+vn++):o+(bn.test(o)?\"&\":\"?\")+\"_=\"+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader(\"If-Modified-Since\",b.lastModified[o]),b.etag[o]&&N.setRequestHeader(\"If-None-Match\",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader(\"Content-Type\",p.contentType),N.setRequestHeader(\"Accept\",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+(\"*\"!==p.dataTypes[0]?\", \"+Dn+\"; q=0.01\":\"\"):p.accepts[\"*\"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T=\"abort\";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger(\"ajaxSend\",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort(\"timeout\")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,\"No Transport\");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||\"\",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader(\"Last-Modified\"),T&&(b.lastModified[o]=T),T=N.getResponseHeader(\"etag\"),T&&(b.etag[o]=T)),204===e?(c=!0,C=\"nocontent\"):304===e?(c=!0,C=\"notmodified\"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C=\"error\",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+\"\",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?\"ajaxSuccess\":\"ajaxError\",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger(\"ajaxComplete\",[N,p]),--b.active||b.event.trigger(\"ajaxStop\")))}return N},getScript:function(e,n){return b.get(e,t,n,\"script\")},getJSON:function(e,t,n){return b.get(e,t,n,\"json\")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while(\"*\"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader(\"Content-Type\"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+\" \"+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if(\"*\"!==r){if(\"*\"!==l&&l!==r){if(i=a[l+\" \"+r]||a[\"* \"+r],!i)for(n in a)if(o=n.split(\" \"),o[1]===r&&(i=a[l+\" \"+o[0]]||a[\"* \"+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e[\"throws\"])t=i(t);else try{t=i(t)}catch(c){return{state:\"parsererror\",error:i?c:\"No conversion from \"+l+\" to \"+r}}}l=r}return{state:\"success\",data:t}}b.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/(?:java|ecma)script/},converters:{\"text script\":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter(\"script\",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\",e.global=!1)}),b.ajaxTransport(\"script\",function(e){if(e.crossDomain){var n,r=o.head||b(\"head\")[0]||o.documentElement;return{send:function(t,i){n=o.createElement(\"script\"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,\"success\"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\\?(?=&|$)|\\?\\?/;b.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=On.pop()||b.expando+\"_\"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter(\"json jsonp\",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?\"url\":\"string\"==typeof n.data&&!(n.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Bn.test(n.data)&&\"data\");return u||\"jsonp\"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,\"$1\"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?\"&\":\"?\")+n.jsonp+\"=\"+o),n.converters[\"script json\"]=function(){return s||b.error(o+\" was not called\"),s[0]},n.dataTypes[0]=\"json\",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),\"script\"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject(\"Microsoft.XMLHTTP\")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&\"withCredentials\"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i[\"X-Requested-With\"]||(i[\"X-Requested-With\"]=\"XMLHttpRequest\");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),\"string\"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=\"\"}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp(\"^(?:([+-])=|)(\"+x+\")([a-z%]*)$\",\"i\"),Jn=/queueHooks$/,Gn=[nr],Qn={\"*\":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?\"\":\"px\"),\"px\"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||\".5\",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn[\"*\"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&\"expand\"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=[\"*\"]):e=e.split(\" \");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,\"fx\"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,\"fx\").length||c.empty.fire()})})),1===e.nodeType&&(\"height\"in t||\"width\"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],\"inline\"===b.css(e,\"display\")&&\"none\"===b.css(e,\"float\")&&(b.support.inlineBlockNeedsLayout&&\"inline\"!==un(e.nodeName)?d.zoom=1:d.display=\"inline-block\")),n.overflow&&(d.overflow=\"hidden\",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||\"toggle\"===a,a===(m?\"hide\":\"show\"))continue;g.push(i)}if(o=g.length){s=b._data(e,\"fxshow\")||b._data(e,\"fxshow\",{}),\"hidden\"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,\"fxshow\");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start=\"width\"===r||\"height\"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||\"swing\",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?\"\":\"px\")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,\"\"),t&&\"auto\"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each([\"toggle\",\"show\",\"hide\"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||\"boolean\"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,\"finish\"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return\"string\"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||\"fx\",[]),this.each(function(){var t=!0,n=null!=e&&e+\"queueHooks\",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||\"fx\"),this.each(function(){var t,n=b._data(this),r=n[e+\"queue\"],i=n[e+\"queueHooks\"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r[\"margin\"+n]=r[\"padding\"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir(\"show\"),slideUp:ir(\"hide\"),slideToggle:ir(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&\"object\"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:\"number\"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,\"position\");\"static\"===r&&(e.style.position=\"relative\");var i=b(e),o=i.offset(),a=b.css(e,\"top\"),s=b.css(e,\"left\"),u=(\"absolute\"===r||\"fixed\"===r)&&b.inArray(\"auto\",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),\"using\"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return\"fixed\"===b.css(r,\"position\")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],\"html\")||(n=e.offset()),n.top+=b.css(e[0],\"borderTopWidth\",!0),n.left+=b.css(e[0],\"borderLeftWidth\",!0)),{top:t.top-n.top-b.css(r,\"marginTop\",!0),left:t.left-n.left-b.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,\"html\")&&\"static\"===b.css(e,\"position\"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:\"height\",Width:\"width\"},function(e,n){b.each({padding:\"inner\"+e,content:n,\"\":\"outer\"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||\"boolean\"!=typeof i),s=r||(i===!0||o===!0?\"margin\":\"border\");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement[\"client\"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body[\"scroll\"+e],o[\"scroll\"+e],n.body[\"offset\"+e],o[\"offset\"+e],o[\"client\"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,\"function\"==typeof define&&define.amd&&define.amd.jQuery&&define(\"jquery\",[],function(){return b})})(window);"
  },
  {
    "path": "_draft/music/md5.js",
    "content": "(function( window ) {\n    function md5cycle(x, k) {\n        var a = x[0],\n            b = x[1],\n            c = x[2],\n            d = x[3];\n\n        a = ff(a, b, c, d, k[0], 7, -680876936);\n        d = ff(d, a, b, c, k[1], 12, -389564586);\n        c = ff(c, d, a, b, k[2], 17, 606105819);\n        b = ff(b, c, d, a, k[3], 22, -1044525330);\n        a = ff(a, b, c, d, k[4], 7, -176418897);\n        d = ff(d, a, b, c, k[5], 12, 1200080426);\n        c = ff(c, d, a, b, k[6], 17, -1473231341);\n        b = ff(b, c, d, a, k[7], 22, -45705983);\n        a = ff(a, b, c, d, k[8], 7, 1770035416);\n        d = ff(d, a, b, c, k[9], 12, -1958414417);\n        c = ff(c, d, a, b, k[10], 17, -42063);\n        b = ff(b, c, d, a, k[11], 22, -1990404162);\n        a = ff(a, b, c, d, k[12], 7, 1804603682);\n        d = ff(d, a, b, c, k[13], 12, -40341101);\n        c = ff(c, d, a, b, k[14], 17, -1502002290);\n        b = ff(b, c, d, a, k[15], 22, 1236535329);\n\n        a = gg(a, b, c, d, k[1], 5, -165796510);\n        d = gg(d, a, b, c, k[6], 9, -1069501632);\n        c = gg(c, d, a, b, k[11], 14, 643717713);\n        b = gg(b, c, d, a, k[0], 20, -373897302);\n        a = gg(a, b, c, d, k[5], 5, -701558691);\n        d = gg(d, a, b, c, k[10], 9, 38016083);\n        c = gg(c, d, a, b, k[15], 14, -660478335);\n        b = gg(b, c, d, a, k[4], 20, -405537848);\n        a = gg(a, b, c, d, k[9], 5, 568446438);\n        d = gg(d, a, b, c, k[14], 9, -1019803690);\n        c = gg(c, d, a, b, k[3], 14, -187363961);\n        b = gg(b, c, d, a, k[8], 20, 1163531501);\n        a = gg(a, b, c, d, k[13], 5, -1444681467);\n        d = gg(d, a, b, c, k[2], 9, -51403784);\n        c = gg(c, d, a, b, k[7], 14, 1735328473);\n        b = gg(b, c, d, a, k[12], 20, -1926607734);\n\n        a = hh(a, b, c, d, k[5], 4, -378558);\n        d = hh(d, a, b, c, k[8], 11, -2022574463);\n        c = hh(c, d, a, b, k[11], 16, 1839030562);\n        b = hh(b, c, d, a, k[14], 23, -35309556);\n        a = hh(a, b, c, d, k[1], 4, -1530992060);\n        d = hh(d, a, b, c, k[4], 11, 1272893353);\n        c = hh(c, d, a, b, k[7], 16, -155497632);\n        b = hh(b, c, d, a, k[10], 23, -1094730640);\n        a = hh(a, b, c, d, k[13], 4, 681279174);\n        d = hh(d, a, b, c, k[0], 11, -358537222);\n        c = hh(c, d, a, b, k[3], 16, -722521979);\n        b = hh(b, c, d, a, k[6], 23, 76029189);\n        a = hh(a, b, c, d, k[9], 4, -640364487);\n        d = hh(d, a, b, c, k[12], 11, -421815835);\n        c = hh(c, d, a, b, k[15], 16, 530742520);\n        b = hh(b, c, d, a, k[2], 23, -995338651);\n\n        a = ii(a, b, c, d, k[0], 6, -198630844);\n        d = ii(d, a, b, c, k[7], 10, 1126891415);\n        c = ii(c, d, a, b, k[14], 15, -1416354905);\n        b = ii(b, c, d, a, k[5], 21, -57434055);\n        a = ii(a, b, c, d, k[12], 6, 1700485571);\n        d = ii(d, a, b, c, k[3], 10, -1894986606);\n        c = ii(c, d, a, b, k[10], 15, -1051523);\n        b = ii(b, c, d, a, k[1], 21, -2054922799);\n        a = ii(a, b, c, d, k[8], 6, 1873313359);\n        d = ii(d, a, b, c, k[15], 10, -30611744);\n        c = ii(c, d, a, b, k[6], 15, -1560198380);\n        b = ii(b, c, d, a, k[13], 21, 1309151649);\n        a = ii(a, b, c, d, k[4], 6, -145523070);\n        d = ii(d, a, b, c, k[11], 10, -1120210379);\n        c = ii(c, d, a, b, k[2], 15, 718787259);\n        b = ii(b, c, d, a, k[9], 21, -343485551);\n\n        x[0] = add32(a, x[0]);\n        x[1] = add32(b, x[1]);\n        x[2] = add32(c, x[2]);\n        x[3] = add32(d, x[3]);\n\n    }\n\n    function cmn(q, a, b, x, s, t) {\n        a = add32(add32(a, q), add32(x, t));\n        return add32((a << s) | (a >>> (32 - s)), b);\n    }\n\n    function ff(a, b, c, d, x, s, t) {\n        return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n    }\n\n    function gg(a, b, c, d, x, s, t) {\n        return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n    }\n\n    function hh(a, b, c, d, x, s, t) {\n        return cmn(b ^ c ^ d, a, b, x, s, t);\n    }\n\n    function ii(a, b, c, d, x, s, t) {\n        return cmn(c ^ (b | (~d)), a, b, x, s, t);\n    }\n\n    function md51(s) {\n        txt = '';\n        var n = s.length,\n            state = [1732584193, -271733879, -1732584194, 271733878],\n            i;\n        for (i = 64; i <= s.length; i += 64) {\n            md5cycle(state, md5blk(s.substring(i - 64, i)));\n        }\n        s = s.substring(i - 64);\n        var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n        for (i = 0; i < s.length; i++)\n            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n        tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n        if (i > 55) {\n            md5cycle(state, tail);\n            for (i = 0; i < 16; i++) tail[i] = 0;\n        }\n        tail[14] = n * 8;\n        md5cycle(state, tail);\n        return state;\n    }\n\n    /* there needs to be support for Unicode here,\n     * unless we pretend that we can redefine the MD-5\n     * algorithm for multi-byte characters (perhaps\n     * by adding every four 16-bit characters and\n     * shortening the sum to 32 bits). Otherwise\n     * I suggest performing MD-5 as if every character\n     * was two bytes--e.g., 0040 0025 = @%--but then\n     * how will an ordinary MD-5 sum be matched?\n     * There is no way to standardize text to something\n     * like UTF-8 before transformation; speed cost is\n     * utterly prohibitive. The JavaScript standard\n     * itself needs to look at this: it should start\n     * providing access to strings as preformed UTF-8\n     * 8-bit unsigned value arrays.\n     */\n\n    function md5blk(s) { /* I figured global was faster.   */\n        var md5blks = [],\n            i; /* Andy King said do it this way. */\n        for (i = 0; i < 64; i += 4) {\n            md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n        }\n        return md5blks;\n    }\n\n    var hex_chr = '0123456789abcdef'.split('');\n\n    function rhex(n) {\n        var s = '',\n            j = 0;\n        for (; j < 4; j++)\n            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n        return s;\n    }\n\n    function hex(x) {\n        for (var i = 0; i < x.length; i++)\n            x[i] = rhex(x[i]);\n        return x.join('');\n    }\n\n    function md5(s) {\n        return hex(md51(s));\n    }\n\n    /* this function is much faster,\nso if possible we use it. Some IEs\nare the only ones I know of that\nneed the idiotic second function,\ngenerated by an if clause.  */\n\n    var add32 = function(a, b) {\n        return (a + b) & 0xFFFFFFFF;\n    }\n\n    if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {\n        add32 = function(x, y) {\n            var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n            return (msw << 16) | (lsw & 0xFFFF);\n        }\n    }\n    window.md5 = md5;\n})( this );"
  },
  {
    "path": "_draft/music/server/fileupload.php",
    "content": "<?php\n/**\n * upload.php\n *\n * Copyright 2013, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\n#!! IMPORTANT:\n#!! this file is just an example, it doesn't incorporate any security checks and\n#!! is not recommended to be used in production environment as it is. Be sure to\n#!! revise it and customize to your needs.\n\n\n// Make sure file is not cached (as it happens for example on iOS devices)\nheader(\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\");\nheader(\"Last-Modified: \" . gmdate(\"D, d M Y H:i:s\") . \" GMT\");\nheader(\"Cache-Control: no-store, no-cache, must-revalidate\");\nheader(\"Cache-Control: post-check=0, pre-check=0\", false);\nheader(\"Pragma: no-cache\");\n\n\n// Support CORS\n// header(\"Access-Control-Allow-Origin: *\");\n// other CORS headers if any...\nif ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {\n    exit; // finish preflight CORS requests here\n}\n\n\nif ( !empty($_REQUEST[ 'debug' ]) ) {\n    $random = rand(0, intval($_REQUEST[ 'debug' ]) );\n    if ( $random === 0 ) {\n        header(\"HTTP/1.0 500 Internal Server Error\");\n        exit;\n    }\n}\n\n\n// 5 minutes execution time\n@set_time_limit(5 * 60);\n\n// Uncomment this one to fake upload time\nusleep(5000);\n\n// Settings\n// $targetDir = ini_get(\"upload_tmp_dir\") . DIRECTORY_SEPARATOR . \"plupload\";\n$targetDir = 'upload_tmp';\n$uploadDir = 'upload';\n\n$cleanupTargetDir = true; // Remove old files\n$maxFileAge = 5 * 3600; // Temp file age in seconds\n\n\n// Create target dir\nif (!file_exists($targetDir)) {\n    @mkdir($targetDir);\n}\n\n// Create target dir\nif (!file_exists($uploadDir)) {\n    @mkdir($uploadDir);\n}\n\n// Get a file name\nif (isset($_REQUEST[\"name\"])) {\n    $fileName = $_REQUEST[\"name\"];\n} elseif (!empty($_FILES)) {\n    $fileName = $_FILES[\"file\"][\"name\"];\n} else {\n    $fileName = uniqid(\"file_\");\n}\n\n$md5File = @file('md5list.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);\n$md5File = $md5File ? $md5File : array();\n\nif (isset($_REQUEST[\"md5\"]) && array_search($_REQUEST[\"md5\"], $md5File ) !== FALSE ) {\n    die('{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\", \"exist\": 1}');\n}\n\n$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;\n$uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;\n\n// Chunking might be enabled\n$chunk = isset($_REQUEST[\"chunk\"]) ? intval($_REQUEST[\"chunk\"]) : 0;\n$chunks = isset($_REQUEST[\"chunks\"]) ? intval($_REQUEST[\"chunks\"]) : 1;\n\n\n// Remove old temp files\nif ($cleanupTargetDir) {\n    if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 100, \"message\": \"Failed to open temp directory.\"}, \"id\" : \"id\"}');\n    }\n\n    while (($file = readdir($dir)) !== false) {\n        $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;\n\n        // If temp file is current file proceed to the next\n        if ($tmpfilePath == \"{$filePath}_{$chunk}.part\" || $tmpfilePath == \"{$filePath}_{$chunk}.parttmp\") {\n            continue;\n        }\n\n        // Remove temp file if it is older than the max age and is not the current file\n        if (preg_match('/\\.(part|parttmp)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {\n            @unlink($tmpfilePath);\n        }\n    }\n    closedir($dir);\n}\n\n\n// Open temp file\nif (!$out = @fopen(\"{$filePath}_{$chunk}.parttmp\", \"wb\")) {\n    die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 102, \"message\": \"Failed to open output stream.\"}, \"id\" : \"id\"}');\n}\n\nif (!empty($_FILES)) {\n    if ($_FILES[\"file\"][\"error\"] || !is_uploaded_file($_FILES[\"file\"][\"tmp_name\"])) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 103, \"message\": \"Failed to move uploaded file.\"}, \"id\" : \"id\"}');\n    }\n\n    // Read binary input stream and append it to temp file\n    if (!$in = @fopen($_FILES[\"file\"][\"tmp_name\"], \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n} else {\n    if (!$in = @fopen(\"php://input\", \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n}\n\nwhile ($buff = fread($in, 4096)) {\n    fwrite($out, $buff);\n}\n\n@fclose($out);\n@fclose($in);\n\nrename(\"{$filePath}_{$chunk}.parttmp\", \"{$filePath}_{$chunk}.part\");\n\n$index = 0;\n$done = true;\nfor( $index = 0; $index < $chunks; $index++ ) {\n    if ( !file_exists(\"{$filePath}_{$index}.part\") ) {\n        $done = false;\n        break;\n    }\n}\nif ( $done ) {\n    if (!$out = @fopen($uploadPath, \"wb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 102, \"message\": \"Failed to open output stream.\"}, \"id\" : \"id\"}');\n    }\n\n    if ( flock($out, LOCK_EX) ) {\n        for( $index = 0; $index < $chunks; $index++ ) {\n            if (!$in = @fopen(\"{$filePath}_{$index}.part\", \"rb\")) {\n                break;\n            }\n\n            while ($buff = fread($in, 4096)) {\n                fwrite($out, $buff);\n            }\n\n            @fclose($in);\n            @unlink(\"{$filePath}_{$index}.part\");\n        }\n        \n        flock($out, LOCK_UN);\n    }\n    @fclose($out);\n\n\n    array_push($md5File, md5(file_get_contents($uploadPath)));\n    $md5File = array_unique($md5File);\n    file_put_contents('md5list.txt', join($md5File, \"\\n\"));\n}\n\n// Return Success JSON-RPC response\ndie('{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\"}');"
  },
  {
    "path": "_draft/music/style.css",
    "content": "/* ----------------Reset Css--------------------- */\nhtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,\nfieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary,\ntime, mark, audio, video, input  {\n    margin: 0;\n    padding: 0;\n    border: none;\n    outline: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: baseline;\n}\n\nhtml, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 {\n    -webkit-text-size-adjust: none;\n}\n\narticle, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {\n    display: block;\n}\n\nbody {\n    font-family: arial, sans-serif;\n}\n\nol, ul {\n    list-style: none;\n}\n\nblockquote, q {\n    quotes: none;\n}\n\nblockquote:before, blockquote:after, q:before, q:after {\n    content: '';\n    content: none;\n}\n\nins {\n    text-decoration: none;\n}\n\ndel {\n    text-decoration: line-through;\n}\n\ntable {\n    border-collapse: collapse;\n    border-spacing: 0;\n}\n\n\n.webuploader-pick {\n    position: relative;\n    display: inline-block;\n    cursor: pointer;\n    background: #00b7ee;\n    padding: 10px 15px;\n    color: #fff;\n    text-align: center;\n    border-radius: 3px;\n    overflow: hidden;\n}\n.webuploader-pick-hover {\n    background: #00a2d4;\n}\n\n.webuploader-dnd {\n    width: 400px;\n    height: 400px;\n    border: 3px solid #ddd;\n}\n\n.webuploader-dnd-over {\n    border-style: dashed;\n}\n\n\n\n/* ------------ */\n#wrapper {\n    width: 980px;\n    margin: 0 auto;\n\n    margin: 1em;\n    width: auto;\n}\n\n#container {\n    border: 1px solid #dadada;\n    color: #838383;\n    font-size: 12px;\n    margin-top: 10px;\n    background-color: #FFF;\n}\n\n#uploader .queueList {\n    margin: 20px;\n}\n\n.element-invisible {\n    position: absolute !important;\n    clip: rect(1px,1px,1px,1px);\n}\n\n#uploader .placeholder {\n    border: 3px dashed #e6e6e6;\n    min-height: 238px;\n    padding-top: 158px;\n    text-align: center;\n    background: url(./image.png) center 93px no-repeat;\n    color: #cccccc;\n    font-size: 18px;\n    position: relative;\n}\n\n#uploader .placeholder .webuploader-pick {\n    font-size: 18px;\n    background: #00b7ee;\n    border-radius: 3px;\n    line-height: 44px;\n    padding: 0 30px;\n    color: #fff;\n    display: inline-block;\n    margin: 20px auto;\n    cursor: pointer;\n    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);\n}\n\n#uploader .placeholder .webuploader-pick-hover {\n    background: #00a2d4;\n}\n\n#uploader .placeholder .flashTip {\n    color: #666666;\n    font-size: 12px;\n    position: absolute;\n    width: 100%;\n    text-align: center;\n    bottom: 20px;\n}\n#uploader .placeholder .flashTip a {\n    color: #0785d1;\n    text-decoration: none;\n}\n#uploader .placeholder .flashTip a:hover {\n    text-decoration: underline;\n}\n\n#uploader .placeholder.webuploader-dnd-over {\n    border-color: #999999;\n}\n\n#uploader .filelist {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n}\n\n#uploader .filelist:after {\n    content: '';\n    display: block;\n    width: 0;\n    height: 0;\n    overflow: hidden;\n    clear: both;\n}\n\n#uploader .filelist li {\n    width: 110px;\n    height: 110px;\n    background: url(./bg.png) no-repeat;\n    text-align: center;\n    margin: 0 8px 20px 0;\n    position: relative;\n    display: inline;\n    float: left;\n    overflow: hidden;\n    font-size: 12px;\n}\n\n#uploader .filelist li p.log {\n    position: relative;\n    top: -45px;\n}\n\n#uploader .filelist li p.title {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow : ellipsis;\n    top: 5px;\n    text-indent: 5px;\n    text-align: left;\n}\n\n#uploader .filelist li p.progress {\n    position: absolute;\n    width: 100%;\n    bottom: 0;\n    left: 0;\n    height: 8px;\n    overflow: hidden;\n    z-index: 50;\n}\n#uploader .filelist li p.progress span {\n    display: none;\n    overflow: hidden;\n    width: 0;\n    height: 100%;\n    background: #1483d8 url(./progress.png) repeat-x;\n\n    -webit-transition: width 200ms linear;\n    -moz-transition: width 200ms linear;\n    -o-transition: width 200ms linear;\n    -ms-transition: width 200ms linear;\n    transition: width 200ms linear;\n\n    -webkit-animation: progressmove 2s linear infinite;\n    -moz-animation: progressmove 2s linear infinite;\n    -o-animation: progressmove 2s linear infinite;\n    -ms-animation: progressmove 2s linear infinite;\n    animation: progressmove 2s linear infinite;\n\n    -webkit-transform: translateZ(0);\n}\n\n@-webkit-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@-moz-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n\n#uploader .filelist li p.imgWrap {\n    position: relative;\n    z-index: 2;\n    line-height: 110px;\n    vertical-align: middle;\n    overflow: hidden;\n    width: 110px;\n    height: 110px;\n\n    -webkit-transform-origin: 50% 50%;\n    -moz-transform-origin: 50% 50%;\n    -o-transform-origin: 50% 50%;\n    -ms-transform-origin: 50% 50%;\n    transform-origin: 50% 50%;\n\n    -webit-transition: 200ms ease-out;\n    -moz-transition: 200ms ease-out;\n    -o-transition: 200ms ease-out;\n    -ms-transition: 200ms ease-out;\n    transition: 200ms ease-out;\n}\n\n#uploader .filelist li img {\n    width: 100%;\n}\n\n#uploader .filelist li p.error {\n    background: #f43838;\n    color: #fff;\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    height: 28px;\n    line-height: 28px;\n    width: 100%;\n    z-index: 100;\n}\n\n#uploader .filelist li .success {\n    display: block;\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    height: 40px;\n    width: 100%;\n    z-index: 200;\n    background: url(./success.png) no-repeat right bottom;\n}\n\n#uploader .filelist div.file-panel {\n    position: absolute;\n    height: 0;\n    filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\\0;\n    background: rgba( 0, 0, 0, 0.5 );\n    width: 100%;\n    top: 0;\n    left: 0;\n    overflow: hidden;\n    z-index: 300;\n}\n\n#uploader .filelist div.file-panel span {\n    width: 24px;\n    height: 24px;\n    display: inline;\n    float: right;\n    text-indent: -9999px;\n    overflow: hidden;\n    background: url(./icons.png) no-repeat;\n    margin: 5px 1px 1px;\n    cursor: pointer;\n}\n\n#uploader .filelist div.file-panel span.rotateLeft {\n    background-position: 0 -24px;\n}\n#uploader .filelist div.file-panel span.rotateLeft:hover {\n    background-position: 0 0;\n}\n\n#uploader .filelist div.file-panel span.rotateRight {\n    background-position: -24px -24px;\n}\n#uploader .filelist div.file-panel span.rotateRight:hover {\n    background-position: -24px 0;\n}\n\n#uploader .filelist div.file-panel span.cancel {\n    background-position: -48px -24px;\n}\n#uploader .filelist div.file-panel span.cancel:hover {\n    background-position: -48px 0;\n}\n\n#uploader .statusBar {\n    height: 63px;\n    border-top: 1px solid #dadada;\n    padding: 0 20px;\n    line-height: 63px;\n    vertical-align: middle;\n    position: relative;\n}\n\n#uploader .statusBar .progress {\n    border: 1px solid #1483d8;\n    width: 198px;\n    background: #fff;\n    height: 18px;\n    position: relative;\n    display: inline-block;\n    text-align: center;\n    line-height: 20px;\n    color: #6dbfff;\n    position: relative;\n    margin-right: 10px;\n}\n#uploader .statusBar .progress span.percentage {\n    width: 0;\n    height: 100%;\n    left: 0;\n    top: 0;\n    background: #1483d8;\n    position: absolute;\n}\n#uploader .statusBar .progress span.text {\n    position: relative;\n    z-index: 10;\n}\n\n#uploader .statusBar .info {\n    display: inline-block;\n    font-size: 14px;\n    color: #666666;\n}\n\n#uploader .statusBar .btns {\n    position: absolute;\n    top: 10px;\n    right: 20px;\n    line-height: 40px;\n}\n\n#filePicker2 {\n    display: inline-block;\n    float: left;\n}\n\n#uploader .statusBar .btns .webuploader-pick,\n#uploader .statusBar .btns .uploadBtn,\n#uploader .statusBar .btns .uploadBtn.state-uploading,\n#uploader .statusBar .btns .uploadBtn.state-paused {\n    background: #ffffff;\n    border: 1px solid #cfcfcf;\n    color: #565656;\n    padding: 0 18px;\n    display: inline-block;\n    border-radius: 3px;\n    margin-left: 10px;\n    cursor: pointer;\n    font-size: 14px;\n    float: left;\n}\n#uploader .statusBar .btns .webuploader-pick-hover,\n#uploader .statusBar .btns .uploadBtn:hover,\n#uploader .statusBar .btns .uploadBtn.state-uploading:hover,\n#uploader .statusBar .btns .uploadBtn.state-paused:hover {\n    background: #f0f0f0;\n}\n\n#uploader .statusBar .btns .uploadBtn {\n    background: #00b7ee;\n    color: #fff;\n    border-color: transparent;\n}\n#uploader .statusBar .btns .uploadBtn:hover {\n    background: #00a2d4;\n}\n\n#uploader .statusBar .btns .uploadBtn.disabled {\n    pointer-events: none;\n    opacity: 0.6;\n}"
  },
  {
    "path": "_draft/music/upload.js",
    "content": "(function($) {\n    // 当domReady的时候开始初始化\n    $(function() {\n        var $wrap = $('#uploader'),\n\n            // 图片容器\n            $queue = $('<ul class=\"filelist\"></ul>')\n                .appendTo($wrap.find('.queueList')),\n\n            // 状态栏，包括进度和控制按钮\n            $statusBar = $wrap.find('.statusBar'),\n\n            // 文件总体选择信息。\n            $info = $statusBar.find('.info'),\n\n            // 上传按钮\n            $upload = $wrap.find('.uploadBtn'),\n\n            // 没选择文件之前的内容。\n            $placeHolder = $wrap.find('.placeholder'),\n\n            $progress = $statusBar.find('.progress').hide(),\n\n            // 添加的文件数量\n            fileCount = 0,\n\n            // 添加的文件总大小\n            fileSize = 0,\n\n            // 可能有pedding, ready, uploading, confirm, done.\n            state = 'pedding',\n\n            // 所有文件的进度信息，key为file id\n            percentages = {},\n\n            // WebUploader实例\n            uploader;\n\n        // 实例化\n        uploader = WebUploader.create({\n            pick: '#filePicker',\n            dnd: '#dndArea',\n            chunked: true,\n            accept: '',\n            server: './server/fileupload.php',\n            // server: 'http://www.2betop.net/fileupload.php',\n        });\n\n        // 添加“添加文件”的按钮，\n        uploader.addButton({\n            id: '#filePicker2',\n            label: '继续添加'\n        });\n\n        // 当有文件添加进来时执行，负责view的创建\n        function addFile(file) {\n            var $li = $('<li id=\"' + file.id + '\">' +\n                '<p class=\"title\">' + file.name + '</p>' +\n                '<p class=\"imgWrap\"></p>' +\n                '<p class=\"progress\"><span></span></p>' +\n                '</li>'),\n\n                $btns = $('<div class=\"file-panel\">' +\n                    '<span class=\"cancel\">删除</span>' + '</div>').appendTo($li),\n                $prgress = $li.find('p.progress span'),\n                $wrap = $li.find('p.imgWrap'),\n                $info = $('<p class=\"error\"></p>'),\n\n                showError = function(code) {\n                    switch (code) {\n                        case 'interrupt':\n                            text = '上传暂停';\n                            break;\n\n                        default:\n                            text = '上传失败，请重试';\n                            break;\n                    }\n\n                    $info.text(text).appendTo($li);\n                };\n\n            if (file.getStatus() === 'invalid') {\n                showError(file.statusText);\n            } else {\n                $wrap.text('不能预览');\n                percentages[file.id] = [file.size, 0];\n                file.rotation = 0;\n            }\n\n            file.on('statuschange', function(cur, prev) {\n                if (prev === 'progress') {\n                    $prgress.hide().width(0);\n                } else if (prev === 'queued') {\n                    $li.off('mouseenter mouseleave');\n                    $btns.remove();\n                }\n\n                // 成功\n                if (cur === 'error' || cur === 'invalid') {\n                    showError(file.statusText);\n                    percentages[file.id][1] = 1;\n                } else if (cur === 'interrupt') {\n                    showError('interrupt');\n                } else if (cur === 'queued') {\n                    percentages[file.id][1] = 0;\n                } else if (cur === 'progress') {\n                    $info.remove();\n                    $prgress.css({\n                        display: 'block',\n                        width: '10%'\n                    });\n                } else if (cur === 'complete') {\n                    $li.append('<span class=\"success\"></span>');\n                }\n\n                $li.removeClass('state-' + prev).addClass('state-' + cur);\n            });\n\n            $li.on('mouseenter', function() {\n                $btns.stop().animate({\n                    height: 30\n                });\n            });\n\n            $li.on('mouseleave', function() {\n                $btns.stop().animate({\n                    height: 0\n                });\n            });\n\n            $btns.on('click', 'span', function() {\n                uploader.removeFile(file);\n            });\n\n            $li.appendTo($queue);\n        }\n\n        // 负责view的销毁\n        function removeFile(file) {\n            var $li = $('#' + file.id);\n\n            delete percentages[file.id];\n            updateTotalProgress();\n            $li.off().find('.file-panel').off().end().remove();\n        }\n\n        function updateTotalProgress() {\n            var loaded = 0,\n                total = 0,\n                spans = $progress.children(),\n                percent;\n\n            $.each(percentages, function(k, v) {\n                total += v[0];\n                loaded += v[0] * v[1];\n            });\n\n            percent = total ? loaded / total : 0;\n\n            spans.eq(0).text(Math.round(percent * 100) + '%');\n            spans.eq(1).css('width', Math.round(percent * 100) + '%');\n            updateStatus();\n        }\n\n        function updateStatus() {\n            var text = '',\n                stats;\n\n            if (state === 'ready') {\n                text = '选中' + fileCount + '张图片，共' +\n                    WebUploader.formatSize(fileSize) + '。';\n            } else if (state === 'confirm') {\n                stats = uploader.getStats();\n                if (stats.uploadFailNum) {\n                    text = '已成功上传' + stats.successNum + '张照片至XX相册，' +\n                        stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n                }\n\n            } else {\n                stats = uploader.getStats();\n                text = '共' + fileCount + '张（' +\n                    WebUploader.formatSize(fileSize) +\n                    '），已上传' + stats.successNum + '张';\n\n                if (stats.uploadFailNum) {\n                    text += '，失败' + stats.uploadFailNum + '张';\n                }\n            }\n\n            $info.html(text);\n        }\n\n        function setState(val) {\n            var file, stats;\n\n            if (val === state) {\n                return;\n            }\n\n            $upload.removeClass('state-' + state);\n            $upload.addClass('state-' + val);\n            state = val;\n\n            switch (state) {\n                case 'pedding':\n                    $placeHolder.removeClass('element-invisible');\n                    $queue.hide();\n                    $statusBar.addClass('element-invisible');\n                    uploader.refresh();\n                    break;\n\n                case 'ready':\n                    $placeHolder.addClass('element-invisible');\n                    $('#filePicker2').removeClass('element-invisible');\n                    $queue.show();\n                    $statusBar.removeClass('element-invisible');\n                    uploader.refresh();\n                    break;\n\n                case 'uploading':\n                    $('#filePicker2').addClass('element-invisible');\n                    $progress.show();\n                    $upload.text('暂停上传');\n                    break;\n\n                case 'paused':\n                    $progress.show();\n                    $upload.text('继续上传');\n                    break;\n\n                case 'confirm':\n                    $progress.hide();\n                    $upload.text('开始上传').addClass('disabled');\n\n                    stats = uploader.getStats();\n                    if (stats.successNum && !stats.uploadFailNum) {\n                        setState('finish');\n                        return;\n                    }\n                    break;\n                case 'finish':\n                    stats = uploader.getStats();\n                    if (stats.successNum) {\n                        alert('上传成功');\n                    } else {\n                        // 没有成功的图片，重设\n                        state = 'done';\n                        location.reload();\n                    }\n                    break;\n            }\n\n            updateStatus();\n        }\n\n        uploader.onUploadProgress = function(file, percentage) {\n            var $li = $('#' + file.id),\n                $percent = $li.find('.progress span');\n\n            $percent.css('width', percentage * 100 + '%');\n            percentages[file.id][1] = percentage;\n            updateTotalProgress();\n        };\n\n        uploader.onFileQueued = function(file) {\n            fileCount++;\n            fileSize += file.size;\n\n            if (fileCount === 1) {\n                $placeHolder.addClass('element-invisible');\n                $statusBar.show();\n            }\n\n            addFile(file);\n            setState('ready');\n            updateTotalProgress();\n        };\n\n        uploader.onFileDequeued = function(file) {\n            fileCount--;\n            fileSize -= file.size;\n\n            if (!fileCount) {\n                setState('pedding');\n            }\n\n            removeFile(file);\n            updateTotalProgress();\n\n        };\n\n        uploader.on('all', function(type) {\n            var stats;\n            switch (type) {\n                case 'uploadFinished':\n                    setState('confirm');\n                    break;\n\n                case 'startUpload':\n                    setState('uploading');\n                    break;\n\n                case 'stopUpload':\n                    setState('paused');\n                    break;\n\n            }\n        });\n\n        uploader.onError = function(code) {\n            alert('Eroor: ' + code);\n        };\n\n        $upload.on('click', function() {\n            if ($(this).hasClass('disabled')) {\n                return false;\n            }\n\n            if (state === 'ready') {\n                uploader.upload();\n            } else if (state === 'paused') {\n                uploader.upload();\n            } else if (state === 'uploading') {\n                uploader.stop();\n            }\n        });\n\n        $info.on('click', '.retry', function() {\n            uploader.retry();\n        });\n\n        $info.on('click', '.ignore', function() {\n            alert('todo');\n        });\n\n        $upload.addClass('state-' + state);\n        updateTotalProgress();\n    });\n\n})(jQuery);\n\n\n(function($) {\n    // 扩展md5逻辑\n    var Uploader = WebUploader.Uploader;\n\n    var fr;\n\n    function readContent(file, cb) {\n        var chunkSize = 2 * 1024 * 1024,\n            chunks = Math.ceil(file.size / chunkSize),\n            chunk = 0,\n            ret = '',\n            blobSlice = file.mozSlice || file.webkitSlice || file.slice,\n            loadNext;\n\n        fr = fr || new FileReader;\n\n        loadNext = function() {\n            var start, end;\n\n            start = chunk * chunkSize;\n            end = start + chunkSize >= file.size ? file.size : start + chunkSize;\n\n            fr.onload = function(e) {\n                ret += fr.result;\n                fr.result = null;\n            };\n\n            fr.onloadend = function() {\n                fr.onload = fr.onloadend = null;\n\n                if (++chunk < chunks) {\n                    setTimeout(loadNext, 1);\n                } else {\n                    fr.readAsBinaryString(new Blob(['a'], {\n                        type: 'text/plain'\n                    }));\n                    cb(ret);\n                    ret = loadNext = blobSlice = file = null;\n                }\n            };\n\n            fr.readAsBinaryString(blobSlice.call(file, start, end));\n        };\n\n        loadNext();\n    }\n\n    function Md5File(file, callback) {\n        console.time('get md5: ' + file.name);\n\n        readContent(file, function(ret) {\n            ret = md5(ret);\n\n            console.timeEnd('get md5: ' + file.name);\n            setTimeout(function() {\n                callback(ret);\n            }, 1);\n        });\n    }\n\n    Uploader.register({\n        'before-send-file': 'preupload'\n    }, {\n        preupload: function(file) {\n            var me = this,\n                owner = this.owner,\n                server = me.options.server,\n                deferred = WebUploader.Deferred(),\n                blob = file.source.getSource();\n\n            Md5File(blob, function(ret) {\n                $.ajax(server, {\n                    dataType: 'json',\n                    data: {\n                        md5: ret\n                    },\n                    success: function( response ) {\n                        if ( response.exist ) {\n                            owner.skipFile( file );\n                            var log = $('#'+file.id).find('p.imgWrap')\n                            log.text('文件重复，已跳过');\n                        }\n                        deferred.resolve( true );\n                    }\n                });\n            });\n\n            return deferred.promise();\n        }\n    });\n})(jQuery);"
  },
  {
    "path": "_draft/music/webuploader.js",
    "content": "/* WebUploader 0.1.0 */\n(function( window, undefined ) {\n    /**\n     * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n     *\n     * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n     */\n    var internalAmd = (function( global, undefined ) {\n            var modules = {},\n    \n                // 简单不完全实现https://github.com/amdjs/amdjs-api/wiki/require\n                require = function( deps, callback ) {\n                    var args, len, i;\n    \n                    // 如果deps不是数组，则直接返回指定module\n                    if ( typeof deps === 'string' ) {\n                        return getModule( deps );\n                    } else {\n                        args = [];\n                        for( len = deps.length, i = 0; i < len; i++ ) {\n                            args.push( getModule( deps[ i ] ) );\n                        }\n    \n                        return callback.apply( null, args );\n                    }\n                },\n    \n                // 内部的define，暂时不支持不指定id.\n                define = function( id, deps, factory ) {\n                    if ( arguments.length === 2 ) {\n                        factory = deps;\n                        deps = null;\n                    }\n    \n                    if ( typeof id !== 'string' || !factory ) {\n                        throw new Error('Define Error');\n                    }\n    \n                    require( deps || [], function() {\n                        setModule( id, factory, arguments );\n                    });\n                },\n    \n                // 设置module, 兼容CommonJs写法。\n                setModule = function( id, factory, args ) {\n                    var module = {\n                            exports: factory\n                        },\n                        returned;\n    \n                    if ( typeof factory === 'function' ) {\n                        args.length || (args = [ require, module.exports, module ]);\n                        returned = factory.apply( null, args );\n                        returned !== undefined && (module.exports = returned);\n                    }\n    \n                    modules[ id ] = module.exports;\n                },\n    \n                // 根据id获取module\n                getModule = function( id ) {\n                    var module = modules[ id ] || global[ id ];\n    \n                    if ( !module ) {\n                        throw new Error( '`' + id + '` is undefined' );\n                    }\n    \n                    return module;\n                };\n    \n            return {\n                define: define,\n                require: require,\n    \n                // 暴露所有的模块。\n                modules: modules\n            };\n        })( window ),\n    \n        /* jshint unused: false */\n        require = internalAmd.require,\n        define = internalAmd.define;\n\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档将可能省略`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define( 'base', [\n        'jQuery'\n    ], function( $ ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.0',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            /**\n             * 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。\n             * 详细的Deferred用法说明，请参照jQuery的API文档。\n             *\n             * Deferred对象在钩子回掉函数中经常要用到，用来处理需要等待的异步操作。\n             *\n             *\n             * @method Deferred\n             * @grammar Base.Deferred() => Deferred\n             * @example\n             * // 在文件开始发送前做些异步操作。\n             * // WebUploader会等待此异步操作完成后，开始发送文件。\n             * Uploader.register({\n             *     'before-send-file': 'doSomthingAsync'\n             * }, {\n             *\n             *     doSomthingAsync: function() {\n             *         var deferred = Base.Deferred();\n             *\n             *         // 模拟一次异步操作。\n             *         setTimeout(deferred.resolve, 2000);\n             *\n             *         return deferred.promise();\n             *     }\n             * });\n             */\n            Deferred: $.Deferred,\n    \n            /**\n             * 判断传入的参数是否为一个promise对象。\n             * @method isPromise\n             * @grammar Base.isPromise( anything ) => Boolean\n             * @param  {*}  anything 检测对象。\n             * @return {Boolean}\n             * @example\n             * console.log( Base.isPromise() );    // => false\n             * console.log( Base.isPromise({ key: '123' }) );    // => false\n             * console.log( Base.isPromise( Base.Deferred().promise() ) );    // => true\n             *\n             * // Deferred也是一个Promise\n             * console.log( Base.isPromise( Base.Deferred() ) );    // => true\n             */\n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            },\n    \n    \n            /**\n             * 返回一个promise，此promise在所有传入的promise都完成了后完成。\n             * 详细请查看[这里](http://api.jquery.com/jQuery.when/)。\n             *\n             * @method when\n             * @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise\n             */\n            when: $.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d.]+)/ ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数loop](#WebUploader:Base.log)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n\n    /**\n     * @fileOverview Mediator\n     */\n    define( 'mediator', [\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define( 'uploader', [\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            // addFile: 'add-file',\n            // addFiles: 'add-file',\n            removeFile: 'remove-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            getRuntimeType: 'get-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     resize: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.options( 'resize', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `queueNum` 还在队列中的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return {\n                    successNum: stats.numOfSuccess,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue\n                };\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if ( Mediator.trigger.apply( this, arguments ) === false ) {\n                    return false;\n                }\n    \n                if ( $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ) {\n                    return false;\n                }\n    \n                if ( $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ) {\n                    return false;\n                }\n    \n                return true;\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop,\n    \n            reset: function() {\n                // @todo\n            }\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define( 'runtime/runtime', [\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = opts.container || $( document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                if ( this._container ) {\n                    this._container.parentNode.removeChild( this.__container );\n                }\n    \n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define( 'runtime/client', [\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache = (function() {\n                var obj = {};\n    \n                return {\n                    add: function( runtime ) {\n                        obj[ runtime.uid ] = runtime;\n                    },\n    \n                    get: function( ruid ) {\n                        var i;\n    \n                        if ( ruid ) {\n                            return obj[ ruid ];\n                        }\n    \n                        for ( i in obj ) {\n                            return obj[ i ];\n                        }\n    \n                        return null;\n                    },\n    \n                    remove: function( runtime ) {\n                        delete obj[ runtime.uid ];\n                    },\n    \n                    has: function() {\n                        return !!this.get.apply( this, arguments );\n                    }\n                };\n            })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n                if ( runtime ) {\n                    return;\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n    \n                // 像filePicker只能独立存在，不能公用。\n                } else if ( !standalone && cache.has() ) {\n                    runtime = cache.get();\n                }\n    \n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    cache.add( runtime );\n                    runtime.promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    runtime.client = 1;\n                    return runtime;\n                }\n    \n                runtime.promise.then( deferred.resolve );\n                runtime.client++;\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.client--;\n    \n                if ( runtime.client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n\n    /**\n     * @fileOverview Blob\n     */\n    define( 'lib/blob', [\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n    \n            RuntimeClient.call( me, 'Blob' );\n    \n            this.uid = source.uid || this.uid;\n            this.type = source.type || '';\n            this.size = source.size || 0;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n\n    /**\n     * @fileOverview File\n     */\n    define( 'lib/file', [\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 0,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            Blob.apply( this, arguments );\n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            if ( !this.type &&  ~'jpg,jpeg,png,gif,bmp'.indexOf( ext ) ) {\n                this.type = 'image/' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate ||\n                    (new Date()).toLocaleString();\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n\n    /**\n     * @fileOverview 错误信息\n     */\n    define( 'lib/filepicker', [\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClent, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n    \n            opts = this.options = $.extend({}, FilePicker.options, opts );\n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.label = opts.label || opts.container.text() || '选择文件';\n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.text( opts.label );\n            opts.container.html( opts.button );\n    \n            RuntimeClent.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            multiple: true,\n            accept: null\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button;\n    \n                button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                return new File( me.getRuid(), file );\n                            }) );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                });\n    \n                $( window ).on( 'resize', function() {\n                    me.refresh();\n                });\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    width = button.outerWidth(),\n                    height = button.outerHeight(),\n                    pos = button.offset();\n    \n                width && shimContainer.css({\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            destroy: function() {\n                if ( this.runtime ) {\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                }\n            }\n        });\n    \n        return FilePicker;\n    });\n\n    /**\n     * @fileOverview 组件基类。\n     */\n    define( 'widgets/widget', [\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [];\n    \n                $.each( widgetClass, function( _, klass ) {\n                    widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    return Base.when.apply( Base, dfds )\n    \n                            // 很重要不能删除。删除了会死循环。\n                            // 保证执行顺序。让callback总是在下一个tick中执行。\n                            .then(function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                setTimeout(function() {\n                                    deferred.resolve.apply( deferred, args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })\n                            .then( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @param  {object} widgetProto 组件原型，构造函数通过constructor属性定义\n         * @param  {object} responseMap API名称与函数实现的映射\n         * @example\n         *     Uploader.register( {\n         *         init: function( options ) {},\n         *         makeThumb: function() {}\n         *     }, {\n         *         'make-thumb': 'makeThumb'\n         *     } );\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n                widgetProto.responseMap = map;\n            } else {\n                widgetProto.responseMap = $.extend( map, responseMap );\n            }\n    \n            klass = Base.inherits( Widget, widgetProto );\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        return Widget;\n    });\n\n    /**\n     * @fileOverview 文件选择相关\n     */\n    define( 'widgets/filepicker', [\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n    \n        Base.$.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor} 指定选择文件的按钮容器，不指定则不创建按钮。\n             * * `label` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Arroy} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            'add-btn': 'addButton',\n            'refresh': 'refresh'\n        }, {\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addButton( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     label: '选择文件'\n             * });\n             */\n            addButton: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    options, picker, deferred;\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                deferred = Base.Deferred();\n    \n                if ( typeof pick === 'string' ) {\n                    pick = {\n                        id: pick\n                    };\n                }\n    \n                options = $.extend({}, pick, {\n                    accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                    swf: opts.swf,\n                    runtimeOrder: opts.runtimeOrder\n                });\n    \n                picker = new FilePicker( options );\n    \n                picker.once( 'ready', deferred.resolve );\n                picker.on( 'select', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                picker.init();\n    \n                this.pickers.push( picker );\n    \n                return deferred.promise();\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define( 'file', [\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'image/png'\n             */\n            this.type = source.type || 'image/png';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destory: function() {\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n\n    /**\n     * @fileOverview 错误信息\n     */\n    define( 'lib/dnd', [\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: true\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                });\n            },\n    \n            destroy: function() {\n                this.disconnectRuntime();\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n\n    /**\n     * @fileOverview 错误信息\n     */\n    define( 'lib/filepaste', [\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                });\n            },\n    \n            destroy: function() {\n                this.exec('destroy');\n                this.disconnectRuntime();\n                this.off();\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n\n    /**\n     * @fileOverview Image\n     */\n    define( 'lib/image', [\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: true,\n    \n            // 是否允许放大。\n            allowMagnify: true\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n\n    /**\n     * @fileOverview Transport\n     */\n    define( 'lib/transport', [\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVar: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVar = key || opts.fileVar;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n\n    /**\n     * @fileOverview 文件队列\n     */\n    define( 'queue', [\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被移除的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n    \n                file.setStatus( STATUS.QUEUED );\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define( 'runtime/compbase', function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define( 'runtime/flash/runtime', [\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destory = this.destory,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function hander( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    hander.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destory = function() {\n                // @todo 删除池子中的所有实例\n                return destory && destory.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.isIE ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.3 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n\n    /**\n     * @fileOverview FilePicker\n     */\n    define( 'runtime/flash/filepicker', [\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts );\n    \n                delete copy.button;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                // todo\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 图片压缩\n     */\n    define( 'runtime/flash/image', [\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define( 'runtime/flash/transport', [\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVar, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    return me.trigger( 'progress', e.loaded / e.total );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus');\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status === 200 ) {\n                        me._response = xhr.exec('getResponse');\n                        me._responseJson = xhr.exec('getResponseAsJson');\n                        return me.trigger('load');\n                    }\n    \n                    me._status = status;\n                    xhr.destroy();\n                    xhr = null;\n    \n                    return me.trigger( 'error', 'http' );\n                });\n    \n                xhr.on( 'error', function() {\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', 'http' );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define( 'runtime/html5/runtime', [\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destory = this.destory;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destory = function() {\n                // @todo 删除池子中的所有实例\n                return destory && destory.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define( 'runtime/html5/blob', [\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define( 'runtime/html5/dnd', [\n        'base',\n        'runtime/html5/runtime',\n        'lib/file'\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                this.elem.addClass('webuploader-dnd-over');\r\n    \r\n                e = e.originalEvent || e;\r\n                e.dataTransfer.dropEffect = 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                if ( !$.contains( this.elem.parent().get( 0 ), e.target ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                this.elem.removeClass('webuploader-dnd-over');\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    me = this,\r\n                    ruid = me.getRuid(),\r\n                    items, files, dataTransfer, file, i, len, canAccessFolder;\r\n    \r\n                // 只处理框内的。\r\n                if ( !$.contains( me.elem.parent().get( 0 ), e.target ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    if ( file.type ) {\r\n                        results.push( file );\r\n                    } else if ( !file.type && canAccessFolder ) {\r\n                        promises.push( this._traverseDirectoryTree(\r\n                                items[ i ].webkitGetAsEntry(), results ) );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                this.elem.removeClass('webuploader-dnd-over');\r\n                return false;\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        file.type && results.push( file );\r\n                        deferred.resolve( true );\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve( true );\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragEnterHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\n\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define( 'runtime/html5/filepaste', [\n        'base',\n        'runtime/html5/runtime',\n        'lib/file'\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    files, file, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                e.preventDefault();\r\n                e.stopPropagation();\r\n    \r\n                files = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n    \r\n                    if ( !file.type || !(blob = file.getAsFile()) ||\r\n                            blob.size < 6 ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                allowed.length && this.trigger( 'paste', allowed );\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\n\n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define( 'runtime/html5/filepicker', [\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    lable = $( document.createElement('label') ),\r\n                    input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n    \r\n                input.css({\r\n                    position: 'absolute',\r\n                    clip: 'rect(1px,1px,1px,1px)'\r\n                });\r\n    \r\n                lable.on( 'click', function() {\r\n                    input.trigger('click');\r\n                });\r\n    \r\n                lable.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( lable );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                input.on( 'change', function( e ) {\r\n                    var fn = arguments.callee,\r\n                        clone;\r\n    \r\n                    me.files = e.target.files;\r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', fn )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                });\r\n    \r\n                lable.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                // todo\r\n            }\r\n        });\r\n    });\n\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define( 'runtime/html5/util', function() {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL;\n    \n        return {\n            createObjectURL: urlAPI && urlAPI.createObjectURL,\n            revokeObjectURL: urlAPI && urlAPI.revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return new Blob([ ab ], {\n                    type: mimetype\n                });\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                return new Blob([ buffer ], type ? { type: type } : {} );\n            }\n        };\n    });\n\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define( 'runtime/html5/imagemeta', function() {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        return api;\n    });\n\n    /**\n     * @fileOverview Image\n     */\n    define( 'runtime/html5/image', [\n        'runtime/html5/runtime',\n        'runtime/html5/util',\n        'runtime/html5/imagemeta'\n    ], function( Html5Runtime, Util, ImageMeta ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    // 读取meta信息。\n                    if ( !me._metas && ~'image/jpegimage/jpg'.indexOf( me.type ) ) {\n                        ImageMeta.parse( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger('complete');\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n                        blob = canvas.toDataURL( 'image/jpeg', opts.quality / 100 );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = ImageMeta.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = canvas.toDataURL( type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return this._canvas.toDataURL( type, opts.quality / 100 );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            _renderImageToCanvas: function( canvas, img, x, y, w, h ) {\n                canvas.getContext('2d').drawImage( img, x, y, w, h );\n            }\n    \n            /*_renderImageToCanvas: (function() {\n                var subsampled, vertSquashRatio;\n    \n                // Detect subsampling in loaded image.\n                // In iOS, larger images than 2M pixels may be subsampled in rendering.\n                function detectSubsampling(img) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight;\n                    if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image\n                        var canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        var ctx = canvas.getContext('2d');\n                        ctx.drawImage(img, -iw + 1, 0);\n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering edge pixel or not.\n                        // if alpha value is 0 image is not covering, hence subsampled.\n                        return ctx.getImageData(0, 0, 1, 1).data[3] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                // Detecting vertical squash in loaded image.\n                // Fixes a bug which squash image vertically while drawing into canvas for some images.\n                function detectVerticalSquash(img, iw, ih) {\n                    var canvas = document.createElement('canvas');\n                    canvas.width = 1;\n                    canvas.height = ih;\n                    var ctx = canvas.getContext('2d');\n                    ctx.drawImage(img, 0, 0);\n                    var data = ctx.getImageData(0, 0, 1, ih).data;\n                    // search image edge pixel position in case it is squashed vertically.\n                    var sy = 0;\n                    var ey = ih;\n                    var py = ih;\n                    while (py > sy) {\n                        var alpha = data[(py - 1) * 4 + 3];\n                        if (alpha === 0) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n                        py = (ey + sy) >> 1;\n                    }\n                    var ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                return function( canvas, img, x, y, w, h ) {\n    \n    \n                    var iw = img.naturalWidth, ih = img.naturalHeight;\n                    var width = w, height = h;\n                    var ctx = canvas.getContext('2d');\n                    ctx.save();\n    \n                    subsampled = typeof subsampled === 'undefined' ? detectSubsampling( img ) : subsampled;\n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    var d = 1024; // size of tiling canvas\n                    var tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n                    var tmpCtx = tmpCanvas.getContext('2d');\n    \n                    vertSquashRatio = vertSquashRatio || detectVerticalSquash(img, iw, ih);\n                    console.log( vertSquashRatio );\n    \n                    var dw = Math.ceil(d * width / iw);\n                    var dh = Math.ceil(d * height / ih / vertSquashRatio);\n                    var sy = 0;\n                    var dy = 0;\n                    while (sy < ih) {\n                      var sx = 0;\n                      var dx = 0;\n                      while (sx < iw) {\n                        tmpCtx.clearRect(0, 0, d, d);\n                        tmpCtx.drawImage(img, x - sx, y - sy );\n                        ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh);\n                        sx += d;\n                        dx += dw;\n                      }\n                      sy += d;\n                      dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()*/\n        });\n    });\n\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define( 'runtime/html5/imagemeta/exif', [\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n\n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define( 'runtime/html5/transport', [\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary;\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVar, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                binary && xhr.overrideMimeType('application/octet-stream');\n                xhr.send( binary || formData );\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n    \n                    // 只考虑200的情况\n                    if ( xhr.status === 200 ) {\n                        me._response = xhr.responseText;\n                        return me.trigger('load');\n                    }\n    \n                    me._status = xhr.status;\n                    xhr = null;\n    \n                    return me.trigger( 'error', me._status ? 'http' : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define( 'widgets/filednd', [\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n                dnd.init();\n    \n                return deferred.promise();\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 组件基类。\n     */\n    define( 'widgets/filepaste', [\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define( 'widgets/image', [\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: false,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n            'make-thumb': 'makeThumb',\n            'before-send-file': 'compressImage'\n        }, {\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             *\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function() {\n                    cb( true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            compressImage: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 300 * 1024,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n                    image.resize( opts.width, opts.height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    blob = image.getAsBlob( opts.type );\n                    size = file.size;\n    \n                    // 如果压缩后，比原来还大则不用压缩后的。\n                    if ( blob.size < size ) {\n                        // file.source.destroy && file.source.destroy();\n                        file.source = blob;\n                        file.size = blob.size;\n    \n                        file.trigger( 'resize', blob.size, size );\n                    }\n    \n                    // 标记，避免重复压缩。\n                    file._compressed = true;\n                    deferred.resolve( true );\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 队列\n     */\n    define( 'widgets/queue', [\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            'add-file': 'addFiles',\n            'get-file': 'getFile',\n            'fetch-file': 'fetchFile',\n            'get-stats': 'getStats',\n            'get-files': 'getFiles',\n            'remove-file': 'removeFile',\n            'retry': 'retry'\n        }, {\n    \n            init: function( opts ) {\n                var len, i, item, arr, accept;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    this.accept = new RegExp( accept, 'i' );\n                }\n    \n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发，此事件的handler返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                if ( !file || file.size < 6 || me.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !me.accept.test( file.name ) ) {\n                    return;\n                }\n    \n                if ( !(file instanceof WUFile) ) {\n                    file = new WUFile( file );\n                }\n    \n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            addFiles: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \n                me.owner.trigger( 'filesQueued', files );\n    \n                if ( me.options.auto ) {\n                    me.request('start-upload');\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                file.setStatus( Status.CANCELLED );\n                me.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            }\n        });\n    \n    });\n\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define( 'widgets/runtime', [\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        return Uploader.register({\n            'predict-runtime-type': 'predictRuntmeType'\n        }, {\n    \n            init: function() {\n                if ( !this.predictRuntmeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntmeType() => String\n             * @method predictRuntmeType\n             * @for  Uploader\n             */\n            predictRuntmeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n\n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define( 'widgets/upload', [\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 对于一个文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formdata]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formdata: null\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len;\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n                pending.push({\n                    file: file,\n                    start: start,\n                    end: start + len,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                fetch: function() {\n                    return pending.shift();\n                }\n            };\n        }\n    \n        Uploader.register({\n            'start-upload': 'start',\n            'stop-upload': 'stop',\n            'skip-file': 'skipFile',\n            'is-in-progress': 'isInProgress'\n        }, {\n    \n            init: function() {\n                var owner = this.owner;\n    \n                this.runing = false;\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                owner.on( 'uploadComplete', function( file ) {\n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             * @grammar upload() => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            start: function() {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                if ( me.runing ) {\n                    return;\n                }\n    \n                me.runing = true;\n    \n                // 如果有暂停的，则续传\n                $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        file.setStatus( Status.PROGRESS );\n                        me._trigged = false;\n                        v.transport && v.transport.send();\n                    }\n                });\n    \n                me._trigged = false;\n                me.owner.trigger('startUpload');\n                Base.nextTick( me.__tick );\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stop: function( interrupt ) {\n                var me = this;\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                me.runing = false;\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * 判断`Uplaode`r是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.runing;\n            },\n    \n            getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 掉过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me.getStats().numOfQueue ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    act = me._act,\n                    opts = me.options,\n                    next, done;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( act && act.has() &&\n                        act.file.getStatus() === Status.PROGRESS ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.fetch();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me.getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me._act = act;\n                        return act.fetch();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    return isPromise( next ) ? next.then( done ) : done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n    \n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.QUEUED ) {\n                            me.owner.trigger( 'uploadStart', file );\n                            file.setStatus( Status.PROGRESS );\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                promise = me.request( 'before-send', block, function() {\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else {\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @description 但请求再发送前触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = me.options,\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers );\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    var totalPercent = 0,\n                        uploaded = 0;\n    \n                    // 可能没有abort掉，progress还是执行进来了。\n                    // if ( !file.blocks ) {\n                    //     return;\n                    // }\n    \n                    totalPercent = block.percentage = percentage;\n    \n                    if ( block.chunks > 1 ) {    // 计算文件的整体速度。\n                        $.each( file.blocks, function( _, v ) {\n                            uploaded += (v.percentage || 0) * (v.end - v.start);\n                        });\n    \n                        totalPercent = uploaded / file.size;\n                    }\n    \n                    owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n                });\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type ) {\n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n                        tr.send();\n    \n                    } else {\n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var ret = tr.getResponseAsJson() || {},\n                        reject, fn;\n    \n                    ret._raw = tr.getResponse();\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    // 如果非预期，转向上传出错。\n                    if ( reject ) {\n                        tr.trigger( 'error', reject );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            }\n    \n        });\n    });\n\n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define( 'widgets/validator', [\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            init: function() {\n                var me = this;\n                $.each( validators, function() {\n                    this.call( me.owner );\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = opts.fileNumLimit >> 0,\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function() {\n    \n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = opts.fileSizeLimit >> 0,\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'fileQueued', function( file ) {\n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                }\n            });\n        });\n    \n        /**\n         * @property {int} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = hashString( file.name + file.size +\n                        file.lastModifiedDate );\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = hashString( file.name + file.size +\n                        file.lastModifiedDate );\n    \n                mapping[ hash ] = true;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = hashString( file.name + file.size +\n                        file.lastModifiedDate );\n    \n                delete mapping[ hash ];\n            });\n        });\n    \n        return api;\n    });\n\n    /**\n     * @file 暴露变量给外部使用。\n     * 此文件也只有在把webupload合并成一个文件使用的时候才会引入。\n     *\n     * 将所有modules，将路径ids装换成对象。\n     */\n    (function( modules ) {\n        var\n            // 让首写字母大写。\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            },\n    \n            // 暴露出去的key\n            exportName = 'WebUploader',\n            exports = modules.base,\n            key, host, parts, part, last, origin;\n    \n        for ( key in modules ) {\n            host = exports;\n    \n            if ( !modules.hasOwnProperty( key ) ) {\n                continue;\n            }\n    \n            parts = key.split('/');\n            last = ucFirst( parts.pop() );\n    \n            while( (part = ucFirst( parts.shift() )) ) {\n                host[ part ] = host[ part ] || {};\n                host = host[ part ];\n            }\n    \n            host[ last ] = modules[ key ];\n        }\n    \n        if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n            module.exports = exports;\n        } else if ( window.define && window.define.amd ) {\n            window.define( function() { return exports; } );\n        } else {\n            origin = window[ exportName ];\n            window[ exportName ] = exports;\n            window[ exportName ].noConflict = function() {\n                window[ exportName ] = origin;\n            };\n        }\n    })( internalAmd.modules );\n    \n})( this );"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"fex-webuploader\",\n  \"main\": \"dist/webuploader.js\",\n  \"homepage\": \"https://github.com/fex-team/webuploader\",\n  \"author\": \"fex-team\",\n  \"description\": \"WebUploader是一个简单的以Html5为主，Flash为辅的现代文件上传组件。在现代的浏览器里面能充分发挥html5的优势，同时又不摒弃主流IE浏览器，延用原来的Flash运行时。两套运行时，同样的调用方式，可供用户任意选用。WebUploader采用大文件分片并发上传，极大的提高了文件上传效率。\",\n  \"keywords\": [\n    \"fileuploader\",\n    \"chunk\",\n    \"uploader\",\n    \"html5\",\n    \"flash\"\n  ],\n  \"dependencies\": {\n    \"jquery\": \">= 1.9.0\"\n  },\n  \"license\": \"BSD\",\n  \"ignore\": [\n    \"**/.*\",\n    \"node_modules\",\n    \"/build\",\n    \"/css\",\n    \"/jekyll\",\n    \"/server\",\n    \"/test\",\n    \"/_draft\",\n    \"/examples\",\n    \"/flash\",\n    \"/pages\"\n  ]\n}\n"
  },
  {
    "path": "build/docTpl/class.ejs",
    "content": "<div class=\"clearfix category\">\n\n\n    <% var item; if( (item = items[ 0 ]) && item.hasOwnProperty('constructor') ) { %>\n    <article class=\"clearfix <%= item.itemtype %>\">\n        <h3 id=\"<%- gennerateID( module + ':' + name ) %>\" <% if ( item.hasOwnProperty( 'deprecated' ) ) { %>class=\"deprecated\"<% } %> ><%= item.name %>\n            <% if ( item.since ) { %><span\n            class=\"version\"><%- item.since %></span><% } %>\n        </h3>\n        <% if ( item[ 'deprecated' ] ) { %><div class=\"warning\"><%- markdown( item[ 'deprecated' ] ) %></div><% } %>\n\n        <% if( item.uses ) { %><%- renderUses( item.uses ) %><% }%>\n\n        <% if ( item.grammars ) { %>\n        <ul class=\"signature\">\n            <% _.forEach( item.grammars, function( grammar ) { %>\n            <li><%= grammar.signature %>\n                <% if ( grammar.return ) { %><span class=\"return\">⇒ <%= grammar.return %></span><% } %>\n                <% if ( grammar.since ) { %><span class=\"version\"><%= grammar.since %></span><% } %>\n            </li><% });%>\n        </ul>\n        <% } %>\n        <% if ( item.params ) { %>\n            <p>参数: </p>\n            <%- renderParams( item.params ) %>\n        <% } %>\n        <% if ( item.returns ) { %>\n            <p>返回值: </p>\n            <%- renderParams( item.returns ) %>\n        <% } %>\n\n        <%- markdown( item.description || description || '' ) %>\n        <% _.forEach( item.examples || [], function( example ) { %>\n            <%- formatExample( example ) %>\n        <% }) %>\n    </article>\n\n    <% } else { %>\n        <h2 id=\"<%- gennerateID( module + ':' + name ) %>\"><%= title || name %></h2>\n        <% if ( description ) { %><%- markdown( description ) %><% } %>\n    <% } %>\n\n    <% if ( options.length ) { %> <%- renderTpl( 'options' ) %>  <% } %>\n    <% if ( events.length ) { %>  <%- renderTpl( 'events' ) %>      <% } %>\n    <% if ( items.length ) { %> <%- renderTpl( 'items' ) %>  <% } %>\n\n    <% if ( plugins.length ) { %> <%- renderTpl( 'plugins' ) %>  <% } %>\n</div>\n<hr />"
  },
  {
    "path": "build/docTpl/content.ejs",
    "content": "<% _.forEach( modules, function( module ) { %>\n    <%- renderTpl( 'module', module ) %>\n<% }) %>"
  },
  {
    "path": "build/docTpl/css/doc.css",
    "content": "ul.signature {\n    margin: 1em 0;\n    padding: 0;\n    font: normal 12px/18px Monaco, Consolas, \"Lucida Console\", monospace;\n}\n\nul.signature li {\n    list-style: none;\n}\n\n.method p {\n    margin: 0.5em 0;\n}\n\n.method h3 {\n    margin-bottom: .2em;\n}\n\n.method h3.deprecated {\n    text-decoration: line-through;\n}\n\nspan.deleted {\n    text-decoration: line-through;\n}\n\n.method pre.cm-s-default {\n    display: block;\n    white-space: pre-wrap;\n    margin: 1em 0px;\n}\n\n.method .warning {\n    margin: 5px 0;\n    font: bold 12px Arial;\n    color: #700;\n}\n\n.method .version {\n    background: #CCF;\n    color: black;\n    padding: 1px 3px;\n    margin: 0 0 0 5px;\n    border-radius: 3px;\n    font: bold 11px Arial;\n    font-weight: normal;\n}\n\ndiv.notice {\n    padding: 4px 10px;\n    border-left: 2px solid #FF8;\n    background: #FFE;\n    color: black;\n    margin: 1em 0;\n}\n\nul.params-list li > span.meta + p, ol.plugin-list li > code + p {\n    display: inline;\n    padding-left: 10px;\n}\n\ntable.table .events-tb-desc {\n    min-width: 250px;\n}"
  },
  {
    "path": "build/docTpl/events.ejs",
    "content": "<div>\n    <h3 id=\"<%- gennerateID(module + ':' + name + ':events') %>\">事件说明</h3>\n    <table class=\"table table-bordered\">\n        <tr>\n            <th>事件名</th>\n            <th class=\"events-tb-desc\">参数说明</th>\n            <th>描述</th>\n        </tr>\n\n        <% _.forEach( events, function( item ) { %>\n        <tr>\n            <td><code><%= item.name %></code></td>\n            <td class=\"events-tb-desc\">\n                <% if ( item.params ) { %>\n                <%- renderParams( item.params ) %>\n                <% } %>\n            </td>\n            <td><%- markdown(item.description) %></td>\n        </tr>\n        <% }) %>\n    </table>\n</div>"
  },
  {
    "path": "build/docTpl/items.ejs",
    "content": "<% _.sortBy( items, function( value ){ return value.uses ? value.uses.length : 0; } ).forEach(function( item ) { %>\n<% if( item.hasOwnProperty('constructor') ) { return; } %>\n\n<article class=\"clearfix <%= item.itemtype %>\">\n    <h3 id=\"<%- gennerateID( item[ 'class' ] ? (item.module + ':' + item[ 'class' ] + ':' + item.name) : (item.module + ':' + item.name)  ) %>\" <% if ( item.hasOwnProperty( 'deprecated' ) ) { %>class=\"deprecated\"<% } %> ><%= item.name %>\n        <% if ( item.since ) { %><span\n        class=\"version\"><%- item.since %></span><% } %>\n    </h3>\n    <% if ( item[ 'deprecated' ] ) { %><div class=\"warning\"><%- markdown( item[ 'deprecated' ] ) %></div><% } %>\n\n    <% if( item.uses ) { %><%- renderUses( item.uses ) %><% }%>\n\n    <% if ( item.grammars ) { %>\n    <ul class=\"signature\">\n        <% _.forEach( item.grammars, function( grammar ) { %>\n        <li><%= grammar.signature %>\n            <% if ( grammar.return ) { %><span class=\"return\">⇒ <%= grammar.return %></span><% } %>\n            <% if ( grammar.since ) { %><span class=\"version\"><%= grammar.since %></span><% } %>\n        </li><% });%>\n    </ul>\n    <% } %>\n    <% if ( item.params ) { %>\n        <p>参数: </p>\n        <%- renderParams( item.params ) %>\n    <% } %>\n    <% if ( item.returns ) { %>\n        <p>返回值: </p>\n        <%- renderParams( item.returns ) %>\n    <% } %>\n\n    <%- markdown( item.description || '' ) %>\n    <% _.forEach( item.examples || [], function( example ) { %>\n        <%- formatExample( example ) %>\n    <% }) %>\n</article>\n\n<% }) %>\n"
  },
  {
    "path": "build/docTpl/layout.ejs",
    "content": "---\nlayout: default\ntitle: <%= title %>\nnavName: API\ngroup: 'nav'\nweight : 3\nstyles:\n  - /doc/css/doc.css\n---\n<%- renderTpl('content').replace(/<pre><code\\sclass=\"lang-([^\"]+)\">([\\s\\S]*?)<\\/code><\\/pre>/igm, function( m0, m1, m2 ) {\n    return '\\n' +\n        '{% highlight ' + m1 + ' %}\\n' +\n\n        m2.replace(/&amp;/g, '&')\n        .replace(/&gt;/g, '>')\n        .replace(/&quot;/g, '\"')\n        .replace(/&#39;/g, '\\'') +\n\n        '\\n{% endhighlight %}\\n';\n}) %>"
  },
  {
    "path": "build/docTpl/module.ejs",
    "content": "<div class=\"container\">\n    <% if ( title ) { %>\n    <div class=\"page-header\">\n      <h1 id=\"<%- gennerateID( name ) %>\"><%= title %></h1>\n    </div>\n    <% } %>\n    <%- markdown( description ) %>\n</div>\n\n\n<div id=\"post-container\" class=\"container\">\n    <div class=\"row\">\n        <div class=\"col-md-3\">\n            <%- renderTpl('sidebar') %>\n        </div>\n        <div class=\"col-md-9\">\n            <div class=\"page-container\">\n\n                <% if ( items ) { %>\n                <a id=\"<%- gennerateID( name + ':' + name ) %>\" ></a>\n                <%- renderTpl( 'items' ) %>\n                <% } %>\n\n                <% _.forEach( classes, function( clazz ) { %>\n                <%- clazz.isPlugin ? '' : renderTpl( 'class', clazz ) %>\n                <% }) %>\n\n            </div>\n        </div>\n    </div>\n</div>\n\n"
  },
  {
    "path": "build/docTpl/options.ejs",
    "content": "<div>\n    <h3 id=\"<%- gennerateID(module + ':' + name + ':options') %>\">参数说明</h3>\n    <ul class=\"params-list\">\n        <% _.sortBy( options, function( value ){ return value.uses ? value.uses.length : 0; } ).forEach(function( item ) { %>\n        <li>\n            <span class=\"meta\">\n                <code><%= item.shortname || item.name %></code>\n                <% if ( item.type ) { %> {<%=: item.type | join:', ' %>} <% }%>\n                <% if ( item.optional ) { %> [可选] <% }%>\n                <% if ( item.defaultvalue ) { %> [默认值：<%= item.defaultvalue %>] <% }%>\n            </span>\n            <%- markdown(item.description) %>\n            <% if ( item.uses ) { %>\n            <%- renderUses( item.uses, '依赖: %s。' ) %>\n            <% } %>\n        </li>\n        <% }) %>\n    </ul>\n</div>"
  },
  {
    "path": "build/docTpl/plugins.ejs",
    "content": "<div>\n    <hr />\n    <h2 id=\"<%- gennerateID(module + ':' + name + ':plugins') %>\">插件介绍</h2>\n    <br />\n    <ol class=\"plugin-list\">\n    <% _.forEach( plugins, function( item ) { %>\n    <li id=\"<%- gennerateID(module + ':' + item.name) %>\">\n        <code><%= item.shortname || item.name %></code><%- markdown(item.description) %>\n        <%- renderFileInfo( item.file ) %>\n    </li>\n    <% }) %>\n    </ol>\n</div>"
  },
  {
    "path": "build/docTpl/sidebar.ejs",
    "content": "<div class=\"post-toc\">\n    <ul class=\"nav\">\n        <% _.forEach( navs, function( classes, modulename ) { %>\n        <% _.forEach( classes, function( items, classname ) { %>\n            <li>\n                <a href=\"<%- forUrl( modulename + ':' + classname ) %>\"><%= classname %></a>\n                <ul class=\"nav\">\n                    <% _.forEach( items, function( item ) { %>\n                    <% if( item.text === classname ) { return; } %>\n\n                    <li class=\"<%- item.skin || '' %>\">\n                        <a class=\"<%= item.classname || '' %>\" href=\"<%- item.href %>\"><%= item.text %></a>\n                    </li>\n                    <% });%>\n                </ul>\n            </li>\n        <% }); %>\n        <% }); %>\n    </ul>\n</div>"
  },
  {
    "path": "build/fis/intro.js",
    "content": "var jQuery = require('example:widget/ui/jquery/jquery.js');\n\nmodule.exports = (function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        };\n\n    return makeExport( jQuery );\n})( window, function( window, define, require ) {\n\n"
  },
  {
    "path": "build/fis/outro.js",
    "content": "\n\n    var _require = require;\n    return _require('webuploader');\n});\n"
  },
  {
    "path": "build/intro.js",
    "content": "/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n"
  },
  {
    "path": "build/outro.js",
    "content": "\n    return require('webuploader');\n});\n"
  },
  {
    "path": "build/tasks/build.js",
    "content": "/**\n * @fileOverview 负责合并amd modules为一个单文件。\n */\n\n'use strict';\n\nvar requirejs = require('requirejs'),\n    fs = require('fs'),\n    path = require('path');\n\n// convert relative path to absolute path.\nfunction convert( name, _path, contents ) {\n    var rDefine = /(define\\s*\\(\\s*('|\").*?\\2\\s*,\\s*\\[)([\\s\\S]*?)\\]/ig,\n        rDeps = /('|\")(.*?)\\1/g,\n        root = _path.substr( 0, _path.length - name.length - 3 ),\n        dir = path.dirname( _path ),\n        m, m2, deps, dep, _path2;\n\n    contents = contents.replace( rDefine, function( m, m1, m2, m3 ) {\n        return m1 + m3.replace( rDeps, function( m, m1, m2 ) {\n            m2 = path.join( dir, m2 );\n            m2 = path.relative( root, m2 );\n\n            m2 = m2.replace(/\\\\/g, '/');\n            return m1 + m2 + m1;\n        }) + ']';\n    });\n\n    return contents;\n}\n\nmodule.exports = function( grunt ) {\n    grunt.registerMultiTask( 'build', '合并amd modules为一个单文件', function() {\n\n        var done = this.async(),\n            options = this.options({\n                banner: '',\n                footer: '',\n                process: null,\n                builtin: {\n                    dollar: false,\n                    promise: false\n                }\n            }),\n            pkg = grunt.config.get('pkg'),\n            dest = this.data.dest,\n            config, flag, custom;\n\n        config = {\n\n            baseUrl: 'src',\n            name: '',\n            out: '',\n\n            // We have multiple minify steps\n            optimize: 'none',\n\n            // Include dependencies loaded with require\n            findNestedDependencies: true,\n\n            // Avoid breaking semicolons inserted by r.js\n            skipSemiColonInsertion: true,\n\n            wrap: {\n                startFile: 'build/intro.js',\n                endFile: 'build/outro.js'\n            },\n\n            rawText: {},\n\n            onBuildWrite: function( name, _path ) {\n                var compiled = convert.apply( null, arguments );\n\n                if ( options.process ) {\n                    compiled = options.process( compiled, _path );\n                }\n\n                // 调整缩进\n                compiled = compiled.replace( /(^|\\r\\n|\\r|\\n)/g, '$1    ');\n\n                compiled = compiled.replace(/@version@/g, function() {\n                    return pkg.version;\n                });\n\n                return compiled;\n            },\n\n            paths: [],\n            include: []\n        };\n\n        options = grunt.util._.extend( options, this.data );\n        config.name = 'webuploader';\n        if ( options.builtin.dollar ) {\n            config.rawText.dollar = 'define([\\n' +\n                    '    \\'./dollar-builtin\\'\\n' +\n                    '], function( $ ) {\\n' +\n                    '    return $;\\n' +\n                    '});';\n        }\n\n        if ( options.builtin.promise ) {\n            config.rawText.promise = 'define([\\n' +\n                    '    \\'./promise-builtin\\'\\n' +\n                    '], function( $ ) {\\n' +\n                    '    return $;\\n' +\n                    '});';\n        }\n\n        if ( this.data.preset === 'custom' ) {\n            custom = [];\n\n            this.files.forEach(function( file ) {\n                var files = file.src,\n                    cwd = file.cwd || '';\n\n                files.filter(function( filepath ) {\n\n                    filepath = path.join( cwd, filepath );\n                    // Warn on and remove invalid source files (if nonull was set).\n                    if (!grunt.file.exists(filepath)) {\n                        grunt.log.warn('Source file \"' + filepath + '\" not found.');\n                        return false;\n                    } else {\n                        return true;\n                    }\n                }).forEach(function( filepath ) {\n                    custom.push( '\\'' + filepath.replace( /\\.\\w+$/, '' ) + '\\'' );\n                });\n            });\n\n            custom.unshift('\\'./base\\'');\n            custom = 'define([\\n    ' + custom.join(',\\n    ') + '\\n], function( Base ) {\\n    return Base;\\n});';\n            config.rawText.webuploader = custom;\n        } else if (this.data.preset) {\n\n            config.rawText.webuploader = 'define([\\n    ' + ['\\'./preset/' +\n                    this.data.preset +'\\''].join(',\\n    ') +\n                    '\\n], function( preset ) {\\n    return preset;\\n});';\n        } else {\n            config.name = this.data.name;\n        }\n\n        // 处理最终输出\n        config.out = function( compiled ) {\n            var arr = [],\n                banner = grunt.template.process(options.banner),\n                footer = grunt.template.process(options.footer),\n                sep = '\\n\\n';\n\n            banner && arr.push( banner );\n\n            if ( options.builtin.dollar ) {\n                compiled = compiled.replace('define([ \\'jquery\\' ], exports );', 'define([], exports);');\n            }\n\n            arr.push(compiled);\n            footer && arr.push( footer );\n\n            // Write concatenated source to file\n            grunt.file.write( dest, arr.join( sep ) );\n\n            process.nextTick(function() {\n                // requirejs有bug, callback不一定会执行，目前调试的结果是\n                // prim的promise实现有问题。\n                if ( flag ) return;\n                grunt.log.ok( \"File '\" + dest + \"' created.\" );\n                done();\n                flag = true;\n            });\n        };\n\n        if (options.fis) {\n            config.wrap = {\n                startFile: 'build/fis/intro.js',\n                endFile: 'build/fis/outro.js'\n            }\n        }\n\n        requirejs.optimize( config, function( response ) {\n            // requirejs有bug, callback不一定会执行，目前调试的结果是\n            // prim的promise实现有问题。\n            if ( flag ) return;\n            grunt.verbose.writeln( response );\n            grunt.log.ok( \"File '\" + name + \"' created.\" );\n            done();\n            flag = true;\n        }, function( err ) {\n            done( err );\n        });\n    });\n};\n"
  },
  {
    "path": "build/tasks/concat.js",
    "content": "/*\n * grunt-contrib-concat\n * http://gruntjs.com/\n *\n * Copyright (c) 2013 \"Cowboy\" Ben Alman, contributors\n * Licensed under the MIT license.\n */\n\n'use strict';\n\nmodule.exports = function(grunt) {\n    var path = require('path');\n\n    function stripBanner(src, options) {\n        if (!options) {\n            options = {};\n        }\n        var m = [];\n        if (options.line) {\n            // Strip // ... leading banners.\n            m.push('(?:.*\\\\/\\\\/.*\\\\r?\\\\n)*\\\\s*');\n        }\n        if (options.block) {\n            // Strips all /* ... */ block comment banners.\n            m.push('\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\/');\n        } else {\n            // Strips only /* ... */ block comment banners, excluding /*! ... */.\n            m.push('\\\\/\\\\*[^!][\\\\s\\\\S]*?\\\\*\\\\/');\n        }\n        var re = new RegExp('^\\\\s*(?:' + m.join('|') + ')\\\\s*', '');\n        return src.replace(re, '');\n    }\n\n    var intro = path.join(__dirname, '../intro.js');\n    var outro = path.join(__dirname, '../outro.js');\n\n    // 排序，把依赖的文件移动到最上面。\n    function filesFilter( f, files ) {\n        var cwd = f.cwd || '',\n            ret = [],\n            process = function( file ) {\n                var fileinfo = path.join( cwd, file ),\n                    dirpath = path.dirname( fileinfo ),\n                    depends = [],\n                    str, matches, idx;\n\n                if ( !grunt.file.exists( fileinfo ) ) {\n                    return;\n                }\n\n                ret.push( file );\n\n                str = grunt.file.read( fileinfo );\n\n                // 从require( dps )中找\n                // 从defind( id?, dps, factory )中找\n                str = str.replace( /(?:define|require)\\s*\\(\\s*\\[([^\\]]+?)\\],/g, function( _, m1 ) {\n                    m1 = m1.replace(/\\s/g, '').split(',');\n                    depends = depends.concat( m1.map(function( item ) {\n                        item = item.substring( 1, item.length - 1 );\n                        item = item.substring(0, 1) === '.' ?\n                                path.join( dirpath, item ):\n                                path.join( cwd, item );\n\n                        return path.relative( cwd, item ) + '.js';\n                    }) );\n\n                    return _;\n                });\n\n                str = str.replace( /require\\s*\\(\\s*('|\")(.+?)\\1/g, function( _, m1, item ) {\n\n\n                    item = item.substring(0, 1) === '.' ?\n                            path.join( dirpath, item ):\n                            path.join( cwd, item );\n\n                    depends.push( path.relative( cwd, item ) + '.js' );\n\n                    return _;\n                });\n\n                if ( depends.length ) {\n                    depends = depends.filter(function( item, idx, array ) {\n                        return array.indexOf( item ) === idx && grunt.file.exists( path.join( cwd, item ) );\n                    });\n\n                    idx = ret.indexOf( file );\n                    [].splice.apply( ret, [ idx, 0 ].concat( depends ) );\n                    depends.forEach( process );\n                }\n            };\n        // console.log( files );\n        files.forEach( process );\n        ret = ret.filter(function( item, idx, arr ){\n            return idx === arr.indexOf( item );\n        });\n\n\n        ret.unshift( path.relative( cwd, intro ) );\n        ret.push( path.relative( cwd, outro ) );\n        return ret;\n    }\n\n    // 缓存版本号\n    var version;\n\n    function fileProcess( src, filepath, cwd ) {\n        var dirpath = path.dirname( filepath );\n\n        version = version || grunt.config.get('pkg.version');\n\n        src = src.replace( /@version@/g, version );\n\n        // 不处理 outro.js\n        if (filepath.indexOf('outro') >= 0) {\n            return src;\n        }\n        // console.log( filepath, cwd );\n\n        // 处理 define( dps ?, factory );\n        // 处理 require( dps );\n        src = src.replace( /(define|require)\\s*\\((?:\\s*\\[([^\\]]+)\\],)?/g, function( _, m1, m2 ) {\n            var str = m1 + '(',\n                item;\n\n            if ( m1 === 'define' ) {\n                item = path.relative( cwd, filepath );\n                item = item.substring( 0, item.length - 3 );\n                str += ' \\'' + item.replace(/\\\\/g, '/') + '\\', ';\n            }\n\n            if ( m2 ) {\n                m2 = m2.replace(/\\s/g, '').split(',');\n\n                m2 = m2.map(function( item ) {\n                    var _file = item;\n                    _file = _file.substring( 1, _file.length - 1 );\n                    _file = _file.substring(0, 1) === '.' ?\n                        path.join( dirpath, _file ) :\n                        path.join( cwd, _file.substring( 1 ) );\n\n                    if ( !grunt.file.exists( _file + '.js' ) ) {\n                        return item;\n                    }\n\n                    _file = path.relative( cwd, _file );\n                    return '\\'' + _file.replace(/\\\\/g, '/').toLowerCase() + '\\'';\n                });\n\n                if ( m2.length ) {\n                    str += '[\\n    ' + m2.join(',\\n    ') + '\\n],';\n                } else {\n                    str += '[],';\n                }\n            }\n\n            // console.log( str );\n            return str;\n        });\n\n        // 处理 require( id );\n        src = src.replace( /require\\s*\\(\\s*('|\")(.+?)\\1\\s*\\)/g, function( _, m1, m2 ) {\n            var _file = m2;\n\n            _file = _file.substring(0, 1) === '.' ?\n                path.join( dirpath, _file ) :\n                path.join( cwd, _file.substring( 1 ) );\n\n            if ( !grunt.file.exists( _file + '.js' ) ) {\n                return _;\n            }\n\n            _file = path.relative( cwd, _file );\n            return 'require(\\'' + _file + '\\')';\n        });\n\n        return src;\n    }\n\n\n    grunt.registerMultiTask('concat', 'Concatenate files.', function() {\n        // Merge task-specific and/or target-specific options with these defaults.\n        var options = this.options({\n            separator: grunt.util.linefeed,\n            banner: '',\n            footer: '',\n            stripBanners: false,\n            process: null,\n            filesFilter: filesFilter\n        });\n\n        // Normalize boolean options that accept options objects.\n        if (options.stripBanners === true) {\n            options.stripBanners = {};\n        }\n        if (options.process === true) {\n            options.process = {};\n        }\n\n        // Process banner and footer.\n        var banner = grunt.template.process(options.banner);\n        var footer = grunt.template.process(options.footer);\n\n        // Iterate over all src-dest file pairs.\n        this.files.forEach(function(f) {\n            var files = f.src;\n\n            if (typeof options.filesFilter === 'function') {\n                files = options.filesFilter( f, files );\n            }\n\n            // Concat banner + specified files + footer.\n            var cwd = f.cwd || '',\n                src = banner + files.filter(function(filepath) {\n\n                filepath = path.join( cwd, filepath );\n                // Warn on and remove invalid source files (if nonull was set).\n                if (!grunt.file.exists(filepath)) {\n                    grunt.log.warn('Source file \"' + filepath + '\" not found.');\n                    return false;\n                } else {\n                    return true;\n                }\n            }).map(function(filepath) {\n                filepath = path.join( cwd, filepath );\n\n                // Read file source.\n                var src = grunt.file.read(filepath);\n\n                // 文件处理，用来支持amdefine\n                src = fileProcess( src, filepath, cwd );\n\n                // Process files as templates if requested.\n                if (typeof options.process === 'function') {\n                    src = options.process(src, filepath);\n                } else if (options.process) {\n                    src = grunt.template.process(src, options.process);\n                }\n\n                // Strip banners if requested.\n                if (options.stripBanners) {\n                    src = stripBanner(src, options.stripBanners);\n                }\n\n                return src;\n            }).join(options.separator) + footer;\n\n            // Write the destination file.\n            grunt.file.write(f.dest, src);\n\n            // Print a success message.\n            grunt.log.writeln('File \"' + f.dest + '\" created.');\n        });\n    });\n\n};\n"
  },
  {
    "path": "build/tasks/doc.js",
    "content": "module.exports = function( grunt ) {\n    grunt.registerTask( 'doc', '生成文档', function() {\n        var Doc = require( 'gmudoc/lib/doc.js' );\n\n        var opts = this.options({\n                cwd: '',\n                files: [],\n                theme: 'gmu',\n                outputDir: './doc'\n            }),\n            done = this.async();\n\n\n        var ins = new Doc( opts );\n\n        ins.run( done );\n    });\n};"
  },
  {
    "path": "css/webuploader.css",
    "content": ".webuploader-container {\n\tposition: relative;\n}\n.webuploader-element-invisible {\n\tposition: absolute !important;\n\tclip: rect(1px 1px 1px 1px); /* IE6, IE7 */\n    clip: rect(1px,1px,1px,1px);\n}\n.webuploader-pick {\n\tposition: relative;\n\tdisplay: inline-block;\n\tcursor: pointer;\n\tbackground: #00b7ee;\n\tpadding: 10px 15px;\n\tcolor: #fff;\n\ttext-align: center;\n\tborder-radius: 3px;\n\toverflow: hidden;\n}\n.webuploader-pick-hover {\n\tbackground: #00a2d4;\n}\n\n.webuploader-pick-disable {\n\topacity: 0.6;\n\tpointer-events:none;\n}\n\n"
  },
  {
    "path": "dist/README.md",
    "content": "目录说明\n========================\n\n```bash\n├── Uploader.swf                      # SWF文件，当使用Flash运行时需要引入。\n├\n├── webuploader.js                    # 完全版本。\n├── webuploader.min.js                # min版本\n├\n├── webuploader.flashonly.js          # 只有Flash实现的版本。\n├── webuploader.flashonly.min.js      # min版本\n├\n├── webuploader.html5only.js          # 只有Html5实现的版本。\n├── webuploader.html5only.min.js      # min版本\n├\n├── webuploader.noimage.js            # 去除图片处理的版本，包括HTML5和FLASH.\n├── webuploader.noimage.min.js        # min版本\n├\n├── webuploader.custom.js             # 自定义打包方案，请查看 Gruntfile.js，满足移动端使用。\n└── webuploader.custom.min.js         # min版本\n```\n\n## 示例\n\n请把整个 Git 包下载下来放在 php 服务器下，因为默认提供的文件接受是用 php 编写的，打开 examples 页面便能查看示例效果。"
  },
  {
    "path": "dist/webuploader.css",
    "content": ".webuploader-container {\n\tposition: relative;\n}\n.webuploader-element-invisible {\n\tposition: absolute !important;\n\tclip: rect(1px 1px 1px 1px); /* IE6, IE7 */\n    clip: rect(1px,1px,1px,1px);\n}\n.webuploader-pick {\n\tposition: relative;\n\tdisplay: inline-block;\n\tcursor: pointer;\n\tbackground: #00b7ee;\n\tpadding: 10px 15px;\n\tcolor: #fff;\n\ttext-align: center;\n\tborder-radius: 3px;\n\toverflow: hidden;\n}\n.webuploader-pick-hover {\n\tbackground: #00a2d4;\n}\n\n.webuploader-pick-disable {\n\topacity: 0.6;\n\tpointer-events:none;\n}\n\n"
  },
  {
    "path": "dist/webuploader.custom.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * 直接来源于jquery的代码。\n     * @fileOverview Promise/A+\n     * @beta\n     */\n    define('promise-builtin',[\n        'dollar'\n    ], function( $ ) {\n    \n        var api;\n    \n        // 简单版Callbacks, 默认memory，可选once.\n        function Callbacks( once ) {\n            var list = [],\n                stack = !once && [],\n                fire = function( data ) {\n                    memory = data;\n                    fired = true;\n                    firingIndex = firingStart || 0;\n                    firingStart = 0;\n                    firingLength = list.length;\n                    firing = true;\n    \n                    for ( ; list && firingIndex < firingLength; firingIndex++ ) {\n                        list[ firingIndex ].apply( data[ 0 ], data[ 1 ] );\n                    }\n                    firing = false;\n    \n                    if ( list ) {\n                        if ( stack ) {\n                            stack.length && fire( stack.shift() );\n                        }  else {\n                            list = [];\n                        }\n                    }\n                },\n                self = {\n                    add: function() {\n                        if ( list ) {\n                            var start = list.length;\n                            (function add ( args ) {\n                                $.each( args, function( _, arg ) {\n                                    var type = $.type( arg );\n                                    if ( type === 'function' ) {\n                                        list.push( arg );\n                                    } else if ( arg && arg.length &&\n                                            type !== 'string' ) {\n    \n                                        add( arg );\n                                    }\n                                });\n                            })( arguments );\n    \n                            if ( firing ) {\n                                firingLength = list.length;\n                            } else if ( memory ) {\n                                firingStart = start;\n                                fire( memory );\n                            }\n                        }\n                        return this;\n                    },\n    \n                    disable: function() {\n                        list = stack = memory = undefined;\n                        return this;\n                    },\n    \n                    // Lock the list in its current state\n                    lock: function() {\n                        stack = undefined;\n                        if ( !memory ) {\n                            self.disable();\n                        }\n                        return this;\n                    },\n    \n                    fireWith: function( context, args ) {\n                        if ( list && (!fired || stack) ) {\n                            args = args || [];\n                            args = [ context, args.slice ? args.slice() : args ];\n                            if ( firing ) {\n                                stack.push( args );\n                            } else {\n                                fire( args );\n                            }\n                        }\n                        return this;\n                    },\n    \n                    fire: function() {\n                        self.fireWith( this, arguments );\n                        return this;\n                    }\n                },\n    \n                fired, firing, firingStart, firingLength, firingIndex, memory;\n    \n            return self;\n        }\n    \n        function Deferred( func ) {\n            var tuples = [\n                    // action, add listener, listener list, final state\n                    [ 'resolve', 'done', Callbacks( true ), 'resolved' ],\n                    [ 'reject', 'fail', Callbacks( true ), 'rejected' ],\n                    [ 'notify', 'progress', Callbacks() ]\n                ],\n                state = 'pending',\n                promise = {\n                    state: function() {\n                        return state;\n                    },\n                    always: function() {\n                        deferred.done( arguments ).fail( arguments );\n                        return this;\n                    },\n                    then: function( /* fnDone, fnFail, fnProgress */ ) {\n                        var fns = arguments;\n                        return Deferred(function( newDefer ) {\n                            $.each( tuples, function( i, tuple ) {\n                                var action = tuple[ 0 ],\n                                    fn = $.isFunction( fns[ i ] ) && fns[ i ];\n    \n                                // deferred[ done | fail | progress ] for\n                                // forwarding actions to newDefer\n                                deferred[ tuple[ 1 ] ](function() {\n                                    var returned;\n    \n                                    returned = fn && fn.apply( this, arguments );\n    \n                                    if ( returned &&\n                                            $.isFunction( returned.promise ) ) {\n    \n                                        returned.promise()\n                                                .done( newDefer.resolve )\n                                                .fail( newDefer.reject )\n                                                .progress( newDefer.notify );\n                                    } else {\n                                        newDefer[ action + 'With' ](\n                                                this === promise ?\n                                                newDefer.promise() :\n                                                this,\n                                                fn ? [ returned ] : arguments );\n                                    }\n                                });\n                            });\n                            fns = null;\n                        }).promise();\n                    },\n    \n                    // Get a promise for this deferred\n                    // If obj is provided, the promise aspect is added to the object\n                    promise: function( obj ) {\n    \n                        return obj != null ? $.extend( obj, promise ) : promise;\n                    }\n                },\n                deferred = {};\n    \n            // Keep pipe for back-compat\n            promise.pipe = promise.then;\n    \n            // Add list-specific methods\n            $.each( tuples, function( i, tuple ) {\n                var list = tuple[ 2 ],\n                    stateString = tuple[ 3 ];\n    \n                // promise[ done | fail | progress ] = list.add\n                promise[ tuple[ 1 ] ] = list.add;\n    \n                // Handle state\n                if ( stateString ) {\n                    list.add(function() {\n                        // state = [ resolved | rejected ]\n                        state = stateString;\n    \n                    // [ reject_list | resolve_list ].disable; progress_list.lock\n                    }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );\n                }\n    \n                // deferred[ resolve | reject | notify ]\n                deferred[ tuple[ 0 ] ] = function() {\n                    deferred[ tuple[ 0 ] + 'With' ]( this === deferred ? promise :\n                            this, arguments );\n                    return this;\n                };\n                deferred[ tuple[ 0 ] + 'With' ] = list.fireWith;\n            });\n    \n            // Make the deferred a promise\n            promise.promise( deferred );\n    \n            // Call given func if any\n            if ( func ) {\n                func.call( deferred, deferred );\n            }\n    \n            // All done!\n            return deferred;\n        }\n    \n        api = {\n            /**\n             * 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。\n             * 详细的Deferred用法说明，请参照jQuery的API文档。\n             *\n             * Deferred对象在钩子回掉函数中经常要用到，用来处理需要等待的异步操作。\n             *\n             * @for  Base\n             * @method Deferred\n             * @grammar Base.Deferred() => Deferred\n             * @example\n             * // 在文件开始发送前做些异步操作。\n             * // WebUploader会等待此异步操作完成后，开始发送文件。\n             * Uploader.register({\n             *     'before-send-file': 'doSomthingAsync'\n             * }, {\n             *\n             *     doSomthingAsync: function() {\n             *         var deferred = Base.Deferred();\n             *\n             *         // 模拟一次异步操作。\n             *         setTimeout(deferred.resolve, 2000);\n             *\n             *         return deferred.promise();\n             *     }\n             * });\n             */\n            Deferred: Deferred,\n    \n            /**\n             * 判断传入的参数是否为一个promise对象。\n             * @method isPromise\n             * @grammar Base.isPromise( anything ) => Boolean\n             * @param  {*}  anything 检测对象。\n             * @return {Boolean}\n             * @for  Base\n             * @example\n             * console.log( Base.isPromise() );    // => false\n             * console.log( Base.isPromise({ key: '123' }) );    // => false\n             * console.log( Base.isPromise( Base.Deferred().promise() ) );    // => true\n             *\n             * // Deferred也是一个Promise\n             * console.log( Base.isPromise( Base.Deferred() ) );    // => true\n             */\n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            },\n    \n            /**\n             * 返回一个promise，此promise在所有传入的promise都完成了后完成。\n             * 详细请查看[这里](http://api.jquery.com/jQuery.when/)。\n             *\n             * @method when\n             * @for  Base\n             * @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise\n             */\n            when: function( subordinate /* , ..., subordinateN */ ) {\n                var i = 0,\n                    slice = [].slice,\n                    resolveValues = slice.call( arguments ),\n                    length = resolveValues.length,\n    \n                    // the count of uncompleted subordinates\n                    remaining = length !== 1 || (subordinate &&\n                        $.isFunction( subordinate.promise )) ? length : 0,\n    \n                    // the master Deferred. If resolveValues consist of\n                    // only a single Deferred, just use that.\n                    deferred = remaining === 1 ? subordinate : Deferred(),\n    \n                    // Update function for both resolve and progress values\n                    updateFunc = function( i, contexts, values ) {\n                        return function( value ) {\n                            contexts[ i ] = this;\n                            values[ i ] = arguments.length > 1 ?\n                                    slice.call( arguments ) : value;\n    \n                            if ( values === progressValues ) {\n                                deferred.notifyWith( contexts, values );\n                            } else if ( !(--remaining) ) {\n                                deferred.resolveWith( contexts, values );\n                            }\n                        };\n                    },\n    \n                    progressValues, progressContexts, resolveContexts;\n    \n                // add listeners to Deferred subordinates; treat others as resolved\n                if ( length > 1 ) {\n                    progressValues = new Array( length );\n                    progressContexts = new Array( length );\n                    resolveContexts = new Array( length );\n                    for ( ; i < length; i++ ) {\n                        if ( resolveValues[ i ] &&\n                                $.isFunction( resolveValues[ i ].promise ) ) {\n    \n                            resolveValues[ i ].promise()\n                                    .done( updateFunc( i, resolveContexts,\n                                            resolveValues ) )\n                                    .fail( deferred.reject )\n                                    .progress( updateFunc( i, progressContexts,\n                                            progressValues ) );\n                        } else {\n                            --remaining;\n                        }\n                    }\n                }\n    \n                // if we're not waiting on anything, resolve the master\n                if ( !remaining ) {\n                    deferred.resolveWith( resolveContexts, resolveValues );\n                }\n    \n                return deferred.promise();\n            }\n        };\n    \n        return api;\n    });\n    define('promise',[\n        'promise-builtin'\n    ], function( $ ) {\n        return $;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 日志组件，主要用来收集错误信息，可以帮助 webuploader 更好的定位问题和发展。\n     *\n     * 如果您不想要启用此功能，请在打包的时候去掉 log 模块。\n     *\n     * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。\n     *\n     * 如：\n     * WebUploader.create({\n     *     ...\n     *\n     *     disableWidgets: 'log',\n     *\n     *     ...\n     * })\n     */\n    define('widgets/log',[\n        'base',\n        'uploader',\n        'widgets/widget'\n    ], function( Base, Uploader ) {\n        var $ = Base.$,\n            logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??',\n            product = (location.hostname || location.host || 'protected').toLowerCase(),\n    \n            // 只针对 baidu 内部产品用户做统计功能。\n            enable = product && /baidu/i.exec(product),\n            base;\n    \n        if (!enable) {\n            return;\n        }\n    \n        base = {\n            dv: 3,\n            master: 'webuploader',\n            online: /test/.exec(product) ? 0 : 1,\n            module: '',\n            product: product,\n            type: 0\n        };\n    \n        function send(data) {\n            var obj = $.extend({}, base, data),\n                url = logUrl.replace(/^(.*)\\?/, '$1' + $.param( obj )),\n                image = new Image();\n    \n            image.src = url;\n        }\n    \n        return Uploader.register({\n            name: 'log',\n    \n            init: function() {\n                var owner = this.owner,\n                    count = 0,\n                    size = 0;\n    \n                owner\n                    .on('error', function(code) {\n                        send({\n                            type: 2,\n                            c_error_code: code\n                        });\n                    })\n                    .on('uploadError', function(file, reason) {\n                        send({\n                            type: 2,\n                            c_error_code: 'UPLOAD_ERROR',\n                            c_reason: '' + reason\n                        });\n                    })\n                    .on('uploadComplete', function(file) {\n                        count++;\n                        size += file.size;\n                    }).\n                    on('uploadFinished', function() {\n                        send({\n                            c_count: count,\n                            c_size: size\n                        });\n                        count = size = 0;\n                    });\n    \n                send({\n                    c_usage: 1\n                });\n            }\n        });\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n     * android里面toDataUrl('image/jpege')得到的结果却是png.\n     *\n     * 所以这里没辙，只能借助这个工具\n     * @fileOverview jpeg encoder\n     */\n    define('runtime/html5/jpegencoder',[], function( require, exports, module ) {\n    \n        /*\n          Copyright (c) 2008, Adobe Systems Incorporated\n          All rights reserved.\n    \n          Redistribution and use in source and binary forms, with or without\n          modification, are permitted provided that the following conditions are\n          met:\n    \n          * Redistributions of source code must retain the above copyright notice,\n            this list of conditions and the following disclaimer.\n    \n          * Redistributions in binary form must reproduce the above copyright\n            notice, this list of conditions and the following disclaimer in the\n            documentation and/or other materials provided with the distribution.\n    \n          * Neither the name of Adobe Systems Incorporated nor the names of its\n            contributors may be used to endorse or promote products derived from\n            this software without specific prior written permission.\n    \n          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n          IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n          THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n          PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n          CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n          EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n          PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n          PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n          LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n          NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        */\n        /*\n        JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n    \n        Basic GUI blocking jpeg encoder\n        */\n    \n        function JPEGEncoder(quality) {\n          var self = this;\n            var fround = Math.round;\n            var ffloor = Math.floor;\n            var YTable = new Array(64);\n            var UVTable = new Array(64);\n            var fdtbl_Y = new Array(64);\n            var fdtbl_UV = new Array(64);\n            var YDC_HT;\n            var UVDC_HT;\n            var YAC_HT;\n            var UVAC_HT;\n    \n            var bitcode = new Array(65535);\n            var category = new Array(65535);\n            var outputfDCTQuant = new Array(64);\n            var DU = new Array(64);\n            var byteout = [];\n            var bytenew = 0;\n            var bytepos = 7;\n    \n            var YDU = new Array(64);\n            var UDU = new Array(64);\n            var VDU = new Array(64);\n            var clt = new Array(256);\n            var RGB_YUV_TABLE = new Array(2048);\n            var currentQuality;\n    \n            var ZigZag = [\n                     0, 1, 5, 6,14,15,27,28,\n                     2, 4, 7,13,16,26,29,42,\n                     3, 8,12,17,25,30,41,43,\n                     9,11,18,24,31,40,44,53,\n                    10,19,23,32,39,45,52,54,\n                    20,22,33,38,46,51,55,60,\n                    21,34,37,47,50,56,59,61,\n                    35,36,48,49,57,58,62,63\n                ];\n    \n            var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n            var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n            var std_ac_luminance_values = [\n                    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n            var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n            var std_ac_chrominance_values = [\n                    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            function initQuantTables(sf){\n                    var YQT = [\n                        16, 11, 10, 16, 24, 40, 51, 61,\n                        12, 12, 14, 19, 26, 58, 60, 55,\n                        14, 13, 16, 24, 40, 57, 69, 56,\n                        14, 17, 22, 29, 51, 87, 80, 62,\n                        18, 22, 37, 56, 68,109,103, 77,\n                        24, 35, 55, 64, 81,104,113, 92,\n                        49, 64, 78, 87,103,121,120,101,\n                        72, 92, 95, 98,112,100,103, 99\n                    ];\n    \n                    for (var i = 0; i < 64; i++) {\n                        var t = ffloor((YQT[i]*sf+50)/100);\n                        if (t < 1) {\n                            t = 1;\n                        } else if (t > 255) {\n                            t = 255;\n                        }\n                        YTable[ZigZag[i]] = t;\n                    }\n                    var UVQT = [\n                        17, 18, 24, 47, 99, 99, 99, 99,\n                        18, 21, 26, 66, 99, 99, 99, 99,\n                        24, 26, 56, 99, 99, 99, 99, 99,\n                        47, 66, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99\n                    ];\n                    for (var j = 0; j < 64; j++) {\n                        var u = ffloor((UVQT[j]*sf+50)/100);\n                        if (u < 1) {\n                            u = 1;\n                        } else if (u > 255) {\n                            u = 255;\n                        }\n                        UVTable[ZigZag[j]] = u;\n                    }\n                    var aasf = [\n                        1.0, 1.387039845, 1.306562965, 1.175875602,\n                        1.0, 0.785694958, 0.541196100, 0.275899379\n                    ];\n                    var k = 0;\n                    for (var row = 0; row < 8; row++)\n                    {\n                        for (var col = 0; col < 8; col++)\n                        {\n                            fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            k++;\n                        }\n                    }\n                }\n    \n                function computeHuffmanTbl(nrcodes, std_table){\n                    var codevalue = 0;\n                    var pos_in_table = 0;\n                    var HT = new Array();\n                    for (var k = 1; k <= 16; k++) {\n                        for (var j = 1; j <= nrcodes[k]; j++) {\n                            HT[std_table[pos_in_table]] = [];\n                            HT[std_table[pos_in_table]][0] = codevalue;\n                            HT[std_table[pos_in_table]][1] = k;\n                            pos_in_table++;\n                            codevalue++;\n                        }\n                        codevalue*=2;\n                    }\n                    return HT;\n                }\n    \n                function initHuffmanTbl()\n                {\n                    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n                }\n    \n                function initCategoryNumber()\n                {\n                    var nrlower = 1;\n                    var nrupper = 2;\n                    for (var cat = 1; cat <= 15; cat++) {\n                        //Positive numbers\n                        for (var nr = nrlower; nr<nrupper; nr++) {\n                            category[32767+nr] = cat;\n                            bitcode[32767+nr] = [];\n                            bitcode[32767+nr][1] = cat;\n                            bitcode[32767+nr][0] = nr;\n                        }\n                        //Negative numbers\n                        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                            category[32767+nrneg] = cat;\n                            bitcode[32767+nrneg] = [];\n                            bitcode[32767+nrneg][1] = cat;\n                            bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                        }\n                        nrlower <<= 1;\n                        nrupper <<= 1;\n                    }\n                }\n    \n                function initRGBYUVTable() {\n                    for(var i = 0; i < 256;i++) {\n                        RGB_YUV_TABLE[i]            =  19595 * i;\n                        RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                        RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                        RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                        RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                        RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                        RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                        RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                    }\n                }\n    \n                // IO functions\n                function writeBits(bs)\n                {\n                    var value = bs[0];\n                    var posval = bs[1]-1;\n                    while ( posval >= 0 ) {\n                        if (value & (1 << posval) ) {\n                            bytenew |= (1 << bytepos);\n                        }\n                        posval--;\n                        bytepos--;\n                        if (bytepos < 0) {\n                            if (bytenew == 0xFF) {\n                                writeByte(0xFF);\n                                writeByte(0);\n                            }\n                            else {\n                                writeByte(bytenew);\n                            }\n                            bytepos=7;\n                            bytenew=0;\n                        }\n                    }\n                }\n    \n                function writeByte(value)\n                {\n                    byteout.push(clt[value]); // write char directly instead of converting later\n                }\n    \n                function writeWord(value)\n                {\n                    writeByte((value>>8)&0xFF);\n                    writeByte((value   )&0xFF);\n                }\n    \n                // DCT & quantization core\n                function fDCTQuant(data, fdtbl)\n                {\n                    var d0, d1, d2, d3, d4, d5, d6, d7;\n                    /* Pass 1: process rows. */\n                    var dataOff=0;\n                    var i;\n                    var I8 = 8;\n                    var I64 = 64;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff+1];\n                        d2 = data[dataOff+2];\n                        d3 = data[dataOff+3];\n                        d4 = data[dataOff+4];\n                        d5 = data[dataOff+5];\n                        d6 = data[dataOff+6];\n                        d7 = data[dataOff+7];\n    \n                        var tmp0 = d0 + d7;\n                        var tmp7 = d0 - d7;\n                        var tmp1 = d1 + d6;\n                        var tmp6 = d1 - d6;\n                        var tmp2 = d2 + d5;\n                        var tmp5 = d2 - d5;\n                        var tmp3 = d3 + d4;\n                        var tmp4 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                        var tmp13 = tmp0 - tmp3;\n                        var tmp11 = tmp1 + tmp2;\n                        var tmp12 = tmp1 - tmp2;\n    \n                        data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                        data[dataOff+4] = tmp10 - tmp11;\n    \n                        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                        data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                        data[dataOff+6] = tmp13 - z1;\n    \n                        /* Odd part */\n                        tmp10 = tmp4 + tmp5; /* phase 2 */\n                        tmp11 = tmp5 + tmp6;\n                        tmp12 = tmp6 + tmp7;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                        var z3 = tmp11 * 0.707106781; /* c4 */\n    \n                        var z11 = tmp7 + z3;    /* phase 5 */\n                        var z13 = tmp7 - z3;\n    \n                        data[dataOff+5] = z13 + z2; /* phase 6 */\n                        data[dataOff+3] = z13 - z2;\n                        data[dataOff+1] = z11 + z4;\n                        data[dataOff+7] = z11 - z4;\n    \n                        dataOff += 8; /* advance pointer to next row */\n                    }\n    \n                    /* Pass 2: process columns. */\n                    dataOff = 0;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff + 8];\n                        d2 = data[dataOff + 16];\n                        d3 = data[dataOff + 24];\n                        d4 = data[dataOff + 32];\n                        d5 = data[dataOff + 40];\n                        d6 = data[dataOff + 48];\n                        d7 = data[dataOff + 56];\n    \n                        var tmp0p2 = d0 + d7;\n                        var tmp7p2 = d0 - d7;\n                        var tmp1p2 = d1 + d6;\n                        var tmp6p2 = d1 - d6;\n                        var tmp2p2 = d2 + d5;\n                        var tmp5p2 = d2 - d5;\n                        var tmp3p2 = d3 + d4;\n                        var tmp4p2 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                        var tmp13p2 = tmp0p2 - tmp3p2;\n                        var tmp11p2 = tmp1p2 + tmp2p2;\n                        var tmp12p2 = tmp1p2 - tmp2p2;\n    \n                        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                        data[dataOff+32] = tmp10p2 - tmp11p2;\n    \n                        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                        data[dataOff+48] = tmp13p2 - z1p2;\n    \n                        /* Odd part */\n                        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                        tmp11p2 = tmp5p2 + tmp6p2;\n                        tmp12p2 = tmp6p2 + tmp7p2;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                        var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n    \n                        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                        var z13p2 = tmp7p2 - z3p2;\n    \n                        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                        data[dataOff+24] = z13p2 - z2p2;\n                        data[dataOff+ 8] = z11p2 + z4p2;\n                        data[dataOff+56] = z11p2 - z4p2;\n    \n                        dataOff++; /* advance pointer to next column */\n                    }\n    \n                    // Quantize/descale the coefficients\n                    var fDCTQuant;\n                    for (i=0; i<I64; ++i)\n                    {\n                        // Apply the quantization and scaling factor & Round to nearest integer\n                        fDCTQuant = data[i]*fdtbl[i];\n                        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                        //outputfDCTQuant[i] = fround(fDCTQuant);\n    \n                    }\n                    return outputfDCTQuant;\n                }\n    \n                function writeAPP0()\n                {\n                    writeWord(0xFFE0); // marker\n                    writeWord(16); // length\n                    writeByte(0x4A); // J\n                    writeByte(0x46); // F\n                    writeByte(0x49); // I\n                    writeByte(0x46); // F\n                    writeByte(0); // = \"JFIF\",'\\0'\n                    writeByte(1); // versionhi\n                    writeByte(1); // versionlo\n                    writeByte(0); // xyunits\n                    writeWord(1); // xdensity\n                    writeWord(1); // ydensity\n                    writeByte(0); // thumbnwidth\n                    writeByte(0); // thumbnheight\n                }\n    \n                function writeSOF0(width, height)\n                {\n                    writeWord(0xFFC0); // marker\n                    writeWord(17);   // length, truecolor YUV JPG\n                    writeByte(8);    // precision\n                    writeWord(height);\n                    writeWord(width);\n                    writeByte(3);    // nrofcomponents\n                    writeByte(1);    // IdY\n                    writeByte(0x11); // HVY\n                    writeByte(0);    // QTY\n                    writeByte(2);    // IdU\n                    writeByte(0x11); // HVU\n                    writeByte(1);    // QTU\n                    writeByte(3);    // IdV\n                    writeByte(0x11); // HVV\n                    writeByte(1);    // QTV\n                }\n    \n                function writeDQT()\n                {\n                    writeWord(0xFFDB); // marker\n                    writeWord(132);    // length\n                    writeByte(0);\n                    for (var i=0; i<64; i++) {\n                        writeByte(YTable[i]);\n                    }\n                    writeByte(1);\n                    for (var j=0; j<64; j++) {\n                        writeByte(UVTable[j]);\n                    }\n                }\n    \n                function writeDHT()\n                {\n                    writeWord(0xFFC4); // marker\n                    writeWord(0x01A2); // length\n    \n                    writeByte(0); // HTYDCinfo\n                    for (var i=0; i<16; i++) {\n                        writeByte(std_dc_luminance_nrcodes[i+1]);\n                    }\n                    for (var j=0; j<=11; j++) {\n                        writeByte(std_dc_luminance_values[j]);\n                    }\n    \n                    writeByte(0x10); // HTYACinfo\n                    for (var k=0; k<16; k++) {\n                        writeByte(std_ac_luminance_nrcodes[k+1]);\n                    }\n                    for (var l=0; l<=161; l++) {\n                        writeByte(std_ac_luminance_values[l]);\n                    }\n    \n                    writeByte(1); // HTUDCinfo\n                    for (var m=0; m<16; m++) {\n                        writeByte(std_dc_chrominance_nrcodes[m+1]);\n                    }\n                    for (var n=0; n<=11; n++) {\n                        writeByte(std_dc_chrominance_values[n]);\n                    }\n    \n                    writeByte(0x11); // HTUACinfo\n                    for (var o=0; o<16; o++) {\n                        writeByte(std_ac_chrominance_nrcodes[o+1]);\n                    }\n                    for (var p=0; p<=161; p++) {\n                        writeByte(std_ac_chrominance_values[p]);\n                    }\n                }\n    \n                function writeSOS()\n                {\n                    writeWord(0xFFDA); // marker\n                    writeWord(12); // length\n                    writeByte(3); // nrofcomponents\n                    writeByte(1); // IdY\n                    writeByte(0); // HTY\n                    writeByte(2); // IdU\n                    writeByte(0x11); // HTU\n                    writeByte(3); // IdV\n                    writeByte(0x11); // HTV\n                    writeByte(0); // Ss\n                    writeByte(0x3f); // Se\n                    writeByte(0); // Bf\n                }\n    \n                function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                    var EOB = HTAC[0x00];\n                    var M16zeroes = HTAC[0xF0];\n                    var pos;\n                    var I16 = 16;\n                    var I63 = 63;\n                    var I64 = 64;\n                    var DU_DCT = fDCTQuant(CDU, fdtbl);\n                    //ZigZag reorder\n                    for (var j=0;j<I64;++j) {\n                        DU[ZigZag[j]]=DU_DCT[j];\n                    }\n                    var Diff = DU[0] - DC; DC = DU[0];\n                    //Encode DC\n                    if (Diff==0) {\n                        writeBits(HTDC[0]); // Diff might be 0\n                    } else {\n                        pos = 32767+Diff;\n                        writeBits(HTDC[category[pos]]);\n                        writeBits(bitcode[pos]);\n                    }\n                    //Encode ACs\n                    var end0pos = 63; // was const... which is crazy\n                    for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                    //end0pos = first element in reverse order !=0\n                    if ( end0pos == 0) {\n                        writeBits(EOB);\n                        return DC;\n                    }\n                    var i = 1;\n                    var lng;\n                    while ( i <= end0pos ) {\n                        var startpos = i;\n                        for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                        var nrzeroes = i-startpos;\n                        if ( nrzeroes >= I16 ) {\n                            lng = nrzeroes>>4;\n                            for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                                writeBits(M16zeroes);\n                            nrzeroes = nrzeroes&0xF;\n                        }\n                        pos = 32767+DU[i];\n                        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                        writeBits(bitcode[pos]);\n                        i++;\n                    }\n                    if ( end0pos != I63 ) {\n                        writeBits(EOB);\n                    }\n                    return DC;\n                }\n    \n                function initCharLookupTable(){\n                    var sfcc = String.fromCharCode;\n                    for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                        clt[i] = sfcc(i);\n                    }\n                }\n    \n                this.encode = function(image,quality) // image data object\n                {\n                    // var time_start = new Date().getTime();\n    \n                    if(quality) setQuality(quality);\n    \n                    // Initialize bit writer\n                    byteout = new Array();\n                    bytenew=0;\n                    bytepos=7;\n    \n                    // Add JPEG headers\n                    writeWord(0xFFD8); // SOI\n                    writeAPP0();\n                    writeDQT();\n                    writeSOF0(image.width,image.height);\n                    writeDHT();\n                    writeSOS();\n    \n    \n                    // Encode 8x8 macroblocks\n                    var DCY=0;\n                    var DCU=0;\n                    var DCV=0;\n    \n                    bytenew=0;\n                    bytepos=7;\n    \n    \n                    this.encode.displayName = \"_encode_\";\n    \n                    var imageData = image.data;\n                    var width = image.width;\n                    var height = image.height;\n    \n                    var quadWidth = width*4;\n                    var tripleWidth = width*3;\n    \n                    var x, y = 0;\n                    var r, g, b;\n                    var start,p, col,row,pos;\n                    while(y < height){\n                        x = 0;\n                        while(x < quadWidth){\n                        start = quadWidth * y + x;\n                        p = start;\n                        col = -1;\n                        row = 0;\n    \n                        for(pos=0; pos < 64; pos++){\n                            row = pos >> 3;// /8\n                            col = ( pos & 7 ) * 4; // %8\n                            p = start + ( row * quadWidth ) + col;\n    \n                            if(y+row >= height){ // padding bottom\n                                p-= (quadWidth*(y+1+row-height));\n                            }\n    \n                            if(x+col >= quadWidth){ // padding right\n                                p-= ((x+col) - quadWidth +4)\n                            }\n    \n                            r = imageData[ p++ ];\n                            g = imageData[ p++ ];\n                            b = imageData[ p++ ];\n    \n    \n                            /* // calculate YUV values dynamically\n                            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                            */\n    \n                            // use lookup table (slightly faster)\n                            YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                            UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n    \n                        }\n    \n                        DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                        DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                        DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                        x+=32;\n                        }\n                        y+=8;\n                    }\n    \n    \n                    ////////////////////////////////////////////////////////////////\n    \n                    // Do the bit alignment of the EOI marker\n                    if ( bytepos >= 0 ) {\n                        var fillbits = [];\n                        fillbits[1] = bytepos+1;\n                        fillbits[0] = (1<<(bytepos+1))-1;\n                        writeBits(fillbits);\n                    }\n    \n                    writeWord(0xFFD9); //EOI\n    \n                    var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n    \n                    byteout = [];\n    \n                    // benchmarking\n                    // var duration = new Date().getTime() - time_start;\n                    // console.log('Encoding time: '+ currentQuality + 'ms');\n                    //\n    \n                    return jpegDataUri\n            }\n    \n            function setQuality(quality){\n                if (quality <= 0) {\n                    quality = 1;\n                }\n                if (quality > 100) {\n                    quality = 100;\n                }\n    \n                if(currentQuality == quality) return // don't recalc if unchanged\n    \n                var sf = 0;\n                if (quality < 50) {\n                    sf = Math.floor(5000 / quality);\n                } else {\n                    sf = Math.floor(200 - quality*2);\n                }\n    \n                initQuantTables(sf);\n                currentQuality = quality;\n                // console.log('Quality set to: '+quality +'%');\n            }\n    \n            function init(){\n                // var time_start = new Date().getTime();\n                if(!quality) quality = 50;\n                // Create tables\n                initCharLookupTable()\n                initHuffmanTbl();\n                initCategoryNumber();\n                initRGBYUVTable();\n    \n                setQuality(quality);\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Initialization '+ duration + 'ms');\n            }\n    \n            init();\n    \n        };\n    \n        JPEGEncoder.encode = function( data, quality ) {\n            var encoder = new JPEGEncoder( quality );\n    \n            return encoder.encode( data );\n        }\n    \n        return JPEGEncoder;\n    });\n    /**\n     * @fileOverview Fix android canvas.toDataUrl bug.\n     */\n    define('runtime/html5/androidpatch',[\n        'runtime/html5/util',\n        'runtime/html5/jpegencoder',\n        'base'\n    ], function( Util, encoder, Base ) {\n        var origin = Util.canvasToDataUrl,\n            supportJpeg;\n    \n        Util.canvasToDataUrl = function( canvas, type, quality ) {\n            var ctx, w, h, fragement, parts;\n    \n            // 非android手机直接跳过。\n            if ( !Base.os.android ) {\n                return origin.apply( null, arguments );\n            }\n    \n            // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n            // JPEG 前两位分别是：255, 216\n            if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n                fragement = origin.apply( null, arguments );\n    \n                parts = fragement.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    fragement = atob( parts[ 1 ] );\n                } else {\n                    fragement = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                fragement = fragement.substring( 0, 2 );\n    \n                supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                        fragement.charCodeAt( 1 ) === 216;\n            }\n    \n            // 只有在android环境下才修复\n            if ( type === 'image/jpeg' && !supportJpeg ) {\n                w = canvas.width;\n                h = canvas.height;\n                ctx = canvas.getContext('2d');\n    \n                return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n            }\n    \n            return origin.apply( null, arguments );\n        };\n    });\n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    define('webuploader',[\n        'base',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/log',\n        'runtime/html5/blob',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/image',\n        'runtime/html5/androidpatch',\n        'runtime/html5/transport'\n    ], function( Base ) {\n        return Base;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.fis.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\nvar jQuery = require('example:widget/ui/jquery/jquery.js');\n\nmodule.exports = (function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        };\n\n    return makeExport( jQuery );\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Md5\n     */\n    define('lib/md5',[\n        'runtime/client',\n        'mediator'\n    ], function( RuntimeClient, Mediator ) {\n    \n        function Md5() {\n            RuntimeClient.call( this, 'Md5' );\n        }\n    \n        // 让 Md5 具备事件功能。\n        Mediator.installTo( Md5.prototype );\n    \n        Md5.prototype.loadFromBlob = function( blob ) {\n            var me = this;\n    \n            if ( me.getRuid() ) {\n                me.disconnectRuntime();\n            }\n    \n            // 连接到blob归属的同一个runtime.\n            me.connectRuntime( blob.ruid, function() {\n                me.exec('init');\n                me.exec( 'loadFromBlob', blob );\n            });\n        };\n    \n        Md5.prototype.getResult = function() {\n            return this.exec('getResult');\n        };\n    \n        return Md5;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/md5',[\n        'base',\n        'uploader',\n        'lib/md5',\n        'lib/blob',\n        'widgets/widget'\n    ], function( Base, Uploader, Md5, Blob ) {\n    \n        return Uploader.register({\n            name: 'md5',\n    \n    \n            /**\n             * 计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。\n             *\n             *\n             * @method md5File\n             * @grammar md5File( file[, start[, end]] ) => promise\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.md5File( file )\n             *\n             *         // 及时显示进度\n             *         .progress(function(percentage) {\n             *             console.log('Percentage:', percentage);\n             *         })\n             *\n             *         // 完成\n             *         .then(function(val) {\n             *             console.log('md5 result:', val);\n             *         });\n             *\n             * });\n             */\n            md5File: function( file, start, end ) {\n                var md5 = new Md5(),\n                    deferred = Base.Deferred(),\n                    blob = (file instanceof Blob) ? file :\n                        this.request( 'get-file', file ).source;\n    \n                md5.on( 'progress load', function( e ) {\n                    e = e || {};\n                    deferred.notify( e.total ? e.loaded / e.total : 1 );\n                });\n    \n                md5.on( 'complete', function() {\n                    deferred.resolve( md5.getResult() );\n                });\n    \n                md5.on( 'error', function( reason ) {\n                    deferred.reject( reason );\n                });\n    \n                if ( arguments.length > 1 ) {\n                    start = start || 0;\n                    end = end || 0;\n                    start < 0 && (start = blob.size + start);\n                    end < 0 && (end = blob.size + end);\n                    end = Math.min( end, blob.size );\n                    blob = blob.slice( start, end );\n                }\n    \n                md5.loadFromBlob( blob );\n    \n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n     * android里面toDataUrl('image/jpege')得到的结果却是png.\n     *\n     * 所以这里没辙，只能借助这个工具\n     * @fileOverview jpeg encoder\n     */\n    define('runtime/html5/jpegencoder',[], function( require, exports, module ) {\n    \n        /*\n          Copyright (c) 2008, Adobe Systems Incorporated\n          All rights reserved.\n    \n          Redistribution and use in source and binary forms, with or without\n          modification, are permitted provided that the following conditions are\n          met:\n    \n          * Redistributions of source code must retain the above copyright notice,\n            this list of conditions and the following disclaimer.\n    \n          * Redistributions in binary form must reproduce the above copyright\n            notice, this list of conditions and the following disclaimer in the\n            documentation and/or other materials provided with the distribution.\n    \n          * Neither the name of Adobe Systems Incorporated nor the names of its\n            contributors may be used to endorse or promote products derived from\n            this software without specific prior written permission.\n    \n          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n          IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n          THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n          PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n          CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n          EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n          PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n          PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n          LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n          NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        */\n        /*\n        JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n    \n        Basic GUI blocking jpeg encoder\n        */\n    \n        function JPEGEncoder(quality) {\n          var self = this;\n            var fround = Math.round;\n            var ffloor = Math.floor;\n            var YTable = new Array(64);\n            var UVTable = new Array(64);\n            var fdtbl_Y = new Array(64);\n            var fdtbl_UV = new Array(64);\n            var YDC_HT;\n            var UVDC_HT;\n            var YAC_HT;\n            var UVAC_HT;\n    \n            var bitcode = new Array(65535);\n            var category = new Array(65535);\n            var outputfDCTQuant = new Array(64);\n            var DU = new Array(64);\n            var byteout = [];\n            var bytenew = 0;\n            var bytepos = 7;\n    \n            var YDU = new Array(64);\n            var UDU = new Array(64);\n            var VDU = new Array(64);\n            var clt = new Array(256);\n            var RGB_YUV_TABLE = new Array(2048);\n            var currentQuality;\n    \n            var ZigZag = [\n                     0, 1, 5, 6,14,15,27,28,\n                     2, 4, 7,13,16,26,29,42,\n                     3, 8,12,17,25,30,41,43,\n                     9,11,18,24,31,40,44,53,\n                    10,19,23,32,39,45,52,54,\n                    20,22,33,38,46,51,55,60,\n                    21,34,37,47,50,56,59,61,\n                    35,36,48,49,57,58,62,63\n                ];\n    \n            var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n            var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n            var std_ac_luminance_values = [\n                    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n            var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n            var std_ac_chrominance_values = [\n                    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            function initQuantTables(sf){\n                    var YQT = [\n                        16, 11, 10, 16, 24, 40, 51, 61,\n                        12, 12, 14, 19, 26, 58, 60, 55,\n                        14, 13, 16, 24, 40, 57, 69, 56,\n                        14, 17, 22, 29, 51, 87, 80, 62,\n                        18, 22, 37, 56, 68,109,103, 77,\n                        24, 35, 55, 64, 81,104,113, 92,\n                        49, 64, 78, 87,103,121,120,101,\n                        72, 92, 95, 98,112,100,103, 99\n                    ];\n    \n                    for (var i = 0; i < 64; i++) {\n                        var t = ffloor((YQT[i]*sf+50)/100);\n                        if (t < 1) {\n                            t = 1;\n                        } else if (t > 255) {\n                            t = 255;\n                        }\n                        YTable[ZigZag[i]] = t;\n                    }\n                    var UVQT = [\n                        17, 18, 24, 47, 99, 99, 99, 99,\n                        18, 21, 26, 66, 99, 99, 99, 99,\n                        24, 26, 56, 99, 99, 99, 99, 99,\n                        47, 66, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99\n                    ];\n                    for (var j = 0; j < 64; j++) {\n                        var u = ffloor((UVQT[j]*sf+50)/100);\n                        if (u < 1) {\n                            u = 1;\n                        } else if (u > 255) {\n                            u = 255;\n                        }\n                        UVTable[ZigZag[j]] = u;\n                    }\n                    var aasf = [\n                        1.0, 1.387039845, 1.306562965, 1.175875602,\n                        1.0, 0.785694958, 0.541196100, 0.275899379\n                    ];\n                    var k = 0;\n                    for (var row = 0; row < 8; row++)\n                    {\n                        for (var col = 0; col < 8; col++)\n                        {\n                            fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            k++;\n                        }\n                    }\n                }\n    \n                function computeHuffmanTbl(nrcodes, std_table){\n                    var codevalue = 0;\n                    var pos_in_table = 0;\n                    var HT = new Array();\n                    for (var k = 1; k <= 16; k++) {\n                        for (var j = 1; j <= nrcodes[k]; j++) {\n                            HT[std_table[pos_in_table]] = [];\n                            HT[std_table[pos_in_table]][0] = codevalue;\n                            HT[std_table[pos_in_table]][1] = k;\n                            pos_in_table++;\n                            codevalue++;\n                        }\n                        codevalue*=2;\n                    }\n                    return HT;\n                }\n    \n                function initHuffmanTbl()\n                {\n                    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n                }\n    \n                function initCategoryNumber()\n                {\n                    var nrlower = 1;\n                    var nrupper = 2;\n                    for (var cat = 1; cat <= 15; cat++) {\n                        //Positive numbers\n                        for (var nr = nrlower; nr<nrupper; nr++) {\n                            category[32767+nr] = cat;\n                            bitcode[32767+nr] = [];\n                            bitcode[32767+nr][1] = cat;\n                            bitcode[32767+nr][0] = nr;\n                        }\n                        //Negative numbers\n                        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                            category[32767+nrneg] = cat;\n                            bitcode[32767+nrneg] = [];\n                            bitcode[32767+nrneg][1] = cat;\n                            bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                        }\n                        nrlower <<= 1;\n                        nrupper <<= 1;\n                    }\n                }\n    \n                function initRGBYUVTable() {\n                    for(var i = 0; i < 256;i++) {\n                        RGB_YUV_TABLE[i]            =  19595 * i;\n                        RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                        RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                        RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                        RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                        RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                        RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                        RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                    }\n                }\n    \n                // IO functions\n                function writeBits(bs)\n                {\n                    var value = bs[0];\n                    var posval = bs[1]-1;\n                    while ( posval >= 0 ) {\n                        if (value & (1 << posval) ) {\n                            bytenew |= (1 << bytepos);\n                        }\n                        posval--;\n                        bytepos--;\n                        if (bytepos < 0) {\n                            if (bytenew == 0xFF) {\n                                writeByte(0xFF);\n                                writeByte(0);\n                            }\n                            else {\n                                writeByte(bytenew);\n                            }\n                            bytepos=7;\n                            bytenew=0;\n                        }\n                    }\n                }\n    \n                function writeByte(value)\n                {\n                    byteout.push(clt[value]); // write char directly instead of converting later\n                }\n    \n                function writeWord(value)\n                {\n                    writeByte((value>>8)&0xFF);\n                    writeByte((value   )&0xFF);\n                }\n    \n                // DCT & quantization core\n                function fDCTQuant(data, fdtbl)\n                {\n                    var d0, d1, d2, d3, d4, d5, d6, d7;\n                    /* Pass 1: process rows. */\n                    var dataOff=0;\n                    var i;\n                    var I8 = 8;\n                    var I64 = 64;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff+1];\n                        d2 = data[dataOff+2];\n                        d3 = data[dataOff+3];\n                        d4 = data[dataOff+4];\n                        d5 = data[dataOff+5];\n                        d6 = data[dataOff+6];\n                        d7 = data[dataOff+7];\n    \n                        var tmp0 = d0 + d7;\n                        var tmp7 = d0 - d7;\n                        var tmp1 = d1 + d6;\n                        var tmp6 = d1 - d6;\n                        var tmp2 = d2 + d5;\n                        var tmp5 = d2 - d5;\n                        var tmp3 = d3 + d4;\n                        var tmp4 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                        var tmp13 = tmp0 - tmp3;\n                        var tmp11 = tmp1 + tmp2;\n                        var tmp12 = tmp1 - tmp2;\n    \n                        data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                        data[dataOff+4] = tmp10 - tmp11;\n    \n                        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                        data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                        data[dataOff+6] = tmp13 - z1;\n    \n                        /* Odd part */\n                        tmp10 = tmp4 + tmp5; /* phase 2 */\n                        tmp11 = tmp5 + tmp6;\n                        tmp12 = tmp6 + tmp7;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                        var z3 = tmp11 * 0.707106781; /* c4 */\n    \n                        var z11 = tmp7 + z3;    /* phase 5 */\n                        var z13 = tmp7 - z3;\n    \n                        data[dataOff+5] = z13 + z2; /* phase 6 */\n                        data[dataOff+3] = z13 - z2;\n                        data[dataOff+1] = z11 + z4;\n                        data[dataOff+7] = z11 - z4;\n    \n                        dataOff += 8; /* advance pointer to next row */\n                    }\n    \n                    /* Pass 2: process columns. */\n                    dataOff = 0;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff + 8];\n                        d2 = data[dataOff + 16];\n                        d3 = data[dataOff + 24];\n                        d4 = data[dataOff + 32];\n                        d5 = data[dataOff + 40];\n                        d6 = data[dataOff + 48];\n                        d7 = data[dataOff + 56];\n    \n                        var tmp0p2 = d0 + d7;\n                        var tmp7p2 = d0 - d7;\n                        var tmp1p2 = d1 + d6;\n                        var tmp6p2 = d1 - d6;\n                        var tmp2p2 = d2 + d5;\n                        var tmp5p2 = d2 - d5;\n                        var tmp3p2 = d3 + d4;\n                        var tmp4p2 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                        var tmp13p2 = tmp0p2 - tmp3p2;\n                        var tmp11p2 = tmp1p2 + tmp2p2;\n                        var tmp12p2 = tmp1p2 - tmp2p2;\n    \n                        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                        data[dataOff+32] = tmp10p2 - tmp11p2;\n    \n                        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                        data[dataOff+48] = tmp13p2 - z1p2;\n    \n                        /* Odd part */\n                        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                        tmp11p2 = tmp5p2 + tmp6p2;\n                        tmp12p2 = tmp6p2 + tmp7p2;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                        var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n    \n                        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                        var z13p2 = tmp7p2 - z3p2;\n    \n                        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                        data[dataOff+24] = z13p2 - z2p2;\n                        data[dataOff+ 8] = z11p2 + z4p2;\n                        data[dataOff+56] = z11p2 - z4p2;\n    \n                        dataOff++; /* advance pointer to next column */\n                    }\n    \n                    // Quantize/descale the coefficients\n                    var fDCTQuant;\n                    for (i=0; i<I64; ++i)\n                    {\n                        // Apply the quantization and scaling factor & Round to nearest integer\n                        fDCTQuant = data[i]*fdtbl[i];\n                        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                        //outputfDCTQuant[i] = fround(fDCTQuant);\n    \n                    }\n                    return outputfDCTQuant;\n                }\n    \n                function writeAPP0()\n                {\n                    writeWord(0xFFE0); // marker\n                    writeWord(16); // length\n                    writeByte(0x4A); // J\n                    writeByte(0x46); // F\n                    writeByte(0x49); // I\n                    writeByte(0x46); // F\n                    writeByte(0); // = \"JFIF\",'\\0'\n                    writeByte(1); // versionhi\n                    writeByte(1); // versionlo\n                    writeByte(0); // xyunits\n                    writeWord(1); // xdensity\n                    writeWord(1); // ydensity\n                    writeByte(0); // thumbnwidth\n                    writeByte(0); // thumbnheight\n                }\n    \n                function writeSOF0(width, height)\n                {\n                    writeWord(0xFFC0); // marker\n                    writeWord(17);   // length, truecolor YUV JPG\n                    writeByte(8);    // precision\n                    writeWord(height);\n                    writeWord(width);\n                    writeByte(3);    // nrofcomponents\n                    writeByte(1);    // IdY\n                    writeByte(0x11); // HVY\n                    writeByte(0);    // QTY\n                    writeByte(2);    // IdU\n                    writeByte(0x11); // HVU\n                    writeByte(1);    // QTU\n                    writeByte(3);    // IdV\n                    writeByte(0x11); // HVV\n                    writeByte(1);    // QTV\n                }\n    \n                function writeDQT()\n                {\n                    writeWord(0xFFDB); // marker\n                    writeWord(132);    // length\n                    writeByte(0);\n                    for (var i=0; i<64; i++) {\n                        writeByte(YTable[i]);\n                    }\n                    writeByte(1);\n                    for (var j=0; j<64; j++) {\n                        writeByte(UVTable[j]);\n                    }\n                }\n    \n                function writeDHT()\n                {\n                    writeWord(0xFFC4); // marker\n                    writeWord(0x01A2); // length\n    \n                    writeByte(0); // HTYDCinfo\n                    for (var i=0; i<16; i++) {\n                        writeByte(std_dc_luminance_nrcodes[i+1]);\n                    }\n                    for (var j=0; j<=11; j++) {\n                        writeByte(std_dc_luminance_values[j]);\n                    }\n    \n                    writeByte(0x10); // HTYACinfo\n                    for (var k=0; k<16; k++) {\n                        writeByte(std_ac_luminance_nrcodes[k+1]);\n                    }\n                    for (var l=0; l<=161; l++) {\n                        writeByte(std_ac_luminance_values[l]);\n                    }\n    \n                    writeByte(1); // HTUDCinfo\n                    for (var m=0; m<16; m++) {\n                        writeByte(std_dc_chrominance_nrcodes[m+1]);\n                    }\n                    for (var n=0; n<=11; n++) {\n                        writeByte(std_dc_chrominance_values[n]);\n                    }\n    \n                    writeByte(0x11); // HTUACinfo\n                    for (var o=0; o<16; o++) {\n                        writeByte(std_ac_chrominance_nrcodes[o+1]);\n                    }\n                    for (var p=0; p<=161; p++) {\n                        writeByte(std_ac_chrominance_values[p]);\n                    }\n                }\n    \n                function writeSOS()\n                {\n                    writeWord(0xFFDA); // marker\n                    writeWord(12); // length\n                    writeByte(3); // nrofcomponents\n                    writeByte(1); // IdY\n                    writeByte(0); // HTY\n                    writeByte(2); // IdU\n                    writeByte(0x11); // HTU\n                    writeByte(3); // IdV\n                    writeByte(0x11); // HTV\n                    writeByte(0); // Ss\n                    writeByte(0x3f); // Se\n                    writeByte(0); // Bf\n                }\n    \n                function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                    var EOB = HTAC[0x00];\n                    var M16zeroes = HTAC[0xF0];\n                    var pos;\n                    var I16 = 16;\n                    var I63 = 63;\n                    var I64 = 64;\n                    var DU_DCT = fDCTQuant(CDU, fdtbl);\n                    //ZigZag reorder\n                    for (var j=0;j<I64;++j) {\n                        DU[ZigZag[j]]=DU_DCT[j];\n                    }\n                    var Diff = DU[0] - DC; DC = DU[0];\n                    //Encode DC\n                    if (Diff==0) {\n                        writeBits(HTDC[0]); // Diff might be 0\n                    } else {\n                        pos = 32767+Diff;\n                        writeBits(HTDC[category[pos]]);\n                        writeBits(bitcode[pos]);\n                    }\n                    //Encode ACs\n                    var end0pos = 63; // was const... which is crazy\n                    for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                    //end0pos = first element in reverse order !=0\n                    if ( end0pos == 0) {\n                        writeBits(EOB);\n                        return DC;\n                    }\n                    var i = 1;\n                    var lng;\n                    while ( i <= end0pos ) {\n                        var startpos = i;\n                        for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                        var nrzeroes = i-startpos;\n                        if ( nrzeroes >= I16 ) {\n                            lng = nrzeroes>>4;\n                            for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                                writeBits(M16zeroes);\n                            nrzeroes = nrzeroes&0xF;\n                        }\n                        pos = 32767+DU[i];\n                        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                        writeBits(bitcode[pos]);\n                        i++;\n                    }\n                    if ( end0pos != I63 ) {\n                        writeBits(EOB);\n                    }\n                    return DC;\n                }\n    \n                function initCharLookupTable(){\n                    var sfcc = String.fromCharCode;\n                    for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                        clt[i] = sfcc(i);\n                    }\n                }\n    \n                this.encode = function(image,quality) // image data object\n                {\n                    // var time_start = new Date().getTime();\n    \n                    if(quality) setQuality(quality);\n    \n                    // Initialize bit writer\n                    byteout = new Array();\n                    bytenew=0;\n                    bytepos=7;\n    \n                    // Add JPEG headers\n                    writeWord(0xFFD8); // SOI\n                    writeAPP0();\n                    writeDQT();\n                    writeSOF0(image.width,image.height);\n                    writeDHT();\n                    writeSOS();\n    \n    \n                    // Encode 8x8 macroblocks\n                    var DCY=0;\n                    var DCU=0;\n                    var DCV=0;\n    \n                    bytenew=0;\n                    bytepos=7;\n    \n    \n                    this.encode.displayName = \"_encode_\";\n    \n                    var imageData = image.data;\n                    var width = image.width;\n                    var height = image.height;\n    \n                    var quadWidth = width*4;\n                    var tripleWidth = width*3;\n    \n                    var x, y = 0;\n                    var r, g, b;\n                    var start,p, col,row,pos;\n                    while(y < height){\n                        x = 0;\n                        while(x < quadWidth){\n                        start = quadWidth * y + x;\n                        p = start;\n                        col = -1;\n                        row = 0;\n    \n                        for(pos=0; pos < 64; pos++){\n                            row = pos >> 3;// /8\n                            col = ( pos & 7 ) * 4; // %8\n                            p = start + ( row * quadWidth ) + col;\n    \n                            if(y+row >= height){ // padding bottom\n                                p-= (quadWidth*(y+1+row-height));\n                            }\n    \n                            if(x+col >= quadWidth){ // padding right\n                                p-= ((x+col) - quadWidth +4)\n                            }\n    \n                            r = imageData[ p++ ];\n                            g = imageData[ p++ ];\n                            b = imageData[ p++ ];\n    \n    \n                            /* // calculate YUV values dynamically\n                            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                            */\n    \n                            // use lookup table (slightly faster)\n                            YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                            UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n    \n                        }\n    \n                        DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                        DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                        DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                        x+=32;\n                        }\n                        y+=8;\n                    }\n    \n    \n                    ////////////////////////////////////////////////////////////////\n    \n                    // Do the bit alignment of the EOI marker\n                    if ( bytepos >= 0 ) {\n                        var fillbits = [];\n                        fillbits[1] = bytepos+1;\n                        fillbits[0] = (1<<(bytepos+1))-1;\n                        writeBits(fillbits);\n                    }\n    \n                    writeWord(0xFFD9); //EOI\n    \n                    var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n    \n                    byteout = [];\n    \n                    // benchmarking\n                    // var duration = new Date().getTime() - time_start;\n                    // console.log('Encoding time: '+ currentQuality + 'ms');\n                    //\n    \n                    return jpegDataUri\n            }\n    \n            function setQuality(quality){\n                if (quality <= 0) {\n                    quality = 1;\n                }\n                if (quality > 100) {\n                    quality = 100;\n                }\n    \n                if(currentQuality == quality) return // don't recalc if unchanged\n    \n                var sf = 0;\n                if (quality < 50) {\n                    sf = Math.floor(5000 / quality);\n                } else {\n                    sf = Math.floor(200 - quality*2);\n                }\n    \n                initQuantTables(sf);\n                currentQuality = quality;\n                // console.log('Quality set to: '+quality +'%');\n            }\n    \n            function init(){\n                // var time_start = new Date().getTime();\n                if(!quality) quality = 50;\n                // Create tables\n                initCharLookupTable()\n                initHuffmanTbl();\n                initCategoryNumber();\n                initRGBYUVTable();\n    \n                setQuality(quality);\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Initialization '+ duration + 'ms');\n            }\n    \n            init();\n    \n        };\n    \n        JPEGEncoder.encode = function( data, quality ) {\n            var encoder = new JPEGEncoder( quality );\n    \n            return encoder.encode( data );\n        }\n    \n        return JPEGEncoder;\n    });\n    /**\n     * @fileOverview Fix android canvas.toDataUrl bug.\n     */\n    define('runtime/html5/androidpatch',[\n        'runtime/html5/util',\n        'runtime/html5/jpegencoder',\n        'base'\n    ], function( Util, encoder, Base ) {\n        var origin = Util.canvasToDataUrl,\n            supportJpeg;\n    \n        Util.canvasToDataUrl = function( canvas, type, quality ) {\n            var ctx, w, h, fragement, parts;\n    \n            // 非android手机直接跳过。\n            if ( !Base.os.android ) {\n                return origin.apply( null, arguments );\n            }\n    \n            // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n            // JPEG 前两位分别是：255, 216\n            if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n                fragement = origin.apply( null, arguments );\n    \n                parts = fragement.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    fragement = atob( parts[ 1 ] );\n                } else {\n                    fragement = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                fragement = fragement.substring( 0, 2 );\n    \n                supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                        fragement.charCodeAt( 1 ) === 216;\n            }\n    \n            // 只有在android环境下才修复\n            if ( type === 'image/jpeg' && !supportJpeg ) {\n                w = canvas.width;\n                h = canvas.height;\n                ctx = canvas.getContext('2d');\n    \n                return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n            }\n    \n            return origin.apply( null, arguments );\n        };\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/html5/md5',[\n        'runtime/html5/runtime'\n    ], function( FlashRuntime ) {\n    \n        /*\n         * Fastest md5 implementation around (JKM md5)\n         * Credits: Joseph Myers\n         *\n         * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n         * @see http://jsperf.com/md5-shootout/7\n         */\n    \n        /* this function is much faster,\n          so if possible we use it. Some IEs\n          are the only ones I know of that\n          need the idiotic second function,\n          generated by an if clause.  */\n        var add32 = function (a, b) {\n            return (a + b) & 0xFFFFFFFF;\n        },\n    \n        cmn = function (q, a, b, x, s, t) {\n            a = add32(add32(a, q), add32(x, t));\n            return add32((a << s) | (a >>> (32 - s)), b);\n        },\n    \n        ff = function (a, b, c, d, x, s, t) {\n            return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n        },\n    \n        gg = function (a, b, c, d, x, s, t) {\n            return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n        },\n    \n        hh = function (a, b, c, d, x, s, t) {\n            return cmn(b ^ c ^ d, a, b, x, s, t);\n        },\n    \n        ii = function (a, b, c, d, x, s, t) {\n            return cmn(c ^ (b | (~d)), a, b, x, s, t);\n        },\n    \n        md5cycle = function (x, k) {\n            var a = x[0],\n                b = x[1],\n                c = x[2],\n                d = x[3];\n    \n            a = ff(a, b, c, d, k[0], 7, -680876936);\n            d = ff(d, a, b, c, k[1], 12, -389564586);\n            c = ff(c, d, a, b, k[2], 17, 606105819);\n            b = ff(b, c, d, a, k[3], 22, -1044525330);\n            a = ff(a, b, c, d, k[4], 7, -176418897);\n            d = ff(d, a, b, c, k[5], 12, 1200080426);\n            c = ff(c, d, a, b, k[6], 17, -1473231341);\n            b = ff(b, c, d, a, k[7], 22, -45705983);\n            a = ff(a, b, c, d, k[8], 7, 1770035416);\n            d = ff(d, a, b, c, k[9], 12, -1958414417);\n            c = ff(c, d, a, b, k[10], 17, -42063);\n            b = ff(b, c, d, a, k[11], 22, -1990404162);\n            a = ff(a, b, c, d, k[12], 7, 1804603682);\n            d = ff(d, a, b, c, k[13], 12, -40341101);\n            c = ff(c, d, a, b, k[14], 17, -1502002290);\n            b = ff(b, c, d, a, k[15], 22, 1236535329);\n    \n            a = gg(a, b, c, d, k[1], 5, -165796510);\n            d = gg(d, a, b, c, k[6], 9, -1069501632);\n            c = gg(c, d, a, b, k[11], 14, 643717713);\n            b = gg(b, c, d, a, k[0], 20, -373897302);\n            a = gg(a, b, c, d, k[5], 5, -701558691);\n            d = gg(d, a, b, c, k[10], 9, 38016083);\n            c = gg(c, d, a, b, k[15], 14, -660478335);\n            b = gg(b, c, d, a, k[4], 20, -405537848);\n            a = gg(a, b, c, d, k[9], 5, 568446438);\n            d = gg(d, a, b, c, k[14], 9, -1019803690);\n            c = gg(c, d, a, b, k[3], 14, -187363961);\n            b = gg(b, c, d, a, k[8], 20, 1163531501);\n            a = gg(a, b, c, d, k[13], 5, -1444681467);\n            d = gg(d, a, b, c, k[2], 9, -51403784);\n            c = gg(c, d, a, b, k[7], 14, 1735328473);\n            b = gg(b, c, d, a, k[12], 20, -1926607734);\n    \n            a = hh(a, b, c, d, k[5], 4, -378558);\n            d = hh(d, a, b, c, k[8], 11, -2022574463);\n            c = hh(c, d, a, b, k[11], 16, 1839030562);\n            b = hh(b, c, d, a, k[14], 23, -35309556);\n            a = hh(a, b, c, d, k[1], 4, -1530992060);\n            d = hh(d, a, b, c, k[4], 11, 1272893353);\n            c = hh(c, d, a, b, k[7], 16, -155497632);\n            b = hh(b, c, d, a, k[10], 23, -1094730640);\n            a = hh(a, b, c, d, k[13], 4, 681279174);\n            d = hh(d, a, b, c, k[0], 11, -358537222);\n            c = hh(c, d, a, b, k[3], 16, -722521979);\n            b = hh(b, c, d, a, k[6], 23, 76029189);\n            a = hh(a, b, c, d, k[9], 4, -640364487);\n            d = hh(d, a, b, c, k[12], 11, -421815835);\n            c = hh(c, d, a, b, k[15], 16, 530742520);\n            b = hh(b, c, d, a, k[2], 23, -995338651);\n    \n            a = ii(a, b, c, d, k[0], 6, -198630844);\n            d = ii(d, a, b, c, k[7], 10, 1126891415);\n            c = ii(c, d, a, b, k[14], 15, -1416354905);\n            b = ii(b, c, d, a, k[5], 21, -57434055);\n            a = ii(a, b, c, d, k[12], 6, 1700485571);\n            d = ii(d, a, b, c, k[3], 10, -1894986606);\n            c = ii(c, d, a, b, k[10], 15, -1051523);\n            b = ii(b, c, d, a, k[1], 21, -2054922799);\n            a = ii(a, b, c, d, k[8], 6, 1873313359);\n            d = ii(d, a, b, c, k[15], 10, -30611744);\n            c = ii(c, d, a, b, k[6], 15, -1560198380);\n            b = ii(b, c, d, a, k[13], 21, 1309151649);\n            a = ii(a, b, c, d, k[4], 6, -145523070);\n            d = ii(d, a, b, c, k[11], 10, -1120210379);\n            c = ii(c, d, a, b, k[2], 15, 718787259);\n            b = ii(b, c, d, a, k[9], 21, -343485551);\n    \n            x[0] = add32(a, x[0]);\n            x[1] = add32(b, x[1]);\n            x[2] = add32(c, x[2]);\n            x[3] = add32(d, x[3]);\n        },\n    \n        /* there needs to be support for Unicode here,\n           * unless we pretend that we can redefine the MD-5\n           * algorithm for multi-byte characters (perhaps\n           * by adding every four 16-bit characters and\n           * shortening the sum to 32 bits). Otherwise\n           * I suggest performing MD-5 as if every character\n           * was two bytes--e.g., 0040 0025 = @%--but then\n           * how will an ordinary MD-5 sum be matched?\n           * There is no way to standardize text to something\n           * like UTF-8 before transformation; speed cost is\n           * utterly prohibitive. The JavaScript standard\n           * itself needs to look at this: it should start\n           * providing access to strings as preformed UTF-8\n           * 8-bit unsigned value arrays.\n           */\n        md5blk = function (s) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n            }\n            return md5blks;\n        },\n    \n        md5blk_array = function (a) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n            }\n            return md5blks;\n        },\n    \n        md51 = function (s) {\n            var n = s.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk(s.substring(i - 64, i)));\n            }\n            s = s.substring(i - 64);\n            length = s.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n            }\n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n            return state;\n        },\n    \n        md51_array = function (a) {\n            var n = a.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n            }\n    \n            // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n            // containing the last element of the parent array if the sub array specified starts\n            // beyond the length of the parent array - weird.\n            // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n            a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n    \n            length = a.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= a[i] << ((i % 4) << 3);\n            }\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n    \n            return state;\n        },\n    \n        hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],\n    \n        rhex = function (n) {\n            var s = '',\n                j;\n            for (j = 0; j < 4; j += 1) {\n                s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n            }\n            return s;\n        },\n    \n        hex = function (x) {\n            var i;\n            for (i = 0; i < x.length; i += 1) {\n                x[i] = rhex(x[i]);\n            }\n            return x.join('');\n        },\n    \n        md5 = function (s) {\n            return hex(md51(s));\n        },\n    \n    \n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * SparkMD5 OOP implementation.\n         *\n         * Use this class to perform an incremental md5, otherwise use the\n         * static methods instead.\n         */\n        SparkMD5 = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n    \n        // In some cases the fast add32 function cannot be used..\n        if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {\n            add32 = function (x, y) {\n                var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                    msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n                return (msw << 16) | (lsw & 0xFFFF);\n            };\n        }\n    \n    \n        /**\n         * Appends a string.\n         * A conversion will be applied if an utf8 string is detected.\n         *\n         * @param {String} str The string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.append = function (str) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            // then append as binary\n            this.appendBinary(str);\n    \n            return this;\n        };\n    \n        /**\n         * Appends a binary string.\n         *\n         * @param {String} contents The binary string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.appendBinary = function (contents) {\n            this._buff += contents;\n            this._length += contents.length;\n    \n            var length = this._buff.length,\n                i;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));\n            }\n    \n            this._buff = this._buff.substr(i - 64);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                i,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        /**\n         * Finish the final calculation based on the tail.\n         *\n         * @param {Array}  tail   The tail (will be modified)\n         * @param {Number} length The length of the remaining buffer\n         */\n        SparkMD5.prototype._finish = function (tail, length) {\n            var i = length,\n                tmp,\n                lo,\n                hi;\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(this._state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Do the final computation based on the tail and length\n            // Beware that the final length may not fit in 32 bits so we take care of that\n            tmp = this._length * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n            md5cycle(this._state, tail);\n        };\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.reset = function () {\n            this._buff = \"\";\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.prototype.destroy = function () {\n            delete this._state;\n            delete this._buff;\n            delete this._length;\n        };\n    \n    \n        /**\n         * Performs the md5 hash on a string.\n         * A conversion will be applied if utf8 string is detected.\n         *\n         * @param {String}  str The string\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hash = function (str, raw) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            var hash = md51(str);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * Performs the md5 hash on a binary string.\n         *\n         * @param {String}  content The binary string\n         * @param {Boolean} raw     True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hashBinary = function (content, raw) {\n            var hash = md51(content);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * SparkMD5 OOP implementation for array buffers.\n         *\n         * Use this class to perform an incremental md5 ONLY for array buffers.\n         */\n        SparkMD5.ArrayBuffer = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * Appends an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array to be appended\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n            // TODO: we could avoid the concatenation here but the algorithm would be more complex\n            //       if you find yourself needing extra performance, please make a PR.\n            var buff = this._concatArrayBuffer(this._buff, arr),\n                length = buff.length,\n                i;\n    \n            this._length += arr.byteLength;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));\n            }\n    \n            // Avoids IE10 weirdness (documented above)\n            this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                i,\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.reset = function () {\n            this._buff = new Uint8Array(0);\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n    \n        /**\n         * Concats two array buffers, returning a new one.\n         *\n         * @param  {ArrayBuffer} first  The first array buffer\n         * @param  {ArrayBuffer} second The second array buffer\n         *\n         * @return {ArrayBuffer} The new array buffer\n         */\n        SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {\n            var firstLength = first.length,\n                result = new Uint8Array(firstLength + second.byteLength);\n    \n            result.set(first);\n            result.set(new Uint8Array(second), firstLength);\n    \n            return result;\n        };\n    \n        /**\n         * Performs the md5 hash on an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array buffer\n         * @param {Boolean}     raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n            var hash = md51_array(new Uint8Array(arr));\n    \n            return !!raw ? hash : hex(hash);\n        };\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( file ) {\n                var blob = file.getSource(),\n                    chunkSize = 2 * 1024 * 1024,\n                    chunks = Math.ceil( blob.size / chunkSize ),\n                    chunk = 0,\n                    owner = this.owner,\n                    spark = new SparkMD5.ArrayBuffer(),\n                    me = this,\n                    blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice,\n                    loadNext, fr;\n    \n                fr = new FileReader();\n    \n                loadNext = function() {\n                    var start, end;\n    \n                    start = chunk * chunkSize;\n                    end = Math.min( start + chunkSize, blob.size );\n    \n                    fr.onload = function( e ) {\n                        spark.append( e.target.result );\n                        owner.trigger( 'progress', {\n                            total: file.size,\n                            loaded: end\n                        });\n                    };\n    \n                    fr.onloadend = function() {\n                        fr.onloadend = fr.onload = null;\n    \n                        if ( ++chunk < chunks ) {\n                            setTimeout( loadNext, 1 );\n                        } else {\n                            setTimeout(function(){\n                                owner.trigger('load');\n                                me.result = spark.end();\n                                loadNext = file = blob = spark = null;\n                                owner.trigger('complete');\n                            }, 50 );\n                        }\n                    };\n    \n                    fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) );\n                };\n    \n                loadNext();\n            },\n    \n            getResult: function() {\n                return this.result;\n            }\n        });\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview 图片压缩\n     */\n    define('runtime/flash/image',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Md5 flash实现\n     */\n    define('runtime/flash/md5',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( blob ) {\n                return this.flashExec( 'Md5', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview 完全版本。\n     */\n    define('preset/all',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n        'widgets/md5',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/androidpatch',\n        'runtime/html5/image',\n        'runtime/html5/transport',\n        'runtime/html5/md5',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/image',\n        'runtime/flash/transport',\n        'runtime/flash/blob',\n        'runtime/flash/md5'\n    ], function( Base ) {\n        return Base;\n    });\n    /**\n     * @fileOverview 日志组件，主要用来收集错误信息，可以帮助 webuploader 更好的定位问题和发展。\n     *\n     * 如果您不想要启用此功能，请在打包的时候去掉 log 模块。\n     *\n     * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。\n     *\n     * 如：\n     * WebUploader.create({\n     *     ...\n     *\n     *     disableWidgets: 'log',\n     *\n     *     ...\n     * })\n     */\n    define('widgets/log',[\n        'base',\n        'uploader',\n        'widgets/widget'\n    ], function( Base, Uploader ) {\n        var $ = Base.$,\n            logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??',\n            product = (location.hostname || location.host || 'protected').toLowerCase(),\n    \n            // 只针对 baidu 内部产品用户做统计功能。\n            enable = product && /baidu/i.exec(product),\n            base;\n    \n        if (!enable) {\n            return;\n        }\n    \n        base = {\n            dv: 3,\n            master: 'webuploader',\n            online: /test/.exec(product) ? 0 : 1,\n            module: '',\n            product: product,\n            type: 0\n        };\n    \n        function send(data) {\n            var obj = $.extend({}, base, data),\n                url = logUrl.replace(/^(.*)\\?/, '$1' + $.param( obj )),\n                image = new Image();\n    \n            image.src = url;\n        }\n    \n        return Uploader.register({\n            name: 'log',\n    \n            init: function() {\n                var owner = this.owner,\n                    count = 0,\n                    size = 0;\n    \n                owner\n                    .on('error', function(code) {\n                        send({\n                            type: 2,\n                            c_error_code: code\n                        });\n                    })\n                    .on('uploadError', function(file, reason) {\n                        send({\n                            type: 2,\n                            c_error_code: 'UPLOAD_ERROR',\n                            c_reason: '' + reason\n                        });\n                    })\n                    .on('uploadComplete', function(file) {\n                        count++;\n                        size += file.size;\n                    }).\n                    on('uploadFinished', function() {\n                        send({\n                            c_count: count,\n                            c_size: size\n                        });\n                        count = size = 0;\n                    });\n    \n                send({\n                    c_usage: 1\n                });\n            }\n        });\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('webuploader',[\n        'preset/all',\n        'widgets/log'\n    ], function( preset ) {\n        return preset;\n    });\n\n    var _require = require;\n    return _require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.flashonly.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview 图片压缩\n     */\n    define('runtime/flash/image',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 只有flash实现的文件版本。\n     */\n    define('preset/flashonly',[\n        'base',\n    \n        // widgets\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n    \n        // runtimes\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/image',\n        'runtime/flash/blob',\n        'runtime/flash/transport'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/flashonly'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.html5nodepend.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview  jq-bridge 主要实现像jQuery一样的功能方法，可以替换成jQuery，\n     * 这里只实现了此组件所需的部分。\n     *\n     * **此文件的代码还不可用，还是直接用jquery吧**\n     * @beta\n     */\n    define('dollar-builtin',[],function() {\n        var doc = window.document,\n            emptyArray = [],\n            slice = emptyArray.slice,\n            class2type = {},\n            hasOwn = class2type.hasOwnProperty,\n            toString = class2type.toString,\n            rId = /^#(.*)$/;\n    \n        function each( obj, iterator ) {\n            var i;\n    \n            //fix error, add guard here\n            if(!obj) {\n                return;\n            }\n    \n            // like array\n            if ( typeof obj !== 'function' && typeof obj.length === 'number' ) {\n                for ( i = 0; i < obj.length; i++ ) {\n                    if ( iterator.call( obj[ i ], i, obj[ i ] ) === false ) {\n                        return obj;\n                    }\n                }\n            } else {\n                for ( i in obj ) {\n                    if ( hasOwn.call( obj, i ) && iterator.call( obj[ i ], i,\n                            obj[ i ] ) === false ) {\n                        return obj;\n                    }\n                }\n            }\n    \n            return obj;\n        }\n    \n        function extend( target, source, deep ) {\n            each( source, function( key, val ) {\n                if ( deep && typeof val === 'object' ) {\n                    if ( typeof target[ key ] !== 'object' ) {\n                        target[ key ] = type( val ) === 'array' ? [] : {};\n                    }\n                    extend( target[ key ], val, deep );\n                } else {\n                    target[ key ] = val;\n                }\n            });\n        }\n    \n        each( ('Boolean Number String Function Array Date RegExp Object' +\n                ' Error').split(' '), function( i, name ) {\n            class2type[ '[object ' + name + ']' ] = name.toLowerCase();\n        });\n    \n        function setAttribute( node, name, value ) {\n            value == null ? node.removeAttribute( name ) :\n                    node.setAttribute( name, value );\n        }\n    \n        /**\n         * 只支持ID选择。\n         */\n        function $( elem ) {\n            var api = {};\n    \n            elem = typeof elem === 'string' && rId.test( elem ) ?\n                    doc.getElementById( RegExp.$1 ) : elem;\n    \n            if ( elem ) {\n                api[ 0 ] = elem;\n                api.length = 1;\n            }\n    \n            return $.extend( api, {\n                _wrap: true,\n    \n                get: function() {\n                    return elem;\n                },\n    \n                /**\n                 * 添加className\n                 */\n                addClass: function( classname ) {\n                    elem.classList.add( classname );\n                    return this;\n                },\n    \n                removeClass: function( classname ) {\n                    elem.classList.remove( classname );\n                    return this;\n                },\n    \n                //fix error, $(...).each is used in the source\n                each: function(callback){\n                  [].every.call(this, function(el, idx){\n                    return callback.call(el, idx, el) !== false\n                  })\n                  return this\n                },\n    \n                html: function( html ) {\n                    if ( html ) {\n                        elem.innerHTML = html;\n                    }\n                    return elem.innerHTML;\n                },\n    \n                attr: function( key, val ) {\n                    if ( $.isObject( key ) ) {\n                        $.each( key, function( k, v ) {\n                            setAttribute( elem, k, v );\n                        });\n                    } else {\n                        setAttribute( elem, key, val );\n                    }\n                },\n    \n                empty: function() {\n                    elem.innerHTML = '';\n                    return this;\n                },\n    \n                before: function( el ) {\n                    elem.parentNode.insertBefore( el, elem );\n                },\n    \n                append: function( el ) {\n                    el = el._wrap ? el.get() : el;\n                    elem.appendChild( el );\n                },\n    \n                text: function() {\n                    return elem.textContent;\n                },\n    \n                // on\n                on: function( type, fn ) {\n                    if ( elem.addEventListener ) {\n                        elem.addEventListener( type, fn, false );\n                    } else if ( elem.attachEvent ) {\n                        elem.attachEvent( 'on' + type, fn );\n                    }\n    \n                    return this;\n                },\n    \n                // off\n                off: function( type, fn ) {\n                    if ( elem.removeEventListener ) {\n                        elem.removeEventListener( type, fn, false );\n                    } else if ( elem.attachEvent ) {\n                        elem.detachEvent( 'on' + type, fn );\n                    }\n                    return this;\n                }\n    \n            });\n        }\n    \n        $.each = each;\n        $.extend = function( /*[deep, ]*/target/*, source...*/ ) {\n            var args = slice.call( arguments, 1 ),\n                deep;\n    \n            if ( typeof target === 'boolean' ) {\n                deep = target;\n                target = args.shift();\n            }\n    \n            args.forEach(function( arg ) {\n                arg && extend( target, arg, deep );\n            });\n    \n            return target;\n        };\n    \n        function type( obj ) {\n    \n            /*jshint eqnull:true*/\n            return obj == null ? String( obj ) :\n                    class2type[ toString.call( obj ) ] || 'object';\n        }\n        $.type = type;\n    \n        //fix error, $.grep is used in the source\n        $.grep = function( elems, callback, invert ) {\n            var callbackInverse,\n                matches = [],\n                i = 0,\n                length = elems.length,\n                callbackExpect = !invert;\n    \n            // Go through the array, only saving the items\n            // that pass the validator function\n            for ( ; i < length; i++ ) {\n                callbackInverse = !callback( elems[ i ], i );\n                if ( callbackInverse !== callbackExpect ) {\n                    matches.push( elems[ i ] );\n                }\n            }\n    \n            return matches;\n        }\n    \n        $.isWindow = function( obj ) {\n            return obj && obj.window === obj;\n        };\n    \n        $.isPlainObject = function( obj ) {\n            if ( type( obj ) !== 'object' || obj.nodeType || $.isWindow( obj ) ) {\n                return false;\n            }\n    \n            try {\n                if ( obj.constructor && !hasOwn.call( obj.constructor.prototype,\n                        'isPrototypeOf' ) ) {\n                    return false;\n                }\n            } catch ( ex ) {\n                return false;\n            }\n    \n            return true;\n        };\n    \n        $.isObject = function( anything ) {\n            return type( anything ) === 'object';\n        };\n    \n        $.trim = function( str ) {\n            return str ? str.trim() : '';\n        };\n    \n        $.isFunction = function( obj ) {\n            return type( obj ) === 'function';\n        };\n    \n        emptyArray = null;\n    \n        return $;\n    });\n    \n    define('dollar',[\n        'dollar-builtin'\n    ], function( $ ) {\n        return $;\n    });\n    /**\n     * 直接来源于jquery的代码。\n     * @fileOverview Promise/A+\n     * @beta\n     */\n    define('promise-builtin',[\n        'dollar'\n    ], function( $ ) {\n    \n        var api;\n    \n        // 简单版Callbacks, 默认memory，可选once.\n        function Callbacks( once ) {\n            var list = [],\n                stack = !once && [],\n                fire = function( data ) {\n                    memory = data;\n                    fired = true;\n                    firingIndex = firingStart || 0;\n                    firingStart = 0;\n                    firingLength = list.length;\n                    firing = true;\n    \n                    for ( ; list && firingIndex < firingLength; firingIndex++ ) {\n                        list[ firingIndex ].apply( data[ 0 ], data[ 1 ] );\n                    }\n                    firing = false;\n    \n                    if ( list ) {\n                        if ( stack ) {\n                            stack.length && fire( stack.shift() );\n                        }  else {\n                            list = [];\n                        }\n                    }\n                },\n                self = {\n                    add: function() {\n                        if ( list ) {\n                            var start = list.length;\n                            (function add ( args ) {\n                                $.each( args, function( _, arg ) {\n                                    var type = $.type( arg );\n                                    if ( type === 'function' ) {\n                                        list.push( arg );\n                                    } else if ( arg && arg.length &&\n                                            type !== 'string' ) {\n    \n                                        add( arg );\n                                    }\n                                });\n                            })( arguments );\n    \n                            if ( firing ) {\n                                firingLength = list.length;\n                            } else if ( memory ) {\n                                firingStart = start;\n                                fire( memory );\n                            }\n                        }\n                        return this;\n                    },\n    \n                    disable: function() {\n                        list = stack = memory = undefined;\n                        return this;\n                    },\n    \n                    // Lock the list in its current state\n                    lock: function() {\n                        stack = undefined;\n                        if ( !memory ) {\n                            self.disable();\n                        }\n                        return this;\n                    },\n    \n                    fireWith: function( context, args ) {\n                        if ( list && (!fired || stack) ) {\n                            args = args || [];\n                            args = [ context, args.slice ? args.slice() : args ];\n                            if ( firing ) {\n                                stack.push( args );\n                            } else {\n                                fire( args );\n                            }\n                        }\n                        return this;\n                    },\n    \n                    fire: function() {\n                        self.fireWith( this, arguments );\n                        return this;\n                    }\n                },\n    \n                fired, firing, firingStart, firingLength, firingIndex, memory;\n    \n            return self;\n        }\n    \n        function Deferred( func ) {\n            var tuples = [\n                    // action, add listener, listener list, final state\n                    [ 'resolve', 'done', Callbacks( true ), 'resolved' ],\n                    [ 'reject', 'fail', Callbacks( true ), 'rejected' ],\n                    [ 'notify', 'progress', Callbacks() ]\n                ],\n                state = 'pending',\n                promise = {\n                    state: function() {\n                        return state;\n                    },\n                    always: function() {\n                        deferred.done( arguments ).fail( arguments );\n                        return this;\n                    },\n                    then: function( /* fnDone, fnFail, fnProgress */ ) {\n                        var fns = arguments;\n                        return Deferred(function( newDefer ) {\n                            $.each( tuples, function( i, tuple ) {\n                                var action = tuple[ 0 ],\n                                    fn = $.isFunction( fns[ i ] ) && fns[ i ];\n    \n                                // deferred[ done | fail | progress ] for\n                                // forwarding actions to newDefer\n                                deferred[ tuple[ 1 ] ](function() {\n                                    var returned;\n    \n                                    returned = fn && fn.apply( this, arguments );\n    \n                                    if ( returned &&\n                                            $.isFunction( returned.promise ) ) {\n    \n                                        returned.promise()\n                                                .done( newDefer.resolve )\n                                                .fail( newDefer.reject )\n                                                .progress( newDefer.notify );\n                                    } else {\n                                        newDefer[ action + 'With' ](\n                                                this === promise ?\n                                                newDefer.promise() :\n                                                this,\n                                                fn ? [ returned ] : arguments );\n                                    }\n                                });\n                            });\n                            fns = null;\n                        }).promise();\n                    },\n    \n                    // Get a promise for this deferred\n                    // If obj is provided, the promise aspect is added to the object\n                    promise: function( obj ) {\n    \n                        return obj != null ? $.extend( obj, promise ) : promise;\n                    }\n                },\n                deferred = {};\n    \n            // Keep pipe for back-compat\n            promise.pipe = promise.then;\n    \n            // Add list-specific methods\n            $.each( tuples, function( i, tuple ) {\n                var list = tuple[ 2 ],\n                    stateString = tuple[ 3 ];\n    \n                // promise[ done | fail | progress ] = list.add\n                promise[ tuple[ 1 ] ] = list.add;\n    \n                // Handle state\n                if ( stateString ) {\n                    list.add(function() {\n                        // state = [ resolved | rejected ]\n                        state = stateString;\n    \n                    // [ reject_list | resolve_list ].disable; progress_list.lock\n                    }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );\n                }\n    \n                // deferred[ resolve | reject | notify ]\n                deferred[ tuple[ 0 ] ] = function() {\n                    deferred[ tuple[ 0 ] + 'With' ]( this === deferred ? promise :\n                            this, arguments );\n                    return this;\n                };\n                deferred[ tuple[ 0 ] + 'With' ] = list.fireWith;\n            });\n    \n            // Make the deferred a promise\n            promise.promise( deferred );\n    \n            // Call given func if any\n            if ( func ) {\n                func.call( deferred, deferred );\n            }\n    \n            // All done!\n            return deferred;\n        }\n    \n        api = {\n            /**\n             * 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。\n             * 详细的Deferred用法说明，请参照jQuery的API文档。\n             *\n             * Deferred对象在钩子回掉函数中经常要用到，用来处理需要等待的异步操作。\n             *\n             * @for  Base\n             * @method Deferred\n             * @grammar Base.Deferred() => Deferred\n             * @example\n             * // 在文件开始发送前做些异步操作。\n             * // WebUploader会等待此异步操作完成后，开始发送文件。\n             * Uploader.register({\n             *     'before-send-file': 'doSomthingAsync'\n             * }, {\n             *\n             *     doSomthingAsync: function() {\n             *         var deferred = Base.Deferred();\n             *\n             *         // 模拟一次异步操作。\n             *         setTimeout(deferred.resolve, 2000);\n             *\n             *         return deferred.promise();\n             *     }\n             * });\n             */\n            Deferred: Deferred,\n    \n            /**\n             * 判断传入的参数是否为一个promise对象。\n             * @method isPromise\n             * @grammar Base.isPromise( anything ) => Boolean\n             * @param  {*}  anything 检测对象。\n             * @return {Boolean}\n             * @for  Base\n             * @example\n             * console.log( Base.isPromise() );    // => false\n             * console.log( Base.isPromise({ key: '123' }) );    // => false\n             * console.log( Base.isPromise( Base.Deferred().promise() ) );    // => true\n             *\n             * // Deferred也是一个Promise\n             * console.log( Base.isPromise( Base.Deferred() ) );    // => true\n             */\n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            },\n    \n            /**\n             * 返回一个promise，此promise在所有传入的promise都完成了后完成。\n             * 详细请查看[这里](http://api.jquery.com/jQuery.when/)。\n             *\n             * @method when\n             * @for  Base\n             * @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise\n             */\n            when: function( subordinate /* , ..., subordinateN */ ) {\n                var i = 0,\n                    slice = [].slice,\n                    resolveValues = slice.call( arguments ),\n                    length = resolveValues.length,\n    \n                    // the count of uncompleted subordinates\n                    remaining = length !== 1 || (subordinate &&\n                        $.isFunction( subordinate.promise )) ? length : 0,\n    \n                    // the master Deferred. If resolveValues consist of\n                    // only a single Deferred, just use that.\n                    deferred = remaining === 1 ? subordinate : Deferred(),\n    \n                    // Update function for both resolve and progress values\n                    updateFunc = function( i, contexts, values ) {\n                        return function( value ) {\n                            contexts[ i ] = this;\n                            values[ i ] = arguments.length > 1 ?\n                                    slice.call( arguments ) : value;\n    \n                            if ( values === progressValues ) {\n                                deferred.notifyWith( contexts, values );\n                            } else if ( !(--remaining) ) {\n                                deferred.resolveWith( contexts, values );\n                            }\n                        };\n                    },\n    \n                    progressValues, progressContexts, resolveContexts;\n    \n                // add listeners to Deferred subordinates; treat others as resolved\n                if ( length > 1 ) {\n                    progressValues = new Array( length );\n                    progressContexts = new Array( length );\n                    resolveContexts = new Array( length );\n                    for ( ; i < length; i++ ) {\n                        if ( resolveValues[ i ] &&\n                                $.isFunction( resolveValues[ i ].promise ) ) {\n    \n                            resolveValues[ i ].promise()\n                                    .done( updateFunc( i, resolveContexts,\n                                            resolveValues ) )\n                                    .fail( deferred.reject )\n                                    .progress( updateFunc( i, progressContexts,\n                                            progressValues ) );\n                        } else {\n                            --remaining;\n                        }\n                    }\n                }\n    \n                // if we're not waiting on anything, resolve the master\n                if ( !remaining ) {\n                    deferred.resolveWith( resolveContexts, resolveValues );\n                }\n    \n                return deferred.promise();\n            }\n        };\n    \n        return api;\n    });\n    define('promise',[\n        'promise-builtin'\n    ], function( $ ) {\n        return $;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 只有html5实现的文件版本。\n     */\n    define('preset/html5only',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/image',\n        'runtime/html5/transport'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/html5only'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.html5only.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 只有html5实现的文件版本。\n     */\n    define('preset/html5only',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/image',\n        'runtime/html5/transport'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/html5only'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Md5\n     */\n    define('lib/md5',[\n        'runtime/client',\n        'mediator'\n    ], function( RuntimeClient, Mediator ) {\n    \n        function Md5() {\n            RuntimeClient.call( this, 'Md5' );\n        }\n    \n        // 让 Md5 具备事件功能。\n        Mediator.installTo( Md5.prototype );\n    \n        Md5.prototype.loadFromBlob = function( blob ) {\n            var me = this;\n    \n            if ( me.getRuid() ) {\n                me.disconnectRuntime();\n            }\n    \n            // 连接到blob归属的同一个runtime.\n            me.connectRuntime( blob.ruid, function() {\n                me.exec('init');\n                me.exec( 'loadFromBlob', blob );\n            });\n        };\n    \n        Md5.prototype.getResult = function() {\n            return this.exec('getResult');\n        };\n    \n        return Md5;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/md5',[\n        'base',\n        'uploader',\n        'lib/md5',\n        'lib/blob',\n        'widgets/widget'\n    ], function( Base, Uploader, Md5, Blob ) {\n    \n        return Uploader.register({\n            name: 'md5',\n    \n    \n            /**\n             * 计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。\n             *\n             *\n             * @method md5File\n             * @grammar md5File( file[, start[, end]] ) => promise\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.md5File( file )\n             *\n             *         // 及时显示进度\n             *         .progress(function(percentage) {\n             *             console.log('Percentage:', percentage);\n             *         })\n             *\n             *         // 完成\n             *         .then(function(val) {\n             *             console.log('md5 result:', val);\n             *         });\n             *\n             * });\n             */\n            md5File: function( file, start, end ) {\n                var md5 = new Md5(),\n                    deferred = Base.Deferred(),\n                    blob = (file instanceof Blob) ? file :\n                        this.request( 'get-file', file ).source;\n    \n                md5.on( 'progress load', function( e ) {\n                    e = e || {};\n                    deferred.notify( e.total ? e.loaded / e.total : 1 );\n                });\n    \n                md5.on( 'complete', function() {\n                    deferred.resolve( md5.getResult() );\n                });\n    \n                md5.on( 'error', function( reason ) {\n                    deferred.reject( reason );\n                });\n    \n                if ( arguments.length > 1 ) {\n                    start = start || 0;\n                    end = end || 0;\n                    start < 0 && (start = blob.size + start);\n                    end < 0 && (end = blob.size + end);\n                    end = Math.min( end, blob.size );\n                    blob = blob.slice( start, end );\n                }\n    \n                md5.loadFromBlob( blob );\n    \n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n     * android里面toDataUrl('image/jpege')得到的结果却是png.\n     *\n     * 所以这里没辙，只能借助这个工具\n     * @fileOverview jpeg encoder\n     */\n    define('runtime/html5/jpegencoder',[], function( require, exports, module ) {\n    \n        /*\n          Copyright (c) 2008, Adobe Systems Incorporated\n          All rights reserved.\n    \n          Redistribution and use in source and binary forms, with or without\n          modification, are permitted provided that the following conditions are\n          met:\n    \n          * Redistributions of source code must retain the above copyright notice,\n            this list of conditions and the following disclaimer.\n    \n          * Redistributions in binary form must reproduce the above copyright\n            notice, this list of conditions and the following disclaimer in the\n            documentation and/or other materials provided with the distribution.\n    \n          * Neither the name of Adobe Systems Incorporated nor the names of its\n            contributors may be used to endorse or promote products derived from\n            this software without specific prior written permission.\n    \n          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n          IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n          THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n          PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n          CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n          EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n          PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n          PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n          LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n          NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        */\n        /*\n        JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n    \n        Basic GUI blocking jpeg encoder\n        */\n    \n        function JPEGEncoder(quality) {\n          var self = this;\n            var fround = Math.round;\n            var ffloor = Math.floor;\n            var YTable = new Array(64);\n            var UVTable = new Array(64);\n            var fdtbl_Y = new Array(64);\n            var fdtbl_UV = new Array(64);\n            var YDC_HT;\n            var UVDC_HT;\n            var YAC_HT;\n            var UVAC_HT;\n    \n            var bitcode = new Array(65535);\n            var category = new Array(65535);\n            var outputfDCTQuant = new Array(64);\n            var DU = new Array(64);\n            var byteout = [];\n            var bytenew = 0;\n            var bytepos = 7;\n    \n            var YDU = new Array(64);\n            var UDU = new Array(64);\n            var VDU = new Array(64);\n            var clt = new Array(256);\n            var RGB_YUV_TABLE = new Array(2048);\n            var currentQuality;\n    \n            var ZigZag = [\n                     0, 1, 5, 6,14,15,27,28,\n                     2, 4, 7,13,16,26,29,42,\n                     3, 8,12,17,25,30,41,43,\n                     9,11,18,24,31,40,44,53,\n                    10,19,23,32,39,45,52,54,\n                    20,22,33,38,46,51,55,60,\n                    21,34,37,47,50,56,59,61,\n                    35,36,48,49,57,58,62,63\n                ];\n    \n            var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n            var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n            var std_ac_luminance_values = [\n                    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n            var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n            var std_ac_chrominance_values = [\n                    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            function initQuantTables(sf){\n                    var YQT = [\n                        16, 11, 10, 16, 24, 40, 51, 61,\n                        12, 12, 14, 19, 26, 58, 60, 55,\n                        14, 13, 16, 24, 40, 57, 69, 56,\n                        14, 17, 22, 29, 51, 87, 80, 62,\n                        18, 22, 37, 56, 68,109,103, 77,\n                        24, 35, 55, 64, 81,104,113, 92,\n                        49, 64, 78, 87,103,121,120,101,\n                        72, 92, 95, 98,112,100,103, 99\n                    ];\n    \n                    for (var i = 0; i < 64; i++) {\n                        var t = ffloor((YQT[i]*sf+50)/100);\n                        if (t < 1) {\n                            t = 1;\n                        } else if (t > 255) {\n                            t = 255;\n                        }\n                        YTable[ZigZag[i]] = t;\n                    }\n                    var UVQT = [\n                        17, 18, 24, 47, 99, 99, 99, 99,\n                        18, 21, 26, 66, 99, 99, 99, 99,\n                        24, 26, 56, 99, 99, 99, 99, 99,\n                        47, 66, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99\n                    ];\n                    for (var j = 0; j < 64; j++) {\n                        var u = ffloor((UVQT[j]*sf+50)/100);\n                        if (u < 1) {\n                            u = 1;\n                        } else if (u > 255) {\n                            u = 255;\n                        }\n                        UVTable[ZigZag[j]] = u;\n                    }\n                    var aasf = [\n                        1.0, 1.387039845, 1.306562965, 1.175875602,\n                        1.0, 0.785694958, 0.541196100, 0.275899379\n                    ];\n                    var k = 0;\n                    for (var row = 0; row < 8; row++)\n                    {\n                        for (var col = 0; col < 8; col++)\n                        {\n                            fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            k++;\n                        }\n                    }\n                }\n    \n                function computeHuffmanTbl(nrcodes, std_table){\n                    var codevalue = 0;\n                    var pos_in_table = 0;\n                    var HT = new Array();\n                    for (var k = 1; k <= 16; k++) {\n                        for (var j = 1; j <= nrcodes[k]; j++) {\n                            HT[std_table[pos_in_table]] = [];\n                            HT[std_table[pos_in_table]][0] = codevalue;\n                            HT[std_table[pos_in_table]][1] = k;\n                            pos_in_table++;\n                            codevalue++;\n                        }\n                        codevalue*=2;\n                    }\n                    return HT;\n                }\n    \n                function initHuffmanTbl()\n                {\n                    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n                }\n    \n                function initCategoryNumber()\n                {\n                    var nrlower = 1;\n                    var nrupper = 2;\n                    for (var cat = 1; cat <= 15; cat++) {\n                        //Positive numbers\n                        for (var nr = nrlower; nr<nrupper; nr++) {\n                            category[32767+nr] = cat;\n                            bitcode[32767+nr] = [];\n                            bitcode[32767+nr][1] = cat;\n                            bitcode[32767+nr][0] = nr;\n                        }\n                        //Negative numbers\n                        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                            category[32767+nrneg] = cat;\n                            bitcode[32767+nrneg] = [];\n                            bitcode[32767+nrneg][1] = cat;\n                            bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                        }\n                        nrlower <<= 1;\n                        nrupper <<= 1;\n                    }\n                }\n    \n                function initRGBYUVTable() {\n                    for(var i = 0; i < 256;i++) {\n                        RGB_YUV_TABLE[i]            =  19595 * i;\n                        RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                        RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                        RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                        RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                        RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                        RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                        RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                    }\n                }\n    \n                // IO functions\n                function writeBits(bs)\n                {\n                    var value = bs[0];\n                    var posval = bs[1]-1;\n                    while ( posval >= 0 ) {\n                        if (value & (1 << posval) ) {\n                            bytenew |= (1 << bytepos);\n                        }\n                        posval--;\n                        bytepos--;\n                        if (bytepos < 0) {\n                            if (bytenew == 0xFF) {\n                                writeByte(0xFF);\n                                writeByte(0);\n                            }\n                            else {\n                                writeByte(bytenew);\n                            }\n                            bytepos=7;\n                            bytenew=0;\n                        }\n                    }\n                }\n    \n                function writeByte(value)\n                {\n                    byteout.push(clt[value]); // write char directly instead of converting later\n                }\n    \n                function writeWord(value)\n                {\n                    writeByte((value>>8)&0xFF);\n                    writeByte((value   )&0xFF);\n                }\n    \n                // DCT & quantization core\n                function fDCTQuant(data, fdtbl)\n                {\n                    var d0, d1, d2, d3, d4, d5, d6, d7;\n                    /* Pass 1: process rows. */\n                    var dataOff=0;\n                    var i;\n                    var I8 = 8;\n                    var I64 = 64;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff+1];\n                        d2 = data[dataOff+2];\n                        d3 = data[dataOff+3];\n                        d4 = data[dataOff+4];\n                        d5 = data[dataOff+5];\n                        d6 = data[dataOff+6];\n                        d7 = data[dataOff+7];\n    \n                        var tmp0 = d0 + d7;\n                        var tmp7 = d0 - d7;\n                        var tmp1 = d1 + d6;\n                        var tmp6 = d1 - d6;\n                        var tmp2 = d2 + d5;\n                        var tmp5 = d2 - d5;\n                        var tmp3 = d3 + d4;\n                        var tmp4 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                        var tmp13 = tmp0 - tmp3;\n                        var tmp11 = tmp1 + tmp2;\n                        var tmp12 = tmp1 - tmp2;\n    \n                        data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                        data[dataOff+4] = tmp10 - tmp11;\n    \n                        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                        data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                        data[dataOff+6] = tmp13 - z1;\n    \n                        /* Odd part */\n                        tmp10 = tmp4 + tmp5; /* phase 2 */\n                        tmp11 = tmp5 + tmp6;\n                        tmp12 = tmp6 + tmp7;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                        var z3 = tmp11 * 0.707106781; /* c4 */\n    \n                        var z11 = tmp7 + z3;    /* phase 5 */\n                        var z13 = tmp7 - z3;\n    \n                        data[dataOff+5] = z13 + z2; /* phase 6 */\n                        data[dataOff+3] = z13 - z2;\n                        data[dataOff+1] = z11 + z4;\n                        data[dataOff+7] = z11 - z4;\n    \n                        dataOff += 8; /* advance pointer to next row */\n                    }\n    \n                    /* Pass 2: process columns. */\n                    dataOff = 0;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff + 8];\n                        d2 = data[dataOff + 16];\n                        d3 = data[dataOff + 24];\n                        d4 = data[dataOff + 32];\n                        d5 = data[dataOff + 40];\n                        d6 = data[dataOff + 48];\n                        d7 = data[dataOff + 56];\n    \n                        var tmp0p2 = d0 + d7;\n                        var tmp7p2 = d0 - d7;\n                        var tmp1p2 = d1 + d6;\n                        var tmp6p2 = d1 - d6;\n                        var tmp2p2 = d2 + d5;\n                        var tmp5p2 = d2 - d5;\n                        var tmp3p2 = d3 + d4;\n                        var tmp4p2 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                        var tmp13p2 = tmp0p2 - tmp3p2;\n                        var tmp11p2 = tmp1p2 + tmp2p2;\n                        var tmp12p2 = tmp1p2 - tmp2p2;\n    \n                        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                        data[dataOff+32] = tmp10p2 - tmp11p2;\n    \n                        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                        data[dataOff+48] = tmp13p2 - z1p2;\n    \n                        /* Odd part */\n                        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                        tmp11p2 = tmp5p2 + tmp6p2;\n                        tmp12p2 = tmp6p2 + tmp7p2;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                        var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n    \n                        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                        var z13p2 = tmp7p2 - z3p2;\n    \n                        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                        data[dataOff+24] = z13p2 - z2p2;\n                        data[dataOff+ 8] = z11p2 + z4p2;\n                        data[dataOff+56] = z11p2 - z4p2;\n    \n                        dataOff++; /* advance pointer to next column */\n                    }\n    \n                    // Quantize/descale the coefficients\n                    var fDCTQuant;\n                    for (i=0; i<I64; ++i)\n                    {\n                        // Apply the quantization and scaling factor & Round to nearest integer\n                        fDCTQuant = data[i]*fdtbl[i];\n                        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                        //outputfDCTQuant[i] = fround(fDCTQuant);\n    \n                    }\n                    return outputfDCTQuant;\n                }\n    \n                function writeAPP0()\n                {\n                    writeWord(0xFFE0); // marker\n                    writeWord(16); // length\n                    writeByte(0x4A); // J\n                    writeByte(0x46); // F\n                    writeByte(0x49); // I\n                    writeByte(0x46); // F\n                    writeByte(0); // = \"JFIF\",'\\0'\n                    writeByte(1); // versionhi\n                    writeByte(1); // versionlo\n                    writeByte(0); // xyunits\n                    writeWord(1); // xdensity\n                    writeWord(1); // ydensity\n                    writeByte(0); // thumbnwidth\n                    writeByte(0); // thumbnheight\n                }\n    \n                function writeSOF0(width, height)\n                {\n                    writeWord(0xFFC0); // marker\n                    writeWord(17);   // length, truecolor YUV JPG\n                    writeByte(8);    // precision\n                    writeWord(height);\n                    writeWord(width);\n                    writeByte(3);    // nrofcomponents\n                    writeByte(1);    // IdY\n                    writeByte(0x11); // HVY\n                    writeByte(0);    // QTY\n                    writeByte(2);    // IdU\n                    writeByte(0x11); // HVU\n                    writeByte(1);    // QTU\n                    writeByte(3);    // IdV\n                    writeByte(0x11); // HVV\n                    writeByte(1);    // QTV\n                }\n    \n                function writeDQT()\n                {\n                    writeWord(0xFFDB); // marker\n                    writeWord(132);    // length\n                    writeByte(0);\n                    for (var i=0; i<64; i++) {\n                        writeByte(YTable[i]);\n                    }\n                    writeByte(1);\n                    for (var j=0; j<64; j++) {\n                        writeByte(UVTable[j]);\n                    }\n                }\n    \n                function writeDHT()\n                {\n                    writeWord(0xFFC4); // marker\n                    writeWord(0x01A2); // length\n    \n                    writeByte(0); // HTYDCinfo\n                    for (var i=0; i<16; i++) {\n                        writeByte(std_dc_luminance_nrcodes[i+1]);\n                    }\n                    for (var j=0; j<=11; j++) {\n                        writeByte(std_dc_luminance_values[j]);\n                    }\n    \n                    writeByte(0x10); // HTYACinfo\n                    for (var k=0; k<16; k++) {\n                        writeByte(std_ac_luminance_nrcodes[k+1]);\n                    }\n                    for (var l=0; l<=161; l++) {\n                        writeByte(std_ac_luminance_values[l]);\n                    }\n    \n                    writeByte(1); // HTUDCinfo\n                    for (var m=0; m<16; m++) {\n                        writeByte(std_dc_chrominance_nrcodes[m+1]);\n                    }\n                    for (var n=0; n<=11; n++) {\n                        writeByte(std_dc_chrominance_values[n]);\n                    }\n    \n                    writeByte(0x11); // HTUACinfo\n                    for (var o=0; o<16; o++) {\n                        writeByte(std_ac_chrominance_nrcodes[o+1]);\n                    }\n                    for (var p=0; p<=161; p++) {\n                        writeByte(std_ac_chrominance_values[p]);\n                    }\n                }\n    \n                function writeSOS()\n                {\n                    writeWord(0xFFDA); // marker\n                    writeWord(12); // length\n                    writeByte(3); // nrofcomponents\n                    writeByte(1); // IdY\n                    writeByte(0); // HTY\n                    writeByte(2); // IdU\n                    writeByte(0x11); // HTU\n                    writeByte(3); // IdV\n                    writeByte(0x11); // HTV\n                    writeByte(0); // Ss\n                    writeByte(0x3f); // Se\n                    writeByte(0); // Bf\n                }\n    \n                function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                    var EOB = HTAC[0x00];\n                    var M16zeroes = HTAC[0xF0];\n                    var pos;\n                    var I16 = 16;\n                    var I63 = 63;\n                    var I64 = 64;\n                    var DU_DCT = fDCTQuant(CDU, fdtbl);\n                    //ZigZag reorder\n                    for (var j=0;j<I64;++j) {\n                        DU[ZigZag[j]]=DU_DCT[j];\n                    }\n                    var Diff = DU[0] - DC; DC = DU[0];\n                    //Encode DC\n                    if (Diff==0) {\n                        writeBits(HTDC[0]); // Diff might be 0\n                    } else {\n                        pos = 32767+Diff;\n                        writeBits(HTDC[category[pos]]);\n                        writeBits(bitcode[pos]);\n                    }\n                    //Encode ACs\n                    var end0pos = 63; // was const... which is crazy\n                    for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                    //end0pos = first element in reverse order !=0\n                    if ( end0pos == 0) {\n                        writeBits(EOB);\n                        return DC;\n                    }\n                    var i = 1;\n                    var lng;\n                    while ( i <= end0pos ) {\n                        var startpos = i;\n                        for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                        var nrzeroes = i-startpos;\n                        if ( nrzeroes >= I16 ) {\n                            lng = nrzeroes>>4;\n                            for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                                writeBits(M16zeroes);\n                            nrzeroes = nrzeroes&0xF;\n                        }\n                        pos = 32767+DU[i];\n                        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                        writeBits(bitcode[pos]);\n                        i++;\n                    }\n                    if ( end0pos != I63 ) {\n                        writeBits(EOB);\n                    }\n                    return DC;\n                }\n    \n                function initCharLookupTable(){\n                    var sfcc = String.fromCharCode;\n                    for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                        clt[i] = sfcc(i);\n                    }\n                }\n    \n                this.encode = function(image,quality) // image data object\n                {\n                    // var time_start = new Date().getTime();\n    \n                    if(quality) setQuality(quality);\n    \n                    // Initialize bit writer\n                    byteout = new Array();\n                    bytenew=0;\n                    bytepos=7;\n    \n                    // Add JPEG headers\n                    writeWord(0xFFD8); // SOI\n                    writeAPP0();\n                    writeDQT();\n                    writeSOF0(image.width,image.height);\n                    writeDHT();\n                    writeSOS();\n    \n    \n                    // Encode 8x8 macroblocks\n                    var DCY=0;\n                    var DCU=0;\n                    var DCV=0;\n    \n                    bytenew=0;\n                    bytepos=7;\n    \n    \n                    this.encode.displayName = \"_encode_\";\n    \n                    var imageData = image.data;\n                    var width = image.width;\n                    var height = image.height;\n    \n                    var quadWidth = width*4;\n                    var tripleWidth = width*3;\n    \n                    var x, y = 0;\n                    var r, g, b;\n                    var start,p, col,row,pos;\n                    while(y < height){\n                        x = 0;\n                        while(x < quadWidth){\n                        start = quadWidth * y + x;\n                        p = start;\n                        col = -1;\n                        row = 0;\n    \n                        for(pos=0; pos < 64; pos++){\n                            row = pos >> 3;// /8\n                            col = ( pos & 7 ) * 4; // %8\n                            p = start + ( row * quadWidth ) + col;\n    \n                            if(y+row >= height){ // padding bottom\n                                p-= (quadWidth*(y+1+row-height));\n                            }\n    \n                            if(x+col >= quadWidth){ // padding right\n                                p-= ((x+col) - quadWidth +4)\n                            }\n    \n                            r = imageData[ p++ ];\n                            g = imageData[ p++ ];\n                            b = imageData[ p++ ];\n    \n    \n                            /* // calculate YUV values dynamically\n                            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                            */\n    \n                            // use lookup table (slightly faster)\n                            YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                            UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n    \n                        }\n    \n                        DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                        DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                        DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                        x+=32;\n                        }\n                        y+=8;\n                    }\n    \n    \n                    ////////////////////////////////////////////////////////////////\n    \n                    // Do the bit alignment of the EOI marker\n                    if ( bytepos >= 0 ) {\n                        var fillbits = [];\n                        fillbits[1] = bytepos+1;\n                        fillbits[0] = (1<<(bytepos+1))-1;\n                        writeBits(fillbits);\n                    }\n    \n                    writeWord(0xFFD9); //EOI\n    \n                    var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n    \n                    byteout = [];\n    \n                    // benchmarking\n                    // var duration = new Date().getTime() - time_start;\n                    // console.log('Encoding time: '+ currentQuality + 'ms');\n                    //\n    \n                    return jpegDataUri\n            }\n    \n            function setQuality(quality){\n                if (quality <= 0) {\n                    quality = 1;\n                }\n                if (quality > 100) {\n                    quality = 100;\n                }\n    \n                if(currentQuality == quality) return // don't recalc if unchanged\n    \n                var sf = 0;\n                if (quality < 50) {\n                    sf = Math.floor(5000 / quality);\n                } else {\n                    sf = Math.floor(200 - quality*2);\n                }\n    \n                initQuantTables(sf);\n                currentQuality = quality;\n                // console.log('Quality set to: '+quality +'%');\n            }\n    \n            function init(){\n                // var time_start = new Date().getTime();\n                if(!quality) quality = 50;\n                // Create tables\n                initCharLookupTable()\n                initHuffmanTbl();\n                initCategoryNumber();\n                initRGBYUVTable();\n    \n                setQuality(quality);\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Initialization '+ duration + 'ms');\n            }\n    \n            init();\n    \n        };\n    \n        JPEGEncoder.encode = function( data, quality ) {\n            var encoder = new JPEGEncoder( quality );\n    \n            return encoder.encode( data );\n        }\n    \n        return JPEGEncoder;\n    });\n    /**\n     * @fileOverview Fix android canvas.toDataUrl bug.\n     */\n    define('runtime/html5/androidpatch',[\n        'runtime/html5/util',\n        'runtime/html5/jpegencoder',\n        'base'\n    ], function( Util, encoder, Base ) {\n        var origin = Util.canvasToDataUrl,\n            supportJpeg;\n    \n        Util.canvasToDataUrl = function( canvas, type, quality ) {\n            var ctx, w, h, fragement, parts;\n    \n            // 非android手机直接跳过。\n            if ( !Base.os.android ) {\n                return origin.apply( null, arguments );\n            }\n    \n            // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n            // JPEG 前两位分别是：255, 216\n            if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n                fragement = origin.apply( null, arguments );\n    \n                parts = fragement.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    fragement = atob( parts[ 1 ] );\n                } else {\n                    fragement = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                fragement = fragement.substring( 0, 2 );\n    \n                supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                        fragement.charCodeAt( 1 ) === 216;\n            }\n    \n            // 只有在android环境下才修复\n            if ( type === 'image/jpeg' && !supportJpeg ) {\n                w = canvas.width;\n                h = canvas.height;\n                ctx = canvas.getContext('2d');\n    \n                return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n            }\n    \n            return origin.apply( null, arguments );\n        };\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/html5/md5',[\n        'runtime/html5/runtime'\n    ], function( FlashRuntime ) {\n    \n        /*\n         * Fastest md5 implementation around (JKM md5)\n         * Credits: Joseph Myers\n         *\n         * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n         * @see http://jsperf.com/md5-shootout/7\n         */\n    \n        /* this function is much faster,\n          so if possible we use it. Some IEs\n          are the only ones I know of that\n          need the idiotic second function,\n          generated by an if clause.  */\n        var add32 = function (a, b) {\n            return (a + b) & 0xFFFFFFFF;\n        },\n    \n        cmn = function (q, a, b, x, s, t) {\n            a = add32(add32(a, q), add32(x, t));\n            return add32((a << s) | (a >>> (32 - s)), b);\n        },\n    \n        ff = function (a, b, c, d, x, s, t) {\n            return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n        },\n    \n        gg = function (a, b, c, d, x, s, t) {\n            return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n        },\n    \n        hh = function (a, b, c, d, x, s, t) {\n            return cmn(b ^ c ^ d, a, b, x, s, t);\n        },\n    \n        ii = function (a, b, c, d, x, s, t) {\n            return cmn(c ^ (b | (~d)), a, b, x, s, t);\n        },\n    \n        md5cycle = function (x, k) {\n            var a = x[0],\n                b = x[1],\n                c = x[2],\n                d = x[3];\n    \n            a = ff(a, b, c, d, k[0], 7, -680876936);\n            d = ff(d, a, b, c, k[1], 12, -389564586);\n            c = ff(c, d, a, b, k[2], 17, 606105819);\n            b = ff(b, c, d, a, k[3], 22, -1044525330);\n            a = ff(a, b, c, d, k[4], 7, -176418897);\n            d = ff(d, a, b, c, k[5], 12, 1200080426);\n            c = ff(c, d, a, b, k[6], 17, -1473231341);\n            b = ff(b, c, d, a, k[7], 22, -45705983);\n            a = ff(a, b, c, d, k[8], 7, 1770035416);\n            d = ff(d, a, b, c, k[9], 12, -1958414417);\n            c = ff(c, d, a, b, k[10], 17, -42063);\n            b = ff(b, c, d, a, k[11], 22, -1990404162);\n            a = ff(a, b, c, d, k[12], 7, 1804603682);\n            d = ff(d, a, b, c, k[13], 12, -40341101);\n            c = ff(c, d, a, b, k[14], 17, -1502002290);\n            b = ff(b, c, d, a, k[15], 22, 1236535329);\n    \n            a = gg(a, b, c, d, k[1], 5, -165796510);\n            d = gg(d, a, b, c, k[6], 9, -1069501632);\n            c = gg(c, d, a, b, k[11], 14, 643717713);\n            b = gg(b, c, d, a, k[0], 20, -373897302);\n            a = gg(a, b, c, d, k[5], 5, -701558691);\n            d = gg(d, a, b, c, k[10], 9, 38016083);\n            c = gg(c, d, a, b, k[15], 14, -660478335);\n            b = gg(b, c, d, a, k[4], 20, -405537848);\n            a = gg(a, b, c, d, k[9], 5, 568446438);\n            d = gg(d, a, b, c, k[14], 9, -1019803690);\n            c = gg(c, d, a, b, k[3], 14, -187363961);\n            b = gg(b, c, d, a, k[8], 20, 1163531501);\n            a = gg(a, b, c, d, k[13], 5, -1444681467);\n            d = gg(d, a, b, c, k[2], 9, -51403784);\n            c = gg(c, d, a, b, k[7], 14, 1735328473);\n            b = gg(b, c, d, a, k[12], 20, -1926607734);\n    \n            a = hh(a, b, c, d, k[5], 4, -378558);\n            d = hh(d, a, b, c, k[8], 11, -2022574463);\n            c = hh(c, d, a, b, k[11], 16, 1839030562);\n            b = hh(b, c, d, a, k[14], 23, -35309556);\n            a = hh(a, b, c, d, k[1], 4, -1530992060);\n            d = hh(d, a, b, c, k[4], 11, 1272893353);\n            c = hh(c, d, a, b, k[7], 16, -155497632);\n            b = hh(b, c, d, a, k[10], 23, -1094730640);\n            a = hh(a, b, c, d, k[13], 4, 681279174);\n            d = hh(d, a, b, c, k[0], 11, -358537222);\n            c = hh(c, d, a, b, k[3], 16, -722521979);\n            b = hh(b, c, d, a, k[6], 23, 76029189);\n            a = hh(a, b, c, d, k[9], 4, -640364487);\n            d = hh(d, a, b, c, k[12], 11, -421815835);\n            c = hh(c, d, a, b, k[15], 16, 530742520);\n            b = hh(b, c, d, a, k[2], 23, -995338651);\n    \n            a = ii(a, b, c, d, k[0], 6, -198630844);\n            d = ii(d, a, b, c, k[7], 10, 1126891415);\n            c = ii(c, d, a, b, k[14], 15, -1416354905);\n            b = ii(b, c, d, a, k[5], 21, -57434055);\n            a = ii(a, b, c, d, k[12], 6, 1700485571);\n            d = ii(d, a, b, c, k[3], 10, -1894986606);\n            c = ii(c, d, a, b, k[10], 15, -1051523);\n            b = ii(b, c, d, a, k[1], 21, -2054922799);\n            a = ii(a, b, c, d, k[8], 6, 1873313359);\n            d = ii(d, a, b, c, k[15], 10, -30611744);\n            c = ii(c, d, a, b, k[6], 15, -1560198380);\n            b = ii(b, c, d, a, k[13], 21, 1309151649);\n            a = ii(a, b, c, d, k[4], 6, -145523070);\n            d = ii(d, a, b, c, k[11], 10, -1120210379);\n            c = ii(c, d, a, b, k[2], 15, 718787259);\n            b = ii(b, c, d, a, k[9], 21, -343485551);\n    \n            x[0] = add32(a, x[0]);\n            x[1] = add32(b, x[1]);\n            x[2] = add32(c, x[2]);\n            x[3] = add32(d, x[3]);\n        },\n    \n        /* there needs to be support for Unicode here,\n           * unless we pretend that we can redefine the MD-5\n           * algorithm for multi-byte characters (perhaps\n           * by adding every four 16-bit characters and\n           * shortening the sum to 32 bits). Otherwise\n           * I suggest performing MD-5 as if every character\n           * was two bytes--e.g., 0040 0025 = @%--but then\n           * how will an ordinary MD-5 sum be matched?\n           * There is no way to standardize text to something\n           * like UTF-8 before transformation; speed cost is\n           * utterly prohibitive. The JavaScript standard\n           * itself needs to look at this: it should start\n           * providing access to strings as preformed UTF-8\n           * 8-bit unsigned value arrays.\n           */\n        md5blk = function (s) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n            }\n            return md5blks;\n        },\n    \n        md5blk_array = function (a) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n            }\n            return md5blks;\n        },\n    \n        md51 = function (s) {\n            var n = s.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk(s.substring(i - 64, i)));\n            }\n            s = s.substring(i - 64);\n            length = s.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n            }\n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n            return state;\n        },\n    \n        md51_array = function (a) {\n            var n = a.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n            }\n    \n            // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n            // containing the last element of the parent array if the sub array specified starts\n            // beyond the length of the parent array - weird.\n            // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n            a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n    \n            length = a.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= a[i] << ((i % 4) << 3);\n            }\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n    \n            return state;\n        },\n    \n        hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],\n    \n        rhex = function (n) {\n            var s = '',\n                j;\n            for (j = 0; j < 4; j += 1) {\n                s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n            }\n            return s;\n        },\n    \n        hex = function (x) {\n            var i;\n            for (i = 0; i < x.length; i += 1) {\n                x[i] = rhex(x[i]);\n            }\n            return x.join('');\n        },\n    \n        md5 = function (s) {\n            return hex(md51(s));\n        },\n    \n    \n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * SparkMD5 OOP implementation.\n         *\n         * Use this class to perform an incremental md5, otherwise use the\n         * static methods instead.\n         */\n        SparkMD5 = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n    \n        // In some cases the fast add32 function cannot be used..\n        if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {\n            add32 = function (x, y) {\n                var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                    msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n                return (msw << 16) | (lsw & 0xFFFF);\n            };\n        }\n    \n    \n        /**\n         * Appends a string.\n         * A conversion will be applied if an utf8 string is detected.\n         *\n         * @param {String} str The string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.append = function (str) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            // then append as binary\n            this.appendBinary(str);\n    \n            return this;\n        };\n    \n        /**\n         * Appends a binary string.\n         *\n         * @param {String} contents The binary string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.appendBinary = function (contents) {\n            this._buff += contents;\n            this._length += contents.length;\n    \n            var length = this._buff.length,\n                i;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));\n            }\n    \n            this._buff = this._buff.substr(i - 64);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                i,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        /**\n         * Finish the final calculation based on the tail.\n         *\n         * @param {Array}  tail   The tail (will be modified)\n         * @param {Number} length The length of the remaining buffer\n         */\n        SparkMD5.prototype._finish = function (tail, length) {\n            var i = length,\n                tmp,\n                lo,\n                hi;\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(this._state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Do the final computation based on the tail and length\n            // Beware that the final length may not fit in 32 bits so we take care of that\n            tmp = this._length * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n            md5cycle(this._state, tail);\n        };\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.reset = function () {\n            this._buff = \"\";\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.prototype.destroy = function () {\n            delete this._state;\n            delete this._buff;\n            delete this._length;\n        };\n    \n    \n        /**\n         * Performs the md5 hash on a string.\n         * A conversion will be applied if utf8 string is detected.\n         *\n         * @param {String}  str The string\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hash = function (str, raw) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            var hash = md51(str);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * Performs the md5 hash on a binary string.\n         *\n         * @param {String}  content The binary string\n         * @param {Boolean} raw     True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hashBinary = function (content, raw) {\n            var hash = md51(content);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * SparkMD5 OOP implementation for array buffers.\n         *\n         * Use this class to perform an incremental md5 ONLY for array buffers.\n         */\n        SparkMD5.ArrayBuffer = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * Appends an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array to be appended\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n            // TODO: we could avoid the concatenation here but the algorithm would be more complex\n            //       if you find yourself needing extra performance, please make a PR.\n            var buff = this._concatArrayBuffer(this._buff, arr),\n                length = buff.length,\n                i;\n    \n            this._length += arr.byteLength;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));\n            }\n    \n            // Avoids IE10 weirdness (documented above)\n            this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                i,\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.reset = function () {\n            this._buff = new Uint8Array(0);\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n    \n        /**\n         * Concats two array buffers, returning a new one.\n         *\n         * @param  {ArrayBuffer} first  The first array buffer\n         * @param  {ArrayBuffer} second The second array buffer\n         *\n         * @return {ArrayBuffer} The new array buffer\n         */\n        SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {\n            var firstLength = first.length,\n                result = new Uint8Array(firstLength + second.byteLength);\n    \n            result.set(first);\n            result.set(new Uint8Array(second), firstLength);\n    \n            return result;\n        };\n    \n        /**\n         * Performs the md5 hash on an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array buffer\n         * @param {Boolean}     raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n            var hash = md51_array(new Uint8Array(arr));\n    \n            return !!raw ? hash : hex(hash);\n        };\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( file ) {\n                var blob = file.getSource(),\n                    chunkSize = 2 * 1024 * 1024,\n                    chunks = Math.ceil( blob.size / chunkSize ),\n                    chunk = 0,\n                    owner = this.owner,\n                    spark = new SparkMD5.ArrayBuffer(),\n                    me = this,\n                    blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice,\n                    loadNext, fr;\n    \n                fr = new FileReader();\n    \n                loadNext = function() {\n                    var start, end;\n    \n                    start = chunk * chunkSize;\n                    end = Math.min( start + chunkSize, blob.size );\n    \n                    fr.onload = function( e ) {\n                        spark.append( e.target.result );\n                        owner.trigger( 'progress', {\n                            total: file.size,\n                            loaded: end\n                        });\n                    };\n    \n                    fr.onloadend = function() {\n                        fr.onloadend = fr.onload = null;\n    \n                        if ( ++chunk < chunks ) {\n                            setTimeout( loadNext, 1 );\n                        } else {\n                            setTimeout(function(){\n                                owner.trigger('load');\n                                me.result = spark.end();\n                                loadNext = file = blob = spark = null;\n                                owner.trigger('complete');\n                            }, 50 );\n                        }\n                    };\n    \n                    fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) );\n                };\n    \n                loadNext();\n            },\n    \n            getResult: function() {\n                return this.result;\n            }\n        });\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview 图片压缩\n     */\n    define('runtime/flash/image',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Md5 flash实现\n     */\n    define('runtime/flash/md5',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( blob ) {\n                return this.flashExec( 'Md5', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview 完全版本。\n     */\n    define('preset/all',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n        'widgets/md5',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/androidpatch',\n        'runtime/html5/image',\n        'runtime/html5/transport',\n        'runtime/html5/md5',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/image',\n        'runtime/flash/transport',\n        'runtime/flash/blob',\n        'runtime/flash/md5'\n    ], function( Base ) {\n        return Base;\n    });\n    /**\n     * @fileOverview 日志组件，主要用来收集错误信息，可以帮助 webuploader 更好的定位问题和发展。\n     *\n     * 如果您不想要启用此功能，请在打包的时候去掉 log 模块。\n     *\n     * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。\n     *\n     * 如：\n     * WebUploader.create({\n     *     ...\n     *\n     *     disableWidgets: 'log',\n     *\n     *     ...\n     * })\n     */\n    define('widgets/log',[\n        'base',\n        'uploader',\n        'widgets/widget'\n    ], function( Base, Uploader ) {\n        var $ = Base.$,\n            logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??',\n            product = (location.hostname || location.host || 'protected').toLowerCase(),\n    \n            // 只针对 baidu 内部产品用户做统计功能。\n            enable = product && /baidu/i.exec(product),\n            base;\n    \n        if (!enable) {\n            return;\n        }\n    \n        base = {\n            dv: 3,\n            master: 'webuploader',\n            online: /test/.exec(product) ? 0 : 1,\n            module: '',\n            product: product,\n            type: 0\n        };\n    \n        function send(data) {\n            var obj = $.extend({}, base, data),\n                url = logUrl.replace(/^(.*)\\?/, '$1' + $.param( obj )),\n                image = new Image();\n    \n            image.src = url;\n        }\n    \n        return Uploader.register({\n            name: 'log',\n    \n            init: function() {\n                var owner = this.owner,\n                    count = 0,\n                    size = 0;\n    \n                owner\n                    .on('error', function(code) {\n                        send({\n                            type: 2,\n                            c_error_code: code\n                        });\n                    })\n                    .on('uploadError', function(file, reason) {\n                        send({\n                            type: 2,\n                            c_error_code: 'UPLOAD_ERROR',\n                            c_reason: '' + reason\n                        });\n                    })\n                    .on('uploadComplete', function(file) {\n                        count++;\n                        size += file.size;\n                    }).\n                    on('uploadFinished', function() {\n                        send({\n                            c_count: count,\n                            c_size: size\n                        });\n                        count = size = 0;\n                    });\n    \n                send({\n                    c_usage: 1\n                });\n            }\n        });\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('webuploader',[\n        'preset/all',\n        'widgets/log'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.noimage.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview 没有图像处理的版本。\n     */\n    define('preset/withoutimage',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/transport',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/transport',\n        'runtime/flash/blob'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/withoutimage'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.nolog.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Md5\n     */\n    define('lib/md5',[\n        'runtime/client',\n        'mediator'\n    ], function( RuntimeClient, Mediator ) {\n    \n        function Md5() {\n            RuntimeClient.call( this, 'Md5' );\n        }\n    \n        // 让 Md5 具备事件功能。\n        Mediator.installTo( Md5.prototype );\n    \n        Md5.prototype.loadFromBlob = function( blob ) {\n            var me = this;\n    \n            if ( me.getRuid() ) {\n                me.disconnectRuntime();\n            }\n    \n            // 连接到blob归属的同一个runtime.\n            me.connectRuntime( blob.ruid, function() {\n                me.exec('init');\n                me.exec( 'loadFromBlob', blob );\n            });\n        };\n    \n        Md5.prototype.getResult = function() {\n            return this.exec('getResult');\n        };\n    \n        return Md5;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/md5',[\n        'base',\n        'uploader',\n        'lib/md5',\n        'lib/blob',\n        'widgets/widget'\n    ], function( Base, Uploader, Md5, Blob ) {\n    \n        return Uploader.register({\n            name: 'md5',\n    \n    \n            /**\n             * 计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。\n             *\n             *\n             * @method md5File\n             * @grammar md5File( file[, start[, end]] ) => promise\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.md5File( file )\n             *\n             *         // 及时显示进度\n             *         .progress(function(percentage) {\n             *             console.log('Percentage:', percentage);\n             *         })\n             *\n             *         // 完成\n             *         .then(function(val) {\n             *             console.log('md5 result:', val);\n             *         });\n             *\n             * });\n             */\n            md5File: function( file, start, end ) {\n                var md5 = new Md5(),\n                    deferred = Base.Deferred(),\n                    blob = (file instanceof Blob) ? file :\n                        this.request( 'get-file', file ).source;\n    \n                md5.on( 'progress load', function( e ) {\n                    e = e || {};\n                    deferred.notify( e.total ? e.loaded / e.total : 1 );\n                });\n    \n                md5.on( 'complete', function() {\n                    deferred.resolve( md5.getResult() );\n                });\n    \n                md5.on( 'error', function( reason ) {\n                    deferred.reject( reason );\n                });\n    \n                if ( arguments.length > 1 ) {\n                    start = start || 0;\n                    end = end || 0;\n                    start < 0 && (start = blob.size + start);\n                    end < 0 && (end = blob.size + end);\n                    end = Math.min( end, blob.size );\n                    blob = blob.slice( start, end );\n                }\n    \n                md5.loadFromBlob( blob );\n    \n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n     * android里面toDataUrl('image/jpege')得到的结果却是png.\n     *\n     * 所以这里没辙，只能借助这个工具\n     * @fileOverview jpeg encoder\n     */\n    define('runtime/html5/jpegencoder',[], function( require, exports, module ) {\n    \n        /*\n          Copyright (c) 2008, Adobe Systems Incorporated\n          All rights reserved.\n    \n          Redistribution and use in source and binary forms, with or without\n          modification, are permitted provided that the following conditions are\n          met:\n    \n          * Redistributions of source code must retain the above copyright notice,\n            this list of conditions and the following disclaimer.\n    \n          * Redistributions in binary form must reproduce the above copyright\n            notice, this list of conditions and the following disclaimer in the\n            documentation and/or other materials provided with the distribution.\n    \n          * Neither the name of Adobe Systems Incorporated nor the names of its\n            contributors may be used to endorse or promote products derived from\n            this software without specific prior written permission.\n    \n          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n          IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n          THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n          PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n          CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n          EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n          PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n          PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n          LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n          NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        */\n        /*\n        JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n    \n        Basic GUI blocking jpeg encoder\n        */\n    \n        function JPEGEncoder(quality) {\n          var self = this;\n            var fround = Math.round;\n            var ffloor = Math.floor;\n            var YTable = new Array(64);\n            var UVTable = new Array(64);\n            var fdtbl_Y = new Array(64);\n            var fdtbl_UV = new Array(64);\n            var YDC_HT;\n            var UVDC_HT;\n            var YAC_HT;\n            var UVAC_HT;\n    \n            var bitcode = new Array(65535);\n            var category = new Array(65535);\n            var outputfDCTQuant = new Array(64);\n            var DU = new Array(64);\n            var byteout = [];\n            var bytenew = 0;\n            var bytepos = 7;\n    \n            var YDU = new Array(64);\n            var UDU = new Array(64);\n            var VDU = new Array(64);\n            var clt = new Array(256);\n            var RGB_YUV_TABLE = new Array(2048);\n            var currentQuality;\n    \n            var ZigZag = [\n                     0, 1, 5, 6,14,15,27,28,\n                     2, 4, 7,13,16,26,29,42,\n                     3, 8,12,17,25,30,41,43,\n                     9,11,18,24,31,40,44,53,\n                    10,19,23,32,39,45,52,54,\n                    20,22,33,38,46,51,55,60,\n                    21,34,37,47,50,56,59,61,\n                    35,36,48,49,57,58,62,63\n                ];\n    \n            var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n            var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n            var std_ac_luminance_values = [\n                    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n            var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n            var std_ac_chrominance_values = [\n                    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            function initQuantTables(sf){\n                    var YQT = [\n                        16, 11, 10, 16, 24, 40, 51, 61,\n                        12, 12, 14, 19, 26, 58, 60, 55,\n                        14, 13, 16, 24, 40, 57, 69, 56,\n                        14, 17, 22, 29, 51, 87, 80, 62,\n                        18, 22, 37, 56, 68,109,103, 77,\n                        24, 35, 55, 64, 81,104,113, 92,\n                        49, 64, 78, 87,103,121,120,101,\n                        72, 92, 95, 98,112,100,103, 99\n                    ];\n    \n                    for (var i = 0; i < 64; i++) {\n                        var t = ffloor((YQT[i]*sf+50)/100);\n                        if (t < 1) {\n                            t = 1;\n                        } else if (t > 255) {\n                            t = 255;\n                        }\n                        YTable[ZigZag[i]] = t;\n                    }\n                    var UVQT = [\n                        17, 18, 24, 47, 99, 99, 99, 99,\n                        18, 21, 26, 66, 99, 99, 99, 99,\n                        24, 26, 56, 99, 99, 99, 99, 99,\n                        47, 66, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99\n                    ];\n                    for (var j = 0; j < 64; j++) {\n                        var u = ffloor((UVQT[j]*sf+50)/100);\n                        if (u < 1) {\n                            u = 1;\n                        } else if (u > 255) {\n                            u = 255;\n                        }\n                        UVTable[ZigZag[j]] = u;\n                    }\n                    var aasf = [\n                        1.0, 1.387039845, 1.306562965, 1.175875602,\n                        1.0, 0.785694958, 0.541196100, 0.275899379\n                    ];\n                    var k = 0;\n                    for (var row = 0; row < 8; row++)\n                    {\n                        for (var col = 0; col < 8; col++)\n                        {\n                            fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            k++;\n                        }\n                    }\n                }\n    \n                function computeHuffmanTbl(nrcodes, std_table){\n                    var codevalue = 0;\n                    var pos_in_table = 0;\n                    var HT = new Array();\n                    for (var k = 1; k <= 16; k++) {\n                        for (var j = 1; j <= nrcodes[k]; j++) {\n                            HT[std_table[pos_in_table]] = [];\n                            HT[std_table[pos_in_table]][0] = codevalue;\n                            HT[std_table[pos_in_table]][1] = k;\n                            pos_in_table++;\n                            codevalue++;\n                        }\n                        codevalue*=2;\n                    }\n                    return HT;\n                }\n    \n                function initHuffmanTbl()\n                {\n                    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n                }\n    \n                function initCategoryNumber()\n                {\n                    var nrlower = 1;\n                    var nrupper = 2;\n                    for (var cat = 1; cat <= 15; cat++) {\n                        //Positive numbers\n                        for (var nr = nrlower; nr<nrupper; nr++) {\n                            category[32767+nr] = cat;\n                            bitcode[32767+nr] = [];\n                            bitcode[32767+nr][1] = cat;\n                            bitcode[32767+nr][0] = nr;\n                        }\n                        //Negative numbers\n                        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                            category[32767+nrneg] = cat;\n                            bitcode[32767+nrneg] = [];\n                            bitcode[32767+nrneg][1] = cat;\n                            bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                        }\n                        nrlower <<= 1;\n                        nrupper <<= 1;\n                    }\n                }\n    \n                function initRGBYUVTable() {\n                    for(var i = 0; i < 256;i++) {\n                        RGB_YUV_TABLE[i]            =  19595 * i;\n                        RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                        RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                        RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                        RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                        RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                        RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                        RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                    }\n                }\n    \n                // IO functions\n                function writeBits(bs)\n                {\n                    var value = bs[0];\n                    var posval = bs[1]-1;\n                    while ( posval >= 0 ) {\n                        if (value & (1 << posval) ) {\n                            bytenew |= (1 << bytepos);\n                        }\n                        posval--;\n                        bytepos--;\n                        if (bytepos < 0) {\n                            if (bytenew == 0xFF) {\n                                writeByte(0xFF);\n                                writeByte(0);\n                            }\n                            else {\n                                writeByte(bytenew);\n                            }\n                            bytepos=7;\n                            bytenew=0;\n                        }\n                    }\n                }\n    \n                function writeByte(value)\n                {\n                    byteout.push(clt[value]); // write char directly instead of converting later\n                }\n    \n                function writeWord(value)\n                {\n                    writeByte((value>>8)&0xFF);\n                    writeByte((value   )&0xFF);\n                }\n    \n                // DCT & quantization core\n                function fDCTQuant(data, fdtbl)\n                {\n                    var d0, d1, d2, d3, d4, d5, d6, d7;\n                    /* Pass 1: process rows. */\n                    var dataOff=0;\n                    var i;\n                    var I8 = 8;\n                    var I64 = 64;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff+1];\n                        d2 = data[dataOff+2];\n                        d3 = data[dataOff+3];\n                        d4 = data[dataOff+4];\n                        d5 = data[dataOff+5];\n                        d6 = data[dataOff+6];\n                        d7 = data[dataOff+7];\n    \n                        var tmp0 = d0 + d7;\n                        var tmp7 = d0 - d7;\n                        var tmp1 = d1 + d6;\n                        var tmp6 = d1 - d6;\n                        var tmp2 = d2 + d5;\n                        var tmp5 = d2 - d5;\n                        var tmp3 = d3 + d4;\n                        var tmp4 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                        var tmp13 = tmp0 - tmp3;\n                        var tmp11 = tmp1 + tmp2;\n                        var tmp12 = tmp1 - tmp2;\n    \n                        data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                        data[dataOff+4] = tmp10 - tmp11;\n    \n                        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                        data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                        data[dataOff+6] = tmp13 - z1;\n    \n                        /* Odd part */\n                        tmp10 = tmp4 + tmp5; /* phase 2 */\n                        tmp11 = tmp5 + tmp6;\n                        tmp12 = tmp6 + tmp7;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                        var z3 = tmp11 * 0.707106781; /* c4 */\n    \n                        var z11 = tmp7 + z3;    /* phase 5 */\n                        var z13 = tmp7 - z3;\n    \n                        data[dataOff+5] = z13 + z2; /* phase 6 */\n                        data[dataOff+3] = z13 - z2;\n                        data[dataOff+1] = z11 + z4;\n                        data[dataOff+7] = z11 - z4;\n    \n                        dataOff += 8; /* advance pointer to next row */\n                    }\n    \n                    /* Pass 2: process columns. */\n                    dataOff = 0;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff + 8];\n                        d2 = data[dataOff + 16];\n                        d3 = data[dataOff + 24];\n                        d4 = data[dataOff + 32];\n                        d5 = data[dataOff + 40];\n                        d6 = data[dataOff + 48];\n                        d7 = data[dataOff + 56];\n    \n                        var tmp0p2 = d0 + d7;\n                        var tmp7p2 = d0 - d7;\n                        var tmp1p2 = d1 + d6;\n                        var tmp6p2 = d1 - d6;\n                        var tmp2p2 = d2 + d5;\n                        var tmp5p2 = d2 - d5;\n                        var tmp3p2 = d3 + d4;\n                        var tmp4p2 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                        var tmp13p2 = tmp0p2 - tmp3p2;\n                        var tmp11p2 = tmp1p2 + tmp2p2;\n                        var tmp12p2 = tmp1p2 - tmp2p2;\n    \n                        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                        data[dataOff+32] = tmp10p2 - tmp11p2;\n    \n                        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                        data[dataOff+48] = tmp13p2 - z1p2;\n    \n                        /* Odd part */\n                        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                        tmp11p2 = tmp5p2 + tmp6p2;\n                        tmp12p2 = tmp6p2 + tmp7p2;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                        var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n    \n                        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                        var z13p2 = tmp7p2 - z3p2;\n    \n                        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                        data[dataOff+24] = z13p2 - z2p2;\n                        data[dataOff+ 8] = z11p2 + z4p2;\n                        data[dataOff+56] = z11p2 - z4p2;\n    \n                        dataOff++; /* advance pointer to next column */\n                    }\n    \n                    // Quantize/descale the coefficients\n                    var fDCTQuant;\n                    for (i=0; i<I64; ++i)\n                    {\n                        // Apply the quantization and scaling factor & Round to nearest integer\n                        fDCTQuant = data[i]*fdtbl[i];\n                        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                        //outputfDCTQuant[i] = fround(fDCTQuant);\n    \n                    }\n                    return outputfDCTQuant;\n                }\n    \n                function writeAPP0()\n                {\n                    writeWord(0xFFE0); // marker\n                    writeWord(16); // length\n                    writeByte(0x4A); // J\n                    writeByte(0x46); // F\n                    writeByte(0x49); // I\n                    writeByte(0x46); // F\n                    writeByte(0); // = \"JFIF\",'\\0'\n                    writeByte(1); // versionhi\n                    writeByte(1); // versionlo\n                    writeByte(0); // xyunits\n                    writeWord(1); // xdensity\n                    writeWord(1); // ydensity\n                    writeByte(0); // thumbnwidth\n                    writeByte(0); // thumbnheight\n                }\n    \n                function writeSOF0(width, height)\n                {\n                    writeWord(0xFFC0); // marker\n                    writeWord(17);   // length, truecolor YUV JPG\n                    writeByte(8);    // precision\n                    writeWord(height);\n                    writeWord(width);\n                    writeByte(3);    // nrofcomponents\n                    writeByte(1);    // IdY\n                    writeByte(0x11); // HVY\n                    writeByte(0);    // QTY\n                    writeByte(2);    // IdU\n                    writeByte(0x11); // HVU\n                    writeByte(1);    // QTU\n                    writeByte(3);    // IdV\n                    writeByte(0x11); // HVV\n                    writeByte(1);    // QTV\n                }\n    \n                function writeDQT()\n                {\n                    writeWord(0xFFDB); // marker\n                    writeWord(132);    // length\n                    writeByte(0);\n                    for (var i=0; i<64; i++) {\n                        writeByte(YTable[i]);\n                    }\n                    writeByte(1);\n                    for (var j=0; j<64; j++) {\n                        writeByte(UVTable[j]);\n                    }\n                }\n    \n                function writeDHT()\n                {\n                    writeWord(0xFFC4); // marker\n                    writeWord(0x01A2); // length\n    \n                    writeByte(0); // HTYDCinfo\n                    for (var i=0; i<16; i++) {\n                        writeByte(std_dc_luminance_nrcodes[i+1]);\n                    }\n                    for (var j=0; j<=11; j++) {\n                        writeByte(std_dc_luminance_values[j]);\n                    }\n    \n                    writeByte(0x10); // HTYACinfo\n                    for (var k=0; k<16; k++) {\n                        writeByte(std_ac_luminance_nrcodes[k+1]);\n                    }\n                    for (var l=0; l<=161; l++) {\n                        writeByte(std_ac_luminance_values[l]);\n                    }\n    \n                    writeByte(1); // HTUDCinfo\n                    for (var m=0; m<16; m++) {\n                        writeByte(std_dc_chrominance_nrcodes[m+1]);\n                    }\n                    for (var n=0; n<=11; n++) {\n                        writeByte(std_dc_chrominance_values[n]);\n                    }\n    \n                    writeByte(0x11); // HTUACinfo\n                    for (var o=0; o<16; o++) {\n                        writeByte(std_ac_chrominance_nrcodes[o+1]);\n                    }\n                    for (var p=0; p<=161; p++) {\n                        writeByte(std_ac_chrominance_values[p]);\n                    }\n                }\n    \n                function writeSOS()\n                {\n                    writeWord(0xFFDA); // marker\n                    writeWord(12); // length\n                    writeByte(3); // nrofcomponents\n                    writeByte(1); // IdY\n                    writeByte(0); // HTY\n                    writeByte(2); // IdU\n                    writeByte(0x11); // HTU\n                    writeByte(3); // IdV\n                    writeByte(0x11); // HTV\n                    writeByte(0); // Ss\n                    writeByte(0x3f); // Se\n                    writeByte(0); // Bf\n                }\n    \n                function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                    var EOB = HTAC[0x00];\n                    var M16zeroes = HTAC[0xF0];\n                    var pos;\n                    var I16 = 16;\n                    var I63 = 63;\n                    var I64 = 64;\n                    var DU_DCT = fDCTQuant(CDU, fdtbl);\n                    //ZigZag reorder\n                    for (var j=0;j<I64;++j) {\n                        DU[ZigZag[j]]=DU_DCT[j];\n                    }\n                    var Diff = DU[0] - DC; DC = DU[0];\n                    //Encode DC\n                    if (Diff==0) {\n                        writeBits(HTDC[0]); // Diff might be 0\n                    } else {\n                        pos = 32767+Diff;\n                        writeBits(HTDC[category[pos]]);\n                        writeBits(bitcode[pos]);\n                    }\n                    //Encode ACs\n                    var end0pos = 63; // was const... which is crazy\n                    for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                    //end0pos = first element in reverse order !=0\n                    if ( end0pos == 0) {\n                        writeBits(EOB);\n                        return DC;\n                    }\n                    var i = 1;\n                    var lng;\n                    while ( i <= end0pos ) {\n                        var startpos = i;\n                        for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                        var nrzeroes = i-startpos;\n                        if ( nrzeroes >= I16 ) {\n                            lng = nrzeroes>>4;\n                            for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                                writeBits(M16zeroes);\n                            nrzeroes = nrzeroes&0xF;\n                        }\n                        pos = 32767+DU[i];\n                        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                        writeBits(bitcode[pos]);\n                        i++;\n                    }\n                    if ( end0pos != I63 ) {\n                        writeBits(EOB);\n                    }\n                    return DC;\n                }\n    \n                function initCharLookupTable(){\n                    var sfcc = String.fromCharCode;\n                    for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                        clt[i] = sfcc(i);\n                    }\n                }\n    \n                this.encode = function(image,quality) // image data object\n                {\n                    // var time_start = new Date().getTime();\n    \n                    if(quality) setQuality(quality);\n    \n                    // Initialize bit writer\n                    byteout = new Array();\n                    bytenew=0;\n                    bytepos=7;\n    \n                    // Add JPEG headers\n                    writeWord(0xFFD8); // SOI\n                    writeAPP0();\n                    writeDQT();\n                    writeSOF0(image.width,image.height);\n                    writeDHT();\n                    writeSOS();\n    \n    \n                    // Encode 8x8 macroblocks\n                    var DCY=0;\n                    var DCU=0;\n                    var DCV=0;\n    \n                    bytenew=0;\n                    bytepos=7;\n    \n    \n                    this.encode.displayName = \"_encode_\";\n    \n                    var imageData = image.data;\n                    var width = image.width;\n                    var height = image.height;\n    \n                    var quadWidth = width*4;\n                    var tripleWidth = width*3;\n    \n                    var x, y = 0;\n                    var r, g, b;\n                    var start,p, col,row,pos;\n                    while(y < height){\n                        x = 0;\n                        while(x < quadWidth){\n                        start = quadWidth * y + x;\n                        p = start;\n                        col = -1;\n                        row = 0;\n    \n                        for(pos=0; pos < 64; pos++){\n                            row = pos >> 3;// /8\n                            col = ( pos & 7 ) * 4; // %8\n                            p = start + ( row * quadWidth ) + col;\n    \n                            if(y+row >= height){ // padding bottom\n                                p-= (quadWidth*(y+1+row-height));\n                            }\n    \n                            if(x+col >= quadWidth){ // padding right\n                                p-= ((x+col) - quadWidth +4)\n                            }\n    \n                            r = imageData[ p++ ];\n                            g = imageData[ p++ ];\n                            b = imageData[ p++ ];\n    \n    \n                            /* // calculate YUV values dynamically\n                            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                            */\n    \n                            // use lookup table (slightly faster)\n                            YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                            UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n    \n                        }\n    \n                        DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                        DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                        DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                        x+=32;\n                        }\n                        y+=8;\n                    }\n    \n    \n                    ////////////////////////////////////////////////////////////////\n    \n                    // Do the bit alignment of the EOI marker\n                    if ( bytepos >= 0 ) {\n                        var fillbits = [];\n                        fillbits[1] = bytepos+1;\n                        fillbits[0] = (1<<(bytepos+1))-1;\n                        writeBits(fillbits);\n                    }\n    \n                    writeWord(0xFFD9); //EOI\n    \n                    var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n    \n                    byteout = [];\n    \n                    // benchmarking\n                    // var duration = new Date().getTime() - time_start;\n                    // console.log('Encoding time: '+ currentQuality + 'ms');\n                    //\n    \n                    return jpegDataUri\n            }\n    \n            function setQuality(quality){\n                if (quality <= 0) {\n                    quality = 1;\n                }\n                if (quality > 100) {\n                    quality = 100;\n                }\n    \n                if(currentQuality == quality) return // don't recalc if unchanged\n    \n                var sf = 0;\n                if (quality < 50) {\n                    sf = Math.floor(5000 / quality);\n                } else {\n                    sf = Math.floor(200 - quality*2);\n                }\n    \n                initQuantTables(sf);\n                currentQuality = quality;\n                // console.log('Quality set to: '+quality +'%');\n            }\n    \n            function init(){\n                // var time_start = new Date().getTime();\n                if(!quality) quality = 50;\n                // Create tables\n                initCharLookupTable()\n                initHuffmanTbl();\n                initCategoryNumber();\n                initRGBYUVTable();\n    \n                setQuality(quality);\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Initialization '+ duration + 'ms');\n            }\n    \n            init();\n    \n        };\n    \n        JPEGEncoder.encode = function( data, quality ) {\n            var encoder = new JPEGEncoder( quality );\n    \n            return encoder.encode( data );\n        }\n    \n        return JPEGEncoder;\n    });\n    /**\n     * @fileOverview Fix android canvas.toDataUrl bug.\n     */\n    define('runtime/html5/androidpatch',[\n        'runtime/html5/util',\n        'runtime/html5/jpegencoder',\n        'base'\n    ], function( Util, encoder, Base ) {\n        var origin = Util.canvasToDataUrl,\n            supportJpeg;\n    \n        Util.canvasToDataUrl = function( canvas, type, quality ) {\n            var ctx, w, h, fragement, parts;\n    \n            // 非android手机直接跳过。\n            if ( !Base.os.android ) {\n                return origin.apply( null, arguments );\n            }\n    \n            // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n            // JPEG 前两位分别是：255, 216\n            if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n                fragement = origin.apply( null, arguments );\n    \n                parts = fragement.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    fragement = atob( parts[ 1 ] );\n                } else {\n                    fragement = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                fragement = fragement.substring( 0, 2 );\n    \n                supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                        fragement.charCodeAt( 1 ) === 216;\n            }\n    \n            // 只有在android环境下才修复\n            if ( type === 'image/jpeg' && !supportJpeg ) {\n                w = canvas.width;\n                h = canvas.height;\n                ctx = canvas.getContext('2d');\n    \n                return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n            }\n    \n            return origin.apply( null, arguments );\n        };\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/html5/md5',[\n        'runtime/html5/runtime'\n    ], function( FlashRuntime ) {\n    \n        /*\n         * Fastest md5 implementation around (JKM md5)\n         * Credits: Joseph Myers\n         *\n         * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n         * @see http://jsperf.com/md5-shootout/7\n         */\n    \n        /* this function is much faster,\n          so if possible we use it. Some IEs\n          are the only ones I know of that\n          need the idiotic second function,\n          generated by an if clause.  */\n        var add32 = function (a, b) {\n            return (a + b) & 0xFFFFFFFF;\n        },\n    \n        cmn = function (q, a, b, x, s, t) {\n            a = add32(add32(a, q), add32(x, t));\n            return add32((a << s) | (a >>> (32 - s)), b);\n        },\n    \n        ff = function (a, b, c, d, x, s, t) {\n            return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n        },\n    \n        gg = function (a, b, c, d, x, s, t) {\n            return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n        },\n    \n        hh = function (a, b, c, d, x, s, t) {\n            return cmn(b ^ c ^ d, a, b, x, s, t);\n        },\n    \n        ii = function (a, b, c, d, x, s, t) {\n            return cmn(c ^ (b | (~d)), a, b, x, s, t);\n        },\n    \n        md5cycle = function (x, k) {\n            var a = x[0],\n                b = x[1],\n                c = x[2],\n                d = x[3];\n    \n            a = ff(a, b, c, d, k[0], 7, -680876936);\n            d = ff(d, a, b, c, k[1], 12, -389564586);\n            c = ff(c, d, a, b, k[2], 17, 606105819);\n            b = ff(b, c, d, a, k[3], 22, -1044525330);\n            a = ff(a, b, c, d, k[4], 7, -176418897);\n            d = ff(d, a, b, c, k[5], 12, 1200080426);\n            c = ff(c, d, a, b, k[6], 17, -1473231341);\n            b = ff(b, c, d, a, k[7], 22, -45705983);\n            a = ff(a, b, c, d, k[8], 7, 1770035416);\n            d = ff(d, a, b, c, k[9], 12, -1958414417);\n            c = ff(c, d, a, b, k[10], 17, -42063);\n            b = ff(b, c, d, a, k[11], 22, -1990404162);\n            a = ff(a, b, c, d, k[12], 7, 1804603682);\n            d = ff(d, a, b, c, k[13], 12, -40341101);\n            c = ff(c, d, a, b, k[14], 17, -1502002290);\n            b = ff(b, c, d, a, k[15], 22, 1236535329);\n    \n            a = gg(a, b, c, d, k[1], 5, -165796510);\n            d = gg(d, a, b, c, k[6], 9, -1069501632);\n            c = gg(c, d, a, b, k[11], 14, 643717713);\n            b = gg(b, c, d, a, k[0], 20, -373897302);\n            a = gg(a, b, c, d, k[5], 5, -701558691);\n            d = gg(d, a, b, c, k[10], 9, 38016083);\n            c = gg(c, d, a, b, k[15], 14, -660478335);\n            b = gg(b, c, d, a, k[4], 20, -405537848);\n            a = gg(a, b, c, d, k[9], 5, 568446438);\n            d = gg(d, a, b, c, k[14], 9, -1019803690);\n            c = gg(c, d, a, b, k[3], 14, -187363961);\n            b = gg(b, c, d, a, k[8], 20, 1163531501);\n            a = gg(a, b, c, d, k[13], 5, -1444681467);\n            d = gg(d, a, b, c, k[2], 9, -51403784);\n            c = gg(c, d, a, b, k[7], 14, 1735328473);\n            b = gg(b, c, d, a, k[12], 20, -1926607734);\n    \n            a = hh(a, b, c, d, k[5], 4, -378558);\n            d = hh(d, a, b, c, k[8], 11, -2022574463);\n            c = hh(c, d, a, b, k[11], 16, 1839030562);\n            b = hh(b, c, d, a, k[14], 23, -35309556);\n            a = hh(a, b, c, d, k[1], 4, -1530992060);\n            d = hh(d, a, b, c, k[4], 11, 1272893353);\n            c = hh(c, d, a, b, k[7], 16, -155497632);\n            b = hh(b, c, d, a, k[10], 23, -1094730640);\n            a = hh(a, b, c, d, k[13], 4, 681279174);\n            d = hh(d, a, b, c, k[0], 11, -358537222);\n            c = hh(c, d, a, b, k[3], 16, -722521979);\n            b = hh(b, c, d, a, k[6], 23, 76029189);\n            a = hh(a, b, c, d, k[9], 4, -640364487);\n            d = hh(d, a, b, c, k[12], 11, -421815835);\n            c = hh(c, d, a, b, k[15], 16, 530742520);\n            b = hh(b, c, d, a, k[2], 23, -995338651);\n    \n            a = ii(a, b, c, d, k[0], 6, -198630844);\n            d = ii(d, a, b, c, k[7], 10, 1126891415);\n            c = ii(c, d, a, b, k[14], 15, -1416354905);\n            b = ii(b, c, d, a, k[5], 21, -57434055);\n            a = ii(a, b, c, d, k[12], 6, 1700485571);\n            d = ii(d, a, b, c, k[3], 10, -1894986606);\n            c = ii(c, d, a, b, k[10], 15, -1051523);\n            b = ii(b, c, d, a, k[1], 21, -2054922799);\n            a = ii(a, b, c, d, k[8], 6, 1873313359);\n            d = ii(d, a, b, c, k[15], 10, -30611744);\n            c = ii(c, d, a, b, k[6], 15, -1560198380);\n            b = ii(b, c, d, a, k[13], 21, 1309151649);\n            a = ii(a, b, c, d, k[4], 6, -145523070);\n            d = ii(d, a, b, c, k[11], 10, -1120210379);\n            c = ii(c, d, a, b, k[2], 15, 718787259);\n            b = ii(b, c, d, a, k[9], 21, -343485551);\n    \n            x[0] = add32(a, x[0]);\n            x[1] = add32(b, x[1]);\n            x[2] = add32(c, x[2]);\n            x[3] = add32(d, x[3]);\n        },\n    \n        /* there needs to be support for Unicode here,\n           * unless we pretend that we can redefine the MD-5\n           * algorithm for multi-byte characters (perhaps\n           * by adding every four 16-bit characters and\n           * shortening the sum to 32 bits). Otherwise\n           * I suggest performing MD-5 as if every character\n           * was two bytes--e.g., 0040 0025 = @%--but then\n           * how will an ordinary MD-5 sum be matched?\n           * There is no way to standardize text to something\n           * like UTF-8 before transformation; speed cost is\n           * utterly prohibitive. The JavaScript standard\n           * itself needs to look at this: it should start\n           * providing access to strings as preformed UTF-8\n           * 8-bit unsigned value arrays.\n           */\n        md5blk = function (s) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n            }\n            return md5blks;\n        },\n    \n        md5blk_array = function (a) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n            }\n            return md5blks;\n        },\n    \n        md51 = function (s) {\n            var n = s.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk(s.substring(i - 64, i)));\n            }\n            s = s.substring(i - 64);\n            length = s.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n            }\n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n            return state;\n        },\n    \n        md51_array = function (a) {\n            var n = a.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n            }\n    \n            // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n            // containing the last element of the parent array if the sub array specified starts\n            // beyond the length of the parent array - weird.\n            // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n            a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n    \n            length = a.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= a[i] << ((i % 4) << 3);\n            }\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n    \n            return state;\n        },\n    \n        hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],\n    \n        rhex = function (n) {\n            var s = '',\n                j;\n            for (j = 0; j < 4; j += 1) {\n                s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n            }\n            return s;\n        },\n    \n        hex = function (x) {\n            var i;\n            for (i = 0; i < x.length; i += 1) {\n                x[i] = rhex(x[i]);\n            }\n            return x.join('');\n        },\n    \n        md5 = function (s) {\n            return hex(md51(s));\n        },\n    \n    \n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * SparkMD5 OOP implementation.\n         *\n         * Use this class to perform an incremental md5, otherwise use the\n         * static methods instead.\n         */\n        SparkMD5 = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n    \n        // In some cases the fast add32 function cannot be used..\n        if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {\n            add32 = function (x, y) {\n                var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                    msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n                return (msw << 16) | (lsw & 0xFFFF);\n            };\n        }\n    \n    \n        /**\n         * Appends a string.\n         * A conversion will be applied if an utf8 string is detected.\n         *\n         * @param {String} str The string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.append = function (str) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            // then append as binary\n            this.appendBinary(str);\n    \n            return this;\n        };\n    \n        /**\n         * Appends a binary string.\n         *\n         * @param {String} contents The binary string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.appendBinary = function (contents) {\n            this._buff += contents;\n            this._length += contents.length;\n    \n            var length = this._buff.length,\n                i;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));\n            }\n    \n            this._buff = this._buff.substr(i - 64);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                i,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        /**\n         * Finish the final calculation based on the tail.\n         *\n         * @param {Array}  tail   The tail (will be modified)\n         * @param {Number} length The length of the remaining buffer\n         */\n        SparkMD5.prototype._finish = function (tail, length) {\n            var i = length,\n                tmp,\n                lo,\n                hi;\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(this._state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Do the final computation based on the tail and length\n            // Beware that the final length may not fit in 32 bits so we take care of that\n            tmp = this._length * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n            md5cycle(this._state, tail);\n        };\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.reset = function () {\n            this._buff = \"\";\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.prototype.destroy = function () {\n            delete this._state;\n            delete this._buff;\n            delete this._length;\n        };\n    \n    \n        /**\n         * Performs the md5 hash on a string.\n         * A conversion will be applied if utf8 string is detected.\n         *\n         * @param {String}  str The string\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hash = function (str, raw) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            var hash = md51(str);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * Performs the md5 hash on a binary string.\n         *\n         * @param {String}  content The binary string\n         * @param {Boolean} raw     True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hashBinary = function (content, raw) {\n            var hash = md51(content);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * SparkMD5 OOP implementation for array buffers.\n         *\n         * Use this class to perform an incremental md5 ONLY for array buffers.\n         */\n        SparkMD5.ArrayBuffer = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * Appends an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array to be appended\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n            // TODO: we could avoid the concatenation here but the algorithm would be more complex\n            //       if you find yourself needing extra performance, please make a PR.\n            var buff = this._concatArrayBuffer(this._buff, arr),\n                length = buff.length,\n                i;\n    \n            this._length += arr.byteLength;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));\n            }\n    \n            // Avoids IE10 weirdness (documented above)\n            this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                i,\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.reset = function () {\n            this._buff = new Uint8Array(0);\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n    \n        /**\n         * Concats two array buffers, returning a new one.\n         *\n         * @param  {ArrayBuffer} first  The first array buffer\n         * @param  {ArrayBuffer} second The second array buffer\n         *\n         * @return {ArrayBuffer} The new array buffer\n         */\n        SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {\n            var firstLength = first.length,\n                result = new Uint8Array(firstLength + second.byteLength);\n    \n            result.set(first);\n            result.set(new Uint8Array(second), firstLength);\n    \n            return result;\n        };\n    \n        /**\n         * Performs the md5 hash on an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array buffer\n         * @param {Boolean}     raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n            var hash = md51_array(new Uint8Array(arr));\n    \n            return !!raw ? hash : hex(hash);\n        };\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( file ) {\n                var blob = file.getSource(),\n                    chunkSize = 2 * 1024 * 1024,\n                    chunks = Math.ceil( blob.size / chunkSize ),\n                    chunk = 0,\n                    owner = this.owner,\n                    spark = new SparkMD5.ArrayBuffer(),\n                    me = this,\n                    blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice,\n                    loadNext, fr;\n    \n                fr = new FileReader();\n    \n                loadNext = function() {\n                    var start, end;\n    \n                    start = chunk * chunkSize;\n                    end = Math.min( start + chunkSize, blob.size );\n    \n                    fr.onload = function( e ) {\n                        spark.append( e.target.result );\n                        owner.trigger( 'progress', {\n                            total: file.size,\n                            loaded: end\n                        });\n                    };\n    \n                    fr.onloadend = function() {\n                        fr.onloadend = fr.onload = null;\n    \n                        if ( ++chunk < chunks ) {\n                            setTimeout( loadNext, 1 );\n                        } else {\n                            setTimeout(function(){\n                                owner.trigger('load');\n                                me.result = spark.end();\n                                loadNext = file = blob = spark = null;\n                                owner.trigger('complete');\n                            }, 50 );\n                        }\n                    };\n    \n                    fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) );\n                };\n    \n                loadNext();\n            },\n    \n            getResult: function() {\n                return this.result;\n            }\n        });\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview 图片压缩\n     */\n    define('runtime/flash/image',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Md5 flash实现\n     */\n    define('runtime/flash/md5',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( blob ) {\n                return this.flashExec( 'Md5', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview 完全版本。\n     */\n    define('preset/all',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n        'widgets/md5',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/androidpatch',\n        'runtime/html5/image',\n        'runtime/html5/transport',\n        'runtime/html5/md5',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/image',\n        'runtime/flash/transport',\n        'runtime/flash/blob',\n        'runtime/flash/md5'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/all'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "dist/webuploader.withoutimage.js",
    "content": "/*! WebUploader 0.1.5 */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     */\n    define('dollar-third',[],function() {\n        var $ = window.__dollar || window.jQuery || window.Zepto;\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.5',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numofInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClent, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClent.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file'\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button;\n    \n                button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n    \n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor} 指定选择文件的按钮容器，不指定则不创建按钮。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Arroy} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n                \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numofDeleted` 被移除的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numofDeleted: 0,\n                numofInterrupt: 0,\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    file.destroy();\n                    this.stats.numofDeleted++;\n                }\n            },\n    \n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numofInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numofInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发，此事件的handler返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \n                me.owner.trigger( 'filesQueued', files );\n    \n                if ( me.options.auto ) {\n                    setTimeout(function() {\n                        me.request('start-upload');\n                    }, 20 );\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 对于一个文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n            /**\n             * @property {Object} [method='POST']\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST`或者`GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                owner.on( 'uploadComplete', function( file ) {\n                    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        $.each( me.pool, function( _, v ) {\n                        \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                        });\n                        \n                        file.setStatus( Status.QUEUED );\n                    } else if (file.getStatus() === Status.PROGRESS) {\n                        return;\n                    } else {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    return;\n                }\n    \n                me.runing = true;\n    \n                // 如果有暂停的，则续传\n                $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        file.setStatus( Status.PROGRESS );\n                        me._trigged = false;\n                        v.transport && v.transport.send();\n                    }\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n                    $.each( me.pool, function( _, v ) {\n                        \n                        // 只 abort 指定的文件。\n                        if (v.file !== file) {\n                            return;\n                        }\n    \n                        v.transport && v.transport.abort();\n                        me._putback(v);\n                        me._popBlock(v);\n                    });\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uplaode`r是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 掉过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numofInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n                            \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS || \n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n                    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                promise = me.request( 'before-send', block, function() {\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else {\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = me.options,\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    var totalPercent = 0,\n                        uploaded = 0;\n    \n                    // 可能没有abort掉，progress还是执行进来了。\n                    // if ( !file.blocks ) {\n                    //     return;\n                    // }\n    \n                    totalPercent = block.percentage = percentage;\n    \n                    if ( block.chunks > 1 ) {    // 计算文件的整体速度。\n                        $.each( file.blocks, function( _, v ) {\n                            uploaded += (v.percentage || 0) * (v.end - v.start);\n                        });\n    \n                        totalPercent = uploaded / file.size;\n                    }\n    \n                    owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort'.indexOf( type ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n                        tr.send();\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            }\n    \n        });\n    });\n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                if ( data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n                \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function() {\r\n                    input.trigger('click');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                input.on( 'change', function( e ) {\r\n                    var fn = arguments.callee,\r\n                        clone;\r\n    \r\n                    me.files = e.target.files;\r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', fn )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                });\r\n    \r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        return me.trigger( 'error', 'server' );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server';\n                    } else {\n                        err = 'http';\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n                            \n                        p = window.JSON && window.JSON.parse || function( s ) {\n                            try {\n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n                            \n                        // }\n                    }\n                    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', 'http' );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    /**\n     * @fileOverview 没有图像处理的版本。\n     */\n    define('preset/withoutimage',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/transport',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/transport'\n    ], function( Base ) {\n        return Base;\n    });\n    define('webuploader',[\n        'preset/withoutimage'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "examples/cropper/cropper.css",
    "content": "/*!\n * Cropper v0.3.2\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright 2014 Fengyuan Chen\n * Released under the MIT license\n */\n\n.cropper-container {\n    position: relative;\n    overflow: hidden;\n    -webkit-user-select: none;\n       -moz-user-select: none;\n        -ms-user-select: none;\n            user-select: none;\n    background-color: #fff;\n\n    -webkit-tap-highlight-color: transparent;\n    -webkit-touch-callout: none;\n}\n\n.cropper-container .cropper-modal {\n    position: absolute;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    left: 0;\n    background: #000;\n    filter: alpha(opacity=50);\n    opacity: .5;\n}\n\n.cropper-container .cropper-dragger {\n    position: absolute;\n    top: 10%;\n    left: 10%;\n    width: 80%;\n    height: 80%;\n}\n\n.cropper-container .cropper-preview {\n    display: block;\n    width: 100%;\n    height: 100%;\n    -webkit-box-sizing: border-box;\n       -moz-box-sizing: border-box;\n            box-sizing: border-box;\n    overflow: hidden;\n    border-color: #69f;\n    border-color: rgba(51, 102, 255, .75);\n    border-style: solid;\n    border-width: 1px;\n}\n\n.cropper-container .cropper-dashed {\n    position: absolute;\n    display: block;\n    filter: alpha(opacity=50);\n    border: 0 dashed #fff;\n    opacity: .5;\n}\n\n.cropper-container .dashed-h {\n    top: 33.3%;\n    left: 0;\n    width: 100%;\n    height: 33.3%;\n    border-top-width: 1px;\n    border-bottom-width: 1px;\n}\n\n.cropper-container .dashed-v {\n    top: 0;\n    left: 33.3%;\n    width: 33.3%;\n    height: 100%;\n    border-right-width: 1px;\n    border-left-width: 1px;\n}\n\n.cropper-container .cropper-face,\n.cropper-container .cropper-line,\n.cropper-container .cropper-point {\n    position: absolute;\n    display: block;\n    width: 100%;\n    height: 100%;\n    filter: alpha(opacity=10);\n    opacity: .1;\n}\n\n.cropper-container .cropper-face {\n    top: 0;\n    left: 0;\n    cursor: move;\n    background-color: #fff;\n}\n\n.cropper-container .cropper-line {\n    background-color: #69f;\n}\n\n.cropper-container .line-e {\n    top: 0;\n    right: -2px;\n    width: 5px;\n    cursor: e-resize;\n}\n\n.cropper-container .line-n {\n    top: -2px;\n    left: 0;\n    height: 5px;\n    cursor: n-resize;\n}\n\n.cropper-container .line-w {\n    top: 0;\n    left: -2px;\n    width: 5px;\n    cursor: w-resize;\n}\n\n.cropper-container .line-s {\n    bottom: -2px;\n    left: 0;\n    height: 5px;\n    cursor: s-resize;\n}\n\n.cropper-container .cropper-point {\n    width: 5px;\n    height: 5px;\n    background-color: #69f;\n    filter: alpha(opacity=75);\n    opacity: .75;\n}\n\n.cropper-container .point-e {\n    top: 49%;\n    right: -2px;\n    cursor: e-resize;\n}\n\n.cropper-container .point-n {\n    top: -2px;\n    left: 49%;\n    cursor: n-resize;\n}\n\n.cropper-container .point-w {\n    top: 49%;\n    left: -2px;\n    cursor: w-resize;\n}\n\n.cropper-container .point-s {\n    bottom: -2px;\n    left: 49%;\n    cursor: s-resize;\n}\n\n.cropper-container .point-ne {\n    top: -2px;\n    right: -2px;\n    cursor: ne-resize;\n}\n\n.cropper-container .point-nw {\n    top: -2px;\n    left: -2px;\n    cursor: nw-resize;\n}\n\n.cropper-container .point-sw {\n    bottom: -2px;\n    left: -2px;\n    cursor: sw-resize;\n}\n\n.cropper-container .point-se {\n    right: -2px;\n    bottom: -2px;\n    width: 20px;\n    height: 20px;\n    cursor: se-resize;\n    filter: alpha(opacity=100);\n    opacity: 1;\n}\n\n.cropper-container .point-se:before {\n    position: absolute;\n    right: -50%;\n    bottom: -50%;\n    display: block;\n    width: 200%;\n    height: 200%;\n    content: \" \";\n    background-color: #69f;\n    filter: alpha(opacity=0);\n    opacity: 0;\n}\n\n@media (min-width: 768px) {\n    .cropper-container .point-se {\n        width: 15px;\n        height: 15px;\n    }\n}\n\n@media (min-width: 992px) {\n    .cropper-container .point-se {\n        width: 10px;\n        height: 10px;\n    }\n}\n\n@media (min-width: 1200px) {\n    .cropper-container .point-se {\n        width: 5px;\n        height: 5px;\n        filter: alpha(opacity=75);\n        opacity: .75;\n    }\n}\n\n.cropper-hidden {\n    display: none !important;\n}\n"
  },
  {
    "path": "examples/cropper/cropper.js",
    "content": "/*!\n * Cropper v0.3.2\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright 2014 Fengyuan Chen\n * Released under the MIT license\n */\n\n(function(factory) {\n    if (typeof define === \"function\" && define.amd) {\n        // AMD. Register as anonymous module.\n        define([\"jquery\"], factory);\n    } else {\n        // Browser globals.\n        factory(jQuery);\n    }\n}(function($) {\n\n    \"use strict\";\n\n    var $window = $(window),\n        Cropper = function(element, options) {\n            options = $.isPlainObject(options) ? options : {};\n            this.$image = $(element);\n            this.defaults = $.extend({}, Cropper.defaults, this.$image.data(), options);\n            this.init();\n        };\n\n    Cropper.prototype = {\n        construstor: Cropper,\n\n        init: function() {\n            this.setAspectRatio(this.defaults.aspectRatio);\n            this.render();\n        },\n\n        render: function(callback) {\n            var that = this,\n                $image = this.$image,\n                $clone,\n                src;\n\n            if (this.active) {\n                return;\n            }\n\n            if (this.$clone) {\n                this.$clone.remove(); // Remove the old clone\n            }\n\n            src = $image.attr(\"src\"); // Don't use \"prop\"\n            $clone = $('<img src=\"' + src + '\">');\n\n            $clone.on(\"load\", function() {\n                var image;\n\n                $clone.off(\"load\");\n\n                if (this.naturalWidth && this.naturalHeight) {\n                    image = {\n                        naturalHeight: this.naturalHeight,\n                        naturalWidth: this.naturalWidth\n                    };\n                } else {\n                    Cropper.fn.size($clone, {\n                        height: \"auto\",\n                        width: \"auto\"\n                    });\n\n                    image = Cropper.fn.size($clone);\n                    image = {\n                        naturalHeight: image.height,\n                        naturalWidth: image.width\n                    };\n                }\n\n                Cropper.fn.size($clone, {\n                    height: \"100%\",\n                    width: \"100%\"\n                });\n\n                image.aspectRatio = image.naturalWidth / image.naturalHeight;\n                that.src = src;\n                that.image = image;\n                that.active = true;\n                that.createCropper();\n            });\n\n            if ($.isFunction(callback)) {\n                $image.on(\"ready.cropper\", callback);\n            }\n\n            this.$clone = $clone;\n            $image.after($clone);\n        },\n\n        unrender: function() {\n            if (this.active) {\n                this.active = false;\n                this.removeCropper();\n                this.src = \"\";\n                this.image = null;\n                this.cropper = null;\n                this.dragger = null;\n            }\n        },\n\n        rerender: function() {\n            this.unrender();\n            this.render();\n        },\n\n        resize: function() {\n            clearTimeout(this.resizing);\n            this.resizing = setTimeout($.proxy(this.rerender, this), 200);\n        },\n\n        createCropper: function() {\n            this.$cropper = $(Cropper.template);\n            this.$dragger = this.$cropper.find(\".cropper-dragger\");\n            Cropper.fn.toggle(this.$image);\n            this.$image.after(this.$cropper);\n            this.$cropper.prepend(this.$clone);\n\n            if (!this.defaults.modal) {\n                Cropper.fn.toggle(this.$cropper.find(\".cropper-modal\"));\n            }\n\n            this.setPreview();\n            this.addListener();\n        },\n\n        removeCropper: function() {\n            this.removeListener();\n            this.$preview = null;\n            this.$clone.remove();\n            this.$clone = null;\n            this.$dragger = null;\n            this.$cropper.remove();\n            this.$cropper = null;\n            Cropper.fn.toggle(this.$image);\n        },\n\n        addListener: function() {\n            this.$cropper.bind(\"mousedown touchstart\", $.proxy(this.dragstart, this));\n            this.$cropper.bind(\"mousemove touchmove\", $.proxy(this.dragmove, this));\n            this.$cropper.bind(\"mouseup mouseleave touchend touchleave\", $.proxy(this.dragend, this));\n            $window.on(\"resize\", $.proxy(this.resize, this));\n        },\n\n        removeListener: function() {\n            this.$cropper.unbind(\"mousedown touchstart\", this.dragstart);\n            this.$cropper.unbind(\"mousemove touchmove\", this.dragmove);\n            this.$cropper.unbind(\"mouseup mouseleave touchend touchleave\", this.dragend);\n            $window.off(\"resize\", this.resize);\n        },\n\n        setPreview: function() {\n            var preview = this.defaults.preview;\n\n            this.$preview = this.$cropper.find(\".cropper-preview\");\n\n            if (typeof preview === \"string\" && preview.length > 0) {\n                this.$preview = this.$preview.add(preview);\n            }\n\n            this.$preview.html('<img src=\"' + this.src + '\">');\n            this.setCropper();\n        },\n\n        setCropper: function() {\n            var $container = this.$image.parent(),\n                container = Cropper.fn.size($container),\n                image = this.image,\n                cropper;\n\n            if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) {\n                cropper = {\n                    height: container.width / image.aspectRatio,\n                    width: container.width,\n                    left: 0\n                };\n\n                cropper.top = (container.height - cropper.height) / 2;\n            } else {\n                cropper = {\n                    height: container.height,\n                    width: container.height * image.aspectRatio,\n                    top: 0\n                };\n\n                cropper.left = (container.width - cropper.width) / 2;\n            }\n\n            $.each(cropper, function(i, n) {\n                cropper[i] = Math.round(n);\n            });\n\n            image.height = cropper.height;\n            image.width = cropper.width;\n            image.ratio = image.width / image.naturalWidth;\n\n            Cropper.fn.position($container);\n            this.$cropper.css({\n                height: cropper.height,\n                left: cropper.left,\n                top: cropper.top,\n                width: cropper.width\n            });\n\n            this.cropper = cropper;\n            this.setDragger();\n        },\n\n        setDragger: function() {\n            var cropper = this.cropper,\n                // If not set, use the original aspect ratio of the image.\n                aspectRatio = this.defaults.aspectRatio || this.image.aspectRatio,\n                dragger;\n\n            if (((cropper.height * aspectRatio) - cropper.width) >= 0) {\n                dragger = {\n                    height: cropper.width / aspectRatio,\n                    width: cropper.width,\n                    left: 0,\n                    top: (cropper.height - (cropper.width / aspectRatio)) / 2,\n                    maxWidth: cropper.width,\n                    maxHeight: cropper.width / aspectRatio\n                };\n            } else {\n                dragger = {\n                    height: cropper.height,\n                    width: cropper.height * aspectRatio,\n                    left: (cropper.width - (cropper.height * aspectRatio)) / 2,\n                    top: 0,\n                    maxHeight: cropper.height,\n                    maxWidth: cropper.height * aspectRatio\n                };\n            }\n\n            dragger.height *= 0.8;\n            dragger.width *= 0.8;\n\n            dragger.left = (cropper.width - dragger.width) / 2;\n            dragger.top = (cropper.height - dragger.height) / 2;\n\n            this.dragger = Cropper.fn.round(dragger);\n            this.setData(this.defaults.data);\n            this.$image.trigger(\"ready.cropper\").off(\"ready.cropper\");\n        },\n\n        resetDragger: function() {\n            var dragger = this.dragger,\n                cropper = this.cropper;\n\n            dragger.width = dragger.width > dragger.maxWidth ? dragger.maxWidth : Math.abs(dragger.width);\n            dragger.height = dragger.height > dragger.maxHeight ? dragger.maxHeight : Math.abs(dragger.height);\n\n            dragger.maxLeft = cropper.width - dragger.width;\n            dragger.maxTop = cropper.height - dragger.height;\n\n            dragger.left = dragger.left < 0 ? 0 : dragger.left > dragger.maxLeft ? dragger.maxLeft : dragger.left;\n            dragger.top = dragger.top < 0 ? 0 : dragger.top > dragger.maxTop ? dragger.maxTop : dragger.top;\n\n            dragger = Cropper.fn.round(dragger);\n\n            this.$dragger.css({\n                height: dragger.height,\n                left: dragger.left,\n                top: dragger.top,\n                width: dragger.width\n            });\n\n            this.dragger = dragger;\n            this.preview();\n            this.output();\n        },\n\n        dragging: function() {\n            var direction = this.direction,\n                dragger = this.dragger,\n                aspectRatio = this.defaults.aspectRatio,\n                range = {\n                    x: this.endX - this.startX,\n                    y: this.endY - this.startY\n                };\n\n            if (aspectRatio) {\n                range.X = range.y * aspectRatio;\n                range.Y = range.x / aspectRatio;\n            }\n\n            switch (direction) {\n\n                // dragging\n                case \"e\":\n                    dragger.width += range.x;\n\n                    if (aspectRatio) {\n                        dragger.height = dragger.width / aspectRatio;\n                        dragger.top -= range.Y / 2;\n                    }\n\n                    if (dragger.width < 0) {\n                        this.direction = \"w\";\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                case \"n\":\n                    dragger.height -= range.y;\n                    dragger.top += range.y;\n\n                    if (aspectRatio) {\n                        dragger.width = dragger.height * aspectRatio;\n                        dragger.left += range.X / 2;\n                    }\n\n                    if (dragger.height < 0) {\n                        this.direction = \"s\";\n                        dragger.height = 0;\n                    }\n\n                    break;\n\n                case \"w\":\n                    dragger.width -= range.x;\n                    dragger.left += range.x;\n\n                    if (aspectRatio) {\n                        dragger.height = dragger.width / aspectRatio;\n                        dragger.top += range.Y / 2;\n                    }\n\n                    if (dragger.width < 0) {\n                        this.direction = \"e\";\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                case \"s\":\n                    dragger.height += range.y;\n\n                    if (aspectRatio) {\n                        dragger.width = dragger.height * aspectRatio;\n                        dragger.left -= range.X / 2;\n                    }\n\n                    if (dragger.height < 0) {\n                        this.direction = \"n\";\n                        dragger.height = 0;\n                    }\n\n                    break;\n\n                case \"ne\":\n                    dragger.height -= range.y;\n                    dragger.top += range.y;\n\n                    if (aspectRatio) {\n                        dragger.width = dragger.height * aspectRatio;\n                    } else {\n                        dragger.width += range.x;\n                    }\n\n                    if (dragger.height < 0) {\n                        this.direction = \"sw\";\n                        dragger.height = 0;\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                case \"nw\":\n                    dragger.height -= range.y;\n                    dragger.top += range.y;\n\n                    if (aspectRatio) {\n                        dragger.width = dragger.height * aspectRatio;\n                        dragger.left += range.X;\n                    } else {\n                        dragger.width -= range.x;\n                        dragger.left += range.x;\n                    }\n\n                    if (dragger.height < 0) {\n                        this.direction = \"se\";\n                        dragger.height = 0;\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                case \"sw\":\n                    dragger.width -= range.x;\n                    dragger.left += range.x;\n\n                    if (aspectRatio) {\n                        dragger.height = dragger.width / aspectRatio;\n                    } else {\n                        dragger.height += range.y;\n                    }\n\n                    if (dragger.width < 0) {\n                        this.direction = \"ne\";\n                        dragger.height = 0;\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                case \"se\":\n                    dragger.width += range.x;\n\n                    if (aspectRatio) {\n                        dragger.height = dragger.width / aspectRatio;\n                    } else {\n                        dragger.height += range.y;\n                    }\n\n                    if (dragger.width < 0) {\n                        this.direction = \"nw\";\n                        dragger.height = 0;\n                        dragger.width = 0;\n                    }\n\n                    break;\n\n                // moving\n                default:\n                    dragger.left += range.x;\n                    dragger.top += range.y;\n            }\n\n            this.resetDragger();\n            this.startX = this.endX;\n            this.startY = this.endY;\n        },\n\n        output: function() {\n            this.defaults.done(this.getData());\n        },\n\n        preview: function() {\n            var that = this,\n                cropper = that.cropper,\n                dragger = that.dragger;\n\n            this.$preview.each(function() {\n                var $this = $(this),\n                    ratio = $this.outerWidth() / dragger.width,\n                    styles = {\n                        height: cropper.height,\n                        marginLeft: - dragger.left,\n                        marginTop: - dragger.top,\n                        width: cropper.width\n                    };\n\n                $this.css({overflow: \"hidden\"});\n                $this.find(\"img\").css(Cropper.fn.round(styles, function(n) {\n                    return n * ratio;\n                }));\n            });\n        },\n\n        // Public methods\n\n        enable: function(callback) {\n            this.render(callback);\n        },\n\n        disable: function() {\n            this.unrender();\n        },\n\n        setAspectRatio: function(aspectRatio) {\n            if (aspectRatio === \"auto\" || ($.isNumeric(aspectRatio) && aspectRatio > 0)) {\n                this.defaults.aspectRatio = aspectRatio === \"auto\" ? NaN : aspectRatio;\n\n                if (this.active) {\n                    this.setDragger();\n                }\n            }\n        },\n\n        setData: function(data) {\n            var cropper = this.cropper,\n                dragger = this.dragger,\n                aspectRatio = this.defaults.aspectRatio,\n                isNumber = function(n) {\n                    return typeof n === \"number\";\n                };\n\n            if (!this.active) {\n                return;\n            }\n\n            if ($.isPlainObject(data) && !$.isEmptyObject(data)) {\n                data = Cropper.fn.transformData(data, this.image.ratio);\n\n                if (isNumber(data.x1) && data.x1 <= cropper.width) {\n                    dragger.left = data.x1;\n                }\n\n                if (isNumber(data.y1) && data.y1 <= cropper.height) {\n                    dragger.top = data.y1;\n                }\n\n                if (aspectRatio){\n                    if (isNumber(data.width) && data.width <= cropper.width) {\n                        dragger.width = data.width;\n                        dragger.height = dragger.width / aspectRatio;\n                    } else if (isNumber(data.height) && data.height <= cropper.height) {\n                        dragger.height = data.height;\n                        dragger.width = dragger.height * aspectRatio;\n                    } else if (isNumber(data.x2) && data.x2 <= cropper.width) {\n                        dragger.width = data.x2 - dragger.left;\n                        dragger.height = dragger.width / aspectRatio;\n                    } else if (isNumber(data.y2) && data.y2 <= cropper.height) {\n                        dragger.height = data.y2 - dragger.top;\n                        dragger.width = dragger.height * aspectRatio;\n                    }\n                } else {\n                    if (isNumber(data.width) && data.width <= cropper.width) {\n                        dragger.width = data.width;\n                    } else if (isNumber(data.x2) && data.x2 <= cropper.width) {\n                        dragger.width = data.x2 - dragger.left;\n                    }\n\n                    if (isNumber(data.height) && data.height <= cropper.height) {\n                        dragger.height = data.height;\n                    } else if (isNumber(data.y2) && data.height <= cropper.height) {\n                        dragger.height = data.y2 - dragger.top;\n                    }\n                }\n            }\n\n            this.dragger = dragger;\n            this.resetDragger();\n        },\n\n        getData: function() {\n            var dragger = this.dragger,\n                data = {};\n\n            if (this.active) {\n                data = {\n                    x1: dragger.left,\n                    y1: dragger.top,\n                    width: dragger.width,\n                    height: dragger.height,\n                    x2: dragger.left + dragger.width,\n                    y2: dragger.top + dragger.height\n                };\n\n                data = Cropper.fn.transformData(data, (1 / this.image.ratio));\n            }\n\n            return data;\n        },\n\n        setImgSrc: function(src) {\n            if (typeof src === \"string\" && src.length > 0 && src !== this.src) {\n                this.$image.attr(\"src\", src);\n                this.rerender();\n            }\n        },\n\n        getImgInfo: function() {\n            return this.image || {};\n        },\n\n        // Public events\n\n        dragstart: function(event) {\n            var touches = Cropper.fn.getOriginalEvent(event).touches,\n                e = event,\n                touching,\n                direction;\n\n            if (touches && touches.length === 1) {\n                e = touches[0];\n                this.touchId = e.identifier;\n                touching = true;\n            }\n\n            direction = $(e.target).data().direction;\n\n            if (Cropper.fn.isDirection(direction)) {\n                this.startX = e.pageX;\n                this.startY = e.pageY;\n                this.direction = direction;\n                this.$image.trigger(\"dragstart\");\n                touching && event.preventDefault();\n            }\n        },\n\n        dragmove: function(event) {\n            var touches = Cropper.fn.getOriginalEvent(event).changedTouches,\n                e = event,\n                touching;\n\n            if (touches && touches.length === 1) {\n                e = touches[0];\n                touching = true;\n\n                if (e.identifier !== this.touchId) {\n                    return;\n                }\n            }\n\n            if (this.direction) {\n                this.$image.trigger(\"dragmove\");\n                touching && event.preventDefault();\n                this.endX = e.pageX;\n                this.endY = e.pageY;\n                this.dragging();\n            }\n        },\n\n        dragend: function(event) {\n            var touches = Cropper.fn.getOriginalEvent(event).changedTouches,\n                e = event,\n                touching;\n\n            if (touches && touches.length === 1) {\n                e = touches[0];\n                touching = true;\n\n                if (e.identifier !== this.touchId) {\n                    return;\n                }\n            }\n\n            if (this.direction) {\n                this.direction = \"\";\n                this.$image.trigger(\"dragend\");\n                touching && event.preventDefault();\n            }\n        }\n    };\n\n    // Common methods\n    Cropper.fn = {\n        toggle: function($e) {\n            $e.toggleClass(\"cropper-hidden\");\n        },\n\n        position: function($e, option) {\n            var position = $e.css(\"position\");\n\n            if (position === \"static\") {\n                $e.css(\"position\", option || \"relative\");\n            }\n        },\n\n        size: function($e, options) {\n            if ($.isPlainObject(options)) {\n                $e.css(options);\n            } else {\n                return {\n                    height: $e.height(),\n                    width: $e.width()\n                };\n            }\n        },\n\n        round: function(data, fn) {\n            var value,\n                i;\n\n            for (i in data) {\n                value = data[i];\n\n                if (data.hasOwnProperty(i) && typeof value === \"number\") {\n                    data[i] = Math.round($.isFunction(fn) ? fn(value) : value);\n                }\n            }\n\n            return data;\n        },\n\n        transformData: function(data, ratio) {\n            var that = this,\n                result = {};\n\n            $.each(data, function(i, n) {\n                if (that.isDataOption(i) && $.isNumeric(n) && n >= 0) {\n                    result[i] = Math.round(n * ratio);\n                }\n            });\n\n            return result;\n        },\n\n        getOriginalEvent: function(event) {\n            if (event && typeof event.originalEvent !== \"undefined\") {\n               event = event.originalEvent;\n            }\n\n            return event;\n        },\n\n        isDataOption: function(s) {\n            return /^(x1|y1|x2|y2|width|height)$/i.test(s);\n        },\n\n        isDirection: function(s) {\n            return /^(\\*|e|n|w|s|ne|nw|sw|se)$/i.test(s);\n        }\n    };\n\n    Cropper.template = [\n        '<div class=\"cropper-container\">',\n            '<div class=\"cropper-modal\"></div>',\n            '<div class=\"cropper-dragger\">',\n                '<span class=\"cropper-preview\"></span>',\n                '<span class=\"cropper-dashed dashed-h\"></span>',\n                '<span class=\"cropper-dashed dashed-v\"></span>',\n                '<span class=\"cropper-face\" data-direction=\"*\"></span>',\n                '<span class=\"cropper-line line-e\" data-direction=\"e\"></span>',\n                '<span class=\"cropper-line line-n\" data-direction=\"n\"></span>',\n                '<span class=\"cropper-line line-w\" data-direction=\"w\"></span>',\n                '<span class=\"cropper-line line-s\" data-direction=\"s\"></span>',\n                '<span class=\"cropper-point point-e\" data-direction=\"e\"></span>',\n                '<span class=\"cropper-point point-n\" data-direction=\"n\"></span>',\n                '<span class=\"cropper-point point-w\" data-direction=\"w\"></span>',\n                '<span class=\"cropper-point point-s\" data-direction=\"s\"></span>',\n                '<span class=\"cropper-point point-ne\" data-direction=\"ne\"></span>',\n                '<span class=\"cropper-point point-nw\" data-direction=\"nw\"></span>',\n                '<span class=\"cropper-point point-sw\" data-direction=\"sw\"></span>',\n                '<span class=\"cropper-point point-se\" data-direction=\"se\"></span>',\n            '</div>',\n        '</div>'\n    ].join(\"\");\n\n    Cropper.defaults = {\n        aspectRatio: \"auto\",\n        data: {},\n        done: function(/* data */) {},\n        modal: true,\n        preview: \"\"\n    };\n\n    Cropper.setDefaults = function(options) {\n        $.extend(Cropper.defaults, options);\n    };\n\n    // Register as jQuery plugin\n    $.fn.cropper = function(options, settings) {\n        var result = this;\n\n        this.each(function() {\n            var $this = $(this),\n                data = $this.data(\"cropper\");\n\n            if (!data) {\n                data = new Cropper(this, options);\n                $this.data(\"cropper\", data);\n            }\n\n            if (typeof options === \"string\" && $.isFunction(data[options])) {\n                result = data[options](settings);\n            }\n        });\n\n        return (typeof result !== \"undefined\" ? result : this);\n    };\n\n    $.fn.cropper.Constructor = Cropper;\n    $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n    $(function() {\n        $(\"img[cropper]\").cropper();\n    });\n}));\n"
  },
  {
    "path": "examples/cropper/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示 - 带裁剪功能 </title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/webuploader.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./cropper.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div class=\"uploader-container\">\n            <div id=\"filePicker\">选择文件</div>\n        </div>\n\n        <!-- Croper container -->\n        <div class=\"cropper-wraper webuploader-element-invisible\">\n            <div class=\"img-container\">\n                <img src=\"\" alt=\"\" />\n            </div>\n\n            <div class=\"upload-btn\">上传所选区域</div>\n\n            <div class=\"img-preview\"></div>\n        </div>\n\n\n    </div>\n    <script type=\"text/javascript\" src=\"./jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../../dist/webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./cropper.js\"></script>\n    <script type=\"text/javascript\" src=\"./uploader.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/cropper/jquery.js",
    "content": "/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license\n*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p=\"1.9.1\",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,w=/\\S+/g,T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,N=/^(?:(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/,C=/^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/,k=/^[\\],:{}\\s]*$/,E=/(?:^|:|,)(?:\\s*\\[)+/g,S=/\\\\(?:[\"\\\\\\/bfnrt]|u[\\da-fA-F]{4})/g,A=/\"[^\"\\\\\\r\\n]*\"|true|false|null|-?(?:\\d+\\.|)\\d+(?:[eE][+-]?\\d+|)/g,j=/^-ms-/,D=/-([\\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||\"load\"===e.type||\"complete\"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener(\"DOMContentLoaded\",H,!1),e.removeEventListener(\"load\",H,!1)):(o.detachEvent(\"onreadystatechange\",H),e.detachEvent(\"onload\",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if(\"string\"==typeof e){if(i=\"<\"===e.charAt(0)&&\">\"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:\"\",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for(\"boolean\"==typeof s&&(c=s,s=arguments[1]||{},u=2),\"object\"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger(\"ready\").off(\"ready\"))}},isFunction:function(e){return\"function\"===b.type(e)},isArray:Array.isArray||function(e){return\"array\"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[m.call(e)]||\"object\":typeof e},isPlainObject:function(e){if(!e||\"object\"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,\"constructor\")&&!y.call(e.constructor.prototype,\"isPrototypeOf\"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||\"string\"!=typeof e)return null;\"boolean\"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:\"string\"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,\"@\").replace(A,\"]\").replace(E,\"\")))?Function(\"return \"+n)():(b.error(\"Invalid JSON: \"+n),t)},parseXML:function(n){var r,i;if(!n||\"string\"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,\"text/xml\")):(r=new ActiveXObject(\"Microsoft.XMLDOM\"),r.async=\"false\",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName(\"parsererror\").length||b.error(\"Invalid XML: \"+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,\"ms-\").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call(\"\\ufeff\\u00a0\")?function(e){return null==e?\"\":v.call(e)}:function(e){return null==e?\"\":(e+\"\").replace(T,\"\")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,\"string\"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if(\"number\"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return\"string\"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if(\"object\"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),\"complete\"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener(\"DOMContentLoaded\",H,!1),e.addEventListener(\"load\",H,!1);else{o.attachEvent(\"onreadystatechange\",H),e.attachEvent(\"onload\",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll(\"left\")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each(\"Boolean Number String Function Array Date RegExp Object Error\".split(\" \"),function(e,t){l[\"[object \"+t+\"]\"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:\"array\"===n||\"function\"!==n&&(0===t||\"number\"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e=\"string\"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);\"function\"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&\"string\"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[[\"resolve\",\"done\",b.Callbacks(\"once memory\"),\"resolved\"],[\"reject\",\"fail\",b.Callbacks(\"once memory\"),\"rejected\"],[\"notify\",\"progress\",b.Callbacks(\"memory\")]],n=\"pending\",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+\"With\"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+\"With\"](this===i?r:this,arguments),this},i[o[0]+\"With\"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement(\"div\");if(d.setAttribute(\"className\",\"t\"),d.innerHTML=\"  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>\",n=d.getElementsByTagName(\"*\"),r=d.getElementsByTagName(\"a\")[0],!n||!r||!n.length)return{};s=o.createElement(\"select\"),l=s.appendChild(o.createElement(\"option\")),a=d.getElementsByTagName(\"input\")[0],r.style.cssText=\"top:1px;float:left;opacity:.5\",t={getSetAttribute:\"t\"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName(\"tbody\").length,htmlSerialize:!!d.getElementsByTagName(\"link\").length,style:/top/.test(r.getAttribute(\"style\")),hrefNormalized:\"/a\"===r.getAttribute(\"href\"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement(\"form\").enctype,html5Clone:\"<:nav></:nav>\"!==o.createElement(\"nav\").cloneNode(!0).outerHTML,boxModel:\"CSS1Compat\"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement(\"input\"),a.setAttribute(\"value\",\"\"),t.input=\"\"===a.getAttribute(\"value\"),a.value=\"t\",a.setAttribute(\"type\",\"radio\"),t.radioValue=\"t\"===a.value,a.setAttribute(\"checked\",\"t\"),a.setAttribute(\"name\",\"t\"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent(\"onclick\",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c=\"on\"+f,\"t\"),t[f+\"Bubbles\"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip=\"content-box\",d.cloneNode(!0).style.backgroundClip=\"\",t.clearCloneStyle=\"content-box\"===d.style.backgroundClip,b(function(){var n,r,a,s=\"padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;\",u=o.getElementsByTagName(\"body\")[0];u&&(n=o.createElement(\"div\"),n.style.cssText=\"border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px\",u.appendChild(n).appendChild(d),d.innerHTML=\"<table><tr><td></td><td>t</td></tr></table>\",a=d.getElementsByTagName(\"td\"),a[0].style.cssText=\"padding:0;margin:0;border:0;display:none\",p=0===a[0].offsetHeight,a[0].style.display=\"\",a[1].style.display=\"none\",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML=\"\",d.style.cssText=\"box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;\",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition=\"1%\"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable=\"4px\"===(e.getComputedStyle(d,null)||{width:\"4px\"}).width,r=d.appendChild(o.createElement(\"div\")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width=\"0\",d.style.width=\"1px\",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML=\"\",d.style.cssText=s+\"width:1px;padding:1px;display:inline;zoom:1\",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display=\"block\",d.innerHTML=\"<div></div>\",d.firstChild.style.width=\"5px\",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u=\"string\"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),(\"object\"==typeof n||\"function\"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(\" \"));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:\"jQuery\"+(p+Math.random()).replace(/\\D/g,\"\"),noData:{embed:!0,object:\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute(\"classid\")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,\"parsedAttrs\"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf(\"data-\")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,\"parsedAttrs\",!0)}return s}return\"object\"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i=\"data-\"+n.replace(B,\"-$1\").toLowerCase();if(r=e.getAttribute(i),\"string\"==typeof r){try{r=\"true\"===r?!0:\"false\"===r?!1:\"null\"===r?null:+r+\"\"===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if((\"data\"!==t||!b.isEmptyObject(e[t]))&&\"toJSON\"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||\"fx\")+\"queue\",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||\"fx\";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};\"inprogress\"===i&&(i=n.shift(),r--),o.cur=i,i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks(\"once memory\").add(function(){b._removeData(e,t+\"queue\"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return\"string\"!=typeof e&&(n=e,e=\"fx\",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),\"fx\"===e&&\"inprogress\"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||\"fx\",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};\"string\"!=typeof e&&(n=e,e=t),e=e||\"fx\";while(s--)r=b._data(a[s],e+\"queueHooks\"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\\t\\r\\n]/g,U=/\\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\" \")){o=0;while(i=t[o++])0>r.indexOf(\" \"+i+\" \")&&(r+=i+\" \");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\"\")){o=0;while(i=t[o++])while(r.indexOf(\" \"+i+\" \")>=0)r=r.replace(\" \"+i+\" \",\" \");n.className=e?b.trim(r):\"\"}return this},toggleClass:function(e,t){var n=typeof e,r=\"boolean\"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(\"string\"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?\"addClass\":\"removeClass\"](o)}else(n===i||\"boolean\"===n)&&(this.className&&b._data(this,\"__className__\",this.className),this.className=this.className||e===!1?\"\":b._data(this,\"__className__\")||\"\")})},hasClass:function(e){var t=\" \"+e+\" \",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(\" \"+this[n].className+\" \").replace(X,\" \").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o=\"\":\"number\"==typeof o?o+=\"\":b.isArray(o)&&(o=b.map(o,function(e){return null==e?\"\":e+\"\"})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&\"set\"in r&&r.set(this,o,\"value\")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&\"get\"in r&&(n=r.get(o,\"value\"))!==t?n:(n=o.value,\"string\"==typeof n?n.replace(U,\"\"):null==n?\"\":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o=\"select-one\"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute(\"disabled\"))||n.parentNode.disabled&&b.nodeName(n.parentNode,\"optgroup\"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find(\"option\").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&\"get\"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&\"set\"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+\"\"),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase(\"default-\"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,\"\"),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&\"radio\"===t&&b.nodeName(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},propFix:{tabindex:\"tabIndex\",readonly:\"readOnly\",\"for\":\"htmlFor\",\"class\":\"className\",maxlength:\"maxLength\",cellspacing:\"cellSpacing\",cellpadding:\"cellPadding\",rowspan:\"rowSpan\",colspan:\"colSpan\",usemap:\"useMap\",frameborder:\"frameBorder\",contenteditable:\"contentEditable\"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&\"set\"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&\"get\"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode(\"tabindex\");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i=\"boolean\"==typeof r&&e.getAttribute(n),o=\"boolean\"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase(\"default-\"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase(\"default-\"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,\"input\")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,\"input\")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&(\"id\"===n||\"name\"===n||\"coords\"===n?\"\"!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+=\"\",\"value\"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,\"\"===t?!1:t,n)}},b.each([\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return\"\"===r?(e.setAttribute(n,\"auto\"),r):t}})})),b.support.hrefNormalized||(b.each([\"href\",\"src\",\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each([\"href\",\"src\"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+\"\"}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype=\"encoding\"),b.support.checkOn||b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute(\"value\")?\"on\":e.value}}}),b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||\"\").match(w)||[\"\"],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||\"\").split(\".\").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(\".\")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent(\"on\"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||\"\").match(w)||[\"\"],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||\"\").split(\".\").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&(\"**\"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,\"events\"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,\"type\")?n.type:n,m=y.call(n,\"namespace\")?n.namespace.split(\".\"):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(\".\")>=0&&(m=g.split(\".\"),g=m.shift(),m.sort()),u=0>g.indexOf(\":\")&&\"on\"+g,n=n[b.expando]?n:new b.Event(g,\"object\"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join(\".\"),n.namespace_re=n.namespace?RegExp(\"(^|\\\\.)\"+m.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,\"events\")||{})[n.type]&&b._data(l,\"handle\"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||\"click\"===g&&b.nodeName(i,\"a\")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,\"events\")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||\"click\"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||\"click\"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+\" \",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:\"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),fixHooks:{},keyHooks:{props:\"char charCode key keyCode\".split(\" \"),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:\"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,\"input\")&&\"checkbox\"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:\"focusin\"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:\"focusout\"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r=\"on\"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;\nreturn(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,\"form\")?!1:(b.event.add(this,\"click._submit keypress._submit\",function(e){var n=e.target,r=b.nodeName(n,\"input\")||b.nodeName(n,\"button\")?n.form:t;r&&!b._data(r,\"submitBubbles\")&&(b.event.add(r,\"submit._submit\",function(e){e._submit_bubble=!0}),b._data(r,\"submitBubbles\",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate(\"submit\",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,\"form\")?!1:(b.event.remove(this,\"._submit\"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?((\"checkbox\"===this.type||\"radio\"===this.type)&&(b.event.add(this,\"propertychange._change\",function(e){\"checked\"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,\"click._change\",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate(\"change\",this,e,!0)})),!1):(b.event.add(this,\"beforeactivate._change\",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,\"changeBubbles\")&&(b.event.add(t,\"change._change\",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate(\"change\",this.parentNode,e,!0)}),b._data(t,\"changeBubbles\",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||\"radio\"!==n.type&&\"checkbox\"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,\"._change\"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if(\"object\"==typeof e){\"string\"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&(\"string\"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+\".\"+i.namespace:i.origType,i.selector,i.handler),this;if(\"object\"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||\"function\"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x=\"sizzle\"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",F=\"(?:\\\\\\\\.|[\\\\w-]|[^\\\\x00-\\\\xa0])+\",O=F.replace(\"w\",\"w#\"),B=\"([*^$|!~]?=)\",P=\"\\\\[\"+_+\"*(\"+F+\")\"+_+\"*(?:\"+B+_+\"*(?:(['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|(\"+O+\")|)|)\"+_+\"*\\\\]\",R=\":(\"+F+\")(?:\\\\(((['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+P.replace(3,8)+\")*)|.*)\\\\)|)\",W=RegExp(\"^\"+_+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+_+\"+$\",\"g\"),$=RegExp(\"^\"+_+\"*,\"+_+\"*\"),I=RegExp(\"^\"+_+\"*([\\\\x20\\\\t\\\\r\\\\n\\\\f>+~])\"+_+\"*\"),z=RegExp(R),X=RegExp(\"^\"+O+\"$\"),U={ID:RegExp(\"^#(\"+F+\")\"),CLASS:RegExp(\"^\\\\.(\"+F+\")\"),NAME:RegExp(\"^\\\\[name=['\\\"]?(\"+F+\")['\\\"]?\\\\]\"),TAG:RegExp(\"^(\"+F.replace(\"w\",\"w*\")+\")\"),ATTR:RegExp(\"^\"+P),PSEUDO:RegExp(\"^\"+R),CHILD:RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+_+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+_+\"*(?:([+-]|)\"+_+\"*(\\\\d+)|))\"+_+\"*\\\\)|)\",\"i\"),needsContext:RegExp(\"^\"+_+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+_+\"*((?:-\\\\d)?\\\\d*)\"+_+\"*\\\\)|)(?=[^-]|$)\",\"i\")},V=/[\\x20\\t\\r\\n\\f]*[+~]/,Y=/^[^{]+\\{\\s*\\[native code/,J=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\\d$/i,K=/'|\\\\/g,Z=/\\=[\\x20\\t\\r\\n\\f]*([^'\"\\]]*)[\\x20\\t\\r\\n\\f]*\\]/g,et=/\\\\([\\da-fA-F]{1,6}[\\x20\\t\\r\\n\\f]?|.)/g,tt=function(e,t){var n=\"0x\"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+\"\")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=\" \")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement(\"div\");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||\"string\"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&\"object\"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute(\"id\"))?g=f.replace(K,\"\\\\$&\"):t.setAttribute(\"id\",g),g=\"[id='\"+g+\"'] \",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(\",\")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute(\"id\")}}}return wt(e.replace(W,\"$1\"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?\"HTML\"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),T.attributes=at(function(e){e.innerHTML=\"<select></select>\";var t=typeof e.lastChild.getAttribute(\"multiple\");return\"boolean\"!==t&&\"string\"!==t}),T.getByClassName=at(function(e){return e.innerHTML=\"<div class='hidden e'></div><div class='hidden'></div>\",e.getElementsByClassName&&e.getElementsByClassName(\"e\").length?(e.lastChild.className=\"e\",2===e.getElementsByClassName(\"e\").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML=\"<a name='\"+x+\"'></a><div name='\"+x+\"'></div>\",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML=\"<a href='#'></a>\",e.firstChild&&typeof e.firstChild.getAttribute!==A&&\"#\"===e.firstChild.getAttribute(\"href\")})?{}:{href:function(e){return e.getAttribute(\"href\",2)},type:function(e){return e.getAttribute(\"type\")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute(\"id\")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode(\"id\").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode(\"id\");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[\":focus\"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML=\"<select><option selected=''></option></select>\",e.querySelectorAll(\"[selected]\").length||h.push(\"\\\\[\"+_+\"*(?:checked|disabled|ismap|multiple|readonly|selected|value)\"),e.querySelectorAll(\":checked\").length||h.push(\":checked\")}),at(function(e){e.innerHTML=\"<input type='hidden' i=''/>\",e.querySelectorAll(\"[i^='']\").length&&h.push(\"[*^$]=\"+_+\"*(?:\\\"\\\"|'')\"),e.querySelectorAll(\":enabled\").length||h.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),h.push(\",.*:\")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,\"div\"),m.call(e,\"[s!='']:x\"),g.push(\"!=\",R)}),h=RegExp(h.join(\"|\")),g=RegExp(g.join(\"|\")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,\"='$1']\"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error(\"Syntax error, unrecognized expression: \"+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return\"input\"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return(\"input\"===n||\"button\"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||\"\").replace(et,tt),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return\"*\"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+\" \"];return t||(t=RegExp(\"(^|\"+_+\")\"+e+\"(\"+_+\"|$)\"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute(\"class\")||\"\")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?\"!=\"===t:t?(i+=\"\",\"=\"===t?i===n:\"!=\"===t?i!==n:\"^=\"===t?n&&0===i.indexOf(n):\"*=\"===t?n&&i.indexOf(n)>-1:\"$=\"===t?n&&i.slice(-n.length)===n:\"~=\"===t?(\" \"+i+\" \").indexOf(n)>-1:\"|=\"===t?i===n||i.slice(0,n.length+1)===n+\"-\":!1):!0}},CHILD:function(e,t,n,r,i){var o=\"nth\"!==e.slice(0,3),a=\"last\"!==e.slice(-4),s=\"of-type\"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?\"nextSibling\":\"previousSibling\",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g=\"only\"===e&&!h&&\"nextSibling\"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error(\"unsupported pseudo: \"+e);return r[x]?r(t):r.length>1?(n=[e,e,\"\",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,\"$1\"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||\"\")||st.error(\"unsupported lang: \"+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute(\"xml:lang\")||t.getAttribute(\"lang\"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+\"-\");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>\"@\"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+\" \"];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W,\" \")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r=\"\";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&\"parentNode\"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+\" \"+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||\"*\",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[\" \"],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&&gt(f),u>1&&dt(e.slice(0,u-1)).replace(W,\"$1\"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b=\"0\",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG(\"*\",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+\" \"];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&\"ID\"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[\":\"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\\[\\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if(\"string\"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+\" \":\"\")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&(\"string\"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||\"string\"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?\"string\"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n=\"string\"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,\"parentNode\")},parentsUntil:function(e,t,n){return b.dir(e,\"parentNode\",n)},next:function(e){return pt(e,\"nextSibling\")},prev:function(e){return pt(e,\"previousSibling\")},nextAll:function(e){return b.dir(e,\"nextSibling\")},prevAll:function(e){return b.dir(e,\"previousSibling\")},nextUntil:function(e,t,n){return b.dir(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return b.dir(e,\"previousSibling\",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,\"iframe\")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&\"string\"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=\":not(\"+e+\")\"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if(\"string\"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split(\"|\"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht=\"abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video\",gt=/ jQuery\\d+=\"(?:null|\\d+)\"/g,mt=RegExp(\"<(?:\"+ht+\")[\\\\s/>]\",\"i\"),yt=/^\\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi,bt=/<([\\w:]+)/,xt=/<tbody/i,wt=/<|&#?\\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\\s*(?:[^=]|=\\s*.checked.)/i,kt=/^$|\\/(?:java|ecma)script/i,Et=/^true\\/(.*)/,St=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g,At={option:[1,\"<select multiple='multiple'>\",\"</select>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],area:[1,\"<map>\",\"</map>\"],param:[1,\"<object>\",\"</object>\"],thead:[1,\"<table>\",\"</table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:b.support.htmlSerialize?[0,\"\",\"\"]:[1,\"X<div>\",\"</div>\"]},jt=dt(o),Dt=jt.appendChild(o.createElement(\"div\"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,\"body\")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,\"script\")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,\"select\")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,\"\"):t;if(!(\"string\"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||[\"\",\"\"])[1].toLowerCase()])){e=e.replace(vt,\"<$1></$2>\");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||\"string\"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||\"string\"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,\"tr\"),s=b.map(Ot(l,\"script\"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,\"script\"))),r.call(n&&b.nodeName(this[c],\"table\")?Lt(this[c],\"tbody\"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||\"\")&&!b._data(o,\"globalEval\")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:\"GET\",dataType:\"script\",async:!1,global:!1,\"throws\":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||\"\").replace(St,\"\")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode(\"type\");return e.type=(t&&t.specified)+\"/\"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute(\"type\"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,\"globalEval\",!t||b._data(t[r],\"globalEval\"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}\"script\"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):\"object\"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):\"input\"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):\"option\"===n?t.defaultSelected=t.selected=e.defaultSelected:(\"input\"===n||\"textarea\"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||\"*\"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||\"*\"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test(\"<\"+e.nodeName+\">\")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,\"script\"),r.length>0&&Mt(r,!u&&Ot(e,\"script\")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if(\"object\"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement(\"div\")),u=(bt.exec(o)||[\"\",\"\"])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,\"<$1></$2>\")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o=\"table\"!==u||xt.test(o)?\"<table>\"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],\"tbody\")&&!l.childNodes.length&&o.removeChild(l)\n}b.merge(d,s.childNodes),s.textContent=\"\";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,\"input\"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),\"script\"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||\"\")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\\([^)]*\\)/i,It=/opacity\\s*=\\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp(\"^(\"+x+\")(.*)$\",\"i\"),Yt=RegExp(\"^(\"+x+\")(?!px)[a-z%]+$\",\"i\"),Jt=RegExp(\"^([+-])=(\"+x+\")\",\"i\"),Gt={BODY:\"block\"},Qt={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Kt={letterSpacing:0,fontWeight:400},Zt=[\"Top\",\"Right\",\"Bottom\",\"Left\"],en=[\"Webkit\",\"O\",\"Moz\",\"ms\"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,\"none\"===b.css(e,\"display\")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,\"olddisplay\"),n=r.style.display,t?(o[a]||\"none\"!==n||(r.style.display=\"\"),\"\"===r.style.display&&nn(r)&&(o[a]=b._data(r,\"olddisplay\",un(r.nodeName)))):o[a]||(i=nn(r),(n&&\"none\"!==n||!i)&&b._data(r,\"olddisplay\",i?n:b.css(r,\"display\"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&\"none\"!==r.style.display&&\"\"!==r.style.display||(r.style.display=t?o[a]||\"\":\"none\"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t=\"boolean\"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{\"float\":b.support.cssFloat?\"cssFloat\":\"styleFloat\"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&\"get\"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,\"string\"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a=\"number\"),!(null==r||\"number\"===a&&isNaN(r)||(\"number\"!==a||b.cssNumber[u]||(r+=\"px\"),b.support.clearCloneStyle||\"\"!==r||0!==n.indexOf(\"background\")||(l[n]=\"inherit\"),s&&\"set\"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&\"get\"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),\"normal\"===a&&n in Kt&&(a=Kt[n]),\"\"===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(\"\"!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left=\"fontSize\"===n?\"1em\":u,u=l.pixelLeft+\"px\",l.left=i,a&&(o.left=a)),\"\"===u?\"auto\":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||\"px\"):t}function an(e,t,n,r,i){var o=n===(r?\"border\":\"content\")?4:\"width\"===t?1:0,a=0;for(;4>o;o+=2)\"margin\"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?(\"content\"===n&&(a-=b.css(e,\"padding\"+Zt[o],!0,i)),\"margin\"!==n&&(a-=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i))):(a+=b.css(e,\"padding\"+Zt[o],!0,i),\"padding\"!==n&&(a+=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i)));return a}function sn(e,t,n){var r=!0,i=\"width\"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?\"border\":\"content\"),r,o)+\"px\"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),\"none\"!==n&&n||(Pt=(Pt||b(\"<iframe frameborder='0' width='0' height='0'/>\").css(\"cssText\",\"display:block !important\")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(\"<!doctype html><html><body>\"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],\"display\");return n.remove(),r}b.each([\"height\",\"width\"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,\"display\"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||\"\")?.01*parseFloat(RegExp.$1)+\"\":t?\"1\":\"\"},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?\"alpha(opacity=\"+100*t+\")\":\"\",o=r&&r.filter||n.filter||\"\";n.zoom=1,(t>=1||\"\"===t)&&\"\"===b.trim(o.replace($t,\"\"))&&n.removeAttribute&&(n.removeAttribute(\"filter\"),\"\"===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+\" \"+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:\"inline-block\"},Wt,[e,\"marginRight\"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each([\"top\",\"left\"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+\"px\":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&\"none\"===(e.style&&e.style.display||b.css(e,\"display\"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:\"\",padding:\"\",border:\"Width\"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o=\"string\"==typeof n?n.split(\" \"):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\\[\\]$/,fn=/\\r?\\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,\"elements\");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(\":disabled\")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,\"\\r\\n\")}}):{name:t.name,value:n.replace(fn,\"\\r\\n\")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?\"\":t,i[i.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join(\"&\").replace(cn,\"+\")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+\"[\"+(\"object\"==typeof i?t:\"\")+\"]\",i,n,r)});else if(n||\"object\"!==b.type(t))r(e,t);else for(i in t)gn(e+\"[\"+i+\"]\",t[i],n,r)}b.each(\"blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu\".split(\" \"),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\\/\\//,En=/^([\\w.+-]+:)(?:\\/\\/([^\\/?#:]*)(?::(\\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn=\"*/\".concat(\"*\");try{yn=a.href}catch(Ln){yn=o.createElement(\"a\"),yn.href=\"\",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return\"string\"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o[\"*\"]&&s(\"*\")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if(\"string\"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(\" \");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&\"object\"==typeof n&&(a=\"POST\"),s.length>0&&b.ajax({url:e,type:a,dataType:\"html\",data:n}).done(function(e){o=arguments,s.html(i?b(\"<div>\").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each([\"get\",\"post\"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:\"GET\",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":Dn,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:\"responseXML\",text:\"responseText\"},converters:{\"* text\":e.String,\"text html\":!0,\"text json\":b.parseJSON,\"text xml\":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){\"object\"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks(\"once memory\"),m=p.statusCode||{},y={},v={},x=0,T=\"canceled\",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+\"\").replace(xn,\"\").replace(kn,mn[1]+\"//\"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||\"*\").toLowerCase().match(w)||[\"\"],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||(\"http:\"===r[1]?80:443))==(mn[3]||(\"http:\"===mn[1]?80:443)))),p.data&&p.processData&&\"string\"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger(\"ajaxStart\"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?\"&\":\"?\")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,\"$1_=\"+vn++):o+(bn.test(o)?\"&\":\"?\")+\"_=\"+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader(\"If-Modified-Since\",b.lastModified[o]),b.etag[o]&&N.setRequestHeader(\"If-None-Match\",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader(\"Content-Type\",p.contentType),N.setRequestHeader(\"Accept\",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+(\"*\"!==p.dataTypes[0]?\", \"+Dn+\"; q=0.01\":\"\"):p.accepts[\"*\"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T=\"abort\";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger(\"ajaxSend\",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort(\"timeout\")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,\"No Transport\");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||\"\",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader(\"Last-Modified\"),T&&(b.lastModified[o]=T),T=N.getResponseHeader(\"etag\"),T&&(b.etag[o]=T)),204===e?(c=!0,C=\"nocontent\"):304===e?(c=!0,C=\"notmodified\"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C=\"error\",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+\"\",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?\"ajaxSuccess\":\"ajaxError\",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger(\"ajaxComplete\",[N,p]),--b.active||b.event.trigger(\"ajaxStop\")))}return N},getScript:function(e,n){return b.get(e,t,n,\"script\")},getJSON:function(e,t,n){return b.get(e,t,n,\"json\")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while(\"*\"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader(\"Content-Type\"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+\" \"+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if(\"*\"!==r){if(\"*\"!==l&&l!==r){if(i=a[l+\" \"+r]||a[\"* \"+r],!i)for(n in a)if(o=n.split(\" \"),o[1]===r&&(i=a[l+\" \"+o[0]]||a[\"* \"+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e[\"throws\"])t=i(t);else try{t=i(t)}catch(c){return{state:\"parsererror\",error:i?c:\"No conversion from \"+l+\" to \"+r}}}l=r}return{state:\"success\",data:t}}b.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/(?:java|ecma)script/},converters:{\"text script\":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter(\"script\",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\",e.global=!1)}),b.ajaxTransport(\"script\",function(e){if(e.crossDomain){var n,r=o.head||b(\"head\")[0]||o.documentElement;return{send:function(t,i){n=o.createElement(\"script\"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,\"success\"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\\?(?=&|$)|\\?\\?/;b.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=On.pop()||b.expando+\"_\"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter(\"json jsonp\",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?\"url\":\"string\"==typeof n.data&&!(n.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Bn.test(n.data)&&\"data\");return u||\"jsonp\"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,\"$1\"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?\"&\":\"?\")+n.jsonp+\"=\"+o),n.converters[\"script json\"]=function(){return s||b.error(o+\" was not called\"),s[0]},n.dataTypes[0]=\"json\",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),\"script\"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject(\"Microsoft.XMLHTTP\")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&\"withCredentials\"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i[\"X-Requested-With\"]||(i[\"X-Requested-With\"]=\"XMLHttpRequest\");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),\"string\"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=\"\"}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp(\"^(?:([+-])=|)(\"+x+\")([a-z%]*)$\",\"i\"),Jn=/queueHooks$/,Gn=[nr],Qn={\"*\":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?\"\":\"px\"),\"px\"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||\".5\",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn[\"*\"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&\"expand\"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=[\"*\"]):e=e.split(\" \");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,\"fx\"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,\"fx\").length||c.empty.fire()})})),1===e.nodeType&&(\"height\"in t||\"width\"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],\"inline\"===b.css(e,\"display\")&&\"none\"===b.css(e,\"float\")&&(b.support.inlineBlockNeedsLayout&&\"inline\"!==un(e.nodeName)?d.zoom=1:d.display=\"inline-block\")),n.overflow&&(d.overflow=\"hidden\",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||\"toggle\"===a,a===(m?\"hide\":\"show\"))continue;g.push(i)}if(o=g.length){s=b._data(e,\"fxshow\")||b._data(e,\"fxshow\",{}),\"hidden\"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,\"fxshow\");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start=\"width\"===r||\"height\"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||\"swing\",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?\"\":\"px\")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,\"\"),t&&\"auto\"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each([\"toggle\",\"show\",\"hide\"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||\"boolean\"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,\"finish\"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return\"string\"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||\"fx\",[]),this.each(function(){var t=!0,n=null!=e&&e+\"queueHooks\",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||\"fx\"),this.each(function(){var t,n=b._data(this),r=n[e+\"queue\"],i=n[e+\"queueHooks\"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r[\"margin\"+n]=r[\"padding\"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir(\"show\"),slideUp:ir(\"hide\"),slideToggle:ir(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&\"object\"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:\"number\"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,\"position\");\"static\"===r&&(e.style.position=\"relative\");var i=b(e),o=i.offset(),a=b.css(e,\"top\"),s=b.css(e,\"left\"),u=(\"absolute\"===r||\"fixed\"===r)&&b.inArray(\"auto\",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),\"using\"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return\"fixed\"===b.css(r,\"position\")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],\"html\")||(n=e.offset()),n.top+=b.css(e[0],\"borderTopWidth\",!0),n.left+=b.css(e[0],\"borderLeftWidth\",!0)),{top:t.top-n.top-b.css(r,\"marginTop\",!0),left:t.left-n.left-b.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,\"html\")&&\"static\"===b.css(e,\"position\"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:\"height\",Width:\"width\"},function(e,n){b.each({padding:\"inner\"+e,content:n,\"\":\"outer\"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||\"boolean\"!=typeof i),s=r||(i===!0||o===!0?\"margin\":\"border\");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement[\"client\"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body[\"scroll\"+e],o[\"scroll\"+e],n.body[\"offset\"+e],o[\"offset\"+e],o[\"client\"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,\"function\"==typeof define&&define.amd&&define.amd.jQuery&&define(\"jquery\",[],function(){return b})})(window);"
  },
  {
    "path": "examples/cropper/style.css",
    "content": "/* ----------------Reset Css--------------------- */\nhtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,\nfieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary,\ntime, mark, audio, video, input  {\n    margin: 0;\n    padding: 0;\n    border: none;\n    outline: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: baseline;\n}\n\nhtml, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 {\n    -webkit-text-size-adjust: none;\n}\n\narticle, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {\n    display: block;\n}\n\nbody {\n    font-family: arial, sans-serif;\n}\n\nol, ul {\n    list-style: none;\n}\n\nblockquote, q {\n    quotes: none;\n}\n\nblockquote:before, blockquote:after, q:before, q:after {\n    content: '';\n    content: none;\n}\n\nins {\n    text-decoration: none;\n}\n\ndel {\n    text-decoration: line-through;\n}\n\ntable {\n    border-collapse: collapse;\n    border-spacing: 0;\n}\n\n/* ------------ */\n#wrapper {\n    width: 980px;\n    margin: 0 auto;\n\n    margin: 1em;\n    width: auto;\n}\n\n.img-preview {\n    width: 160px;\n    height: 90px;\n    margin-top: 1em;\n    border: 1px solid #ccc;\n}\n\n.cropper-wraper {\n    position: relative;\n}\n\n.upload-btn {\n    background: #ffffff;\n    border: 1px solid #cfcfcf;\n    color: #565656;\n    padding: 10px 18px;\n    display: inline-block;\n    border-radius: 3px;\n    margin-left: 10px;\n    cursor: pointer;\n    font-size: 14px;\n\n    position: absolute;\n    right: 1em;\n    bottom: 2em;\n}\n.upload-btn:hover {\n    background: #f0f0f0;\n}"
  },
  {
    "path": "examples/cropper/uploader.js",
    "content": "(function( factory ) {\n    if ( !window.jQuery ) {\n        alert('jQuery is required.')\n    }\n\n    jQuery(function() {\n        factory.call( null, jQuery );\n    });\n})(function( $ ) {\n// -----------------------------------------------------\n// ------------ START ----------------------------------\n// -----------------------------------------------------\n\n// ---------------------------------\n// ---------  Uploader -------------\n// ---------------------------------\nvar Uploader = (function() {\n\n    // -------setting-------\n    // 如果使用原始大小，超大的图片可能会出现 Croper UI 卡顿，所以这里建议先缩小后再crop.\n    var FRAME_WIDTH = 1600;\n\n\n    var _ = WebUploader;\n    var Uploader = _.Uploader;\n    var uploaderContainer = $('.uploader-container');\n    var uploader, file;\n\n    if ( !Uploader.support() ) {\n        alert( 'Web Uploader 不支持您的浏览器！');\n        throw new Error( 'WebUploader does not support the browser you are using.' );\n    }\n\n    // hook,\n    // 在文件开始上传前进行裁剪。\n    Uploader.register({\n        'before-send-file': 'cropImage'\n    }, {\n\n        cropImage: function( file ) {\n\n            var data = file._cropData,\n                image, deferred;\n\n            file = this.request( 'get-file', file );\n            deferred = _.Deferred();\n\n            image = new _.Lib.Image();\n\n            deferred.always(function() {\n                image.destroy();\n                image = null;\n            });\n            image.once( 'error', deferred.reject );\n            image.once( 'load', function() {\n                if (file._info)\n                {\n                    image.resize( file._info.width, file._info.height );\n                }\n            });\n\n            image.once( 'complete', function() {\n                image.crop( data.x, data.y, data.width, data.height, data.scale );\n                var blob, size;\n\n                // 移动端 UC / qq 浏览器的无图模式下\n                // ctx.getImageData 处理大图的时候会报 Exception\n                // INDEX_SIZE_ERR: DOM Exception 1\n                try {\n                    blob = image.getAsBlob(file.type);\n                    size = file.size;\n                    file.source = blob;\n                    file.size = blob.size;\n\n                    file.trigger( 'resize', blob.size, size );\n\n                    deferred.resolve();\n                } catch ( e ) {\n                    console.log( e );\n                    // 出错了直接继续，让其上传原始图片\n                    deferred.resolve();\n                }\n            });\n\n            file._info && image.info( file._info );\n            file._meta && image.meta( file._meta );\n            image.loadFromBlob( file.source );\n            return deferred.promise();\n        }\n    });\n\n    return {\n        init: function( selectCb ) {\n            uploader = new Uploader({\n                pick: {\n                    id: '#filePicker',\n                    multiple: false\n                },\n\n                runtimeOrder: 'flash',\n\n                // 设置用什么方式去生成缩略图。\n                thumb: {\n                    quality: 70,\n\n                    // 不允许放大\n                    allowMagnify: false,\n\n                    // 是否采用裁剪模式。如果采用这样可以避免空白内容。\n                    crop: false\n                },\n\n                // 禁掉分块传输，默认是开起的。\n                chunked: false,\n\n                // 禁掉上传前压缩功能，因为会手动裁剪。\n                compress: false,\n\n                // fileSingleSizeLimit: 2 * 1024 * 1024,\n\n                server: '../../server/fileupload.php',\n                swf: '../../dist/Uploader.swf',\n                fileNumLimit: 1,\n                onError: function() {\n                    var args = [].slice.call(arguments, 0);\n                    alert(args.join('\\n'));\n                }\n            });\n\n            uploader.on('fileQueued', function( _file ) {\n                file = _file;\n\n                uploader.makeThumb( file, function( error, src ) {\n\n                    if ( error ) {\n                        alert('不能预览');\n                        return;\n                    }\n\n                    selectCb( src );\n\n                }, FRAME_WIDTH, 1 );   // 注意这里的 height 值是 1，被当成了 100% 使用。\n            });\n        },\n\n        crop: function( data ) {\n\n            var scale = Croper.getImageSize().width / file._info.width;\n            data.scale = scale;\n\n            file._cropData = {\n                x: data.x1,\n                y: data.y1,\n                width: data.width,\n                height: data.height,\n                scale: data.scale\n            };\n        },\n\n        upload: function() {\n            uploader.upload();\n        }\n    }\n})();\n\n// ---------------------------------\n// ---------  Crpper ---------------\n// ---------------------------------\nvar Croper = (function() {\n    var container = $('.cropper-wraper');\n    var $image = container.find('.img-container img');\n    var btn = $('.upload-btn');\n    var isBase64Supported, callback;\n\n    $image.cropper({\n        aspectRatio: 16 / 9,\n        preview: \".img-preview\",\n        done: function(data) {\n            // console.log(data);\n        }\n    });\n\n    function srcWrap( src, cb ) {\n\n        // we need to check this at the first time.\n        if (typeof isBase64Supported === 'undefined') {\n            (function() {\n                var data = new Image();\n                var support = true;\n                data.onload = data.onerror = function() {\n                    if( this.width != 1 || this.height != 1 ) {\n                        support = false;\n                    }\n                }\n                data.src = src;\n                isBase64Supported = support;\n            })();\n        }\n\n        if ( isBase64Supported ) {\n            cb( src );\n        } else {\n            // otherwise we need server support.\n            // convert base64 to a file.\n            $.ajax('../../server/preview.php', {\n                method: 'POST',\n                data: src,\n                dataType:'json'\n            }).done(function( response ) {\n                if (response.result) {\n                    cb( response.result );\n                } else {\n                    alert(\"预览出错\");\n                }\n            });\n        }\n    }\n\n    btn.on('click', function() {\n        callback && callback($image.cropper(\"getData\"));\n        return false;\n    });\n\n    return {\n        setSource: function( src ) {\n\n            // 处理 base64 不支持的情况。\n            // 一般出现在 ie6-ie8\n            srcWrap( src, function( src ) {\n                $image.cropper(\"setImgSrc\", src);\n            });\n\n            container.removeClass('webuploader-element-invisible');\n\n            return this;\n        },\n\n        getImageSize: function() {\n            var img = $image.get(0);\n            return {\n                width: img.naturalWidth,\n                height: img.naturalHeight\n            }\n        },\n\n        setCallback: function( cb ) {\n            callback = cb;\n            return this;\n        },\n\n        disable: function() {\n            $image.cropper(\"disable\");\n            return this;\n        },\n\n        enable: function() {\n            $image.cropper(\"enable\");\n            return this;\n        }\n    }\n\n})();\n\n\n// ------------------------------\n// -----------logic--------------\n// ------------------------------\nvar container = $('.uploader-container');\n\nUploader.init(function( src ) {\n\n    Croper.setSource( src );\n\n    // 隐藏选择按钮。\n    container.addClass('webuploader-element-invisible');\n\n    // 当用户选择上传的时候，开始上传。\n    Croper.setCallback(function( data ) {\n        Uploader.crop(data);\n        Uploader.upload();\n    });\n});\n\n\n\n// -----------------------------------------------------\n// ------------ END ------------------------------------\n// -----------------------------------------------------\n});\n"
  },
  {
    "path": "examples/image-upload/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/webuploader.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div id=\"container\">\n            <!--头部，相册选择和格式选择-->\n\n            <div id=\"uploader\">\n                <div class=\"queueList\">\n                    <div id=\"dndArea\" class=\"placeholder\">\n                        <div id=\"filePicker\"></div>\n                        <p>或将照片拖到这里，单次最多可选300张</p>\n                    </div>\n                </div>\n                <div class=\"statusBar\" style=\"display:none;\">\n                    <div class=\"progress\">\n                        <span class=\"text\">0%</span>\n                        <span class=\"percentage\"></span>\n                    </div><div class=\"info\"></div>\n                    <div class=\"btns\">\n                        <div id=\"filePicker2\"></div><div class=\"uploadBtn\">开始上传</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script type=\"text/javascript\" src=\"./jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../../dist/webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./upload.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/image-upload/jquery.js",
    "content": "/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license\n*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p=\"1.9.1\",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,w=/\\S+/g,T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,N=/^(?:(<[\\w\\W]+>)[^>]*|#([\\w-]*))$/,C=/^<(\\w+)\\s*\\/?>(?:<\\/\\1>|)$/,k=/^[\\],:{}\\s]*$/,E=/(?:^|:|,)(?:\\s*\\[)+/g,S=/\\\\(?:[\"\\\\\\/bfnrt]|u[\\da-fA-F]{4})/g,A=/\"[^\"\\\\\\r\\n]*\"|true|false|null|-?(?:\\d+\\.|)\\d+(?:[eE][+-]?\\d+|)/g,j=/^-ms-/,D=/-([\\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||\"load\"===e.type||\"complete\"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener(\"DOMContentLoaded\",H,!1),e.removeEventListener(\"load\",H,!1)):(o.detachEvent(\"onreadystatechange\",H),e.detachEvent(\"onload\",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if(\"string\"==typeof e){if(i=\"<\"===e.charAt(0)&&\">\"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:\"\",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for(\"boolean\"==typeof s&&(c=s,s=arguments[1]||{},u=2),\"object\"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger(\"ready\").off(\"ready\"))}},isFunction:function(e){return\"function\"===b.type(e)},isArray:Array.isArray||function(e){return\"array\"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[m.call(e)]||\"object\":typeof e},isPlainObject:function(e){if(!e||\"object\"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,\"constructor\")&&!y.call(e.constructor.prototype,\"isPrototypeOf\"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||\"string\"!=typeof e)return null;\"boolean\"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:\"string\"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,\"@\").replace(A,\"]\").replace(E,\"\")))?Function(\"return \"+n)():(b.error(\"Invalid JSON: \"+n),t)},parseXML:function(n){var r,i;if(!n||\"string\"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,\"text/xml\")):(r=new ActiveXObject(\"Microsoft.XMLDOM\"),r.async=\"false\",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName(\"parsererror\").length||b.error(\"Invalid XML: \"+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,\"ms-\").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call(\"\\ufeff\\u00a0\")?function(e){return null==e?\"\":v.call(e)}:function(e){return null==e?\"\":(e+\"\").replace(T,\"\")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,\"string\"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if(\"number\"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return\"string\"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if(\"object\"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),\"complete\"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener(\"DOMContentLoaded\",H,!1),e.addEventListener(\"load\",H,!1);else{o.attachEvent(\"onreadystatechange\",H),e.attachEvent(\"onload\",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll(\"left\")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each(\"Boolean Number String Function Array Date RegExp Object Error\".split(\" \"),function(e,t){l[\"[object \"+t+\"]\"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:\"array\"===n||\"function\"!==n&&(0===t||\"number\"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e=\"string\"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);\"function\"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&\"string\"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[[\"resolve\",\"done\",b.Callbacks(\"once memory\"),\"resolved\"],[\"reject\",\"fail\",b.Callbacks(\"once memory\"),\"rejected\"],[\"notify\",\"progress\",b.Callbacks(\"memory\")]],n=\"pending\",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+\"With\"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+\"With\"](this===i?r:this,arguments),this},i[o[0]+\"With\"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement(\"div\");if(d.setAttribute(\"className\",\"t\"),d.innerHTML=\"  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>\",n=d.getElementsByTagName(\"*\"),r=d.getElementsByTagName(\"a\")[0],!n||!r||!n.length)return{};s=o.createElement(\"select\"),l=s.appendChild(o.createElement(\"option\")),a=d.getElementsByTagName(\"input\")[0],r.style.cssText=\"top:1px;float:left;opacity:.5\",t={getSetAttribute:\"t\"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName(\"tbody\").length,htmlSerialize:!!d.getElementsByTagName(\"link\").length,style:/top/.test(r.getAttribute(\"style\")),hrefNormalized:\"/a\"===r.getAttribute(\"href\"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement(\"form\").enctype,html5Clone:\"<:nav></:nav>\"!==o.createElement(\"nav\").cloneNode(!0).outerHTML,boxModel:\"CSS1Compat\"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement(\"input\"),a.setAttribute(\"value\",\"\"),t.input=\"\"===a.getAttribute(\"value\"),a.value=\"t\",a.setAttribute(\"type\",\"radio\"),t.radioValue=\"t\"===a.value,a.setAttribute(\"checked\",\"t\"),a.setAttribute(\"name\",\"t\"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent(\"onclick\",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c=\"on\"+f,\"t\"),t[f+\"Bubbles\"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip=\"content-box\",d.cloneNode(!0).style.backgroundClip=\"\",t.clearCloneStyle=\"content-box\"===d.style.backgroundClip,b(function(){var n,r,a,s=\"padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;\",u=o.getElementsByTagName(\"body\")[0];u&&(n=o.createElement(\"div\"),n.style.cssText=\"border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px\",u.appendChild(n).appendChild(d),d.innerHTML=\"<table><tr><td></td><td>t</td></tr></table>\",a=d.getElementsByTagName(\"td\"),a[0].style.cssText=\"padding:0;margin:0;border:0;display:none\",p=0===a[0].offsetHeight,a[0].style.display=\"\",a[1].style.display=\"none\",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML=\"\",d.style.cssText=\"box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;\",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition=\"1%\"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable=\"4px\"===(e.getComputedStyle(d,null)||{width:\"4px\"}).width,r=d.appendChild(o.createElement(\"div\")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width=\"0\",d.style.width=\"1px\",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML=\"\",d.style.cssText=s+\"width:1px;padding:1px;display:inline;zoom:1\",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display=\"block\",d.innerHTML=\"<div></div>\",d.firstChild.style.width=\"5px\",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u=\"string\"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),(\"object\"==typeof n||\"function\"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(\" \"));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:\"jQuery\"+(p+Math.random()).replace(/\\D/g,\"\"),noData:{embed:!0,object:\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute(\"classid\")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,\"parsedAttrs\"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf(\"data-\")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,\"parsedAttrs\",!0)}return s}return\"object\"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i=\"data-\"+n.replace(B,\"-$1\").toLowerCase();if(r=e.getAttribute(i),\"string\"==typeof r){try{r=\"true\"===r?!0:\"false\"===r?!1:\"null\"===r?null:+r+\"\"===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if((\"data\"!==t||!b.isEmptyObject(e[t]))&&\"toJSON\"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||\"fx\")+\"queue\",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||\"fx\";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};\"inprogress\"===i&&(i=n.shift(),r--),o.cur=i,i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks(\"once memory\").add(function(){b._removeData(e,t+\"queue\"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return\"string\"!=typeof e&&(n=e,e=\"fx\",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),\"fx\"===e&&\"inprogress\"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||\"fx\",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};\"string\"!=typeof e&&(n=e,e=t),e=e||\"fx\";while(s--)r=b._data(a[s],e+\"queueHooks\"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\\t\\r\\n]/g,U=/\\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\" \")){o=0;while(i=t[o++])0>r.indexOf(\" \"+i+\" \")&&(r+=i+\" \");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||\"string\"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||\"\").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(\" \"+n.className+\" \").replace(X,\" \"):\"\")){o=0;while(i=t[o++])while(r.indexOf(\" \"+i+\" \")>=0)r=r.replace(\" \"+i+\" \",\" \");n.className=e?b.trim(r):\"\"}return this},toggleClass:function(e,t){var n=typeof e,r=\"boolean\"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(\"string\"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?\"addClass\":\"removeClass\"](o)}else(n===i||\"boolean\"===n)&&(this.className&&b._data(this,\"__className__\",this.className),this.className=this.className||e===!1?\"\":b._data(this,\"__className__\")||\"\")})},hasClass:function(e){var t=\" \"+e+\" \",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(\" \"+this[n].className+\" \").replace(X,\" \").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o=\"\":\"number\"==typeof o?o+=\"\":b.isArray(o)&&(o=b.map(o,function(e){return null==e?\"\":e+\"\"})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&\"set\"in r&&r.set(this,o,\"value\")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&\"get\"in r&&(n=r.get(o,\"value\"))!==t?n:(n=o.value,\"string\"==typeof n?n.replace(U,\"\"):null==n?\"\":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o=\"select-one\"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute(\"disabled\"))||n.parentNode.disabled&&b.nodeName(n.parentNode,\"optgroup\"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find(\"option\").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&\"get\"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&\"set\"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+\"\"),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase(\"default-\"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,\"\"),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&\"radio\"===t&&b.nodeName(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},propFix:{tabindex:\"tabIndex\",readonly:\"readOnly\",\"for\":\"htmlFor\",\"class\":\"className\",maxlength:\"maxLength\",cellspacing:\"cellSpacing\",cellpadding:\"cellPadding\",rowspan:\"rowSpan\",colspan:\"colSpan\",usemap:\"useMap\",frameborder:\"frameBorder\",contenteditable:\"contentEditable\"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&\"set\"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&\"get\"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode(\"tabindex\");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i=\"boolean\"==typeof r&&e.getAttribute(n),o=\"boolean\"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase(\"default-\"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase(\"default-\"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,\"input\")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,\"input\")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&(\"id\"===n||\"name\"===n||\"coords\"===n?\"\"!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+=\"\",\"value\"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,\"\"===t?!1:t,n)}},b.each([\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return\"\"===r?(e.setAttribute(n,\"auto\"),r):t}})})),b.support.hrefNormalized||(b.each([\"href\",\"src\",\"width\",\"height\"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each([\"href\",\"src\"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+\"\"}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype=\"encoding\"),b.support.checkOn||b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute(\"value\")?\"on\":e.value}}}),b.each([\"radio\",\"checkbox\"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||\"\").match(w)||[\"\"],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||\"\").split(\".\").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(\".\")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent(\"on\"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||\"\").match(w)||[\"\"],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||\"\").split(\".\").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&(\"**\"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,\"events\"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,\"type\")?n.type:n,m=y.call(n,\"namespace\")?n.namespace.split(\".\"):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(\".\")>=0&&(m=g.split(\".\"),g=m.shift(),m.sort()),u=0>g.indexOf(\":\")&&\"on\"+g,n=n[b.expando]?n:new b.Event(g,\"object\"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join(\".\"),n.namespace_re=n.namespace?RegExp(\"(^|\\\\.)\"+m.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,\"events\")||{})[n.type]&&b._data(l,\"handle\"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||\"click\"===g&&b.nodeName(i,\"a\")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,\"events\")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||\"click\"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||\"click\"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+\" \",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:\"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),fixHooks:{},keyHooks:{props:\"char charCode key keyCode\".split(\" \"),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:\"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,\"input\")&&\"checkbox\"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:\"focusin\"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:\"focusout\"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r=\"on\"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;\nreturn(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,\"form\")?!1:(b.event.add(this,\"click._submit keypress._submit\",function(e){var n=e.target,r=b.nodeName(n,\"input\")||b.nodeName(n,\"button\")?n.form:t;r&&!b._data(r,\"submitBubbles\")&&(b.event.add(r,\"submit._submit\",function(e){e._submit_bubble=!0}),b._data(r,\"submitBubbles\",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate(\"submit\",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,\"form\")?!1:(b.event.remove(this,\"._submit\"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?((\"checkbox\"===this.type||\"radio\"===this.type)&&(b.event.add(this,\"propertychange._change\",function(e){\"checked\"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,\"click._change\",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate(\"change\",this,e,!0)})),!1):(b.event.add(this,\"beforeactivate._change\",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,\"changeBubbles\")&&(b.event.add(t,\"change._change\",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate(\"change\",this.parentNode,e,!0)}),b._data(t,\"changeBubbles\",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||\"radio\"!==n.type&&\"checkbox\"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,\"._change\"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if(\"object\"==typeof e){\"string\"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&(\"string\"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+\".\"+i.namespace:i.origType,i.selector,i.handler),this;if(\"object\"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||\"function\"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x=\"sizzle\"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",F=\"(?:\\\\\\\\.|[\\\\w-]|[^\\\\x00-\\\\xa0])+\",O=F.replace(\"w\",\"w#\"),B=\"([*^$|!~]?=)\",P=\"\\\\[\"+_+\"*(\"+F+\")\"+_+\"*(?:\"+B+_+\"*(?:(['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|(\"+O+\")|)|)\"+_+\"*\\\\]\",R=\":(\"+F+\")(?:\\\\(((['\\\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+P.replace(3,8)+\")*)|.*)\\\\)|)\",W=RegExp(\"^\"+_+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+_+\"+$\",\"g\"),$=RegExp(\"^\"+_+\"*,\"+_+\"*\"),I=RegExp(\"^\"+_+\"*([\\\\x20\\\\t\\\\r\\\\n\\\\f>+~])\"+_+\"*\"),z=RegExp(R),X=RegExp(\"^\"+O+\"$\"),U={ID:RegExp(\"^#(\"+F+\")\"),CLASS:RegExp(\"^\\\\.(\"+F+\")\"),NAME:RegExp(\"^\\\\[name=['\\\"]?(\"+F+\")['\\\"]?\\\\]\"),TAG:RegExp(\"^(\"+F.replace(\"w\",\"w*\")+\")\"),ATTR:RegExp(\"^\"+P),PSEUDO:RegExp(\"^\"+R),CHILD:RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+_+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+_+\"*(?:([+-]|)\"+_+\"*(\\\\d+)|))\"+_+\"*\\\\)|)\",\"i\"),needsContext:RegExp(\"^\"+_+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+_+\"*((?:-\\\\d)?\\\\d*)\"+_+\"*\\\\)|)(?=[^-]|$)\",\"i\")},V=/[\\x20\\t\\r\\n\\f]*[+~]/,Y=/^[^{]+\\{\\s*\\[native code/,J=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\\d$/i,K=/'|\\\\/g,Z=/\\=[\\x20\\t\\r\\n\\f]*([^'\"\\]]*)[\\x20\\t\\r\\n\\f]*\\]/g,et=/\\\\([\\da-fA-F]{1,6}[\\x20\\t\\r\\n\\f]?|.)/g,tt=function(e,t){var n=\"0x\"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+\"\")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=\" \")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement(\"div\");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||\"string\"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&\"object\"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute(\"id\"))?g=f.replace(K,\"\\\\$&\"):t.setAttribute(\"id\",g),g=\"[id='\"+g+\"'] \",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(\",\")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute(\"id\")}}}return wt(e.replace(W,\"$1\"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?\"HTML\"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),T.attributes=at(function(e){e.innerHTML=\"<select></select>\";var t=typeof e.lastChild.getAttribute(\"multiple\");return\"boolean\"!==t&&\"string\"!==t}),T.getByClassName=at(function(e){return e.innerHTML=\"<div class='hidden e'></div><div class='hidden'></div>\",e.getElementsByClassName&&e.getElementsByClassName(\"e\").length?(e.lastChild.className=\"e\",2===e.getElementsByClassName(\"e\").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML=\"<a name='\"+x+\"'></a><div name='\"+x+\"'></div>\",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML=\"<a href='#'></a>\",e.firstChild&&typeof e.firstChild.getAttribute!==A&&\"#\"===e.firstChild.getAttribute(\"href\")})?{}:{href:function(e){return e.getAttribute(\"href\",2)},type:function(e){return e.getAttribute(\"type\")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute(\"id\")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode(\"id\").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode(\"id\");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[\":focus\"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML=\"<select><option selected=''></option></select>\",e.querySelectorAll(\"[selected]\").length||h.push(\"\\\\[\"+_+\"*(?:checked|disabled|ismap|multiple|readonly|selected|value)\"),e.querySelectorAll(\":checked\").length||h.push(\":checked\")}),at(function(e){e.innerHTML=\"<input type='hidden' i=''/>\",e.querySelectorAll(\"[i^='']\").length&&h.push(\"[*^$]=\"+_+\"*(?:\\\"\\\"|'')\"),e.querySelectorAll(\":enabled\").length||h.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),h.push(\",.*:\")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,\"div\"),m.call(e,\"[s!='']:x\"),g.push(\"!=\",R)}),h=RegExp(h.join(\"|\")),g=RegExp(g.join(\"|\")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,\"='$1']\"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error(\"Syntax error, unrecognized expression: \"+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return\"input\"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return(\"input\"===n||\"button\"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n=\"\",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if(\"string\"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||\"\").replace(et,tt),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return\"*\"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+\" \"];return t||(t=RegExp(\"(^|\"+_+\")\"+e+\"(\"+_+\"|$)\"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute(\"class\")||\"\")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?\"!=\"===t:t?(i+=\"\",\"=\"===t?i===n:\"!=\"===t?i!==n:\"^=\"===t?n&&0===i.indexOf(n):\"*=\"===t?n&&i.indexOf(n)>-1:\"$=\"===t?n&&i.slice(-n.length)===n:\"~=\"===t?(\" \"+i+\" \").indexOf(n)>-1:\"|=\"===t?i===n||i.slice(0,n.length+1)===n+\"-\":!1):!0}},CHILD:function(e,t,n,r,i){var o=\"nth\"!==e.slice(0,3),a=\"last\"!==e.slice(-4),s=\"of-type\"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?\"nextSibling\":\"previousSibling\",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g=\"only\"===e&&!h&&\"nextSibling\"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error(\"unsupported pseudo: \"+e);return r[x]?r(t):r.length>1?(n=[e,e,\"\",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,\"$1\"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||\"\")||st.error(\"unsupported lang: \"+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute(\"xml:lang\")||t.getAttribute(\"lang\"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+\"-\");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&!!e.checked||\"option\"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>\"@\"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return\"input\"===t&&\"button\"===e.type||\"button\"===t},text:function(e){var t;return\"input\"===e.nodeName.toLowerCase()&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+\" \"];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W,\" \")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r=\"\";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&\"parentNode\"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+\" \"+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||\"*\",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[\" \"],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&&gt(f),u>1&&dt(e.slice(0,u-1)).replace(W,\"$1\"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b=\"0\",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG(\"*\",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+\" \"];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&\"ID\"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[\":\"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\\[\\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if(\"string\"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+\" \":\"\")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&(\"string\"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||\"string\"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?\"string\"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n=\"string\"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,\"parentNode\")},parentsUntil:function(e,t,n){return b.dir(e,\"parentNode\",n)},next:function(e){return pt(e,\"nextSibling\")},prev:function(e){return pt(e,\"previousSibling\")},nextAll:function(e){return b.dir(e,\"nextSibling\")},prevAll:function(e){return b.dir(e,\"previousSibling\")},nextUntil:function(e,t,n){return b.dir(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return b.dir(e,\"previousSibling\",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,\"iframe\")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&\"string\"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=\":not(\"+e+\")\"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if(\"string\"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split(\"|\"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht=\"abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video\",gt=/ jQuery\\d+=\"(?:null|\\d+)\"/g,mt=RegExp(\"<(?:\"+ht+\")[\\\\s/>]\",\"i\"),yt=/^\\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi,bt=/<([\\w:]+)/,xt=/<tbody/i,wt=/<|&#?\\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\\s*(?:[^=]|=\\s*.checked.)/i,kt=/^$|\\/(?:java|ecma)script/i,Et=/^true\\/(.*)/,St=/^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g,At={option:[1,\"<select multiple='multiple'>\",\"</select>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],area:[1,\"<map>\",\"</map>\"],param:[1,\"<object>\",\"</object>\"],thead:[1,\"<table>\",\"</table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],_default:b.support.htmlSerialize?[0,\"\",\"\"]:[1,\"X<div>\",\"</div>\"]},jt=dt(o),Dt=jt.appendChild(o.createElement(\"div\"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,\"body\")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,\"script\")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,\"select\")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,\"\"):t;if(!(\"string\"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||[\"\",\"\"])[1].toLowerCase()])){e=e.replace(vt,\"<$1></$2>\");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||\"string\"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||\"string\"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,\"tr\"),s=b.map(Ot(l,\"script\"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,\"script\"))),r.call(n&&b.nodeName(this[c],\"table\")?Lt(this[c],\"tbody\"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||\"\")&&!b._data(o,\"globalEval\")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:\"GET\",dataType:\"script\",async:!1,global:!1,\"throws\":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||\"\").replace(St,\"\")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode(\"type\");return e.type=(t&&t.specified)+\"/\"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute(\"type\"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,\"globalEval\",!t||b._data(t[r],\"globalEval\"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}\"script\"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):\"object\"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):\"input\"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):\"option\"===n?t.defaultSelected=t.selected=e.defaultSelected:(\"input\"===n||\"textarea\"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||\"*\"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||\"*\"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test(\"<\"+e.nodeName+\">\")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,\"script\"),r.length>0&&Mt(r,!u&&Ot(e,\"script\")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if(\"object\"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement(\"div\")),u=(bt.exec(o)||[\"\",\"\"])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,\"<$1></$2>\")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o=\"table\"!==u||xt.test(o)?\"<table>\"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],\"tbody\")&&!l.childNodes.length&&o.removeChild(l)\n}b.merge(d,s.childNodes),s.textContent=\"\";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,\"input\"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),\"script\"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||\"\")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\\([^)]*\\)/i,It=/opacity\\s*=\\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp(\"^(\"+x+\")(.*)$\",\"i\"),Yt=RegExp(\"^(\"+x+\")(?!px)[a-z%]+$\",\"i\"),Jt=RegExp(\"^([+-])=(\"+x+\")\",\"i\"),Gt={BODY:\"block\"},Qt={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Kt={letterSpacing:0,fontWeight:400},Zt=[\"Top\",\"Right\",\"Bottom\",\"Left\"],en=[\"Webkit\",\"O\",\"Moz\",\"ms\"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,\"none\"===b.css(e,\"display\")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,\"olddisplay\"),n=r.style.display,t?(o[a]||\"none\"!==n||(r.style.display=\"\"),\"\"===r.style.display&&nn(r)&&(o[a]=b._data(r,\"olddisplay\",un(r.nodeName)))):o[a]||(i=nn(r),(n&&\"none\"!==n||!i)&&b._data(r,\"olddisplay\",i?n:b.css(r,\"display\"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&\"none\"!==r.style.display&&\"\"!==r.style.display||(r.style.display=t?o[a]||\"\":\"none\"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t=\"boolean\"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{\"float\":b.support.cssFloat?\"cssFloat\":\"styleFloat\"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&\"get\"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,\"string\"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a=\"number\"),!(null==r||\"number\"===a&&isNaN(r)||(\"number\"!==a||b.cssNumber[u]||(r+=\"px\"),b.support.clearCloneStyle||\"\"!==r||0!==n.indexOf(\"background\")||(l[n]=\"inherit\"),s&&\"set\"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&\"get\"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),\"normal\"===a&&n in Kt&&(a=Kt[n]),\"\"===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(\"\"!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left=\"fontSize\"===n?\"1em\":u,u=l.pixelLeft+\"px\",l.left=i,a&&(o.left=a)),\"\"===u?\"auto\":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||\"px\"):t}function an(e,t,n,r,i){var o=n===(r?\"border\":\"content\")?4:\"width\"===t?1:0,a=0;for(;4>o;o+=2)\"margin\"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?(\"content\"===n&&(a-=b.css(e,\"padding\"+Zt[o],!0,i)),\"margin\"!==n&&(a-=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i))):(a+=b.css(e,\"padding\"+Zt[o],!0,i),\"padding\"!==n&&(a+=b.css(e,\"border\"+Zt[o]+\"Width\",!0,i)));return a}function sn(e,t,n){var r=!0,i=\"width\"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?\"border\":\"content\"),r,o)+\"px\"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),\"none\"!==n&&n||(Pt=(Pt||b(\"<iframe frameborder='0' width='0' height='0'/>\").css(\"cssText\",\"display:block !important\")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(\"<!doctype html><html><body>\"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],\"display\");return n.remove(),r}b.each([\"height\",\"width\"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,\"display\"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&\"border-box\"===b.css(e,\"boxSizing\",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||\"\")?.01*parseFloat(RegExp.$1)+\"\":t?\"1\":\"\"},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?\"alpha(opacity=\"+100*t+\")\":\"\",o=r&&r.filter||n.filter||\"\";n.zoom=1,(t>=1||\"\"===t)&&\"\"===b.trim(o.replace($t,\"\"))&&n.removeAttribute&&(n.removeAttribute(\"filter\"),\"\"===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+\" \"+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:\"inline-block\"},Wt,[e,\"marginRight\"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each([\"top\",\"left\"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+\"px\":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&\"none\"===(e.style&&e.style.display||b.css(e,\"display\"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:\"\",padding:\"\",border:\"Width\"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o=\"string\"==typeof n?n.split(\" \"):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\\[\\]$/,fn=/\\r?\\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,\"elements\");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(\":disabled\")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,\"\\r\\n\")}}):{name:t.name,value:n.replace(fn,\"\\r\\n\")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?\"\":t,i[i.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join(\"&\").replace(cn,\"+\")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+\"[\"+(\"object\"==typeof i?t:\"\")+\"]\",i,n,r)});else if(n||\"object\"!==b.type(t))r(e,t);else for(i in t)gn(e+\"[\"+i+\"]\",t[i],n,r)}b.each(\"blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu\".split(\" \"),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\\/\\//,En=/^([\\w.+-]+:)(?:\\/\\/([^\\/?#:]*)(?::(\\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn=\"*/\".concat(\"*\");try{yn=a.href}catch(Ln){yn=o.createElement(\"a\"),yn.href=\"\",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return\"string\"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o[\"*\"]&&s(\"*\")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if(\"string\"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(\" \");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&\"object\"==typeof n&&(a=\"POST\"),s.length>0&&b.ajax({url:e,type:a,dataType:\"html\",data:n}).done(function(e){o=arguments,s.html(i?b(\"<div>\").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each([\"get\",\"post\"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:\"GET\",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":Dn,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:\"responseXML\",text:\"responseText\"},converters:{\"* text\":e.String,\"text html\":!0,\"text json\":b.parseJSON,\"text xml\":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){\"object\"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks(\"once memory\"),m=p.statusCode||{},y={},v={},x=0,T=\"canceled\",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+\"\").replace(xn,\"\").replace(kn,mn[1]+\"//\"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||\"*\").toLowerCase().match(w)||[\"\"],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||(\"http:\"===r[1]?80:443))==(mn[3]||(\"http:\"===mn[1]?80:443)))),p.data&&p.processData&&\"string\"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger(\"ajaxStart\"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?\"&\":\"?\")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,\"$1_=\"+vn++):o+(bn.test(o)?\"&\":\"?\")+\"_=\"+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader(\"If-Modified-Since\",b.lastModified[o]),b.etag[o]&&N.setRequestHeader(\"If-None-Match\",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader(\"Content-Type\",p.contentType),N.setRequestHeader(\"Accept\",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+(\"*\"!==p.dataTypes[0]?\", \"+Dn+\"; q=0.01\":\"\"):p.accepts[\"*\"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T=\"abort\";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger(\"ajaxSend\",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort(\"timeout\")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,\"No Transport\");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||\"\",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader(\"Last-Modified\"),T&&(b.lastModified[o]=T),T=N.getResponseHeader(\"etag\"),T&&(b.etag[o]=T)),204===e?(c=!0,C=\"nocontent\"):304===e?(c=!0,C=\"notmodified\"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C=\"error\",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+\"\",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?\"ajaxSuccess\":\"ajaxError\",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger(\"ajaxComplete\",[N,p]),--b.active||b.event.trigger(\"ajaxStop\")))}return N},getScript:function(e,n){return b.get(e,t,n,\"script\")},getJSON:function(e,t,n){return b.get(e,t,n,\"json\")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while(\"*\"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader(\"Content-Type\"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+\" \"+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if(\"*\"!==r){if(\"*\"!==l&&l!==r){if(i=a[l+\" \"+r]||a[\"* \"+r],!i)for(n in a)if(o=n.split(\" \"),o[1]===r&&(i=a[l+\" \"+o[0]]||a[\"* \"+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e[\"throws\"])t=i(t);else try{t=i(t)}catch(c){return{state:\"parsererror\",error:i?c:\"No conversion from \"+l+\" to \"+r}}}l=r}return{state:\"success\",data:t}}b.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/(?:java|ecma)script/},converters:{\"text script\":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter(\"script\",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\",e.global=!1)}),b.ajaxTransport(\"script\",function(e){if(e.crossDomain){var n,r=o.head||b(\"head\")[0]||o.documentElement;return{send:function(t,i){n=o.createElement(\"script\"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,\"success\"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\\?(?=&|$)|\\?\\?/;b.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=On.pop()||b.expando+\"_\"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter(\"json jsonp\",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?\"url\":\"string\"==typeof n.data&&!(n.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Bn.test(n.data)&&\"data\");return u||\"jsonp\"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,\"$1\"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?\"&\":\"?\")+n.jsonp+\"=\"+o),n.converters[\"script json\"]=function(){return s||b.error(o+\" was not called\"),s[0]},n.dataTypes[0]=\"json\",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),\"script\"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject(\"Microsoft.XMLHTTP\")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&\"withCredentials\"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i[\"X-Requested-With\"]||(i[\"X-Requested-With\"]=\"XMLHttpRequest\");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),\"string\"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=\"\"}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp(\"^(?:([+-])=|)(\"+x+\")([a-z%]*)$\",\"i\"),Jn=/queueHooks$/,Gn=[nr],Qn={\"*\":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?\"\":\"px\"),\"px\"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||\".5\",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn[\"*\"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&\"expand\"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=[\"*\"]):e=e.split(\" \");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,\"fx\"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,\"fx\").length||c.empty.fire()})})),1===e.nodeType&&(\"height\"in t||\"width\"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],\"inline\"===b.css(e,\"display\")&&\"none\"===b.css(e,\"float\")&&(b.support.inlineBlockNeedsLayout&&\"inline\"!==un(e.nodeName)?d.zoom=1:d.display=\"inline-block\")),n.overflow&&(d.overflow=\"hidden\",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||\"toggle\"===a,a===(m?\"hide\":\"show\"))continue;g.push(i)}if(o=g.length){s=b._data(e,\"fxshow\")||b._data(e,\"fxshow\",{}),\"hidden\"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,\"fxshow\");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start=\"width\"===r||\"height\"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||\"swing\",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?\"\":\"px\")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,\"\"),t&&\"auto\"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each([\"toggle\",\"show\",\"hide\"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||\"boolean\"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,\"finish\"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return\"string\"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||\"fx\",[]),this.each(function(){var t=!0,n=null!=e&&e+\"queueHooks\",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||\"fx\"),this.each(function(){var t,n=b._data(this),r=n[e+\"queue\"],i=n[e+\"queueHooks\"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r[\"margin\"+n]=r[\"padding\"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir(\"show\"),slideUp:ir(\"hide\"),slideToggle:ir(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&\"object\"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:\"number\"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,\"position\");\"static\"===r&&(e.style.position=\"relative\");var i=b(e),o=i.offset(),a=b.css(e,\"top\"),s=b.css(e,\"left\"),u=(\"absolute\"===r||\"fixed\"===r)&&b.inArray(\"auto\",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),\"using\"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return\"fixed\"===b.css(r,\"position\")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],\"html\")||(n=e.offset()),n.top+=b.css(e[0],\"borderTopWidth\",!0),n.left+=b.css(e[0],\"borderLeftWidth\",!0)),{top:t.top-n.top-b.css(r,\"marginTop\",!0),left:t.left-n.left-b.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,\"html\")&&\"static\"===b.css(e,\"position\"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:\"height\",Width:\"width\"},function(e,n){b.each({padding:\"inner\"+e,content:n,\"\":\"outer\"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||\"boolean\"!=typeof i),s=r||(i===!0||o===!0?\"margin\":\"border\");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement[\"client\"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body[\"scroll\"+e],o[\"scroll\"+e],n.body[\"offset\"+e],o[\"offset\"+e],o[\"client\"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,\"function\"==typeof define&&define.amd&&define.amd.jQuery&&define(\"jquery\",[],function(){return b})})(window);"
  },
  {
    "path": "examples/image-upload/style.css",
    "content": "/* ----------------Reset Css--------------------- */\nhtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,\nfieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary,\ntime, mark, audio, video, input  {\n    margin: 0;\n    padding: 0;\n    border: none;\n    outline: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: baseline;\n}\n\nhtml, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 {\n    -webkit-text-size-adjust: none;\n}\n\narticle, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {\n    display: block;\n}\n\nbody {\n    font-family: arial, sans-serif;\n}\n\nol, ul {\n    list-style: none;\n}\n\nblockquote, q {\n    quotes: none;\n}\n\nblockquote:before, blockquote:after, q:before, q:after {\n    content: '';\n    content: none;\n}\n\nins {\n    text-decoration: none;\n}\n\ndel {\n    text-decoration: line-through;\n}\n\ntable {\n    border-collapse: collapse;\n    border-spacing: 0;\n}\n\n/* ------------ */\n#wrapper {\n    width: 980px;\n    margin: 0 auto;\n\n    margin: 1em;\n    width: auto;\n}\n\n#container {\n    border: 1px solid #dadada;\n    color: #838383;\n    font-size: 12px;\n    margin-top: 10px;\n    background-color: #FFF;\n}\n\n#uploader .queueList {\n    margin: 20px;\n}\n\n.element-invisible {\n    position: absolute !important;\n    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */\n    clip: rect(1px,1px,1px,1px);\n}\n\n#uploader .placeholder {\n    border: 3px dashed #e6e6e6;\n    min-height: 238px;\n    padding-top: 158px;\n    text-align: center;\n    background: url(./image.png) center 93px no-repeat;\n    color: #cccccc;\n    font-size: 18px;\n    position: relative;\n}\n\n#uploader .placeholder .webuploader-pick {\n    font-size: 18px;\n    background: #00b7ee;\n    border-radius: 3px;\n    line-height: 44px;\n    padding: 0 30px;\n    color: #fff;\n    display: inline-block;\n    margin: 20px auto;\n    cursor: pointer;\n    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);\n}\n\n#uploader .placeholder .webuploader-pick-hover {\n    background: #00a2d4;\n}\n\n#uploader .placeholder .flashTip {\n    color: #666666;\n    font-size: 12px;\n    position: absolute;\n    width: 100%;\n    text-align: center;\n    bottom: 20px;\n}\n#uploader .placeholder .flashTip a {\n    color: #0785d1;\n    text-decoration: none;\n}\n#uploader .placeholder .flashTip a:hover {\n    text-decoration: underline;\n}\n\n#uploader .placeholder.webuploader-dnd-over {\n    border-color: #999999;\n}\n\n#uploader .placeholder.webuploader-dnd-over.webuploader-dnd-denied {\n    border-color: red;\n}\n\n#uploader .filelist {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n}\n\n#uploader .filelist:after {\n    content: '';\n    display: block;\n    width: 0;\n    height: 0;\n    overflow: hidden;\n    clear: both;\n}\n\n#uploader .filelist li {\n    width: 110px;\n    height: 110px;\n    background: url(./bg.png) no-repeat;\n    text-align: center;\n    margin: 0 8px 20px 0;\n    position: relative;\n    display: inline;\n    float: left;\n    overflow: hidden;\n    font-size: 12px;\n}\n\n#uploader .filelist li p.log {\n    position: relative;\n    top: -45px;\n}\n\n#uploader .filelist li p.title {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow : ellipsis;\n    top: 5px;\n    text-indent: 5px;\n    text-align: left;\n}\n\n#uploader .filelist li p.progress {\n    position: absolute;\n    width: 100%;\n    bottom: 0;\n    left: 0;\n    height: 8px;\n    overflow: hidden;\n    z-index: 50;\n}\n#uploader .filelist li p.progress span {\n    display: none;\n    overflow: hidden;\n    width: 0;\n    height: 100%;\n    background: #1483d8 url(./progress.png) repeat-x;\n\n    -webit-transition: width 200ms linear;\n    -moz-transition: width 200ms linear;\n    -o-transition: width 200ms linear;\n    -ms-transition: width 200ms linear;\n    transition: width 200ms linear;\n\n    -webkit-animation: progressmove 2s linear infinite;\n    -moz-animation: progressmove 2s linear infinite;\n    -o-animation: progressmove 2s linear infinite;\n    -ms-animation: progressmove 2s linear infinite;\n    animation: progressmove 2s linear infinite;\n\n    -webkit-transform: translateZ(0);\n}\n\n@-webkit-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@-moz-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n\n#uploader .filelist li p.imgWrap {\n    position: relative;\n    z-index: 2;\n    line-height: 110px;\n    vertical-align: middle;\n    overflow: hidden;\n    width: 110px;\n    height: 110px;\n\n    -webkit-transform-origin: 50% 50%;\n    -moz-transform-origin: 50% 50%;\n    -o-transform-origin: 50% 50%;\n    -ms-transform-origin: 50% 50%;\n    transform-origin: 50% 50%;\n\n    -webit-transition: 200ms ease-out;\n    -moz-transition: 200ms ease-out;\n    -o-transition: 200ms ease-out;\n    -ms-transition: 200ms ease-out;\n    transition: 200ms ease-out;\n}\n\n#uploader .filelist li img {\n    width: 100%;\n}\n\n#uploader .filelist li p.error {\n    background: #f43838;\n    color: #fff;\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    height: 28px;\n    line-height: 28px;\n    width: 100%;\n    z-index: 100;\n}\n\n#uploader .filelist li .success {\n    display: block;\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    height: 40px;\n    width: 100%;\n    z-index: 200;\n    background: url(./success.png) no-repeat right bottom;\n}\n\n#uploader .filelist div.file-panel {\n    position: absolute;\n    height: 0;\n    filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\\0;\n    background: rgba( 0, 0, 0, 0.5 );\n    width: 100%;\n    top: 0;\n    left: 0;\n    overflow: hidden;\n    z-index: 300;\n}\n\n#uploader .filelist div.file-panel span {\n    width: 24px;\n    height: 24px;\n    display: inline;\n    float: right;\n    text-indent: -9999px;\n    overflow: hidden;\n    background: url(./icons.png) no-repeat;\n    margin: 5px 1px 1px;\n    cursor: pointer;\n}\n\n#uploader .filelist div.file-panel span.rotateLeft {\n    background-position: 0 -24px;\n}\n#uploader .filelist div.file-panel span.rotateLeft:hover {\n    background-position: 0 0;\n}\n\n#uploader .filelist div.file-panel span.rotateRight {\n    background-position: -24px -24px;\n}\n#uploader .filelist div.file-panel span.rotateRight:hover {\n    background-position: -24px 0;\n}\n\n#uploader .filelist div.file-panel span.cancel {\n    background-position: -48px -24px;\n}\n#uploader .filelist div.file-panel span.cancel:hover {\n    background-position: -48px 0;\n}\n\n#uploader .statusBar {\n    height: 63px;\n    border-top: 1px solid #dadada;\n    padding: 0 20px;\n    line-height: 63px;\n    vertical-align: middle;\n    position: relative;\n}\n\n#uploader .statusBar .progress {\n    border: 1px solid #1483d8;\n    width: 198px;\n    background: #fff;\n    height: 18px;\n    position: relative;\n    display: inline-block;\n    text-align: center;\n    line-height: 20px;\n    color: #6dbfff;\n    position: relative;\n    margin-right: 10px;\n}\n#uploader .statusBar .progress span.percentage {\n    width: 0;\n    height: 100%;\n    left: 0;\n    top: 0;\n    background: #1483d8;\n    position: absolute;\n}\n#uploader .statusBar .progress span.text {\n    position: relative;\n    z-index: 10;\n}\n\n#uploader .statusBar .info {\n    display: inline-block;\n    font-size: 14px;\n    color: #666666;\n}\n\n#uploader .statusBar .btns {\n    position: absolute;\n    top: 10px;\n    right: 20px;\n    line-height: 40px;\n}\n\n#filePicker2 {\n    display: inline-block;\n    float: left;\n}\n\n#uploader .statusBar .btns .webuploader-pick,\n#uploader .statusBar .btns .uploadBtn,\n#uploader .statusBar .btns .uploadBtn.state-uploading,\n#uploader .statusBar .btns .uploadBtn.state-paused {\n    background: #ffffff;\n    border: 1px solid #cfcfcf;\n    color: #565656;\n    padding: 0 18px;\n    display: inline-block;\n    border-radius: 3px;\n    margin-left: 10px;\n    cursor: pointer;\n    font-size: 14px;\n    float: left;\n}\n#uploader .statusBar .btns .webuploader-pick-hover,\n#uploader .statusBar .btns .uploadBtn:hover,\n#uploader .statusBar .btns .uploadBtn.state-uploading:hover,\n#uploader .statusBar .btns .uploadBtn.state-paused:hover {\n    background: #f0f0f0;\n}\n\n#uploader .statusBar .btns .uploadBtn {\n    background: #00b7ee;\n    color: #fff;\n    border-color: transparent;\n}\n#uploader .statusBar .btns .uploadBtn:hover {\n    background: #00a2d4;\n}\n\n#uploader .statusBar .btns .uploadBtn.disabled {\n    pointer-events: none;\n    opacity: 0.6;\n}"
  },
  {
    "path": "examples/image-upload/upload.js",
    "content": "(function( $ ){\n    // 当domReady的时候开始初始化\n    $(function() {\n        var $wrap = $('#uploader'),\n\n            // 图片容器\n            $queue = $( '<ul class=\"filelist\"></ul>' )\n                .appendTo( $wrap.find( '.queueList' ) ),\n\n            // 状态栏，包括进度和控制按钮\n            $statusBar = $wrap.find( '.statusBar' ),\n\n            // 文件总体选择信息。\n            $info = $statusBar.find( '.info' ),\n\n            // 上传按钮\n            $upload = $wrap.find( '.uploadBtn' ),\n\n            // 没选择文件之前的内容。\n            $placeHolder = $wrap.find( '.placeholder' ),\n\n            $progress = $statusBar.find( '.progress' ).hide(),\n\n            // 添加的文件数量\n            fileCount = 0,\n\n            // 添加的文件总大小\n            fileSize = 0,\n\n            // 优化retina, 在retina下这个值是2\n            ratio = window.devicePixelRatio || 1,\n\n            // 缩略图大小\n            thumbnailWidth = 110 * ratio,\n            thumbnailHeight = 110 * ratio,\n\n            // 可能有pedding, ready, uploading, confirm, done.\n            state = 'pedding',\n\n            // 所有文件的进度信息，key为file id\n            percentages = {},\n            // 判断浏览器是否支持图片的base64\n            isSupportBase64 = ( function() {\n                var data = new Image();\n                var support = true;\n                data.onload = data.onerror = function() {\n                    if( this.width != 1 || this.height != 1 ) {\n                        support = false;\n                    }\n                }\n                data.src = \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\n                return support;\n            } )(),\n\n            // 检测是否已经安装flash，检测flash的版本\n            flashVersion = ( function() {\n                var version;\n\n                try {\n                    version = navigator.plugins[ 'Shockwave Flash' ];\n                    version = version.description;\n                } catch ( ex ) {\n                    try {\n                        version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                                .GetVariable('$version');\n                    } catch ( ex2 ) {\n                        version = '0.0';\n                    }\n                }\n                version = version.match( /\\d+/g );\n                return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n            } )(),\n\n            supportTransition = (function(){\n                var s = document.createElement('p').style,\n                    r = 'transition' in s ||\n                            'WebkitTransition' in s ||\n                            'MozTransition' in s ||\n                            'msTransition' in s ||\n                            'OTransition' in s;\n                s = null;\n                return r;\n            })(),\n\n            // WebUploader实例\n            uploader;\n\n        if ( !WebUploader.Uploader.support('flash') && WebUploader.browser.ie ) {\n\n            // flash 安装了但是版本过低。\n            if (flashVersion) {\n                (function(container) {\n                    window['expressinstallcallback'] = function( state ) {\n                        switch(state) {\n                            case 'Download.Cancelled':\n                                alert('您取消了更新！')\n                                break;\n\n                            case 'Download.Failed':\n                                alert('安装失败')\n                                break;\n\n                            default:\n                                alert('安装已成功，请刷新！');\n                                break;\n                        }\n                        delete window['expressinstallcallback'];\n                    };\n\n                    var swf = './expressInstall.swf';\n                    // insert flash object\n                    var html = '<object type=\"application/' +\n                            'x-shockwave-flash\" data=\"' +  swf + '\" ';\n\n                    if (WebUploader.browser.ie) {\n                        html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                    }\n\n                    html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                        '<param name=\"movie\" value=\"' + swf + '\" />' +\n                        '<param name=\"wmode\" value=\"transparent\" />' +\n                        '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                    '</object>';\n\n                    container.html(html);\n\n                })($wrap);\n\n            // 压根就没有安转。\n            } else {\n                $wrap.html('<a href=\"http://www.adobe.com/go/getflashplayer\" target=\"_blank\" border=\"0\"><img alt=\"get flash player\" src=\"http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg\" /></a>');\n            }\n\n            return;\n        } else if (!WebUploader.Uploader.support()) {\n            alert( 'Web Uploader 不支持您的浏览器！');\n            return;\n        }\n\n        // 实例化\n        uploader = WebUploader.create({\n            pick: {\n                id: '#filePicker',\n                label: '点击选择图片'\n            },\n            formData: {\n                uid: 123\n            },\n            dnd: '#uploader .queueList',\n            paste: '#uploader',\n            swf: '../../dist/Uploader.swf',\n            chunked: false,\n            chunkSize: 512 * 1024,\n            server: '../../server/fileupload.php',\n            // runtimeOrder: 'flash',\n\n            // accept: {\n            //     title: 'Images',\n            //     extensions: 'gif,jpg,jpeg,bmp,png',\n            //     mimeTypes: 'image/*'\n            // },\n\n            // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候，把图片打开。\n            disableGlobalDnd: true,\n            fileNumLimit: 300,\n            fileSizeLimit: 200 * 1024 * 1024,    // 200 M\n            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M\n        });\n\n        // 拖拽时不接受 js, txt 文件。\n        uploader.on( 'dndAccept', function( items ) {\n            var denied = false,\n                len = items.length,\n                i = 0,\n                // 修改js类型\n                unAllowed = 'text/plain;application/javascript ';\n\n            for ( ; i < len; i++ ) {\n                // 如果在列表里面\n                if ( ~unAllowed.indexOf( items[ i ].type ) ) {\n                    denied = true;\n                    break;\n                }\n            }\n\n            return !denied;\n        });\n\n        uploader.on('dialogOpen', function() {\n            console.log('here');\n        });\n\n        // uploader.on('filesQueued', function() {\n        //     uploader.sort(function( a, b ) {\n        //         if ( a.name < b.name )\n        //           return -1;\n        //         if ( a.name > b.name )\n        //           return 1;\n        //         return 0;\n        //     });\n        // });\n\n        // 添加“添加文件”的按钮，\n        uploader.addButton({\n            id: '#filePicker2',\n            label: '继续添加'\n        });\n\n        uploader.on('ready', function() {\n            window.uploader = uploader;\n        });\n\n        // 当有文件添加进来时执行，负责view的创建\n        function addFile( file ) {\n            var $li = $( '<li id=\"' + file.id + '\">' +\n                    '<p class=\"title\">' + file.name + '</p>' +\n                    '<p class=\"imgWrap\"></p>'+\n                    '<p class=\"progress\"><span></span></p>' +\n                    '</li>' ),\n\n                $btns = $('<div class=\"file-panel\">' +\n                    '<span class=\"cancel\">删除</span>' +\n                    '<span class=\"rotateRight\">向右旋转</span>' +\n                    '<span class=\"rotateLeft\">向左旋转</span></div>').appendTo( $li ),\n                $prgress = $li.find('p.progress span'),\n                $wrap = $li.find( 'p.imgWrap' ),\n                $info = $('<p class=\"error\"></p>'),\n\n                showError = function( code ) {\n                    switch( code ) {\n                        case 'exceed_size':\n                            text = '文件大小超出';\n                            break;\n\n                        case 'interrupt':\n                            text = '上传暂停';\n                            break;\n\n                        default:\n                            text = '上传失败，请重试';\n                            break;\n                    }\n\n                    $info.text( text ).appendTo( $li );\n                };\n\n            if ( file.getStatus() === 'invalid' ) {\n                showError( file.statusText );\n            } else {\n                // @todo lazyload\n                $wrap.text( '预览中' );\n                uploader.makeThumb( file, function( error, src ) {\n                    var img;\n\n                    if ( error ) {\n                        $wrap.text( '不能预览' );\n                        return;\n                    }\n\n                    if( isSupportBase64 ) {\n                        img = $('<img src=\"'+src+'\">');\n                        $wrap.empty().append( img );\n                    } else {\n                        $.ajax('../../server/preview.php', {\n                            method: 'POST',\n                            data: src,\n                            dataType:'json'\n                        }).done(function( response ) {\n                            if (response.result) {\n                                img = $('<img src=\"'+response.result+'\">');\n                                $wrap.empty().append( img );\n                            } else {\n                                $wrap.text(\"预览出错\");\n                            }\n                        });\n                    }\n                }, thumbnailWidth, thumbnailHeight );\n\n                percentages[ file.id ] = [ file.size, 0 ];\n                file.rotation = 0;\n            }\n\n            file.on('statuschange', function( cur, prev ) {\n                if ( prev === 'progress' ) {\n                    $prgress.hide().width(0);\n                } else if ( prev === 'queued' ) {\n                    $li.off( 'mouseenter mouseleave' );\n                    $btns.remove();\n                }\n\n                // 成功\n                if ( cur === 'error' || cur === 'invalid' ) {\n                    console.log( file.statusText );\n                    showError( file.statusText );\n                    percentages[ file.id ][ 1 ] = 1;\n                } else if ( cur === 'interrupt' ) {\n                    showError( 'interrupt' );\n                } else if ( cur === 'queued' ) {\n                    $info.remove();\n                    $prgress.css('display', 'block');\n                    percentages[ file.id ][ 1 ] = 0;\n                } else if ( cur === 'progress' ) {\n                    $info.remove();\n                    $prgress.css('display', 'block');\n                } else if ( cur === 'complete' ) {\n                    $prgress.hide().width(0);\n                    $li.append( '<span class=\"success\"></span>' );\n                }\n\n                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );\n            });\n\n            $li.on( 'mouseenter', function() {\n                $btns.stop().animate({height: 30});\n            });\n\n            $li.on( 'mouseleave', function() {\n                $btns.stop().animate({height: 0});\n            });\n\n            $btns.on( 'click', 'span', function() {\n                var index = $(this).index(),\n                    deg;\n\n                switch ( index ) {\n                    case 0:\n                        uploader.removeFile( file );\n                        return;\n\n                    case 1:\n                        file.rotation += 90;\n                        break;\n\n                    case 2:\n                        file.rotation -= 90;\n                        break;\n                }\n\n                if ( supportTransition ) {\n                    deg = 'rotate(' + file.rotation + 'deg)';\n                    $wrap.css({\n                        '-webkit-transform': deg,\n                        '-mos-transform': deg,\n                        '-o-transform': deg,\n                        'transform': deg\n                    });\n                } else {\n                    $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');\n                    // use jquery animate to rotation\n                    // $({\n                    //     rotation: rotation\n                    // }).animate({\n                    //     rotation: file.rotation\n                    // }, {\n                    //     easing: 'linear',\n                    //     step: function( now ) {\n                    //         now = now * Math.PI / 180;\n\n                    //         var cos = Math.cos( now ),\n                    //             sin = Math.sin( now );\n\n                    //         $wrap.css( 'filter', \"progid:DXImageTransform.Microsoft.Matrix(M11=\" + cos + \",M12=\" + (-sin) + \",M21=\" + sin + \",M22=\" + cos + \",SizingMethod='auto expand')\");\n                    //     }\n                    // });\n                }\n\n\n            });\n\n            $li.appendTo( $queue );\n        }\n\n        // 负责view的销毁\n        function removeFile( file ) {\n            var $li = $('#'+file.id);\n\n            delete percentages[ file.id ];\n            updateTotalProgress();\n            $li.off().find('.file-panel').off().end().remove();\n        }\n\n        function updateTotalProgress() {\n            var loaded = 0,\n                total = 0,\n                spans = $progress.children(),\n                percent;\n\n            $.each( percentages, function( k, v ) {\n                total += v[ 0 ];\n                loaded += v[ 0 ] * v[ 1 ];\n            } );\n\n            percent = total ? loaded / total : 0;\n\n\n            spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );\n            spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );\n            updateStatus();\n        }\n\n        function updateStatus() {\n            var text = '', stats;\n\n            if ( state === 'ready' ) {\n                text = '选中' + fileCount + '张图片，共' +\n                        WebUploader.formatSize( fileSize ) + '。';\n            } else if ( state === 'confirm' ) {\n                stats = uploader.getStats();\n                if ( stats.uploadFailNum ) {\n                    text = '已成功上传' + stats.successNum+ '张照片至XX相册，'+\n                        stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n                }\n\n            } else {\n                stats = uploader.getStats();\n                text = '共' + fileCount + '张（' +\n                        WebUploader.formatSize( fileSize )  +\n                        '），已上传' + stats.successNum + '张';\n\n                if ( stats.uploadFailNum ) {\n                    text += '，失败' + stats.uploadFailNum + '张';\n                }\n            }\n\n            $info.html( text );\n        }\n\n        function setState( val ) {\n            var file, stats;\n\n            if ( val === state ) {\n                return;\n            }\n\n            $upload.removeClass( 'state-' + state );\n            $upload.addClass( 'state-' + val );\n            state = val;\n\n            switch ( state ) {\n                case 'pedding':\n                    $placeHolder.removeClass( 'element-invisible' );\n                    $queue.hide();\n                    $statusBar.addClass( 'element-invisible' );\n                    uploader.refresh();\n                    break;\n\n                case 'ready':\n                    $placeHolder.addClass( 'element-invisible' );\n                    $( '#filePicker2' ).removeClass( 'element-invisible');\n                    $queue.show();\n                    $statusBar.removeClass('element-invisible');\n                    uploader.refresh();\n                    break;\n\n                case 'uploading':\n                    $( '#filePicker2' ).addClass( 'element-invisible' );\n                    $progress.show();\n                    $upload.text( '暂停上传' );\n                    break;\n\n                case 'paused':\n                    $progress.show();\n                    $upload.text( '继续上传' );\n                    break;\n\n                case 'confirm':\n                    $progress.hide();\n                    $( '#filePicker2' ).removeClass( 'element-invisible' );\n                    $upload.text( '开始上传' );\n\n                    stats = uploader.getStats();\n                    if ( stats.successNum && !stats.uploadFailNum ) {\n                        setState( 'finish' );\n                        return;\n                    }\n                    break;\n                case 'finish':\n                    stats = uploader.getStats();\n                    if ( stats.successNum ) {\n                        alert( '上传成功' );\n                    } else {\n                        // 没有成功的图片，重设\n                        state = 'done';\n                        location.reload();\n                    }\n                    break;\n            }\n\n            updateStatus();\n        }\n\n        uploader.onUploadProgress = function( file, percentage ) {\n            var $li = $('#'+file.id),\n                $percent = $li.find('.progress span');\n\n            $percent.css( 'width', percentage * 100 + '%' );\n            percentages[ file.id ][ 1 ] = percentage;\n            updateTotalProgress();\n        };\n\n        uploader.onFileQueued = function( file ) {\n            fileCount++;\n            fileSize += file.size;\n\n            if ( fileCount === 1 ) {\n                $placeHolder.addClass( 'element-invisible' );\n                $statusBar.show();\n            }\n\n            addFile( file );\n            setState( 'ready' );\n            updateTotalProgress();\n        };\n\n        uploader.onFileDequeued = function( file ) {\n            fileCount--;\n            fileSize -= file.size;\n\n            if ( !fileCount ) {\n                setState( 'pedding' );\n            }\n\n            removeFile( file );\n            updateTotalProgress();\n\n        };\n\n        uploader.on( 'all', function( type ) {\n            var stats;\n            switch( type ) {\n                case 'uploadFinished':\n                    setState( 'confirm' );\n                    break;\n\n                case 'startUpload':\n                    setState( 'uploading' );\n                    break;\n\n                case 'stopUpload':\n                    setState( 'paused' );\n                    break;\n\n            }\n        });\n\n        uploader.onError = function( code ) {\n            alert( 'Eroor: ' + code );\n        };\n\n        $upload.on('click', function() {\n            if ( $(this).hasClass( 'disabled' ) ) {\n                return false;\n            }\n\n            if ( state === 'ready' ) {\n                uploader.upload();\n            } else if ( state === 'paused' ) {\n                uploader.upload();\n            } else if ( state === 'uploading' ) {\n                uploader.stop();\n            }\n        });\n\n        $info.on( 'click', '.retry', function() {\n            uploader.retry();\n        } );\n\n        $info.on( 'click', '.ignore', function() {\n            alert( 'todo' );\n        } );\n\n        $upload.addClass( 'state-' + state );\n        updateTotalProgress();\n    });\n\n})( jQuery );\n"
  },
  {
    "path": "examples/md5-demo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示 - 带裁剪功能 </title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/webuploader.css\" />\n</head>\n<body>\n    <div id=\"wrapper\">\n        <div class=\"uploader-container\">\n            <div id=\"filePicker\">html5版本</div>\n            <div id=\"filePicker2\">flash版本</div>\n        </div>\n        <div id=\"log\">\n            <strong>console 会影响 md5 时间的计算，所以在测试 md5 速度的时候，把 console 关了</strong>\n        </div>\n    </div>\n    <script type=\"text/javascript\" src=\"../image-upload/jquery.js\"></script>\n    <script type=\"text/javascript\" src=\"../../dist/webuploader.js\"></script>\n    <script type=\"text/javascript\" src=\"./script.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/md5-demo/script.js",
    "content": "(function($) {\n    $(function() {\n        var log = (function() {\n            var dom = $('#log');\n\n            return function( str ) {\n                dom.append('<p>' + str + '</p>')\n            }\n        })();\n\n        WebUploader.create({\n            pick: '#filePicker'\n        }).on('fileQueued', function( file ) {\n            var start =  +new Date();\n\n            // 返回的是 promise 对象\n            this.md5File(file, 0, 1 * 1024 * 1024)\n\n                // 可以用来监听进度\n                .progress(function(percentage) {\n                    // console.log('Percentage:', percentage);\n                })\n\n                // 处理完成后触发\n                .then(function(ret) {\n                    // console.log('md5:', ret);\n                    \n                    var end = +new Date();\n                    log('HTML5: md5 ' + file.name + ' cost ' + (end - start) + 'ms get value: ' + ret);\n                });\n        });\n\n        WebUploader.create({\n            pick: '#filePicker2',\n            swf: '../../dist/Uploader.swf',\n            runtimeOrder: 'flash'\n        }).on('fileQueued', function( file ) {\n            var start =  +new Date();\n\n            // 返回的是 promise 对象\n            this.md5File(file, 0, 1 * 1024 * 1024)\n\n                // 可以用来监听进度\n                .progress(function(percentage) {\n                    // console.log('Percentage:', percentage);\n                })\n\n                // 处理完成后触发\n                .then(function(ret) {\n                    // console.log('md5:', ret);\n                    \n                    var end = +new Date();\n                    log('Flash: md5 ' + file.name + ' cost ' + (end - start) + 'ms get value: ' + ret);\n                });\n        });\n    });\n})(jQuery);"
  },
  {
    "path": "examples/requirejs/app.js",
    "content": "requirejs.config({\n    baseUrl: '../../dist',\n    paths: {\n        jquery: '../examples/image-upload/jquery'\n    }\n});\n\nrequire([ 'webuploader.flashonly' ], function( WebUploader ) {\n    // 当domReady的时候开始初始化\n    $(function() {\n        var $wrap = $('#uploader'),\n\n            // 图片容器\n            $queue = $( '<ul class=\"filelist\"></ul>' )\n                .appendTo( $wrap.find( '.queueList' ) ),\n\n            // 状态栏，包括进度和控制按钮\n            $statusBar = $wrap.find( '.statusBar' ),\n\n            // 文件总体选择信息。\n            $info = $statusBar.find( '.info' ),\n\n            // 上传按钮\n            $upload = $wrap.find( '.uploadBtn' ),\n\n            // 没选择文件之前的内容。\n            $placeHolder = $wrap.find( '.placeholder' ),\n\n            $progress = $statusBar.find( '.progress' ).hide(),\n\n            // 添加的文件数量\n            fileCount = 0,\n\n            // 添加的文件总大小\n            fileSize = 0,\n\n            // 优化retina, 在retina下这个值是2\n            ratio = window.devicePixelRatio || 1,\n\n            // 缩略图大小\n            thumbnailWidth = 110 * ratio,\n            thumbnailHeight = 110 * ratio,\n\n            // 可能有pedding, ready, uploading, confirm, done.\n            state = 'pedding',\n\n            // 所有文件的进度信息，key为file id\n            percentages = {},\n\n            supportTransition = (function(){\n                var s = document.createElement('p').style,\n                    r = 'transition' in s ||\n                          'WebkitTransition' in s ||\n                          'MozTransition' in s ||\n                          'msTransition' in s ||\n                          'OTransition' in s;\n                s = null;\n                return r;\n            })(),\n\n            // WebUploader实例\n            uploader;\n\n        // 实例化\n        uploader = WebUploader.create({\n            pick: {\n                id: '#filePicker',\n                label: '点击选择图片'\n            },\n            dnd: '#dndArea',\n            paste: '#uploader',\n            swf: '../../dist/Uploader.swf',\n            chunked: true,\n            // runtimeOrder: 'flash',\n            sendAsBinary: true,\n            server: '../../server/fileupload.php',\n            // server: 'http://liaoxuezhi.fe.baidu.com/webupload/fileupload.php',\n            // server: 'http://www.2betop.net/fileupload.php',\n            fileNumLimit: 300,\n            fileSizeLimit: 200 * 1024 * 1024,    // 200 M\n            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M\n        });\n\n        // 添加“添加文件”的按钮，\n        uploader.addButton({\n            id: '#filePicker2',\n            label: '继续添加'\n        });\n\n        // 当有文件添加进来时执行，负责view的创建\n        function addFile( file ) {\n            var $li = $( '<li id=\"' + file.id + '\">' +\n                    '<p class=\"title\">' + file.name + '</p>' +\n                    '<p class=\"imgWrap\"></p>'+\n                    '<p class=\"progress\"><span></span></p>' +\n                    '</li>' ),\n\n                $btns = $('<div class=\"file-panel\">' +\n                    '<span class=\"cancel\">删除</span>' +\n                    '<span class=\"rotateRight\">向右旋转</span>' +\n                    '<span class=\"rotateLeft\">向左旋转</span></div>').appendTo( $li ),\n                $prgress = $li.find('p.progress span'),\n                $wrap = $li.find( 'p.imgWrap' ),\n                $info = $('<p class=\"error\"></p>'),\n\n                showError = function( code ) {\n                    switch( code ) {\n                        case 'exceed_size':\n                            text = '文件大小超出';\n                            break;\n\n                        case 'interrupt':\n                            text = '上传暂停';\n                            break;\n\n                        default:\n                            text = '上传失败，请重试';\n                            break;\n                    }\n\n                    $info.text( text ).appendTo( $li );\n                };\n\n            if ( file.getStatus() === 'invalid' ) {\n                showError( file.statusText );\n            } else {\n                // @todo lazyload\n                $wrap.text( '预览中' );\n                uploader.makeThumb( file, function( error, src ) {\n                    if ( error ) {\n                        $wrap.text( '不能预览' );\n                        return;\n                    }\n\n                    var img = $('<img src=\"'+src+'\">');\n                    $wrap.empty().append( img );\n                }, thumbnailWidth, thumbnailHeight );\n\n                percentages[ file.id ] = [ file.size, 0 ];\n                file.rotation = 0;\n            }\n\n            file.on('statuschange', function( cur, prev ) {\n                if ( prev === 'progress' ) {\n                    $prgress.hide().width(0);\n                } else if ( prev === 'queued' ) {\n                    $li.off( 'mouseenter mouseleave' );\n                    $btns.remove();\n                }\n\n                // 成功\n                if ( cur === 'error' || cur === 'invalid' ) {\n                    console.log( file.statusText );\n                    showError( file.statusText );\n                    percentages[ file.id ][ 1 ] = 1;\n                } else if ( cur === 'interrupt' ) {\n                    showError( 'interrupt' );\n                } else if ( cur === 'queued' ) {\n                    percentages[ file.id ][ 1 ] = 0;\n                } else if ( cur === 'progress' ) {\n                    $info.remove();\n                    $prgress.css('display', 'block');\n                } else if ( cur === 'complete' ) {\n                    $li.append( '<span class=\"success\"></span>' );\n                }\n\n                $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );\n            });\n\n            $li.on( 'mouseenter', function() {\n                $btns.stop().animate({height: 30});\n            });\n\n            $li.on( 'mouseleave', function() {\n                $btns.stop().animate({height: 0});\n            });\n\n            $btns.on( 'click', 'span', function() {\n                var index = $(this).index(),\n                    deg;\n\n                switch ( index ) {\n                    case 0:\n                        uploader.removeFile( file );\n                        return;\n\n                    case 1:\n                        file.rotation += 90;\n                        break;\n\n                    case 2:\n                        file.rotation -= 90;\n                        break;\n                }\n\n                if ( supportTransition ) {\n                    deg = 'rotate(' + file.rotation + 'deg)';\n                    $wrap.css({\n                        '-webkit-transform': deg,\n                        '-mos-transform': deg,\n                        '-o-transform': deg,\n                        'transform': deg\n                    });\n                } else {\n                    $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');\n                    // use jquery animate to rotation\n                    // $({\n                    //     rotation: rotation\n                    // }).animate({\n                    //     rotation: file.rotation\n                    // }, {\n                    //     easing: 'linear',\n                    //     step: function( now ) {\n                    //         now = now * Math.PI / 180;\n\n                    //         var cos = Math.cos( now ),\n                    //             sin = Math.sin( now );\n\n                    //         $wrap.css( 'filter', \"progid:DXImageTransform.Microsoft.Matrix(M11=\" + cos + \",M12=\" + (-sin) + \",M21=\" + sin + \",M22=\" + cos + \",SizingMethod='auto expand')\");\n                    //     }\n                    // });\n                }\n\n\n            });\n\n            $li.appendTo( $queue );\n        }\n\n        // 负责view的销毁\n        function removeFile( file ) {\n            var $li = $('#'+file.id);\n\n            delete percentages[ file.id ];\n            updateTotalProgress();\n            $li.off().find('.file-panel').off().end().remove();\n        }\n\n        function updateTotalProgress() {\n            var loaded = 0,\n                total = 0,\n                spans = $progress.children(),\n                percent;\n\n            $.each( percentages, function( k, v ) {\n                total += v[ 0 ];\n                loaded += v[ 0 ] * v[ 1 ];\n            } );\n\n            percent = total ? loaded / total : 0;\n\n            spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );\n            spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );\n            updateStatus();\n        }\n\n        function updateStatus() {\n            var text = '', stats;\n\n            if ( state === 'ready' ) {\n                text = '选中' + fileCount + '张图片，共' +\n                        WebUploader.formatSize( fileSize ) + '。';\n            } else if ( state === 'confirm' ) {\n                stats = uploader.getStats();\n                if ( stats.uploadFailNum ) {\n                    text = '已成功上传' + stats.successNum+ '张照片至XX相册，'+\n                        stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n                }\n\n            } else {\n                stats = uploader.getStats();\n                text = '共' + fileCount + '张（' +\n                        WebUploader.formatSize( fileSize )  +\n                        '），已上传' + stats.successNum + '张';\n\n                if ( stats.uploadFailNum ) {\n                    text += '，失败' + stats.uploadFailNum + '张';\n                }\n            }\n\n            $info.html( text );\n        }\n\n        function setState( val ) {\n            var file, stats;\n\n            if ( val === state ) {\n                return;\n            }\n\n            $upload.removeClass( 'state-' + state );\n            $upload.addClass( 'state-' + val );\n            state = val;\n\n            switch ( state ) {\n                case 'pedding':\n                    $placeHolder.removeClass( 'element-invisible' );\n                    $queue.hide();\n                    $statusBar.addClass( 'element-invisible' );\n                    uploader.refresh();\n                    break;\n\n                case 'ready':\n                    $placeHolder.addClass( 'element-invisible' );\n                    $( '#filePicker2' ).removeClass( 'element-invisible');\n                    $queue.show();\n                    $statusBar.removeClass('element-invisible');\n                    uploader.refresh();\n                    break;\n\n                case 'uploading':\n                    $( '#filePicker2' ).addClass( 'element-invisible' );\n                    $progress.show();\n                    $upload.text( '暂停上传' );\n                    break;\n\n                case 'paused':\n                    $progress.show();\n                    $upload.text( '继续上传' );\n                    break;\n\n                case 'confirm':\n                    $progress.hide();\n                    $upload.text( '开始上传' ).addClass( 'disabled' );\n\n                    stats = uploader.getStats();\n                    if ( stats.successNum && !stats.uploadFailNum ) {\n                        setState( 'finish' );\n                        return;\n                    }\n                    break;\n                case 'finish':\n                    stats = uploader.getStats();\n                    if ( stats.successNum ) {\n                        alert( '上传成功' );\n                    } else {\n                        // 没有成功的图片，重设\n                        state = 'done';\n                        location.reload();\n                    }\n                    break;\n            }\n\n            updateStatus();\n        }\n\n        uploader.onUploadProgress = function( file, percentage ) {\n            var $li = $('#'+file.id),\n                $percent = $li.find('.progress span');\n\n            $percent.css( 'width', percentage * 100 + '%' );\n            percentages[ file.id ][ 1 ] = percentage;\n            updateTotalProgress();\n        };\n\n        uploader.onFileQueued = function( file ) {\n            fileCount++;\n            fileSize += file.size;\n\n            if ( fileCount === 1 ) {\n                $placeHolder.addClass( 'element-invisible' );\n                $statusBar.show();\n            }\n\n            addFile( file );\n            setState( 'ready' );\n            updateTotalProgress();\n        };\n\n        uploader.onFileDequeued = function( file ) {\n            fileCount--;\n            fileSize -= file.size;\n\n            if ( !fileCount ) {\n                setState( 'pedding' );\n            }\n\n            removeFile( file );\n            updateTotalProgress();\n\n        };\n\n        uploader.on( 'all', function( type ) {\n            var stats;\n            switch( type ) {\n                case 'uploadFinished':\n                    setState( 'confirm' );\n                    break;\n\n                case 'startUpload':\n                    setState( 'uploading' );\n                    break;\n\n                case 'stopUpload':\n                    setState( 'paused' );\n                    break;\n\n            }\n        });\n\n        uploader.onError = function( code ) {\n            alert( 'Eroor: ' + code );\n        };\n\n        $upload.on('click', function() {\n            if ( $(this).hasClass( 'disabled' ) ) {\n                return false;\n            }\n\n            if ( state === 'ready' ) {\n                uploader.upload();\n            } else if ( state === 'paused' ) {\n                uploader.upload();\n            } else if ( state === 'uploading' ) {\n                uploader.stop();\n            }\n        });\n\n        $info.on( 'click', '.retry', function() {\n            uploader.retry();\n        } );\n\n        $info.on( 'click', '.ignore', function() {\n            alert( 'todo' );\n        } );\n\n        $upload.addClass( 'state-' + state );\n        updateTotalProgress();\n    });\n});"
  },
  {
    "path": "examples/requirejs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebUploader演示</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../css/webuploader.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../../examples/image-upload/style.css\" />\n</head>\n\n\n<body>\n    <div id=\"wrapper\">\n        <div id=\"container\">\n            <!--头部，相册选择和格式选择-->\n\n            <div id=\"uploader\">\n                <div class=\"queueList\">\n                    <div id=\"dndArea\" class=\"placeholder\">\n                        <div id=\"filePicker\"></div>\n                        <p>或将照片拖到这里，单次最多可选300张</p>\n                    </div>\n                </div>\n                <div class=\"statusBar\" style=\"display:none;\">\n                    <div class=\"progress\">\n                        <span class=\"text\">0%</span>\n                        <span class=\"percentage\"></span>\n                    </div><div class=\"info\"></div>\n                    <div class=\"btns\">\n                        <div id=\"filePicker2\"></div><div class=\"uploadBtn\">开始上传</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n    <script src=\"./require.js\" data-main=\"app.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/requirejs/require.js",
    "content": "/*\n RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.\n Available via the MIT or new BSD license.\n see: http://github.com/jrburke/requirejs for details\n*/\nvar requirejs,require,define;\n(function(ca){function G(b){return\"[object Function]\"===N.call(b)}function H(b){return\"[object Array]\"===N.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&\"object\"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof\nRegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split(\".\"),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+\"\\nhttp://requirejs.org/docs/errors.html#\"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split(\"/\");n=I;var m=l.map,k=m&&m[\"*\"];if(a&&\".\"===a.charAt(0))if(e){n=\nI.slice(0,I.length-1);a=a.split(\"/\");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,\"\"));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],\".\"===c)n.splice(e,1),e-=1;else if(\"..\"===c)if(1===e&&(\"..\"===n[2]||\"..\"===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join(\"/\")}else 0===a.indexOf(\"./\")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split(\"/\");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join(\"/\");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join(\"/\")))if(b=j(b,d)){f=b;\ng=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join(\"/\"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName(\"script\"),function(e){if(e.getAttribute(\"data-requiremodule\")===a&&e.getAttribute(\"data-requirecontext\")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf(\"!\"):-1;-1<b&&(e=a.substring(0,\nb),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k=\"\";a||(m=!1,a=\"_@r\"+(N+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?\"_unnormalized\"+(Q+=1):\"\";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+\"!\"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));\nreturn b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))\"defined\"===e&&b(p[f]);else if(n=q(a),n.error&&\"error\"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit(\"error\",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit(\"error\",a.error):(e[f]=\n!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C(\"timeout\",\"Load timeout for modules: \"+\nf,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function L(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent(\"onreadystatechange\",e):a.removeEventListener(\"load\",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener(\"error\",e,!1);return{node:a,id:a&&a.getAttribute(\"data-requiremodule\")}}function M(){var a;\nfor(x();A.length;){a=A.shift();if(null===a[0])return w(C(\"mismatch\",\"Mismatched anonymous define() module: \"+a[a.length-1]));E(a)}}var X,$,i,K,Y,l={waitSeconds:7,baseUrl:\"./\",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},N=1,Q=1;K={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?a.module:\na.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:K.exports(a)}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on(\"error\",b);else this.events.error&&(b=t(this,function(a){this.emit(\"error\",a)}));this.depMaps=a&&a.slice(0);this.errback=b;this.inited=\n!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=\nthis.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit(\"error\",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&\n(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?\"define\":\"require\",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit(\"defined\",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=\nthis.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,\"defined\",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||\"\"),f=m(a.prefix+\"!\"+J,this.map.parentMap),r(f,\"defined\",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);\nif(this.events.error)g.on(\"error\",t(this,function(a){this.emit(\"error\",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+\"_unnormalized\")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C(\"fromtexteval\",\n\"fromText eval for \"+b+\" failed: \"+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if(\"string\"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(K,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,\"defined\",t(this,function(a){this.defineDep(b,\na);this.check()}));this.errback&&r(a,\"error\",t(this,this.errback))}c=a.id;f=k[c];!s(K,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});\"error\"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,\nnextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&\"/\"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+=\"/\");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,\na=\"string\"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+\"/\"+(a.main||\"main\").replace(ja,\"\").replace(R,\"\")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=\n!0);if(\"string\"===typeof f){if(G(c))return w(C(\"requireargs\",\"Invalid require call\"),d);if(a&&s(K,f))return K[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C(\"notloaded\",'Module name \"'+j+'\" has not been loaded yet for context: '+b+(a?\"\":\". Use require([])\"))):p[j]}M();i.nextTick(function(){M();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf(\".\"),g=b.split(\"/\")[0];if(-1!==\nd&&(!(\".\"===g||\"..\"===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,\nc,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C(\"nodefine\",\"No define call for \"+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||\"\");else{f=l.paths;a=a.split(\"/\");for(d=a.length;0<d;d-=1)if(g=a.slice(0,\nd).join(\"/\"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join(\"/\");f+=b||(/^data\\:|\\?/.test(f)||c?\"\":\".js\");f=(\"/\"===f.charAt(0)||f.match(/^[\\w\\+\\.\\-]+:/)?\"\":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf(\"?\")?\"?\":\"&\")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if(\"load\"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=L(a),i.completeLoad(a.id)},onScriptError:function(a){var b=L(a);if(!g(b.id))return w(C(\"scripterror\",\n\"Script error for: \"+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,L,E,P,M,q,Q,la=/(\\/\\*([\\s\\S]*?)\\*\\/|([^:]|^)\\/\\/(.*)$)/mg,ma=/[^.]\\s*require\\s*\\(\\s*[\"']([^'\"\\s]+)[\"']\\s*\\)/g,R=/\\.js$/,ja=/^\\.\\//;x=Object.prototype;var N=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!(\"undefined\"!==typeof window&&\"undefined\"!==typeof navigator&&window.document),fa=!z&&\"undefined\"!==typeof importScripts,ka=z&&\"PLAYSTATION 3\"===navigator.platform?/^complete$/:/^(complete|loaded)$/,\nZ=\"undefined\"!==typeof opera&&\"[object Opera]\"===opera.toString(),F={},r={},S=[],O=!1;if(\"undefined\"===typeof define){if(\"undefined\"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}\"undefined\"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m=\"_\";!H(b)&&\"string\"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};\nh.nextTick=\"undefined\"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version=\"2.1.10\";h.jsExtRegExp=/^\\/|:|\\?|\\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v([\"toUrl\",\"undef\",\"defined\",\"specified\"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName(\"head\")[0],D=document.getElementsByTagName(\"base\")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=\nb.xhtml?document.createElementNS(\"http://www.w3.org/1999/xhtml\",\"html:script\"):document.createElement(\"script\");c.type=b.scriptType||\"text/javascript\";c.charset=\"utf-8\";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute(\"data-requirecontext\",b.contextName),g.setAttribute(\"data-requiremodule\",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf(\"[native code\"))&&!Z?(O=!0,g.attachEvent(\"onreadystatechange\",b.onScriptLoad)):\n(g.addEventListener(\"load\",b.onScriptLoad,!1),g.addEventListener(\"error\",b.onScriptError,!1)),g.src=d,M=g,D?y.insertBefore(g,D):y.appendChild(g),M=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C(\"importscripts\",\"importScripts failed for \"+c+\" at \"+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName(\"script\"),function(b){y||(y=b.parentNode);if(L=b.getAttribute(\"data-main\"))return q=L,r.baseUrl||(E=q.split(\"/\"),q=E.pop(),Q=E.length?E.join(\"/\")+\"/\":\"./\",r.baseUrl=\nQ),q=q.replace(R,\"\"),h.jsExtRegExp.test(q)&&(q=L),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;\"string\"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,\"\").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?[\"require\"]:[\"require\",\"exports\",\"module\"]).concat(c)));if(O){if(!(g=M))P&&\"interactive\"===P.readyState||U(document.getElementsByTagName(\"script\"),function(b){if(\"interactive\"===b.readyState)return P=b}),g=P;g&&(b||\n(b=g.getAttribute(\"data-requiremodule\")),h=F[g.getAttribute(\"data-requirecontext\")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);\n"
  },
  {
    "path": "flash/.actionScriptProperties",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<actionScriptProperties analytics=\"false\" mainApplicationPath=\"Uploader.as\" projectUUID=\"3c8f9be3-73ae-4f47-b9d1-72da3f51bedb\" version=\"11\">\n  <compiler additionalCompilerArguments=\"-locale en_US\" advancedTelemetry=\"false\" autoRSLOrdering=\"true\" copyDependentFiles=\"true\" fteInMXComponents=\"false\" generateAccessible=\"false\" htmlExpressInstall=\"true\" htmlGenerate=\"false\" htmlHistoryManagement=\"true\" htmlPlayerVersionCheck=\"true\" includeNetmonSwc=\"false\" outputFolderLocation=\"/Users/liaoxuezhi/www/webuploader/dist\" outputFolderPath=\"bin-debug\" removeUnusedRSL=\"true\" sourceFolderPath=\"src\" strict=\"true\" targetPlayerVersion=\"10.1.0\" useApolloConfig=\"false\" useDebugRSLSwfs=\"true\" useFlashSDK=\"true\" verifyDigests=\"true\" warn=\"true\">\n    <compilerSourcePath/>\n    <libraryPath defaultLinkType=\"0\">\n      <libraryPathEntry kind=\"1\" linkType=\"1\" path=\"lib\"/>\n      <libraryPathEntry kind=\"4\" path=\"\">\n        <excludedEntries>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation_charts.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"1\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/locale/{locale}\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/advancedgrids.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/qtp.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation_air.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/charts.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/framework.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/mx/mx.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/netmon.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/spark.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/sparkskins.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/rpc.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/videoPlayer.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/qtp_air.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/datavisualization.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/spark_dmv.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/flash-integration.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc\" useDefaultLinkType=\"false\"/>\n          <libraryPathEntry kind=\"3\" linkType=\"1\" path=\"${PROJECT_FRAMEWORKS}/libs/automation_agent.swc\" useDefaultLinkType=\"false\"/>\n        </excludedEntries>\n      </libraryPathEntry>\n    </libraryPath>\n    <sourceAttachmentPath/>\n  </compiler>\n  <applications>\n    <application path=\"Uploader.as\"/>\n  </applications>\n  <modules/>\n  <workers/>\n  <buildCSSFiles/>\n  <flashCatalyst validateFlashCatalystCompatibility=\"false\"/>\n</actionScriptProperties>\n"
  },
  {
    "path": "flash/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>Uploader</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>com.adobe.flexbuilder.project.flexbuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>\n\t</natures>\n\t<linkedResources>\n\t\t<link>\n\t\t\t<name>bin-debug</name>\n\t\t\t<type>2</type>\n\t\t\t<location>/Users/liaoxuezhi/www/webuploader/dist</location>\n\t\t</link>\n\t\t<link>\n\t\t\t<name>bin-release</name>\n\t\t\t<type>2</type>\n\t\t\t<location>/Library/WebServer/www/webuploader/dist</location>\n\t\t</link>\n\t\t<link>\n\t\t\t<name>bin-release1</name>\n\t\t\t<type>2</type>\n\t\t\t<location>/Users/liaoxuezhi/www/webuploader/dist</location>\n\t\t</link>\n\t</linkedResources>\n</projectDescription>\n"
  },
  {
    "path": "flash/.settings/org.eclipse.core.resources.prefs",
    "content": "#Fri Oct 25 17:23:15 CST 2013\neclipse.preferences.version=1\nencoding/<project>=utf-8\n"
  },
  {
    "path": "flash/Uploader.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"Flex\" version=\"4\">\n  <component name=\"FlexBuildConfigurationManager\" active=\"Uploader\">\n    <configurations>\n      <configuration name=\"Uploader\" pure-as=\"true\" main-class=\"Uploader\" output-file=\"Uploader.swf\" output-folder=\"$MODULE_DIR$/../dist\">\n        <dependencies target-player=\"11.4\">\n          <entries>\n            <entry library-id=\"57d771d1-d676-4656-bbf2-aca9a23211c1\">\n              <dependency linkage=\"Merged\" />\n            </entry>\n          </entries>\n          <sdk name=\"AIRSDK\" />\n        </dependencies>\n        <compiler-options>\n          <map>\n            <entry key=\"compiler.locale\" value=\"en_US\" />\n          </map>\n        </compiler-options>\n        <packaging-air-desktop />\n        <packaging-android />\n        <packaging-ios />\n      </configuration>\n    </configurations>\n    <compiler-options />\n  </component>\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"false\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"AIRSDK\" jdkType=\"Flex SDK Type (new)\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"module-library\" exported=\"\">\n      <library type=\"flex\">\n        <properties id=\"57d771d1-d676-4656-bbf2-aca9a23211c1\" />\n        <CLASSES>\n          <root url=\"file://$MODULE_DIR$/lib\" />\n        </CLASSES>\n        <JAVADOC />\n        <SOURCES />\n        <jarDirectory url=\"file://$MODULE_DIR$/lib\" recursive=\"false\" />\n      </library>\n    </orderEntry>\n  </component>\n</module>\n\n"
  },
  {
    "path": "flash/src/ComponentFactory.as",
    "content": "package\n{\n    import com.Blob;\n    import com.File;\n    import com.FilePicker;\n    import com.FileReader;\n    import com.FileReaderSync;\n    import com.Image;\n    import com.Md5;\n    import com.XMLHttpRequest;\n    import com.errors.RuntimeError;\n    \n    import flash.system.ApplicationDomain;\n    import flash.utils.getDefinitionByName;\n    import flash.utils.getQualifiedSuperclassName;\n\n    public class ComponentFactory\n    {\n        FilePicker, Blob, File, FileReader, FileReaderSync, Image, XMLHttpRequest, Md5;\n\n        private var _registry:Object = {};\n\n        public function add(uid:String, comp:*) : void\n        {\n            if (_registry.hasOwnProperty(uid)) {\n                throw new RuntimeError(RuntimeError.COMP_CONFLICT);\n            }\n            _registry[uid] = comp;\n        }\n\n\n        public function remove(uid:String) : Boolean\n        {\n            if (!_registry.hasOwnProperty(uid)) {\n                return false;\n            }\n            delete _registry.uid;\n            return true;\n        }\n\n\n        public function get(uid:String) : *\n        {\n            return _registry.hasOwnProperty(uid) ? _registry[uid] : false;\n        }\n\n\n        public function create(uploader:Uploader, uid:String, compName:String) : *\n        {\n            var compFQName:String, compClass:Class, exType:String, comp:*;\n\n            compFQName = \"com.\" + compName;\n\n            if (ApplicationDomain.currentDomain.hasDefinition(compFQName)) {\n                compClass = getDefinitionByName(compFQName) as Class;\n                comp = new compClass;\n\n                // if object dispatches events attach event listeners (@see for example FileInput for the interface)\n                if (compClass.dispatches) {\n                    for (exType in compClass.dispatches) {\n                        // new context required to handle this properly\n                        (function(type:String, exType:String) : void {\n\t\t\t\t\t\t\tcomp.addEventListener(type, function(e:*) : void {\n\t\t\t\t\t\t\t\tuploader.onComponentEvent(uid, e, exType);\n                            });\n                        }(compClass.dispatches[exType], /*compName + '::' + */exType));\n                    }\n                }\n\n                // Moxie.log([uid, compName]);\n\n                // if component is descendant of the Sprite, add it to the stage\n                if (/Sprite$/.test(getQualifiedSuperclassName(comp))) {\n\t\t\t\t\tuploader.addChild(comp);\n                }\n\n                if (_registry.hasOwnProperty(uid)) {\n                    throw new RuntimeError(RuntimeError.COMP_CONFLICT);\n                }\n                return (_registry[uid] = comp);\n            } else {\n                throw new RuntimeError(RuntimeError.NOT_SUPPORTED_ERR);\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "flash/src/Uploader.as",
    "content": "package\n{\n    import com.errors.RuntimeError;\n    import com.events.OProgressEvent;\n    import com.utils.Utils;\n    import com.utils.Zhenpin;\n    \n    import flash.display.Sprite;\n    import flash.display.StageAlign;\n    import flash.display.StageScaleMode;\n    import flash.events.Event;\n    import flash.events.ProgressEvent;\n    import flash.external.ExternalInterface;\n    import flash.system.Security;\n    import flash.ui.ContextMenu;\n    import flash.ui.ContextMenuItem;\n    import flash.utils.getQualifiedClassName;\n    \n//    import cmodule.as3_jpeg_wrapper.CLibInit;\n\n    [SWF(width='500', height='500')]\n    public class Uploader extends Sprite\n    {\n        public static var uid:String;\n\n        private var jsReciver:String = \"Uploader.reciver\";\n\t\t\n//\t\tprivate static var clib:Object;\n\n        public static var compFactory:ComponentFactory;\n\n        /**\n         * Main constructor for the Uploader class.\n         */\n        public function Uploader()\n        {\n            if (stage) {\n                _init();\n            } else {\n                addEventListener(Event.ADDED_TO_STAGE, _init);\n            }\n        }\n\n\n        /**\n         * Initialization event handler.\n         *\n         * @param e Event object.\n         */\n        private function _init(e:Event = null):void\n        {\n            removeEventListener(Event.ADDED_TO_STAGE, _init);\n\n            // Allow scripting on swf loaded from another domain\n\t\t\tSecurity.allowDomain(\"*\");\n\n//\t\t\tvar loader:CLibInit = new CLibInit();\n//\t\t\tclib = loader.init();\n\n            // Align and scale stage\n            stage.align = StageAlign.TOP_LEFT;\n            stage.scaleMode = StageScaleMode.EXACT_FIT;\n\t\t\t\n\t\t\tvar menu:ContextMenu = new ContextMenu();\n\t\t\tmenu.hideBuiltInItems();\n\t\t\tmenu.customItems.push(new ContextMenuItem(\"webuploader v1.0\"));//???preprocessor.xml, antä¼???¨æ??°æ??????\n\t\t\tthis.contextMenu = menu;\n\n            var params:Object = stage.loaderInfo.parameters;\n\n            // Setup id\n            Uploader.uid = Utils.sanitize(params[\"uid\"]);\n\n            // Event dispatcher\n            if (params.hasOwnProperty(\"jsreciver\") && /^[\\w\\.]+$/.test(params[\"jsreciver\"])) {\n                jsReciver = params[\"jsreciver\"];\n            }\n\t\t\t\n\t\t\t// 整个很重要，用来增加as贞频的。\n            // 整个很重要，用来增加as贞频的。\n            try {\n                Zhenpin.start();\n            } catch (err:*) {\n                _fireEvent(Uploader.uid, err);\n            }\n\n            //ExternalInterface.marshallExceptions = true; // propagate AS exceptions to JS and vice-versa\n            ExternalInterface.addCallback('exec', exec);\n\n            // initialize component factory\n            Uploader.compFactory = new ComponentFactory;\n\n\t\t\t_fireEvent(Uploader.uid + \"::Ready\");\n        }\n\n\n        public function exec(uid:String, compName:String, action:String, args:* = null) : *\n        {\n            // Uploader.log(arguments);\n\n            uid = Utils.sanitize(uid); // make it safe\n\n            var comp:* = Uploader.compFactory.get(uid),\n\t\t\t\tret:*;\n\n            // WebUploader.log([compName, action]);\n\n            try {\n\t\t\t\tif ( action == 'destroy' ) {\n\t\t\t\t\t\n\t\t\t\t\tif ( comp.hasOwnProperty(action) ) {\n\t\t\t\t\t\tret = comp[action].apply(comp, args as Array);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tUploader.compFactory.remove(uid);\n\t\t\t\t\t// Uploader.log(['destory', compName, uid]);\n\t\t\t\t\t\n\t\t\t\t\treturn ret;\n\t\t\t\t\t\n\t\t\t\t} else if (!comp) {\n                    comp = Uploader.compFactory.create(this, uid, compName);\n                }\n\n                // execute the action if available\n                if (comp.hasOwnProperty(action)) {\n\t\t\t\t\t// Uploader.log([uid, compName, action, args]);\n\t\t\t\t\tret = comp[action].apply(comp, args as Array);\n\t\t\t\t\t\n\t\t\t\t\t// Uploader.log([uid, compName, action, args, ret]);\n                    return ret;\n                }\n\n            } catch(err:*) { // re-route exceptions thrown by components (TODO: check marshallExceptions feature)\n\t\t\t\t// Uploader.log([ getQualifiedClassName(err), compName, action]);\n\t\t\t\t_fireEvent(uid + \"::Exception\", { name: getQualifiedClassName(err).replace(/^[^:*]::/, ''), code: err.errorID });\n            }\n        }\n\n\n        /**\n         * Intercept component events and do some operations if required\n         *\n         * @param uid String unique identifier of the component throwing the event\n         * @param e mixed Event object\n         * @param exType String event type in WebUploader format\n         */\n        public function onComponentEvent(uid:String, e:*, exType:String) : void\n        {\n            var evt:Object = {};\n\n            switch (e.type)\n            {\n                case ProgressEvent.PROGRESS:\n                case OProgressEvent.PROGRESS:\n                    evt.loaded = e.bytesLoaded;\n                    evt.total = e.bytesTotal;\n                    break;\n            }\n\n            evt.type = [uid, exType].join('::');\n\t\t\t// Uploader.log([uid, exType, e.hasOwnProperty('data') ? e.data : null]);\n\t\t\t_fireEvent(evt, e.hasOwnProperty('data') ? e.data : null);\n        }\n\t\t\n//\t\tpublic static function encodeJpeg(ba:ByteArray, width:uint, height:uint, quality:uint=90 ):ByteArray{\n//\t\t\treturn clib.write_jpeg_file(ba, width, height, 3, 2, quality);\n//\t\t}\n\n\n\n        /**\n         * Fires an event from the flash movie out to the page level JS.\n         *\n         * @param uid String unique identifier of the component throwing the event\n         * @param type Name of event to fire.\n         * @param obj Object with optional data.\n         */\n        private function _fireEvent(evt:*, obj:Object = null):void {\n            try {\n\t\t\t\tExternalInterface.call(jsReciver, evt, obj);\n            } catch(err:*) {\n\t\t\t\tUploader.log(err);\n                //_fireEvent(\"Exception\", { name: 'RuntimeError', message: 4 });\n\n                // throwing an exception would be better here\n            }\n        }\n\n\n        public static function log(obj:*) : void {\n\t\t\tExternalInterface.call('console.log', obj);\n        }\n\n    }\n}"
  },
  {
    "path": "flash/src/com/Blob.as",
    "content": "package com\n{\n\timport com.utils.Buffer;\n\t\n\timport flash.net.FileReference;\n\timport flash.utils.ByteArray;\n\timport com.utils.Utils;\n\n\tpublic class Blob\n\t{\t\n\t\tprivate var _uid:String;\n\t\tpublic function get uid() : String {\n\t\t\treturn _uid;\n\t\t}\t\t\n\t\t\n\t\tprivate var _size:Number;\t\t\n\t\tpublic function\tget size() : Number {\n\t\t\treturn _size;\n\t\t}\n\t\t\n\t\tprivate var _type:String = '';\n\n        public function get type() : String {\n            if (_type !== '') {\n                return _type;\n            }\n            // if source is not a FileReference return default name\n            if (!isFileRef()) {\n                return _type;\n            }\n\n            // otherwise return original name\n            return ''; //_sources[0].buffer.fileRef.type;\n        }\n\t\t\n\t\t// cumulative size of all the sources this blob is part of\n\t\tpublic function get realSize() : uint {\n\t\t\tvar src:Object, size:uint = 0;\n\t\t\t\n\t\t\tfor each (src in _sources) {\n\t\t\t\tsize += src.buffer.size;\n\t\t\t}\n\t\t\treturn size;\n\t\t}\n\t\t\n\t\t// cumulative size of all preloaded sources this blob is part of\n\t\tpublic function get cachedSize() : uint {\n\t\t\tvar src:Object, size:uint = 0; \n\t\t\t\n\t\t\tfor each (src in _sources) {\n\t\t\t\tif (!src.buffer.data) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsize += src.buffer.data.length;\n\t\t\t}\n\t\t\treturn size;\n\t\t}\n\t\t\n\t\tpublic var _sources:Array = [];\n\t\t\n\t\tprotected var _pointer:Number = 0;\n\t\t\n\t\t\n\t\tpublic function Blob(sources:Array, properties:* = null) \n\t\t{\t\t\t\n\t\t\tfor each (var source:* in sources) {\n\t\t\t\tif (source is FileReference) {\n\t\t\t\t\t_sources.push({\n\t\t\t\t\t\tbuffer: new Buffer(source),\n\t\t\t\t\t\tstart: 0,\n\t\t\t\t\t\tend: source.size\n\t\t\t\t\t});\n\t\t\t\t\t_pointer += source.size;\n\t\t\t\t} else if (source is ByteArray) {\n\t\t\t\t\t_sources.push({\n\t\t\t\t\t\tbuffer: new Buffer(source),\n\t\t\t\t\t\tstart: 0,\n\t\t\t\t\t\tend: source.length\n\t\t\t\t\t});\n\t\t\t\t\t_pointer += source.length;\n\t\t\t\t} else if (source is Blob) {\n\t\t\t\t\t// increment reference counters for associated buffers\n\t\t\t\t\tfor (var i:uint = 0, max:uint = source._sources.length; i < max; i++) {\n\t\t\t\t\t\tsource._sources[i].buffer.refs++;\n\t\t\t\t\t}\n\t\t\t\t\t// simply copy over the sources\n\t\t\t\t\t_sources.push.apply(source._sources);\n\t\t\t\t\t_pointer += source.size;\n\t\t\t\t} else if (source.hasOwnProperty('buffer') && source.buffer is Buffer) {\n\t\t\t\t\t_sources.push({\n\t\t\t\t\t\tbuffer: source.buffer,\n\t\t\t\t\t\tstart: source.start,\n\t\t\t\t\t\tend: source.end\n\t\t\t\t\t});\n\t\t\t\t\t_pointer += source.end - source.start;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (properties is String) {\n\t\t\t\t_type = properties;\n\t\t\t} else if (properties is Object && properties.hasOwnProperty('type')) {\n\t\t\t\t_type = properties.type;\n\t\t\t}\n\t\t\t\n\t\t\t_size = _pointer;\n\t\t\t_uid = Utils.guid('uid_');\n\t\t}\n\t\t\n\t\tpublic function slice(... args) : Object \n\t\t{\n\t\t\tvar blob:Blob = _slice.apply(null, args);\n\t\t\tUploader.compFactory.add(blob.uid, blob);\n\t\t\treturn blob.toObject(); \n\t\t}\n\t\t\n\t\t\n\t\tprivate function _slice(... args) : Blob {\n\t\t\tvar src:Object, \n\t\t\t\tstart:int = args[0] || 0, \n\t\t\t\tend:int = args[1] || _size, \n\t\t\t\tcontentType:String = args[2] || '',\n\t\t\t\tsize:uint, offset:uint = 0,\n\t\t\t\tsources:Array = [];\n\t\t\t\t\t\t\t\n\t\t\tif (start > end) {\n\t\t\t\treturn new Blob([], contentType); \n\t\t\t}\n\t\t\t\n\t\t\tfor (var i:uint = 0, length:uint = _sources.length; i < length; i++) {\n\t\t\t\tsrc = _sources[i];\n\t\t\t\tsize = src.end - src.start;\n\t\t\t\t\t\t\t\t\n\t\t\t\tif (start > offset + size) { // start is outside of the current source's boundaries \n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Moxie.log([src.start, src.end, start, end, size]);\n\t\t\t\t\t\t\t\t\n\t\t\t\tsources.push({\n\t\t\t\t\tbuffer: src.buffer,\n\t\t\t\t\tstart: src.start + start - offset,\n\t\t\t\t\tend: Math.min(src.end, end)\n\t\t\t\t});\n\t\t\t\toffset += size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tif (i == length || offset > end) {\n\t\t\t\treturn new Blob(sources, contentType);\n\t\t\t} \n\t\t\t\n\t\t\t// loop for the end otherwise\n\t\t\tfor (; i < length; src = _sources[i], i++) {\n\t\t\t\toffset += src.end - src.start;\n\t\t\t\tif (offset < end) {\n\t\t\t\t\tsources.push(src);\n\t\t\t\t} else {\n\t\t\t\t\tsources.push({\n\t\t\t\t\t\tbuffer: src.buffer,\n\t\t\t\t\t\tstart: src.start,\n\t\t\t\t\t\tend: src.end - (offset - end)\n\t\t\t\t\t});\n\t\t\t\t\tbreak; // we have found the end\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn new Blob(sources, contentType);\n\t\t}\n\t\t\n\t\tpublic function isEmpty() : Boolean {\n\t\t\treturn !this._sources.length;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function isFileRef() : Boolean \n\t\t{\n\t\t\treturn false; // Blob as a rule contains only part of the source\n\t\t}\n\n        private var _isLoading:Boolean =false;\n        public function isLoading() : Boolean {\n            return _isLoading;\n        }\n        public function setLoading(val:Boolean):void {\n            _isLoading = val;\n        }\n\t\t\n\t\t\n\t\tpublic function getFileRef() : FileReference {\n\t\t\tif (isFileRef()) {\n\t\t\t\treturn _sources[0].buffer.fileRef;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function toObject() : Object \n\t\t{\n\t\t\treturn {\n\t\t\t\tuid: uid,\n\t\t\t\truid: Uploader.uid,\n\t\t\t\tsize: size,\n\t\t\t\ttype: type\n\t\t\t};\n\t\t}\n\t\t\n\t\tpublic function purge() : void\n\t\t{\t\t\t\n\t\t\tfor each (var src:Object in _sources) {\n\t\t\t\tsrc.buffer.purge();\t\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function destroy() : void\n\t\t{\t\t\t\n\t\t\tfor each (var src:Object in _sources) {\n\t\t\t\tif (--src.buffer.refs <= 0) {\n\t\t\t\t\tsrc.buffer.destroy();\t\n\t\t\t\t}\n\t\t\t}\n\t\t\tUploader.compFactory.remove(_uid);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/File.as",
    "content": "package com\n{\n\timport com.utils.Utils;\n\n\tpublic class File extends Blob\n\t{\n\t\tprivate var _name:String = '';\n\t\tpublic function get name() : String {\n\t\t\tif (_name !== '') {\n\t\t\t\treturn _name;\n\t\t\t}\n\t\t\t// if source is not a FileReference return default name\n\t\t\tif (!isFileRef()) {\n\t\t\t\treturn uid;\n\t\t\t}\n\t\t\t// otherwise return original name\n\t\t\treturn _sources[0].buffer.fileRef.name;\n\t\t}\n\t\t\t\t\n\t\t\n\t\tprivate var _lastModifiedDate:Date;\n\t\tpublic function get lastModifiedDate() : Date {\n\t\t\tif (!isFileRef()) {\n\t\t\t\treturn new Date();\n\t\t\t}\n\t\t\treturn _sources[0].buffer.fileRef.modificationDate;\n\t\t}\n\t\t\t\t\n\t\t\n\t\tpublic function File(sources:Array, properties:* = null)\n\t\t{\n\t\t\tif (properties is Object && properties.hasOwnProperty('name')) {\n\t\t\t\t_name = properties.name;\n\t\t\t}\n\n            super(sources, properties);\n\t\t}\n\t\t\n\t\t\n\t\tpublic override function isFileRef() : Boolean \n\t\t{\n\t\t\treturn !!(_sources.length === 1 && _sources[0].buffer.fileRef);\n\t\t}\n\t\t\n\t\t\n\t\tpublic override function toObject() : Object \n\t\t{\n\t\t\treturn Utils.extend(super.toObject(), {\n\t\t\t\tname: name,\n\t\t\t\tlastModifiedDate: lastModifiedDate\n\t\t\t});\n\t\t}\n\t\t\n\t}\n}"
  },
  {
    "path": "flash/src/com/FilePicker.as",
    "content": "package com\n{\n\timport com.events.FilePickerEvent;\n\n\timport flash.display.Sprite;\n\timport flash.events.Event;\n\timport flash.events.MouseEvent;\n\timport flash.net.FileFilter;\n\timport flash.net.FileReference;\n\timport flash.net.FileReferenceList;\n\timport com.utils.Utils;\n\n\tpublic class FilePicker extends Sprite\n\t{\n\t\tpublic static var dispatches:Object = { // hash of events dispatched by this class\n\t\t\t\"Cancel\": FilePickerEvent.CANCEL,\n\t\t\t\"Change\": FilePickerEvent.SELECT,\n\t\t\t\"DialogOpen\": FilePickerEvent.OPEN,\n\t\t\t\"MouseEnter\": MouseEvent.ROLL_OVER,\n\t\t\t\"MouseLeave\": MouseEvent.ROLL_OUT,\n\t\t\t\"MouseDown\": MouseEvent.MOUSE_DOWN,\n\t\t\t\"MouseUp\": MouseEvent.MOUSE_UP\n\t\t};\n\n\t\tpublic static var stageOccupied:Boolean = false;\n\n\t\tprotected var _options:Object = {};\n\n\t\tprotected var _disabled:Boolean = false;\n\n\t\tprotected var _filters:Array = null;\n\n\t\tprotected var _picker:*;\n\n\t\tprotected var _button:Sprite;\n\n\t\tprotected var _files:Array = [];\n\n\t\tpublic function init(options:Object = null) : void {\n\t\t\t_options = Utils.extend({\n\t\t\t\tname: 'Filedata',\n\t\t\t\tmultiple: false,\n\t\t\t\taccept: null\n\t\t\t}, options);\n\n\n\t\t\tif (_options.accept !== null) {\n\t\t\t\t_filters = [];\n\n\t\t\t\tfor (var i:int = 0; i < _options.accept.length; i++) {\n\t\t\t\t\t_filters.push(new FileFilter(\n\t\t\t\t\t\t_options.accept[i].title,\n\t\t\t\t\t\t'*.' + _options.accept[i].extensions.replace(/,/g, \";*.\")\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_button = new Sprite;\n\n\t\t\t_button.graphics.beginFill(0x000000, 0); // Fill with transparent color\n\t\t\t_button.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);\n\t\t\t_button.graphics.endFill();\n\t\t\t_button.buttonMode = true;\n\t\t\t_button.useHandCursor = true;\n\n\t\t\t_button.addEventListener(MouseEvent.CLICK, onClick);\n\t\t\t_button.addEventListener(MouseEvent.ROLL_OVER, onEvent);\n\t\t\t_button.addEventListener(MouseEvent.ROLL_OUT, onEvent);\n\t\t\t_button.addEventListener(MouseEvent.MOUSE_DOWN, onEvent);\n\t\t\t_button.addEventListener(MouseEvent.MOUSE_UP, onEvent);\n\n\t\t\taddChild(_button);\n\t\t}\n\n\n\t\tpublic function disable(disabled:Boolean = true) : void {\n\t\t\t_disabled = disabled;\n\t\t\t_button.useHandCursor = !disabled;\n\t\t}\n\n\n\t\tpublic function destroy() : void {\n\n\t\t}\n\n\n\t\tprivate function onEvent(e:MouseEvent) : void {\n\t\t\te.stopPropagation();\n\n\t\t\tif (_disabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdispatchEvent(e);\n\t\t}\n\n\n\t\tprivate function onClick(e:MouseEvent) : void {\n\t\t\tif (_disabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t_picker = _options.multiple ? new FileReferenceList : new FileReference;\n\n\t\t\t_picker.addEventListener(Event.CANCEL, onDialogEvent);\n\t\t\t_picker.addEventListener(Event.SELECT, onDialogEvent);\n\t\t\t_picker.browse(_filters);\n\t\t\tdispatchEvent(new FilePickerEvent(FilePickerEvent.OPEN));\n\t\t}\n\n\n\t\tpublic function getFiles() : Array {\n\t\t\tvar files:Array = [];\n\n\t\t\tfor each (var file:File in _files) {\n\t\t\t\tUploader.compFactory.add(file.uid, file);\n\t\t\t\tfiles.push(file.toObject());\n\t\t\t}\n\t\t\treturn files;\n\t\t}\n\n\n\t\tprivate function onDialogEvent(e:Event) : void {\n\t\t\t_picker.removeEventListener(Event.CANCEL, onDialogEvent);\n\t\t\t_picker.removeEventListener(Event.SELECT, onDialogEvent);\n\n\t\t\tswitch (e.type) {\n\n\t\t\t\tcase Event.CANCEL:\n\t\t\t\t\tdispatchEvent(new FilePickerEvent(FilePickerEvent.CANCEL));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase Event.SELECT:\n\t\t\t\t\tvar fileRefList:Array = [];\n\n\t\t\t\t\tif (!_options.multiple) {\n\t\t\t\t\t\tfileRefList.push(_picker);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfileRefList = _picker.fileList;\n\t\t\t\t\t}\n\n\t\t\t\t\t_files = [];\n\n\t\t\t\t\tfor (var i:uint = 0; i < fileRefList.length; i++) {\n\t\t\t\t\t\t_files.push(new File([fileRefList[i]]));\n\t\t\t\t\t}\n\n\t\t\t\t\tdispatchEvent(new FilePickerEvent(FilePickerEvent.SELECT));\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/FileReader.as",
    "content": "package com\n{\n\timport com.errors.DOMError;\n\timport com.events.OErrorEvent;\n\timport com.events.OProgressEvent;\n\timport com.utils.Base64;\n\timport com.utils.Buffer;\n\timport com.utils.OEventDispatcher;\n\t\n\timport flash.events.Event;\n\timport flash.events.IOErrorEvent;\n\timport flash.events.ProgressEvent;\n\timport flash.net.FileReference;\n\timport flash.utils.ByteArray;\n\t\n\tpublic class FileReader extends OEventDispatcher\n\t{\n\t\t// events dispatched by this class\n\t\tpublic static var dispatches:Object = { \n\t\t\t\"LoadStart\": Event.OPEN,\n\t\t\t\"Progress\": OProgressEvent.PROGRESS,\n\t\t\t\"Load\": Event.COMPLETE,\n\t\t\t\"Error\": OErrorEvent.ERROR\n\t\t};\n\t\t\n\t\tpublic static const EMPTY:int = 0;\n\t\tpublic static const LOADING:int = 1;\n\t\tpublic static const DONE:int = 2;\n\t\t\n\t\tpublic var readyState:int = FileReader.EMPTY;\n\t\tpublic var result:*;\n\t\t\n\t\tprivate var _blob:*;\n\t\tprivate var _index:int = 0;\n\t\tprivate var _position:int = 0;\n\t\tprivate var _src:Object;\n\t\t\n\t\tprivate var _sizeRatio:Number = 1;\n\t\tprivate var _notCachedSize:uint = 0;\n\t\t\n\t\tprivate var _op:String;\n\t\t\n\t\tprivate var _ba:ByteArray = new ByteArray;\n\t\tprivate var _str:String;\n\t\t\n\t\t\n\t\tpublic function readAsByteArray(blob:*) : void {\n\t\t\t_op = \"asByteArray\";\n\t\t\t_read(blob); \n\t\t}\n\t\t\n\t\tpublic function readAsBase64(blob:*) : void {\n\t\t\t_str = '';\n\t\t\t_op = \"asBase64\";\n\t\t\t_read(blob); \n\t\t}\n\t\t\n\t\tpublic function abort() : void {\t\n\t\t\tif (this.readyState !== FileReader.LOADING) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\tif (_src && _src.buffer.fileRef) {\n\t\t\t\t_src.buffer.fileRef.cancel();\n\t\t\t\t_removeAllEventListeners(_src.dataObject.fileRef);\n\t\t\t}\n\t\t\t\n\t\t\tclear();\n\t\t\t\n\t\t\tthis.readyState = FileReader.DONE;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function clear() : void {\n\t\t\tif (this.result is ByteArray) {\n\t\t\t\tthis.result.clear();\n\t\t\t} else if (this.result is String) {\n\t\t\t\tthis.result = '';\n\t\t\t}\n\t\t\tthis.result = null;\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _read(blob:*) : void {\n\t\t\t\t\t\t\n\t\t\tif (typeof blob === 'string') {\n\t\t\t\tblob = Uploader.compFactory.get(blob);\n\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\tif (!blob || !(blob is Blob) || blob.isEmpty()) {\n\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, DOMError.NOT_FOUND_ERR));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\tif (this.readyState === FileReader.LOADING) {\n\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, DOMError.SECURITY_ERR)); // should be invalid state\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t_blob = blob;\n\t\t\t\t\t\t\n\t\t\t_sizeRatio = _blob.size / _blob.realSize;\n\t\t\t_notCachedSize = _blob.realSize - _blob.cachedSize;\n\t\t\t\t\t\t\n\t\t\tthis.readyState = FileReader.LOADING;\n\t\t\t\n\t\t\tdispatchEvent(new Event(Event.OPEN));\n\t\t\t\n\t\t\t_ba.clear();\n\t\t\t_index = 0;\n\t\t\t_src = blob._sources[_index++];\n\t\t\t\n\t\t\t// this will run recursively until all DataSources are read and concatenated into _ba\n\t\t\t_loadSource(); \n\t\t}\n\t\t\n\t\tprivate function _loadSource() : void {\t\n\t\t\tvar buffer:Buffer = _src.buffer;\n\t\t\t\n\t\t\tif (this.readyState != FileReader.LOADING) { // it might have been aborted by now\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (buffer.data && buffer.data.length) {\t\n\t\t\t\tonComplete({ // mimic event object structure\n\t\t\t\t\ttype: Event.COMPLETE,\n\t\t\t\t\ttarget: {\n\t\t\t\t\t\tdata: buffer.data\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else if (buffer.fileRef) {\n                _blob.setLoading(true);\n                buffer.fileRef.addEventListener(ProgressEvent.PROGRESS, onProgress);\n\t\t\t\tbuffer.fileRef.addEventListener(Event.COMPLETE, onComplete);\n\t\t\t\tbuffer.fileRef.addEventListener(IOErrorEvent.IO_ERROR, onIOError);\n\t\t\t\tbuffer.fileRef.load();\n\t\t\t} else {\n\t\t\t\tonIOError({ \n\t\t\t\t\ttype: IOErrorEvent.IO_ERROR,\n\t\t\t\t\ttarget: {}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onProgress(e:ProgressEvent) : void {\n\t\t\tvar bytesLoaded:*;\n\t\t\t\n\t\t\te.stopPropagation();\n\t\t\t\n\t\t\t// hold the value within the size of the blob\n\t\t\tbytesLoaded = Math.floor((e.bytesLoaded + _position) * _sizeRatio);\n\t\t\t\n\t\t\t// Moxie.log([bytesLoaded, _blob.size]);\n\t\t\t\n\t\t\tdispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, bytesLoaded, _blob.size));\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onIOError(e:*) : void {\n\t\t\tthis.readyState = FileReader.DONE;\n\t\t\tclear();\n\t\t\t_removeAllEventListeners(e.target);\n\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, DOMError.NOT_READABLE_ERR));\t\t\n\t\t}\t\t\n\t\t\n\t\t\n\t\tprivate function onComplete(e:*) : void {\n\t\t\tvar length:Number, data:ByteArray;\n\t\t\t\n\t\t\tthis.readyState = FileReader.DONE;\n\t\t\t\t\t\t\n\t\t\tif (e.target is FileReference) { // this func might be called directly\n\t\t\t\t_removeAllEventListeners(e.target);\n\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\tlength = _src.end - _src.start;\n\t\t\tdata = e.target.data;\n\t\t\t\n\t\t\tdata.position = _src.start;\n\t\t\tdata.readBytes(_ba, 0, length);\n\t\t\t\n\t\t\t_position += length;\n\t\t\t\t\t\t\t\t\t\n\t\t\t_index++;\n\t\t\tif (_blob._sources[_index]) {\n\t\t\t\t_src = _blob._sources[_index];\n\t\t\t\t_loadSource();\n\t\t\t} else if (_position === _blob.size) { \n\t\t\t\t_finalize(); // we've reached the end, send the blob data out\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _removeAllEventListeners(target:*) : void {\n            _blob.setLoading(false);\n            target.removeEventListener(ProgressEvent.PROGRESS, onProgress);\n\t\t\ttarget.removeEventListener(Event.COMPLETE, onComplete);\n\t\t\ttarget.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _finalize() : void {\t\t\t\n\t\t\tswitch (_op) {\n\t\t\t\tcase 'asByteArray':\n\t\t\t\t\tthis.result = _ba;\n\t\t\t\t\t_ba = null;\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tcase 'asBase64':\n\t\t\t\t\t_toBase64();\n\t\t\t\t\tthis.result = _str;\n\t\t\t\t\t_ba.clear(); // we won't be needing this one in this case anymore\n\t\t\t\t\t_str = null;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tdispatchEvent(new Event(Event.COMPLETE));\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _toBase64() : void {\n\t\t\tvar base64:String, chunk_size:uint, chunk:ByteArray, length:uint, loaded:uint;\n\t\t\t\n\t\t\t// by this moment _ba should be populated with binary data\n\t\t\tif (!_ba.length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tchunk_size = 204798; // bytes, should divide by three\n\t\t\tchunk = new ByteArray;\n\t\t\tlength = _ba.length;\n\t\t\tloaded = 0;\n\t\t\t\n\t\t\t_ba.position = 0;\n\t\t\t\n\t\t\twhile (_ba.bytesAvailable > 0) {\n\t\t\t\t\n\t\t\t\tif (chunk_size > _ba.bytesAvailable) {\n\t\t\t\t\tchunk_size = _ba.bytesAvailable; // last chunk might be small\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_ba.readBytes(chunk, 0, chunk_size);\n\t\t\t\t\n\t\t\t\tloaded += chunk_size;\n\t\t\t\t\n\t\t\t\tbase64 = Base64.encode(chunk);\n\t\t\t\t_str += base64;\n\t\t\t\tchunk.clear();\n\t\t\t\t\n\t\t\t\tdispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, (loaded < length ? loaded : length), length, base64));\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t}\n}"
  },
  {
    "path": "flash/src/com/FileReaderSync.as",
    "content": "package com\n{\t\n\timport com.errors.DOMError;\n\timport com.utils.Base64;\n\timport flash.utils.ByteArray;\n\t\n\tpublic class FileReaderSync\n\t{\t\t\n\t\t\n\t\tpublic function readAsBase64(blob:*) : String {\n\t\t\tvar ba:ByteArray = _read(blob);\t\t\n\t\t\treturn Base64.encode(ba);\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _read(blob:*) : ByteArray {\n\t\t\tvar src:Object, \n\t\t\tba:ByteArray = new ByteArray;\n\t\t\t\n\t\t\tif (typeof blob === 'string') {\n\t\t\t\tblob = Uploader.compFactory.get(blob);\n\t\t\t}\n\t\t\t\n\t\t\tif (!blob || !(blob is Blob) || blob.isEmpty()) {\n\t\t\t\tthrow new DOMError(DOMError.NOT_FOUND_ERR);\n\t\t\t}\n\t\t\t\n\t\t\tfor each (src in blob._sources) {\n\t\t\t\tif (src.buffer.isEmpty()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tsrc.buffer.data.position = src.start;\n\t\t\t\tsrc.buffer.data.readBytes(ba, 0, src.end - src.start);\t\t\t\t\n\t\t\t}\n\t\t\treturn ba;\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/Image.as",
    "content": "package com\n{\n\timport com.errors.ImageError;\n\timport com.events.ODataEvent;\n\timport com.events.OErrorEvent;\n\timport com.events.OProgressEvent;\n\timport com.image.BMP;\n\timport com.image.GIF;\n\timport com.image.ImageEditor;\n\timport com.image.JPEG;\n\timport com.image.JPEGEncoder;\n\timport com.image.PNG;\n\timport com.utils.BMPDecoder;\n\timport com.utils.Base64;\n\timport com.utils.OEventDispatcher;\n\timport com.utils.Utils;\n\t\n\timport flash.display.Bitmap;\n\timport flash.display.BitmapData;\n\timport flash.display.IBitmapDrawable;\n\timport flash.display.Loader;\n\timport flash.display.PNGEncoderOptions;\n\timport flash.events.Event;\n\timport flash.events.IOErrorEvent;\nimport flash.events.ProgressEvent;\nimport flash.geom.Matrix;\n\timport flash.system.System;\n\timport flash.utils.ByteArray;\n\timport flash.geom.Rectangle;\n\timport flash.geom.Point;\n\t\n\tpublic class Image extends OEventDispatcher\n\t{\n\t\t// events dispatched by this class\n\t\tpublic static var dispatches:Object = { \n\t\t\t\"Progress\": OProgressEvent.PROGRESS,\n\t\t\t\"Complete\": ODataEvent.DATA,\n\t\t\t\"Load\": Event.COMPLETE,\n\t\t\t\"Error\": OErrorEvent.ERROR\n\t\t};\n\t\t\n\t\tprivate var _orientation:uint = 1;\n\t\tprivate var _bd:BitmapData;\n\t\tprivate var _ba:ByteArray;\n\t\tprivate var _meta:Object;\n\t\tprivate var _info:Object;\n\t\tprivate var _img:*;\n\t\t\n\t\tpublic var type:String = 'image/jpeg';\n\t\tpublic var quality:uint = 70;\n\t\tpublic var _crop:Boolean = true;\n\t\tpublic var allowMagnify:Boolean = true;\n\t\tpublic var preserveHeaders:Boolean = false;\n\t\t\n\t\tpublic function init( options:Object = null ):void {\n\t\t\tUtils.extend(this, options, true);\n\t\t\tif (options.hasOwnProperty(\"crop\")){\t\t\t\t\n\t\t\t\t_crop = options[\"crop\"];\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function loadFromBlob( blob:* = null ):void {\n\t\t\tvar fr:FileReader;\n\n            \n\n\t\t\tif (typeof blob === 'string') {\n\t\t\t\tblob = Uploader.compFactory.get(blob);\n\t\t\t}\n\t\t\t\n\t\t\tif (!blob) {\n\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, ImageError.WRONG_FORMAT));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tfr = new FileReader;\n\n            fr.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) : void {\n                dispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, e.bytesLoaded, e.bytesTotal));\n            });\n\t\t\t\n\t\t\tfr.addEventListener(Event.COMPLETE, function(e:Event) : void {\n\t\t\t\tfr.removeAllEventsListeners();\n\t\t\t\t_loadFromByteArray(fr.result);\n\t\t\t\tdispatchEvent( new Event(Event.COMPLETE) );\n\t\t\t});\n\t\t\t\n\t\t\tfr.readAsByteArray(blob);\n\t\t}\n\t\t\n\t\tpublic function info( val:Object = null):Object {\n\t\t\tif ( val ) {\n\t\t\t\t_info = val;\n\t\t\t}\n\t\t\t\n\t\t\treturn _info;\n\t\t}\n\t\t\n\t\tpublic function meta( val:Object = null):Object {\n\t\t\tif ( val ) {\n\t\t\t\t_meta = val;\n\t\t\t}\n\t\t\t\n\t\t\treturn _meta;\n\t\t}\n\t\t\n\t\tprivate function _loadFromByteArray( ba:ByteArray ):void {\n\t\t\tif ( !_info ) {\n\t\t\t\tif (JPEG.test(ba)) {\n\t\t\t\t\t_img = new JPEG(ba);\t\t\n\t\t\t\t\t_meta = _img.metaInfo();\n\t\t\t\t\t_img.extractHeaders();\n\t\t\t\t} else if (PNG.test(ba)) {\n\t\t\t\t\t_img = new PNG(ba);\n\t\t\t\t} else if ( GIF.test(ba) ) {\n\t\t\t\t\t_img = new GIF( ba );\n\t\t\t\t} else if ( BMP.test(ba) ) {\n\t\t\t\t\t_img = new BMP( ba );\n\t\t\t\t} else {\n\t\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, ImageError.WRONG_FORMAT));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t_info = _img.info();\n\t\t\t} else if ( _info.type === 'image/jpeg' ) {\n\t\t\t\t_img = new JPEG( ba );\n\t\t\t\t_img.extractHeaders();\n\t\t\t}\n\t\t\t\n\t\t\ttype = _info.type;\n\t\t\t_ba = ba;\n\t\t}\n\t\t\n\t\tpublic function resize( width:Number = 110, height:Number = 110 ):void {\n\t\t\tvar naturalWidth:uint, naturalHeight:uint;\n\t\t\t\n\t\t\ttype = _info.type;\n\t\t\tnaturalWidth = _info.width;\n\t\t\tnaturalHeight = _info.height;\n\t\t\t\n\t\t\t// take into account Orientation tag\n\t\t\tif (type == 'image/jpeg' && _meta.hasOwnProperty('tiff') && _meta.tiff.hasOwnProperty('Orientation')) {\n\t\t\t\t_orientation = parseInt(_meta.tiff.Orientation, 10);\n\t\t\t}\n\t\t\t\n\t\t\tif ([5,6,7,8].indexOf(_orientation) !== -1) { // values that have different orientation\n\t\t\t\t// 交互宽度和高度值\n\t\t\t\twidth ^= height;\n\t\t\t\theight ^= width;\n\t\t\t\twidth ^= height;\n\t\t\t}\n\t\t\t\n\t\t\tvar selector:Function, scale:Number;\n\t\t\tselector = _crop ? Math.max : Math.min;\n\t\t\tscale = selector(  width / naturalWidth, height / naturalHeight );\n\t\t\t\n\t\t\tif ( !allowMagnify && scale > 1 ) {\n\t\t\t\tscale = 1;\n\t\t\t}\n\t\t\t\n\t\t\tvar destWidth:uint, destHeight:uint;\n\t\t\tdestWidth = naturalWidth * scale;\n\t\t\tdestHeight = naturalHeight * scale;\n\t\t\t\n\t\t\t\n\t\t\tvar matrix:Matrix = new Matrix;\n\t\t\t\n\t\t\tmatrix.scale( scale, scale );\n\t\t\tif ( destWidth > width ) {\n\t\t\t\tmatrix.translate(-Math.round((destWidth - width) / 2), 0);\n\t\t\t}\n\t\t\t\n\t\t\tif ( destHeight > height ) {\n\t\t\t\tmatrix.translate( 0, -Math.round(( destHeight - height) / 2));\n\t\t\t}\n\t\t\t\n\t\t\t_bd = new BitmapData( Math.min( width, destWidth), Math.min( height, destHeight) );\n\t\t\t\n\t\t\t\n\t\t\tif ( type == 'image/bmp' ) {\n\t\t\t\tvar decoder:BMPDecoder = new BMPDecoder();\n\t\t\t\tvar bmp:Bitmap = new Bitmap( decoder.decode( _ba ) );\n\t\t\t\t\n\t\t\t\t// draw preloaded data onto the prepared BitmapData\n\t\t\t\t_bd.draw(bmp, matrix, null, null, null, true);\n\t\t\t\t\n\t\t\t\tdispatchEvent(new ODataEvent(ODataEvent.DATA));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\tvar loader:Loader = new Loader;\n\t\t\tloader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event) : void {\n\t\t\t\t\n\t\t\t\tloader.contentLoaderInfo.removeEventListener(Event.COMPLETE, arguments.callee);\n\t\t\t\t\n\t\t\t\t// draw preloaded data onto the prepared BitmapData\n\t\t\t\t_bd.draw(e.target.content as IBitmapDrawable, matrix, null, null, null, true);\n\t\t\t\t\n\t\t\t\tloader.unload();\n\t\t\t\t_ba.clear();\n\t\t\t\t\n\t\t\t\tdispatchEvent(new ODataEvent(ODataEvent.DATA));\n\t\t\t});\n\t\t\t\n\t\t\tloader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function(e:*) : void {\n\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, 2));\n\t\t\t\t\n\t\t\t\t_ba.clear();\n\t\t\t\t_bd.dispose();\n\t\t\t\tloader.unload();\n\t\t\t});\n\t\t\t\n\t\t\ttry {\n\t\t\t\tloader.loadBytes(_ba);\n\t\t\t} catch (ex:*) {\n\t\t\t\tUploader.log([ex]);\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function crop( x:Number = 0, y:Number = 0, width:Number = 110, height:Number = 110, scale:Number = 1  ):void {\n\t\t\tvar croppedBD:BitmapData = new BitmapData(width, height);\n\t\t\tcroppedBD.copyPixels(_bd, new Rectangle(x, y, width, height), new Point(0, 0));\n\t\t\t_bd.dispose();\n\t\t\t_bd = croppedBD\n\t\t}\n\t\t\n\t\tpublic function getAsDataUrl( _type:String = null):String {\n\t\t\t_type = _type || type;\n\t\t\t\n\t\t\tvar ba:ByteArray =  _encodeBitmapData( _bd, _type );\n\t\t\t\n\t\t\treturn 'data:' + _type + ';base64,' + Base64.encode( ba );\n\t\t}\n\t\t\n\t\tpublic function getAsBlob( _type:String = null):Object {\n\t\t\t_type = _type || type;\n\t\t\t\n\t\t\tvar blob:Blob = new Blob([_encodeBitmapData( _bd, _type )], { type: _type });\n\t\t\tUploader.compFactory.add(blob.uid, blob);\n\t\t\treturn blob.toObject();\n\t\t}\n\t\t\n\t\t\n\t\tpublic function destroy():void {\n\t\t\tif (_bd) {\n\t\t\t\t_bd.dispose();\n\t\t\t\t_bd = null;\n\t\t\t}\n\t\t\t\n\t\t\t// one call to mark any dereferenced objects and sweep away old marks, \t\t\t\n\t\t\tflash.system.System.gc();\n\t\t\t// ...and the second to now sweep away marks from the first call.\n\t\t\tflash.system.System.gc();\n\t\t}\n\t\t\n\t\tprivate function _encodeBitmapData( bd:BitmapData, type:String ):ByteArray {\n\t\t\tvar encoder:JPEGEncoder, ba:ByteArray;\n\t\t\t\n\t\t\t// todo 支持其他格式。\n\t\t\t\n\t\t\tif ( type === 'image/png' ) {\n\t\t\t\tba = bd.encode(bd.rect, new PNGEncoderOptions());\n\t\t\t} else {\n\t\t\t\tif (type == 'image/jpeg' && !preserveHeaders ) {\n\t\t\t\t\tbd = _rotateToOrientation(_orientation, bd);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tencoder = new JPEGEncoder( quality );\n\t\t\t\tba = encoder.encode(bd);\n\t\t\t\t\n\t\t\t\tif (_img && _img is JPEG) {\n\t\t\t\t\t// strip off any headers that might be left by encoder, etc\n\t\t\t\t\t_img.stripHeaders(ba);\n\t\t\t\t\t\n\t\t\t\t\t// restore the original headers if requested\n\t\t\t\t\tif (preserveHeaders) {\n\t\t\t\t\t\t_img.insertHeaders(ba);\n\t\t\t\t\t\t_img.updateDimensions(bd.width, bd.height);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn ba;\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _rotateToOrientation(orientation:uint, bd:BitmapData) : BitmapData\n\t\t{\n\t\t\tvar imageEditor:ImageEditor = new ImageEditor(bd);\n\t\t\t\n\t\t\tswitch (orientation) {\n\t\t\t\tcase 2:\n\t\t\t\t\t// horizontal flip\n\t\t\t\t\timageEditor.modify(\"flipH\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\t// 180 rotate left\n\t\t\t\t\timageEditor.modify(\"rotate\", 180);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\t// vertical flip\n\t\t\t\t\timageEditor.modify(\"flipV\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\t// vertical flip + 90 rotate right\n\t\t\t\t\timageEditor.modify(\"flipV\");\n\t\t\t\t\timageEditor.modify(\"rotate\", 90);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\t// 90 rotate right\n\t\t\t\t\timageEditor.modify(\"rotate\", 90);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\t// horizontal flip + 90 rotate right\n\t\t\t\t\timageEditor.modify(\"flipH\");\n\t\t\t\t\timageEditor.modify(\"rotate\", 90);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\t// 90 rotate left\n\t\t\t\t\timageEditor.modify(\"rotate\", -90);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\timageEditor.commit();\n\t\t\t\n\t\t\tbd.dispose();\n\t\t\t_bd = imageEditor.bitmapData;\n\t\t\timageEditor.purge();\n\t\t\treturn _bd;\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/Md5.as",
    "content": "package com\n{\nimport com.errors.ImageError;\nimport com.events.ODataEvent;\nimport com.events.OErrorEvent;\nimport com.events.OProgressEvent;\nimport com.utils.OEventDispatcher;\nimport com.utils.Utils;\n\nimport flash.events.Event;\nimport flash.events.ProgressEvent;\nimport flash.system.System;\nimport flash.utils.ByteArray;\n\nimport cmodule.stringecho.CLibInit;\n\npublic class Md5 extends OEventDispatcher\n{\n    // events dispatched by this class\n    public static var dispatches:Object = {\n        \"Progress\": OProgressEvent.PROGRESS,\n        \"Complete\": ODataEvent.DATA,\n        \"Load\": Event.COMPLETE,\n        \"Error\": OErrorEvent.ERROR\n    };\n\n    private static var ci:Object;\n    private var ret:String;\n\n    public function init( options:Object = null ):void {\n        Utils.extend(this, options, true);\n    }\n\n    public function loadFromBlob( blob:* = null ):void {\n        var fr:FileReader;\n\n\n\n        if (typeof blob === 'string') {\n            blob = Uploader.compFactory.get(blob);\n        }\n\n        if (!blob) {\n            dispatchEvent(new OErrorEvent(OErrorEvent.ERROR, ImageError.WRONG_FORMAT));\n            return;\n        }\n\n        fr = new FileReader;\n\n\n        fr.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) : void {\n            dispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, e.bytesLoaded, e.bytesTotal));\n        });\n\n        fr.addEventListener(Event.COMPLETE, function(e:Event) : void {\n            fr.removeAllEventsListeners();\n            dispatchEvent(new Event(Event.COMPLETE));\n            _loadFromByteArray(fr.result);\n            dispatchEvent(new Event(ODataEvent.DATA));\n        });\n\n        fr.readAsByteArray(blob);\n    }\n\n    public function getResult():String {\n        return ret;\n    }\n\n\n    private function _loadFromByteArray( ba:ByteArray ):void {\n        if (!ci) {\n            ci = new CLibInit().init();\n        }\n\n        ret = ci.md5BytesMethod(ba);\n    }\n\n\n    public function destroy():void {\n        // one call to mark any dereferenced objects and sweep away old marks,\n        flash.system.System.gc();\n        // ...and the second to now sweep away marks from the first call.\n        flash.system.System.gc();\n    }\n\n}\n}"
  },
  {
    "path": "flash/src/com/XMLHttpRequest.as",
    "content": "package com\n{\n\timport com.adobe.serialization.json.JSON;\n\timport com.errors.RuntimeError;\n\timport com.events.OErrorEvent;\n\timport com.events.OProgressEvent;\nimport com.utils.Buffer;\nimport com.utils.URLStreamProgress;\n\timport com.utils.Utils;\n\t\n\timport flash.events.DataEvent;\n\timport flash.events.Event;\n\timport flash.events.EventDispatcher;\n\timport flash.events.HTTPStatusEvent;\n\timport flash.events.IOErrorEvent;\n\timport flash.events.ProgressEvent;\n\timport flash.events.SecurityErrorEvent;\n\timport flash.net.FileReference;\n\timport flash.net.URLRequest;\n\timport flash.net.URLRequestHeader;\n\timport flash.net.URLRequestMethod;\n\timport flash.net.URLStream;\n\timport flash.utils.ByteArray;\n\timport flash.utils.clearTimeout;\n\timport flash.utils.setTimeout;\n\n\t\n\tpublic class XMLHttpRequest extends EventDispatcher\n\t{\n\t\t// events dispatched by this class\n\t\tpublic static var dispatches:Object = { \n\t\t\t\"LoadStart\": Event.OPEN,\n\t\t\t\"Progress\": OProgressEvent.PROGRESS,\n\t\t\t\"UploadProgress\": ProgressEvent.PROGRESS,\n\t\t\t\"Load\": Event.COMPLETE,\n\t\t\t\"Error\": OErrorEvent.ERROR\n\t\t};\n\t\t\n\t\tpublic static const UNSENT:uint = 0;\n\t\tpublic static const OPENED:uint = 1;\n\t\tpublic static const HEADERS_RECEIVED:uint = 2;\n\t\tpublic static const LOADING:uint = 3;\n\t\tpublic static const DONE:uint = 4;\n\t\tpublic static const ABORTED:uint = 5;\n\t\t\t\t\n\t\tprivate var _methods:Object = {\n\t\t\t'GET': URLRequestMethod.GET,\n\t\t\t'POST': URLRequestMethod.POST\n\t\t};\n\t\t\n\t\tprivate var _options:Object;\n\t\t\n\t\tprivate var _multipart:Boolean = false;\n\t\t\n\t\tprivate var _headers:Object = {};\n\t\t\n\t\tprivate var _blob:*;\n\t\t\n\t\tprivate var _blobName:String;\n\t\t\n\t\tprivate var _blobFieldName:String = 'Filedata';\n\t\t\n\t\tprivate var _postData:Array = [];\n\t\t\n\t\tprivate var _conn:*;\n\t\t\n\t\tprivate var _response:ByteArray;\n\t\t\n\t\tprivate var _status:int;\n\t\t\n\t\tprivate var _readyState:uint = XMLHttpRequest.UNSENT;\n\t\t\n\t\tprivate var _onCompleteTimeout:uint = 0;\n\t\t\n\t\tprivate var _notCachedSize:uint = 0;\n\t\t\t\t\n\t\t\n\t\tpublic function append(name:String, value:*) : void\n\t\t{\n\t\t\t_multipart = true;\n\t\t\t\n\t\t\tif (name == 'Filename') { // Flash will add this by itself, so we need to omit potential duplicate\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t_postData.push([name, value]);\n\t\t}\n\t\t\n\t\t\n\t\tpublic function appendBlob(name:String, blob:*, fileName:String = null) : void\n\t\t{\t\t\t\n\t\t\t_multipart = true;\n\t\t\t\n\t\t\t_blobName = fileName || 'blob' + new Date().getTime();\n\t\t\t\n\t\t\tif (typeof blob === 'string') {\n\t\t\t\tblob = Uploader.compFactory.get(blob);\n\t\t\t\tif (!fileName && blob is File && blob.hasOwnProperty('name')) { \n\t\t\t\t\t_blobName = blob.name;\n\t\t\t\t} \n\t\t\t}\n\t\t\t\n\t\t\t_blob = blob;\n\t\t\t_blobFieldName = name;\t\t\t\t\t\t\n\t\t}\n\t\t\n\t\t\n\t\tpublic function setRequestHeader(name:String, value:String) : void\n\t\t{\n\t\t\t_headers[name] = value;\n\t\t}\n\t\t\n\t\t\t\t\n\t\tpublic function send(meta:Object, blob:* = null) : void\n\t\t{\t\n\t\t\tif (_blob) {\n\t\t\t\tblob = _blob;\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\tmeta.method = meta.method.toUpperCase();\n\t\t\t_options = meta;\n\t\t\t\t\t\t\t\t\t\n\t\t\tif (typeof blob === 'string') {\n\t\t\t\tblob = Uploader.compFactory.get(blob);\n\t\t\t\tif (blob is File && blob.hasOwnProperty('name')) { \n\t\t\t\t\t_blobName = blob.name;\n\t\t\t\t} \n\t\t\t}\n\n            // Uploader.log([\"forceUrlStream is : \", _options.forceURLStream]);\n\t\t\t\t\t\t\n\t\t\tif (blob && _options.method == 'POST') {\n\t\t\t\tif (_multipart && blob.isFileRef() && Utils.isEmptyObj(_headers) && (!meta.hasOwnProperty(\"forceURLStream\") || !meta.forceURLStream)) {\n                    _uploadFileRef(blob);\n\t\t\t\t} else {\n\t\t\t\t\t_preloadBlob(blob, _doURLStreamRequest);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t_doURLStreamRequest();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function getResponseAsBlob() : Object\n\t\t{\n\t\t\tvar blob:Blob;\n\t\t\t\n\t\t\tif (!_response) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\tblob = new File([_response], { name: _options.url.replace(/^.+?\\/([\\w\\-\\.]+)$/, '$1').toLowerCase() });\n\t\t\tUploader.compFactory.add(blob.uid, blob);\n\t\t\treturn blob.toObject();\n\t\t}\n\n        public function _getResponse():String {\n            var ret:String;\n            if ( !_response ) {\n                return '';\n            }\n            _response.position = 0;\n            ret = _response.readUTFBytes(_response.length);\n\n            return ret;\n        }\n\t\t\n\t\tpublic function getResponse():String {\n            return encodeURIComponent(_getResponse());\n\t\t}\n\t\t\n\t\tpublic function getResponseAsJson() : Object\n\t\t{\n            var ret:Object;\n\n            try {\n                var str:String = _getResponse();\n\n                if (!str || !str.match(/^\\s*{/)) {\n                    return {};\n                }\n\n                ret = com.adobe.serialization.json.JSON.decode(str);\n            } catch( e:Error ) {\n                ret = {};\n            }\n\t\t\t\n\t\t\treturn ret;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function getStatus() : int\n\t\t{\t\t\n\t\t\treturn _status;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function abort() : void \n\t\t{\t\n\t\t\t// mark as aborted in order to be handled as soon as possible\n\t\t\t_readyState = XMLHttpRequest.ABORTED; \n\t\t\t\n\t\t\tif (_conn is FileReference) {\n\t\t\t\t_conn.cancel();\n\t\t\t} else if (_conn is URLStream && _conn.connected) {\n\t\t\t\t_conn.close();\n\t\t\t}\n\t\t\t\n\t\t\tremoveEventListeners(_conn);\t\n\t\t\t_conn = null;\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onOpen() : void {\n\t\t\tdispatchEvent(new Event(Event.OPEN));\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onProgress(e:ProgressEvent) : void {\n\t\t\tdispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, e.bytesLoaded, e.bytesTotal));\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onUploadProgress(e:ProgressEvent):void { \n\t\t\tdispatchEvent(e);\n\t\t}\n\t\t\n\t\t\n\t\tprivate function onUploadComplete(e:*) : void {\n\t\t\tif (_onCompleteTimeout) {\n\t\t\t\tclearTimeout(_onCompleteTimeout);\n\t\t\t}\n\t\t\t\n\t\t\t// if status still equal to zero, than we are ok\n\t\t\tif (_status == 0) { \n\t\t\t\t_status = 200;\n\t\t\t}\n\t\t\t\n\t\t\t// save response\n\t\t\t_response = new ByteArray;\n\t\t\t\t\t\t\n\t\t\tif (_conn is FileReference && e.hasOwnProperty('data')) {\n\t\t\t\t_response.writeUTFBytes(e.data);\n\t\t\t} else if (_conn is URLStream) {\n\t\t\t\t_conn.readBytes(_response);\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\t// Uploader.log([\"here\", getResponse()]);\n\t\t\t\n\t\t\tremoveEventListeners(e.target);\t\n\t\t\t_readyState = XMLHttpRequest.DONE;\n\t\t\tdispatchEvent(new Event(Event.COMPLETE));\n\t\t}\n\t\t\n\t\tprivate function onComplete(e:*) : void {\n            // give upload complete event a chance to fire\n\t\t\t_onCompleteTimeout = setTimeout(onUploadComplete, 500, e);\n\t\t}\n\t\t\n\t\t// The httpStatus event is dispatched only for upload failures.\n\t\tprivate function onStatus(e:HTTPStatusEvent) : void {\n\t\t\t_status = e.status;\n\t\t}\n\t\t\n\t\tprivate function onIOError(e:IOErrorEvent) : void {\n\t\t\tif (_status == 0) { // httpStatus might have already set status to some failure code (according to livedocs)\n\t\t\t\t_status = 404; // assume that request succeeded, but url was wrong\n\t\t\t}\n\t\t\tonUploadComplete(e);\n\t\t}\n\t\t\n\t\tprivate function onError(e:*) : void {\n\t\t\tremoveEventListeners(e.target);\n\t\t\t_readyState = XMLHttpRequest.DONE;\n\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR));\n\t\t}\n\t\t\n\t\tprivate function removeEventListeners(target:*) : void {\n\t\t\t// some of these event handlers may not even be attached, but we try to remove them all for simplicity\n\t\t\ttarget.removeEventListener(Event.OPEN, onOpen);\n\t\t\ttarget.removeEventListener(ProgressEvent.PROGRESS, onProgress);\n\t\t\ttarget.removeEventListener(ProgressEvent.PROGRESS, onUploadProgress);\n\t\t\ttarget.removeEventListener(Event.COMPLETE, onComplete);\n\t\t\ttarget.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);\n\t\t\ttarget.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);\n\t\t\ttarget.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);\n\t\t\ttarget.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _uploadFileRef(blob:Blob) : void\n\t\t{\t\t\t\n\t\t\tvar request:URLRequest, queryString:Array = [], param:Array;\n\t\t\t\t\t\t\n\t\t\trequest = new URLRequest();\n\t\t\trequest.method = URLRequestMethod.POST;\n\t\t\trequest.url = _options.url;\n\t\t\t\n\t\t\tfor each (param in _postData) {\n\t\t\t\tqueryString.push(encodeURIComponent(param[0]) + '=' + encodeURIComponent(param[1]));\n\t\t\t}\n\t\t\trequest.data = queryString.join('&');\n\t\t\t\t\t\t\t\t\n\t\t\t_conn = blob.getFileRef();\n            _readyState = XMLHttpRequest.OPENED;\n\n            if (blob.isLoading()) {\n                _conn.addEventListener(Event.COMPLETE, function( e:Event = null ):void {\n                    _conn.removeEventListener(Event.COMPLETE, arguments.callee);\n                    doUpload();\n                });\n            } else {\n                doUpload();\n            }\n\t\t\t\t\t\t\n\t\t\tfunction doUpload():void {\n                _conn.addEventListener(Event.COMPLETE, onComplete);\n                _conn.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);\n                _conn.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);\n                _conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);\n                _conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);\n                _conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);\n\n                // _conn.addEventListener(Event.OPEN, onOpen); doesn't fire, ideas?\n                onOpen(); // trigger it manually\n\n                _readyState = XMLHttpRequest.LOADING;\n                _conn.upload(request, _blobFieldName, false);\n            }\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _preloadBlob(blob:*, callback:Function) : void\n\t\t{\n\t\t\tvar fr:FileReader = new FileReader;\n\t\t\t\n\t\t\tfr.addEventListener(Event.OPEN, function(e:Event) : void {\n\t\t\t\tif (_readyState == XMLHttpRequest.ABORTED) {\n\t\t\t\t\tfr.abort();\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\tfr.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) : void {\n\t\t\t\tif (_readyState == XMLHttpRequest.ABORTED) {\n\t\t\t\t\tfr.abort();\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\tfr.addEventListener(Event.COMPLETE, function() : void {\t\n\t\t\t\tfr.removeAllEventsListeners();\n\t\t\t\tif (_readyState == XMLHttpRequest.ABORTED) {\n\t\t\t\t\tfr.abort();\n\t\t\t\t} else {\n\t\t\t\t\tcallback(fr.result);\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\t//_notCachedSize = blob.realSize - blob.cachedSize;\n\t\t\t\n\t\t\t_readyState = XMLHttpRequest.OPENED;\n\t\t\tonOpen(); // trigger it manually\n\t\t\tfr.readAsByteArray(blob);\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _doURLStreamRequest(ba:ByteArray = null) : void\n\t\t{\t\t\t\t\t\t\t\t\t\n\t\t\tif (_readyState == XMLHttpRequest.ABORTED) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tvar request:URLRequest,\n\t\t\tprogress:URLStreamProgress, \n\t\t\tstart:Date, end:Date; // we are going to measure upload time for each piece of data and make correction to the progress watch\n\t\t\t\t\t\t\t\t\n\t\t\trequest = new URLRequest(_options.url);\n\t\t\t\n\t\t\t// set custom headers if required\n\t\t\tif (!Utils.isEmptyObj(_headers)) {\n\t\t\t\tfor (var name:String in _headers) {\n\t\t\t\t\trequest.requestHeaders.push(new URLRequestHeader(name, _headers[name]));\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t\t\n\t\t\t// only GET/POST is supported at the moment\n\t\t\tif (['POST', 'GET'].indexOf(_options.method) === -1) {\n\t\t\t\tdispatchEvent(new OErrorEvent(OErrorEvent.ERROR, RuntimeError.SYNTAX_ERR));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\trequest.method = _methods[_options.method];\n\t\t\t\n\t\t\tif (_multipart) {\n\t\t\t\trequest.data = _formatAsMultipart(ba, request);\n\t\t\t} else if (ba) {\n\t\t\t\t// set content-type manually\n\t\t\t\tif (!_headers['content-type']) { // if it wasn't already set above\n\t\t\t\t\trequest.requestHeaders.push(new URLRequestHeader(\"content-type\", _options.mimeType));\n\t\t\t\t}\n\t\t\t\trequest.data = ba;\n\t\t\t}\t\t\n\t\t\t\t\t\t\n\t\t\t_conn = new URLStream;\n\t\t\t\n\t\t\t_conn.addEventListener(ProgressEvent.PROGRESS, onProgress);\n\t\t\t_conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);\n\t\t\t_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);\n\t\t\t_conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);\n\t\t\t\n\t\t\tif (ba) { \n\t\t\t\tprogress = new URLStreamProgress({ url: _options.url });\n\t\t\t\t\n\t\t\t\tprogress.addEventListener(ProgressEvent.PROGRESS, function( e:ProgressEvent ):void{\n\t\t\t\t\tif ( _readyState == XMLHttpRequest.ABORTED ) {\n\t\t\t\t\t\tprogress.stop();\n\t\t\t\t\t\tprogress.removeAllEventsListeners();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tonUploadProgress(e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\tprogress.addEventListener(Event.OPEN, function() : void {\n\t\t\t\t\tstart = new Date;\n\t\t\t\t\t_conn.load(request);\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\t_conn.addEventListener(Event.COMPLETE, function(e:Event) : void {\n\t\t\t\t\tprogress.stop();\n\t\t\t\t\tprogress.removeAllEventsListeners();\n\t\t\t\t\t\n\t\t\t\t\t// correlate upload speed\n\t\t\t\t\tend = new Date;\n\t\t\t\t\tURLStreamProgress.calculateSpeed(request.data.length, end.time - start.time);\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tonComplete(e);\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tprogress.start(request.data.length);\n\t\t\t}\t\n\t\t\telse {\t\n\t\t\t\t_conn.addEventListener(Event.COMPLETE, onComplete);\n\t\t\t\t\n\t\t\t\tonOpen(); // trigger it manually\n\t\t\t\t_conn.load(request);\n\t\t\t}\n\t\t\t\n\t\t\t_readyState = XMLHttpRequest.LOADING;\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _formatAsMultipart(ba:ByteArray, request:URLRequest) : ByteArray\n\t\t{\n\t\t\tvar boundary:String = '----webuploaderboundary' + new Date().getTime(),\n\t\t\t\tdashdash:String = '--', crlf:String = '\\r\\n',\n\t\t\t\ttmpBa:ByteArray, param:Array;\n\t\t\t\n\t\t\ttmpBa = new ByteArray;\n\t\t\t\t\t\t\n\t\t\trequest.requestHeaders.push(new URLRequestHeader(\"Content-Type\", 'multipart/form-data; boundary=' + boundary));\n\t\t\t\n\t\t\tfor each (param in _postData) {\n\t\t\t\ttmpBa.writeUTFBytes(\n\t\t\t\t\tdashdash + boundary + crlf +\n\t\t\t\t\t'Content-Disposition: form-data; name=\"' + param[0] + '\"' + crlf + crlf +\n\t\t\t\t\tparam[1] + crlf\n\t\t\t\t);\n\t\t\t}\n\t\t\t\n\t\t\t// append file if available\n\t\t\tif (ba) {\n\t\t\t\ttmpBa.writeUTFBytes(\n\t\t\t\t\tdashdash + boundary + crlf +\n\t\t\t\t\t'Content-Disposition: form-data; name=\"' + _blobFieldName + '\"; filename=\"' + _blobName + '\"' + crlf +\n\t\t\t\t\t'Content-Type: ' + _options.mimeType + crlf + crlf\n\t\t\t\t);\n\t\t\t\n\t\t\t\ttmpBa.writeBytes(ba, 0, ba.length);\n\t\t\t\tba.clear();\n\t\t\t\t\n\t\t\t\ttmpBa.writeUTFBytes(crlf);\n\t\t\t}\n\t\t\t\n\t\t\t// wrap it up\n\t\t\ttmpBa.writeUTFBytes(dashdash + boundary + dashdash + crlf);\n\t\t\t\n\t\t\treturn tmpBa;\n\t\t}\n\t\t\n\t}\n}\n"
  },
  {
    "path": "flash/src/com/errors/DOMError.as",
    "content": "package com.errors\n{\n\tpublic class DOMError extends Error\n\t{\n\t\tpublic static const NOT_FOUND_ERR:uint = 1;\n\t\tpublic static const SECURITY_ERR:uint = 2;\n\t\tpublic static const ABORT_ERR:uint = 3;\n\t\tpublic static const NOT_READABLE_ERR:uint = 4;\n\t\tpublic static const ENCODING_ERR:uint = 5;\n\t\t\n\t\tpublic function DOMError(id:int = 0) {\n\t\t\tsuper(\"\", id);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/errors/ImageError.as",
    "content": "package com.errors\n{\n\tpublic class ImageError\n\t{\n\t\tpublic static const WRONG_FORMAT:uint = 0;\n\t\tpublic static const OUT_OF_DIMENSIONS:uint = 1;\n\t}\n}"
  },
  {
    "path": "flash/src/com/errors/RuntimeError.as",
    "content": "package com.errors\n{\n\tpublic class RuntimeError extends Error\n\t{\n\t\tpublic static const NOT_INIT_ERR:uint = 1;\n\t\tpublic static const NOT_SUPPORTED_ERR:uint = 9;\n\t\tpublic static const JS_ERR:uint = 4;\n\t\tpublic static const OUT_OF_MEMORY:uint = 5;\n\t\tpublic static const INVALID_STATE_ERR:uint = 11;\n\t\tpublic static const SYNTAX_ERR:uint = 12;\n\t\tpublic static const COMP_CONFLICT:uint = 23;\n\t\t\n\t\tpublic function RuntimeError(id:* = 0) {\n\t\t\tsuper(\"\", id);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/FilePickerEvent.as",
    "content": "package com.events\n{\n\timport flash.events.Event;\n\t\n\tpublic class FilePickerEvent extends Event\n\t{\n\t\tpublic static const SELECT:String = 'filepickerselect';\n\t\tpublic static const CANCEL:String = 'filepickercancel';\n\t\tpublic static const OPEN:String = 'filepickeropen';\n\t\t\n\t\tpublic var data:*;\n\t\t\n\t\tpublic function FilePickerEvent(type:String, data:* = false)\n\t\t{\n\t\t\tthis.data = data;\n\t\t\tsuper(type, false, false);\n\t\t}\n\t\t\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/ImageEvent.as",
    "content": "package com.events\n{\n\timport flash.events.Event;\n\t\n\tpublic class ImageEvent extends Event\n\t{\n\t\tpublic static const RESIZE:String = 'imageresize';\n\t\t\n\t\tpublic var data:*;\n\t\t\n\t\tpublic function ImageEvent(type:String, data:* = null)\n\t\t{\n\t\t\tthis.data = data;\n\t\t\tsuper(type, bubbles, cancelable);\n\t\t}\n\t\t\n\t\tpublic override function clone() : Event {\n\t\t\treturn new ImageEvent(type, data);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/ODataEvent.as",
    "content": "package com.events\n{\n\timport flash.events.Event;\n\t\n\tpublic class ODataEvent extends Event\n\t{\n\t\tpublic static const DATA:String = 'webuploaderdata';\n\t\t\n\t\tpublic var data:*;\n\t\t\n\t\tpublic function ODataEvent(type:String, data:* = null)\n\t\t{\n\t\t\tthis.data = data;\n\t\t\tsuper(type, bubbles, cancelable);\n\t\t}\n\t\t\n\t\tpublic override function clone() : Event {\n\t\t\treturn new ODataEvent(type, data);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/OErrorEvent.as",
    "content": "package com.events\n{\n\timport flash.events.Event;\n\n\tpublic class OErrorEvent extends Event\n\t{\n\t\tpublic static const ERROR:String = 'moxieerror';\n\t\t\n\t\tpublic var code:uint;\n\t\t\n\t\tpublic function OErrorEvent(type:String, code:int = 0) {\n\t\t\tthis.code = code;\n\t\t\tsuper(type, true, false);\t\t\t\t\t\t\n\t\t}\n\t\t\n\t\tpublic override function clone() : Event {\n\t\t\treturn new OErrorEvent(type, code);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/OProgressEvent.as",
    "content": "package com.events\n{\n\timport flash.events.Event;\n\timport flash.events.ProgressEvent;\n\n\tpublic class OProgressEvent extends ProgressEvent\n\t{\n\t\tpublic static const PROGRESS:String = 'webuploaderprogress';\n\t\t\n\t\tpublic var data:*;\n\t\t\n\t\tpublic function OProgressEvent(type:String, bytesLoaded:uint = 0, bytesTotal:uint = 0, data:* = null)\n\t\t{\n\t\t\tthis.data = data;\n\t\t\tsuper(type, true, false, bytesLoaded, bytesTotal);\t\t\t\t\t\t\n\t\t}\n\t\t\n\t\tpublic override function clone() : Event {\n\t\t\treturn new OProgressEvent(type, bytesLoaded, bytesTotal, data);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/events/URLStreamProgressEvent.as",
    "content": "package com.events\n{\t\n\timport flash.events.Event;\n\n\tpublic class URLStreamProgressEvent extends Event\n\t{\n\t\tpublic static const PROBE_COMPLETE:String = 'probecomplete';\n\t\t\n\t\tpublic function URLStreamProgressEvent(type:String)\n\t\t{\n\t\t\tsuper(type, false, false);\n\t\t}\n\t\t\n\t\tpublic override function clone() : Event {\n\t\t\treturn new URLStreamProgressEvent(type);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/BMP.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image\n{\n\timport com.utils.BinaryReader;\n\t\n\timport flash.utils.ByteArray;\n\t\n\tpublic class BMP \n\t{\t\t\n\t\tpublic static const MIME:String = 'image/bmp';\n\t\t\n\t\tprotected var _br:BinaryReader;\n\t\t\n\t\tpublic function BMP(binData:ByteArray)\n\t\t{\n\t\t\t_br = new BinaryReader;\n\t\t\t_br.init(binData);\n\t\t}\n\t\t\n\t\tstatic public function test(binData:ByteArray) : Boolean\n\t\t{\n\t\t\tvar sign:Array = [ 66, 77 ];\n\t\t\t\n\t\t\tfor (var i:int = sign.length - 1; i >= 0 ; i--) {\n\t\t\t\tif (binData[i] != sign[i]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function info() : Object\n\t\t{\n\t\t\tvar a:uint = _br.BYTE(18),\n\t\t\t\tb:uint = _br.BYTE(19),\n\t\t\t\tc:uint = _br.BYTE(22),\n\t\t\t\td:uint = _br.BYTE(23);\n\t\t\t\n\t\t\treturn {\n\t\t\t\twidth: b * 256 + a,\n\t\t\t\theight: d * 256 + c,\n\t\t\t\ttype: BMP.MIME\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function metaInfo() : Object\n\t\t{\n\t\t\treturn {};\n\t\t}\n\t\t\n\t\tpublic function purge() : void\n\t\t{\n\t\t\t_br.clear();\n\t\t}\n\t\t\n\t}\n\t\n}\n\n"
  },
  {
    "path": "flash/src/com/image/ExifParser.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image {\n\timport flash.events.EventDispatcher;\n\timport flash.external.ExternalInterface;\n\timport flash.utils.ByteArray;\n\n\timport com.utils.BinaryReader;\n\n\tpublic class ExifParser extends EventDispatcher {\n\n\t\tprivate var data:BinaryReader = new BinaryReader();\n\n\t\tprivate var Tiff:Object;\n\n\t\tprivate var offsets:Object = {\n\t\t\ttiffHeader : 10\n\t\t};\n\n\t\tprivate var tags:Object = {\n\n\t\t\ttiff: {\n\t\t\t\t0x0112: 'Orientation',\n\t\t\t\t0x010E: 'ImageDescription',\n\t\t\t\t0x010F: 'Make',\n\t\t\t\t0x0110: 'Model',\n\t\t\t\t0x0131: 'Software',\n\t\t\t\t0x8769: 'ExifIFDPointer',\n\t\t\t\t0x8825:\t'GPSInfoIFDPointer'\n\t\t\t},\n\n\t\t\texif: {\n\t\t\t\t0x9000: 'ExifVersion',\n\t\t\t\t0xA001: 'ColorSpace',\n\t\t\t\t0xA002: 'PixelXDimension',\n\t\t\t\t0xA003: 'PixelYDimension',\n\t\t\t\t0x9003: 'DateTimeOriginal',\n\t\t\t\t0x829A: 'ExposureTime',\n\t\t\t\t0x829D: 'FNumber',\n\t\t\t\t0x8827: 'ISOSpeedRatings',\n\t\t\t\t0x9201: 'ShutterSpeedValue',\n\t\t\t\t0x9202: 'ApertureValue'\t,\n\t\t\t\t0x9207: 'MeteringMode',\n\t\t\t\t0x9208: 'LightSource',\n\t\t\t\t0x9209: 'Flash',\n\t\t\t\t0x920A: 'FocalLength',\n\t\t\t\t0xA402: 'ExposureMode',\n\t\t\t\t0xA403: 'WhiteBalance',\n\t\t\t\t0xA406: 'SceneCaptureType',\n\t\t\t\t0xA404: 'DigitalZoomRatio',\n\t\t\t\t0xA408: 'Contrast',\n\t\t\t\t0xA409: 'Saturation',\n\t\t\t\t0xA40A: 'Sharpness'\n\t\t\t},\n\n\t\t\tgps: {\n\t\t\t\t0x0000: 'GPSVersionID',\n\t\t\t\t0x0001: 'GPSLatitudeRef',\n\t\t\t\t0x0002: 'GPSLatitude',\n\t\t\t\t0x0003: 'GPSLongitudeRef',\n\t\t\t\t0x0004: 'GPSLongitude'\n\t\t\t}\n\t\t},\n\n\t\t\ttagDescs:Object = {\n\t\t\t\t'ColorSpace': {\n\t\t\t\t\t1: 'sRGB',\n\t\t\t\t\t0: 'Uncalibrated'\n\t\t\t\t},\n\t\t\t\t'MeteringMode': {\n\t\t\t\t\t0: 'Unknown',\n\t\t\t\t\t1: 'Average',\n\t\t\t\t\t2: 'CenterWeightedAverage',\n\t\t\t\t\t3: 'Spot',\n\t\t\t\t\t4: 'MultiSpot',\n\t\t\t\t\t5: 'Pattern',\n\t\t\t\t\t6: 'Partial',\n\t\t\t\t\t255: 'Other'\n\t\t\t\t},\n\t\t\t\t'LightSource': {\n\t\t\t\t\t1: 'Daylight',\n\t\t\t\t\t2: 'Fliorescent',\n\t\t\t\t\t3: 'Tungsten',\n\t\t\t\t\t4: 'Flash',\n\t\t\t\t\t9: 'Fine weather',\n\t\t\t\t\t10: 'Cloudy weather',\n\t\t\t\t\t11: 'Shade',\n\t\t\t\t\t12: 'Daylight fluorescent (D 5700 - 7100K)',\n\t\t\t\t\t13: 'Day white fluorescent (N 4600 -5400K)',\n\t\t\t\t\t14: 'Cool white fluorescent (W 3900 - 4500K)',\n\t\t\t\t\t15: 'White fluorescent (WW 3200 - 3700K)',\n\t\t\t\t\t17: 'Standard light A',\n\t\t\t\t\t18: 'Standard light B',\n\t\t\t\t\t19: 'Standard light C',\n\t\t\t\t\t20: 'D55',\n\t\t\t\t\t21: 'D65',\n\t\t\t\t\t22: 'D75',\n\t\t\t\t\t23: 'D50',\n\t\t\t\t\t24: 'ISO studio tungsten',\n\t\t\t\t\t255: 'Other'\n\t\t\t\t},\n\t\t\t\t'Flash': {\n\t\t\t\t\t0x0000: 'Flash did not fire.',\n\t\t\t\t\t0x0001: 'Flash fired.',\n\t\t\t\t\t0x0005: 'Strobe return light not detected.',\n\t\t\t\t\t0x0007: 'Strobe return light detected.',\n\t\t\t\t\t0x0009: 'Flash fired, compulsory flash mode',\n\t\t\t\t\t0x000D: 'Flash fired, compulsory flash mode, return light not detected',\n\t\t\t\t\t0x000F: 'Flash fired, compulsory flash mode, return light detected',\n\t\t\t\t\t0x0010: 'Flash did not fire, compulsory flash mode',\n\t\t\t\t\t0x0018: 'Flash did not fire, auto mode',\n\t\t\t\t\t0x0019: 'Flash fired, auto mode',\n\t\t\t\t\t0x001D: 'Flash fired, auto mode, return light not detected',\n\t\t\t\t\t0x001F: 'Flash fired, auto mode, return light detected',\n\t\t\t\t\t0x0020: 'No flash function',\n\t\t\t\t\t0x0041: 'Flash fired, red-eye reduction mode',\n\t\t\t\t\t0x0045: 'Flash fired, red-eye reduction mode, return light not detected',\n\t\t\t\t\t0x0047: 'Flash fired, red-eye reduction mode, return light detected',\n\t\t\t\t\t0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',\n\t\t\t\t\t0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',\n\t\t\t\t\t0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',\n\t\t\t\t\t0x0059: 'Flash fired, auto mode, red-eye reduction mode',\n\t\t\t\t\t0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',\n\t\t\t\t\t0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'\n\t\t\t\t},\n\t\t\t\t'ExposureMode': {\n\t\t\t\t\t0: 'Auto exposure',\n\t\t\t\t\t1: 'Manual exposure',\n\t\t\t\t\t2: 'Auto bracket'\n\t\t\t\t},\n\t\t\t\t'WhiteBalance': {\n\t\t\t\t\t0: 'Auto white balance',\n\t\t\t\t\t1: 'Manual white balance'\n\t\t\t\t},\n\t\t\t\t'SceneCaptureType': {\n\t\t\t\t\t0: 'Standard',\n\t\t\t\t\t1: 'Landscape',\n\t\t\t\t\t2: 'Portrait',\n\t\t\t\t\t3: 'Night scene'\n\t\t\t\t},\n\t\t\t\t'Contrast': {\n\t\t\t\t\t0: 'Normal',\n\t\t\t\t\t1: 'Soft',\n\t\t\t\t\t2: 'Hard'\n\t\t\t\t},\n\t\t\t\t'Saturation': {\n\t\t\t\t\t0: 'Normal',\n\t\t\t\t\t1: 'Low saturation',\n\t\t\t\t\t2: 'High saturation'\n\t\t\t\t},\n\t\t\t\t'Sharpness': {\n\t\t\t\t\t0: 'Normal',\n\t\t\t\t\t1: 'Soft',\n\t\t\t\t\t2: 'Hard'\n\t\t\t\t},\n\n\t\t\t\t// GPS related\n\t\t\t\t'GPSLatitudeRef': {\n\t\t\t\t\tN: 'North latitude',\n\t\t\t\t\tS: 'South latitude'\n\t\t\t\t},\n\t\t\t\t'GPSLongitudeRef': {\n\t\t\t\t\tE: 'East longitude',\n\t\t\t\t\tW: 'West longitude'\n\t\t\t\t}\n\t\t\t};\n\n\t\tpublic function init(segment:ByteArray):Boolean {\n\t\t\t// Reset internal data\n\t\t\toffsets = {\n\t\t\t\ttiffHeader: 10\n\t\t\t};\n\n\t\t\tif (!segment || !segment.length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdata.init(segment);\n\n\t\t\t// Check if that's APP1 and that it has EXIF\n\t\t\tif (data.SHORT(0) === 0xFFE1 && data.STRING(4, 4).toUpperCase() === \"EXIF\") {\n\t\t\t\treturn getIFDOffsets();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic function TIFF():Object {\n\t\t\treturn Tiff;\n\t\t}\n\n\n\t\tpublic function EXIF():Object {\n\t\t\tvar Exif:Object;\n\n\t\t\tif (!offsets.hasOwnProperty('exifIFD')) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\ttry { // survive invalid offsets\n\t\t\t\tExif = extractTags(offsets['exifIFD'], tags.exif);\n\t\t\t} catch (ex:Error) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// fix formatting of some tags\n\t\t\tif (Exif.hasOwnProperty('ExifVersion') && Exif.ExifVersion is Array) {\n\t\t\t\tfor (var i:uint = 0, exifVersion:String = ''; i < Exif.ExifVersion.length; i++) {\n\t\t\t\t\texifVersion += String.fromCharCode(Exif.ExifVersion[i]);\n\t\t\t\t}\n\t\t\t\tExif.ExifVersion = exifVersion;\n\t\t\t}\n\n\t\t\treturn Exif;\n\t\t}\n\n\t\tpublic function GPS():Object {\n\t\t\tvar Gps:Object;\n\n\t\t\tif (!offsets.hasOwnProperty('gpsIFD')) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\ttry { // survive invalid offsets\n\t\t\t\tGps = extractTags(offsets['gpsIFD'], tags.gps);\n\t\t\t} catch (ex:Error) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (Gps.hasOwnProperty('GPSVersionID') && Gps.GPSVersionID is Array) {\n\t\t\t\tGps.GPSVersionID = Gps.GPSVersionID.join('.');\n\t\t\t}\n\n\t\t\treturn Gps;\n\t\t}\n\n\n\t\tpublic function setExif(tag:String, value:*) : Boolean {\n\t\t\t// Right now only setting of width/height is possible\n\t\t\tif (tag !== 'PixelXDimension' && tag !== 'PixelYDimension') return false;\n\n\t\t\treturn setTag('exif', tag, value);\n\t\t}\n\n\n\t\tpublic function getBinary():ByteArray {\n\t\t\treturn data.SEGMENT();\n\t\t}\n\n\n\t\tprivate function isJPEG():Boolean {\n\t\t\treturn data.SHORT(0) == 0xFFD8;\n\t\t}\n\n\n\t\tprivate function getIFDOffsets():Boolean {\n\t\t\tvar idx:uint = offsets.tiffHeader;\n\n\t\t\t// Set read order of multi-byte data\n\t\t\tdata.II(data.SHORT(idx) == 0x4949);\n\n\t\t\t// Check if always present bytes are indeed present\n\t\t\tif (data.SHORT(idx+=2) !== 0x002A) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\toffsets['IFD0'] = offsets.tiffHeader + data.LONG(idx += 2);\n\t\t\tTiff = extractTags(offsets['IFD0'], tags.tiff);\n\n\t\t\tif (Tiff.hasOwnProperty('ExifIFDPointer')) {\n\t\t\t\toffsets['exifIFD'] = offsets.tiffHeader + Tiff.ExifIFDPointer;\n\t\t\t\tdelete Tiff.ExifIFDPointer;\n\t\t\t}\n\n\t\t\tif (Tiff.hasOwnProperty('GPSInfoIFDPointer')) {\n\t\t\t\toffsets['gpsIFD'] = offsets.tiffHeader + Tiff.GPSInfoIFDPointer;\n\t\t\t\tdelete Tiff.GPSInfoIFDPointer;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\n\t\tprivate function extractTags(IFD_offset:int, tags2extract:Object):Object {\n\t\t\tvar length:uint, i:uint, ii:uint,\n\t\t\t\ttag:String, type:uint, count:uint, tagOffset:uint, offset:uint, value:*,\n\t\t\t\tvalues:Array = [], hash:Object = {};\n\n\t\t\tlength = data.SHORT(IFD_offset);\n\n\t\t\tfor (i = 0; i < length; i++) {\n\t\t\t\t// Set binary reader pointer to beginning of the next tag\n\t\t\t\toffset = tagOffset = IFD_offset + 12 * i + 2;\n\n\t\t\t\ttag = tags2extract[data.SHORT(offset)];\n\n\t\t\t\tif (!tag) {\n\t\t\t\t\tcontinue; // Not the tag we requested\n\t\t\t\t}\n\n\t\t\t\ttype = data.SHORT(offset+=2);\n\t\t\t\tcount = data.LONG(offset+=2);\n\n\t\t\t\toffset += 4;\n\t\t\t\tvalues = [];\n\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase 1: // BYTE\n\t\t\t\t\tcase 7: // UNDEFINED\n\t\t\t\t\t\tif (count > 4) {\n\t\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.BYTE(offset + ii);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 2: // STRING\n\t\t\t\t\t\tif (count > 4) {\n\t\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\thash[tag] = data.STRING(offset, count - 1);\n\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tcase 3: // SHORT\n\t\t\t\t\t\tif (count > 2) {\n\t\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.SHORT(offset + ii*2);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 4: // LONG\n\t\t\t\t\t\tif (count > 1) {\n\t\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.LONG(offset + ii*4);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 5: // RATIONAL\n\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.LONG(offset + ii*4) / data.LONG(offset + ii*4 + 4);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 9: // SLONG\n\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.SLONG(offset + ii*4);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 10: // SRATIONAL\n\t\t\t\t\t\toffset = data.LONG(offset) + offsets.tiffHeader;\n\n\t\t\t\t\t\tfor (ii = 0; ii < count; ii++) {\n\t\t\t\t\t\t\tvalues[ii] = data.SLONG(offset + ii*4) / data.SLONG(offset + ii*4 + 4);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvalue = (count == 1 ? values[0] : values);\n\n\t\t\t\tif (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {\n\t\t\t\t\thash[tag] = tagDescs[tag][value];\n\t\t\t\t} else {\n\t\t\t\t\thash[tag] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn hash;\n\t\t}\n\n\n\t\t// At the moment only setting of simple (LONG) values, that do not require offset recalculation, is supported\n\t\tprivate function setTag(ifd:String, tag:*, value:*) : Boolean {\n\t\t\tvar offset:*, length:uint, tagOffset:uint, valueOffset:uint = 0, hex:*;\n\n\t\t\t// If tag name passed translate into hex key\n\t\t\tif (tag is String) {\n\t\t\t\tvar tmpTags:Object = tags[ifd.toLowerCase()];\n\t\t\t\tfor (hex in tmpTags) {\n\t\t\t\t\tif (tmpTags[hex] === tag) {\n\t\t\t\t\t\ttag = hex;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset = offsets[ifd.toLowerCase() + 'IFD'];\n\t\t\tif (offset === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tlength = data.SHORT(offset);\n\n\t\t\tfor (var i:uint = 0; i < length; i++) {\n\t\t\t\ttagOffset = offset + 12 * i + 2;\n\n\t\t\t\tif (data.SHORT(tagOffset) == tag) {\n\t\t\t\t\tvalueOffset = tagOffset + 8;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!valueOffset) return false;\n\n\t\t\tdata.LONG(valueOffset, value);\n\t\t\treturn true;\n\t\t}\n\n\n\t\tpublic function purge() : void {\n\t\t\tdata.clear();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "flash/src/com/image/GIF.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image\n{\n\timport com.utils.BinaryReader;\n\t\n\timport flash.utils.ByteArray;\n\t\n\tpublic class GIF \n\t{\t\t\n\t\tpublic static const MIME:String = 'image/gif';\n\t\t\n\t\tprotected var _br:BinaryReader;\n\t\t\n\t\tpublic function GIF(binData:ByteArray)\n\t\t{\n\t\t\t_br = new BinaryReader;\n\t\t\t_br.init(binData);\n\t\t}\n\t\t\n\t\tstatic public function test(binData:ByteArray) : Boolean\n\t\t{\n\t\t\tvar sign:Array = [ 71, 73, 70 ];\n\t\t\t\n\t\t\tfor (var i:int = sign.length - 1; i >= 0 ; i--) {\n\t\t\t\tif (binData[i] != sign[i]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function info() : Object\n\t\t{\n\t\t\tvar a:uint = _br.BYTE(6),\n\t\t\t\tb:uint = _br.BYTE(7),\n\t\t\t\tc:uint = _br.BYTE(8),\n\t\t\t\td:uint = _br.BYTE(9);\n\t\t\t\n\t\t\treturn {\n\t\t\t\twidth: b * 256 + a,\n\t\t\t\theight: d * 256 + c,\n\t\t\t\ttype: GIF.MIME\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function metaInfo() : Object\n\t\t{\n\t\t\treturn {};\n\t\t}\n\t\t\n\t\t\n\t\tpublic function purge() : void\n\t\t{\n\t\t\t_br.clear();\n\t\t}\n\t\t\n\t}\n\t\n}\n\n"
  },
  {
    "path": "flash/src/com/image/ImageEditor.as",
    "content": "package com.image\n{\n\timport flash.display.Bitmap;\n\timport flash.display.BitmapData;\n\timport flash.filters.BlurFilter;\n\timport flash.filters.ColorMatrixFilter;\n\timport flash.filters.ConvolutionFilter;\n\timport flash.geom.ColorTransform;\n\timport flash.geom.Matrix;\n\timport flash.geom.Point;\n\timport flash.geom.Rectangle;\n\t\n\timport com.image.ascb.filters.ColorMatrixArrays;\n\timport com.image.ascb.filters.ConvolutionMatrixArrays;\n\t\n\t\n\tpublic class ImageEditor\n\t{\t\n\t\tprivate var _history:Array = [];\n\t\t\n\t\tprivate var _historyIndex:int = -1;\n\t\t\n\t\tprivate var _lastReleaseIndex:int = 0;\n\t\t\n\t\tprivate var _bdOriginal:BitmapData;\n\t\t\n\t\tprivate var _bd:BitmapData;\n\t\t\n\t\tpublic function get bitmapData() : BitmapData {\n\t\t\treturn (_bd ? _bd : _bdOriginal).clone();\n\t\t}\n\t\t\n\t\tprivate var _crop:Rectangle = null;\t\t\n\t\t\n\t\tprivate var _matrix:Matrix = new Matrix();\n\t\t\t\t\n\t\tprivate var _commitAfterEveryModify:Boolean = false;\n\t\t\n\t\t\n\t\tpublic function ImageEditor(bd:BitmapData)\n\t\t{\n\t\t\t_bdOriginal = bd.clone();\n\t\t}\n\t\t\n\t\t\n\t\tpublic function modify(op:String, ... args) : void\n\t\t{\t\n\t\t\tif (canRedo()) {\n\t\t\t\t_history.length = _historyIndex + 1; // discard extra redoable ops\n\t\t\t}\n\t\t\t\n\t\t\t_history.push({\n\t\t\t\t'op': op,\n\t\t\t\t'args': args\n\t\t\t});\n\n\t\t\tif (_commitAfterEveryModify) {\n\t\t\t\tcommit();\n\t\t\t}\n\t\t\t\n\t\t\t_historyIndex++;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function commit() : void\n\t\t{\n\t\t\t_doModifications(_lastReleaseIndex, _historyIndex); // do only incremental modifications if possible\n\t\t\t_lastReleaseIndex = _historyIndex;\n\t\t\t_matrix = new Matrix();\n\t\t}\n\t\t\n\t\t\n\t\tpublic function canUndo() : Boolean\n\t\t{\n\t\t\treturn !!_history.length;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function canRedo() : Boolean\n\t\t{\n\t\t\treturn _historyIndex < _history.length;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function undo() : void\n\t\t{\n\t\t\tif (canUndo()) {\t\n\t\t\t\t_historyIndex--;\n\t\t\t\t\n\t\t\t\tif (_historyIndex < _lastReleaseIndex) {\n\t\t\t\t\t_lastReleaseIndex = 0;\n\t\t\t\t\tif (_bd) {\n\t\t\t\t\t\t_bd.dispose();\n\t\t\t\t\t\t_bd = null;\n\t\t\t\t\t}\n\t\t\t\t\t_matrix = new Matrix();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function redo() : void\n\t\t{\n\t\t\tif (canRedo()) {\n\t\t\t\t_historyIndex++;\n\t\t\t}\t\n\t\t}\n\t\t\n\t\t\n\t\tpublic function purge() : void \n\t\t{\n\t\t\tif (_bd) {\n\t\t\t\t_bd.dispose();\n\t\t\t}\n\t\t\t_bdOriginal.dispose();\n\t\t}\n\t\t\n\t\t\n\t\tprotected function _doModifications(start:int, end:int) : void\n\t\t{\t\t\n\t\t\tif (!_bd) {\n\t\t\t\t_bd = _bdOriginal.clone();\n\t\t\t}\n\t\t\t\n\t\t\tvar mod:Object;\n\t\t\tfor (var i:int = start; i <= end; i++) {\n\t\t\t\tmod = _history[i];\n\t\t\t\tif (typeof(this[mod.op]) == 'function') {\n\t\t\t\t\tthis[mod.op].apply(null, mod.args);\n\t\t\t\t} \n\t\t\t}\n\t\t\t\n\t\t\t_draw();\n\t\t}\n\t\t\n\t\t\n\t\tprotected function rotate(angle:Number) : void\n\t\t{\t\t\n\t\t\t_matrix.translate(-_bd.width/2,-_bd.height/2);\n\t\t\t_matrix.rotate(angle / 180 * Math.PI);\n\t\t\t_matrix.translate(_bd.width/2,_bd.height/2);\n\t\t}\n\t\t\n\t\tprotected function flipH() : void\n\t\t{\n\t\t\t_matrix.scale(-1, 1);\n\t\t\t_matrix.translate(_bd.width, 0);\n\t\t}\n\t\t\n\t\t\n\t\tprotected function flipV() : void\n\t\t{\n\t\t\t_matrix.scale(1, -1);\n\t\t\t_matrix.translate(0, _bd.height);\n\t\t}\n\t\t\n\t\tprotected function resize(w:Number, h:Number) : void\n\t\t{\n\t\t\t_matrix.scale(w / _bd.width, h / _bd.height);\n\t\t}\n\t\t\n\t\t\n\t\tprotected function crop(rect:Rectangle) : void\n\t\t{\n\t\t\t\n\t\t}\n\t\t\n\t\t\n\t\tprotected function sharpen() : void\n\t\t{\n\t\t\tapplyConvolution(ConvolutionMatrixArrays.SHARPEN);\n\t\t}\n\t\t\n\t\tprotected function emboss() : void\n\t\t{\n\t\t\tapplyConvolution(ConvolutionMatrixArrays.EMBOSS);\n\t\t}\n\t\t\n\t\tprotected function grayscale() : void\n\t\t{\n\t\t\tapplyColorMatrix(ColorMatrixArrays.GRAYSCALE);\n\t\t}\n\t\t\n\t\tprotected function sepia() : void\n\t\t{\n\t\t\tapplyColorMatrix(ColorMatrixArrays.SEPIA);\n\t\t}\n\t\t\n\t\t\n\t\tprotected function invert() : void\n\t\t{\n\t\t\tapplyColorMatrix(ColorMatrixArrays.DIGITAL_NEGATIVE);\n\t\t}\n\t\t\n\t\t\n\t\tprotected function brightness(value:int) : void\n\t\t{\t\t\t\n\t\t\tapplyColorMatrix(ColorMatrixArrays.getBrightnessArray(Math.floor(255 * value)));\n\t\t}\n\t\t\n\t\t\n\t\tprotected function contrast(value:Number) : void\n\t\t{\t\n\t\t\tapplyColorMatrix(ColorMatrixArrays.getContrastArray(value));\n\t\t}\n\t\t\n\t\tprotected function saturate(value:Number) : void\n\t\t{\t\n\t\t\tapplyColorMatrix(ColorMatrixArrays.getSaturationArray(value));\n\t\t}\n\t\t\n\t\t\n\t\tprotected function blur(value:Number = 0.02, quality:int = 1) : void\n\t\t{\n\t\t\tif (!_bd) {\n\t\t\t\t_bd = _bdOriginal.clone();\n\t\t\t}\n\t\t\t\n\t\t\tvalue = Math.floor(255 * value);\n\t\t\t\n\t\t\tif (value & 1) {\n\t\t\t\tvalue--; // even values are processed faster\n\t\t\t}\n\t\t\t\n\t\t\t_bd.applyFilter(_bd, _bd.rect, new Point(0, 0), new BlurFilter(value, value, quality));\n\t\t}\n\t\t\n\t\t\n\t\tprotected function applyConvolution(matrix:Array, devisor:Number = 1.0) : void\n\t\t{\n\t\t\tif (!_bd) {\n\t\t\t\t_bd = _bdOriginal.clone();\n\t\t\t}\n\t\t\t_bd.applyFilter(_bd, _bd.rect, new Point(0, 0), new ConvolutionFilter(3, 3, matrix, devisor));\n\t\t}\n\t\t\n\t\t\n\t\tprotected function applyColorMatrix(matrix:Array) : void\n\t\t{\n\t\t\tif (!_bd) {\n\t\t\t\t_bd = _bdOriginal.clone();\n\t\t\t}\n\t\t\t_bd.applyFilter(_bd, _bd.rect, new Point(0, 0), new ColorMatrixFilter(matrix));\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _draw() : void\n\t\t{\t\n\t\t\t// Finding the four corners of the bounfing box after transformation\n\t\t\tvar tl:Point = _matrix.transformPoint(new Point(0, 0));\n\t\t\tvar tr:Point = _matrix.transformPoint(new Point(_bd.width, 0));\n\t\t\tvar bl:Point = _matrix.transformPoint(new Point(0, _bd.height));\n\t\t\tvar br:Point = _matrix.transformPoint(new Point(_bd.width, _bd.height));\n\t\t\t\n\t\t\t// Calculating \"who\" is \"where\"\n\t\t\tvar top:Number = Math.min(tl.y, tr.y, bl.y, br.y);\n\t\t\tvar bottom:Number = Math.max(tl.y, tr.y, bl.y, br.y);\n\t\t\tvar left:Number = Math.min(tl.x, tr.x, bl.x, br.x);\n\t\t\tvar right:Number = Math.max(tl.x, tr.x, bl.x, br.x);\n\t\t\t\n\t\t\t// Ajusting final position\n\t\t\t_matrix.translate(-left, -top);\n\t\t\t\n\t\t\t// Calculating the size of the new BitmapData\n\t\t\tvar width:Number = right - left;\n\t\t\tvar height:Number = bottom - top;\n\t\t\t\n\t\t\t// Creating and drawing (with transformation)\n\t\t\tvar result:BitmapData = new BitmapData(width, height);\n\t\t\tresult.draw(_bd, _matrix);\t\n\t\t\t_bd.dispose();\n\t\t\t_bd = result;\n\t\t}\n\t\t\n\t\t\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/JPEG.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image \n{\t\n\timport flash.utils.ByteArray;\n\t\n\timport com.utils.BinaryReader;\n\timport com.image.ExifParser;\n\n\tpublic class JPEG \n\t{\t\t\n\t\tpublic static const MIME:String = 'image/jpeg';\n\t\t\n\t\tprotected var _headers:Array = [];\n\t\tprotected var _br:BinaryReader;\n\t\t\n\t\tpublic function JPEG(binData:ByteArray) \n\t\t{\n\t\t\t_br = new BinaryReader;\n\t\t\t_br.init(binData);\n\t\t}\n\t\t\n\t\tstatic public function test(binData:ByteArray) : Boolean\n\t\t{\n\t\t\tvar sign:Array = [ 255, 216 ];\n\t\t\t\t\t\t\n\t\t\tfor (var i:int = sign.length - 1; i >= 0 ; i--) {\n\t\t\t\tif (binData[i] != sign[i]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tpublic function info() : Object \n\t\t{\n\t\t\tvar idx:uint = 0, marker:uint, length:uint;\n\t\t\t\n\t\t\t// examine all through the end, since some images might have very large APP segments\n\t\t\twhile (idx <= _br.length) {\n\t\t\t\tmarker = _br.SHORT(idx += 2);\n\t\t\t\t\n\t\t\t\tif (marker >= 0xFFC0 && marker <= 0xFFC3) { // SOFn\n\t\t\t\t\tidx += 5; // marker (2 bytes) + length (2 bytes) + Sample precision (1 byte)\n\t\t\t\t\treturn {\n\t\t\t\t\t\theight: _br.SHORT(idx),\n\t\t\t\t\t\twidth: _br.SHORT(idx += 2),\n\t\t\t\t\t\ttype: JPEG.MIME\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tlength = _br.SHORT(idx += 2);\n\t\t\t\tidx += length - 2;\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\treturn null;\n\t\t}\t\n\t\t\n\t\t\n\t\tpublic function metaInfo() : Object\n\t\t{\n\t\t\tvar exifParser:ExifParser, headers:Array, \n\t\t\t\tmeta:Object = {}, exif:Object, gps:Object, tiff:Object;\n\t\t\t\n\t\t\theaders = getHeaders('app1');\n\t\t\t\n\t\t\tif (headers.length) {\n\t\t\t\texifParser = new ExifParser;\n\t\t\t\tif (exifParser.init(headers[0])) {\t\n\t\t\t\t\t\n\t\t\t\t\ttiff = exifParser.TIFF();\n\t\t\t\t\tif (tiff) {\n\t\t\t\t\t\tmeta['tiff'] = tiff;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\texif = exifParser.EXIF();\n\t\t\t\t\tif (exif) {\n\t\t\t\t\t\tmeta['exif'] = exif;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tgps = exifParser.GPS();\n\t\t\t\t\tif (gps) {\n\t\t\t\t\t\tmeta['gps'] = gps;\n\t\t\t\t\t}\n\t\n\t\t\t\t\texifParser.purge();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn meta;\n\t\t}\n\n\t\t\n\t\n\t\tpublic function extractHeaders() : Array\n\t\t{\n\t\t\tvar idx:uint, marker:uint, length:uint;\n\t\t\t\n\t\t\tidx = 2;\n\t\t\t\t\n\t\t\twhile (idx <= _br.length) {\n\t\t\t\tmarker = _br.SHORT(idx);\n\t\t\t\t\n\t\t\t\t// omit RST (restart) markers\n\t\t\t\tif (marker >= 0xFFD0 && marker <= 0xFFD7) {\n\t\t\t\t\tidx += 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// no headers allowed after SOS marker\n\t\t\t\tif (marker === 0xFFDA || marker === 0xFFD9) {\n\t\t\t\t\tbreak;\t\n\t\t\t\t}\t\n\t\t\t\t\n\t\t\t\tlength = _br.SHORT(idx + 2) + 2;\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t// APPn marker detected\n\t\t\t\tif (marker >= 0xFFE1 && marker <= 0xFFEF) {\n\t\t\t\t\t_headers.push({\n\t\t\t\t\t\thex: marker,\n\t\t\t\t\t\tname: 'APP' + (marker & 0x000F),\n\t\t\t\t\t\tstart: idx,\n\t\t\t\t\t\tlength: length,\n\t\t\t\t\t\tsegment: _br.SEGMENT(idx, length)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tidx += length;\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\treturn _headers;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function stripHeaders(binData:ByteArray) : void \n\t\t{\n\t\t\tvar img:JPEG = new JPEG(binData), headers:Array, br:BinaryReader = new BinaryReader();\n\t\t\t\n\t\t\timg.extractHeaders();\n\t\t\theaders = img.getHeaders();\n\t\t\timg.purge();\n\t\t\t\t\t\t\n\t\t\tif (headers.length) {\n\t\t\t\tbr.init(binData);\n\t\t\t\tfor (var i:int = headers.length - 1; i >= 0; i--) {\n\t\t\t\t\tbr.SEGMENT(headers[i].start, headers[i].length, null);\n\t\t\t\t}\n\t\t\t\tbinData.clear();\n\t\t\t\tbinData.writeBytes(br.SEGMENT());\n\t\t\t\tbr.clear();\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t\n\t\tpublic function getHeaders(name:String = null) : Array\n\t\t{\n\t\t\tvar headers:Array, array:Array = [];\n\t\t\t\n\t\t\theaders = _headers.length ? _headers : extractHeaders();\n\t\t\t\n\t\t\tif (!name) {\n\t\t\t\treturn headers;\n\t\t\t}\n\t\t\t\n\t\t\tfor (var i:uint = 0, max:uint = headers.length; i < max; i++) {\n\t\t\t\tif (headers[i].name === name.toUpperCase()) {\n\t\t\t\t\tarray.push(headers[i].segment);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn array;\n\t\t}\n\t\t\n\t\tpublic function setHeaders(name:String, segment:*) : void\n\t\t{\n\t\t\tvar array:Array = [];\n\t\t\t\t\t\n\t\t\tif (segment is ByteArray) {\n\t\t\t\tarray.push(segment);\t\n\t\t\t} else {\n\t\t\t\tarray = segment;\t\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\tfor (var i:uint = 0, ii:uint = 0, max:uint = _headers.length; i < max; i++) {\n\t\t\t\tif (_headers[i].name === name.toUpperCase()) {\n\t\t\t\t\t_headers[i].segment = array[ii];\n\t\t\t\t\t_headers[i].length = array[ii].length;\n\t\t\t\t\tii++;\n\t\t\t\t}\n\t\t\t\tif (ii >= array.length) break;\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function updateDimensions(width:uint, height:uint) : void\n\t\t{\n\t\t\tvar exifParser:ExifParser, headers:Array;\n\t\t\t\n\t\t\theaders = getHeaders('app1');\n\t\t\t\n\t\t\tif (headers.length) {\n\t\t\t\texifParser = new ExifParser;\n\t\t\t\tif (exifParser.init(headers[0])) {\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\texifParser.setExif('PixelXDimension', width);\n\t\t\t\t\texifParser.setExif('PixelYDimension', height);\n\t\t\t\t\t\n\t\t\t\t\tsetHeaders('app1', exifParser.getBinary());\t\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\texifParser.purge();\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function insertHeaders(binData:ByteArray, headers:Array = null) : void\n\t\t{\n\t\t\tvar idx:uint, br:BinaryReader = new BinaryReader;\n\t\t\t\n\t\t\tif (!headers || !headers.length) {\n\t\t\t\theaders = _headers;\n\t\t\t}\n\t\t\t\n\t\t\tbr.init(binData);\n\t\t\t\t\t\n\t\t\t// Check if data is jpeg\n\t\t\tif (br.SHORT(0) !== 0xFFD8) {\n\t\t\t\tthrow new Error(\"Invalid JPEG\");\n\t\t\t}\t\n\t\t\t\n\t\t\tif (headers.length) {\n\t\t\t\tidx = br.SHORT(2) == 0xFFE0 ? 4 + br.SHORT(4) : 2;\n\t\t\t\t\n\t\t\t\tfor (var i:uint = 0, max:uint = headers.length; i < max; i++) {\n\t\t\t\t\tbr.SEGMENT(idx, 0, headers[i].segment);\t\n\t\t\t\t\tidx += headers[i].length;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbinData.clear();\n\t\t\tbinData.writeBytes(br.SEGMENT());\n\t\t\tbr.clear();\n\t\t}\n\t\t\n\t\tpublic function purge() : void\n\t\t{\n\t\t\t_br.clear();\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/JPEGEncoder.as",
    "content": "package com.image\n{\n\t/*\n\t*\n\t* MUCH (4x) faster version of jpeg encoder. Use instead of com.adobe.images.JPGEncoder\n\t*\n\t* taken from bytearray.org/?p=775, created by Thibault Imbert\n\t**/\n\t\n\t\n\timport flash.display.BitmapData;\n\timport flash.utils.ByteArray;\n\t\n\tpublic final class JPEGEncoder\n\t{\n\t\t// Static table initialization\n\t\tprivate const ZigZag:Vector.<int> = Vector.<int>([\n\t\t\t0, 1, 5, 6,14,15,27,28,\n\t\t\t2, 4, 7,13,16,26,29,42,\n\t\t\t3, 8,12,17,25,30,41,43,\n\t\t\t9,11,18,24,31,40,44,53,\n\t\t\t10,19,23,32,39,45,52,54,\n\t\t\t20,22,33,38,46,51,55,60,\n\t\t\t21,34,37,47,50,56,59,61,\n\t\t\t35,36,48,49,57,58,62,63\n\t\t]);\n\t\tprivate var YTable:Vector.<int> = new Vector.<int>(64, true);\n\t\tprivate var UVTable:Vector.<int> = new Vector.<int>(64, true);\n\t\tprivate var outputfDCTQuant:Vector.<int> = new Vector.<int>(64, true);\n\t\tprivate var fdtbl_Y:Vector.<Number> = new Vector.<Number>(64, true);\n\t\tprivate var fdtbl_UV:Vector.<Number> = new Vector.<Number>(64, true);\n\t\tprivate var sf:int;\n\t\t\n\t\tprivate const aasf:Vector.<Number> = Vector.<Number>([\n\t\t\t1.0, 1.387039845, 1.306562965, 1.175875602,\n\t\t\t1.0, 0.785694958, 0.541196100, 0.275899379\n\t\t]);\n\t\t\n\t\tprivate var YQT:Vector.<int> = Vector.<int>([\n\t\t\t16, 11, 10, 16, 24, 40, 51, 61,\n\t\t\t12, 12, 14, 19, 26, 58, 60, 55,\n\t\t\t14, 13, 16, 24, 40, 57, 69, 56,\n\t\t\t14, 17, 22, 29, 51, 87, 80, 62,\n\t\t\t18, 22, 37, 56, 68,109,103, 77,\n\t\t\t24, 35, 55, 64, 81,104,113, 92,\n\t\t\t49, 64, 78, 87,103,121,120,101,\n\t\t\t72, 92, 95, 98,112,100,103, 99\n\t\t]);\n\t\t\n\t\tprivate const UVQT:Vector.<int> = Vector.<int>([\n\t\t\t17, 18, 24, 47, 99, 99, 99, 99,\n\t\t\t18, 21, 26, 66, 99, 99, 99, 99,\n\t\t\t24, 26, 56, 99, 99, 99, 99, 99,\n\t\t\t47, 66, 99, 99, 99, 99, 99, 99,\n\t\t\t99, 99, 99, 99, 99, 99, 99, 99,\n\t\t\t99, 99, 99, 99, 99, 99, 99, 99,\n\t\t\t99, 99, 99, 99, 99, 99, 99, 99,\n\t\t\t99, 99, 99, 99, 99, 99, 99, 99\n\t\t]);\n\t\t\n\t\tprivate function initQuantTables(sf:int):void\n\t\t{\n\t\t\tvar i:int;\n\t\t\tconst I64:int = 64;\n\t\t\tconst I8:int = 8;\n\t\t\tfor (i = 0; i < I64; ++i)\n\t\t\t{\n\t\t\t\tvar t:int = int((YQT[i]*sf+50)*0.01);\n\t\t\t\tif (t < 1) {\n\t\t\t\t\tt = 1;\n\t\t\t\t} else if (t > 255) {\n\t\t\t\t\tt = 255;\n\t\t\t\t}\n\t\t\t\tYTable[ZigZag[i]] = t;\n\t\t\t}\n\t\t\t\n\t\t\tfor (i = 0; i < I64; i++)\n\t\t\t{\n\t\t\t\tvar u:int = int((UVQT[i]*sf+50)*0.01);\n\t\t\t\tif (u < 1) {\n\t\t\t\t\tu = 1;\n\t\t\t\t} else if (u > 255) {\n\t\t\t\t\tu = 255;\n\t\t\t\t}\n\t\t\t\tUVTable[ZigZag[i]] = u;\n\t\t\t}\n\t\t\ti = 0;\n\t\t\tfor (var row:int = 0; row < I8; ++row)\n\t\t\t{\n\t\t\t\tfor (var col:int = 0; col < I8; ++col)\n\t\t\t\t{\n\t\t\t\t\tfdtbl_Y[i]  = (1 / (YTable [ZigZag[i]] * aasf[row] * aasf[col] * I8));\n\t\t\t\t\tfdtbl_UV[i] = (1 / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * I8));\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate var YDC_HT:Vector.<BitString>;\n\t\tprivate var UVDC_HT:Vector.<BitString>;\n\t\tprivate var YAC_HT:Vector.<BitString>;\n\t\tprivate var UVAC_HT:Vector.<BitString>;\n\t\t\n\t\tprivate function computeHuffmanTbl(nrcodes:Vector.<int>, std_table:Vector.<int>):Vector.<BitString>\n\t\t{\n\t\t\tvar codevalue:int = 0;\n\t\t\tvar pos_in_table:int = 0;\n\t\t\tvar HT:Vector.<BitString> = new Vector.<BitString>(251, true);\n\t\t\tvar bitString:BitString;\n\t\t\tfor (var k:int=1; k<=16; ++k)\n\t\t\t{\n\t\t\t\tfor (var j:int=1; j<=nrcodes[k]; ++j)\n\t\t\t\t{\n\t\t\t\t\tHT[std_table[pos_in_table]] = bitString = new BitString();\n\t\t\t\t\tbitString.val = codevalue;\n\t\t\t\t\tbitString.len = k;\n\t\t\t\t\tpos_in_table++;\n\t\t\t\t\tcodevalue++;\n\t\t\t\t}\n\t\t\t\tcodevalue<<=1;\n\t\t\t}\n\t\t\treturn HT;\n\t\t}\n\t\t\n\t\tprivate var std_dc_luminance_nrcodes:Vector.<int> = Vector.<int>([0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]);\n\t\tprivate var std_dc_luminance_values:Vector.<int> = Vector.<int>([0,1,2,3,4,5,6,7,8,9,10,11]);\n\t\tprivate var std_ac_luminance_nrcodes:Vector.<int> = Vector.<int>([0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]);\n\t\tprivate var std_ac_luminance_values:Vector.<int> = Vector.<int>([0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n\t\t\t0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n\t\t\t0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n\t\t\t0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n\t\t\t0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n\t\t\t0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n\t\t\t0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n\t\t\t0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n\t\t\t0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n\t\t\t0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n\t\t\t0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n\t\t\t0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n\t\t\t0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n\t\t\t0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n\t\t\t0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n\t\t\t0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n\t\t\t0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n\t\t\t0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n\t\t\t0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n\t\t\t0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n\t\t\t0xf9,0xfa]);\n\t\t\n\t\tprivate var std_dc_chrominance_nrcodes:Vector.<int> = Vector.<int>([0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]);\n\t\tprivate var std_dc_chrominance_values:Vector.<int> = Vector.<int>([0,1,2,3,4,5,6,7,8,9,10,11]);\n\t\tprivate var std_ac_chrominance_nrcodes:Vector.<int> = Vector.<int>([0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]);\n\t\tprivate var std_ac_chrominance_values:Vector.<int> = Vector.<int>([0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n\t\t\t0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n\t\t\t0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n\t\t\t0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n\t\t\t0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n\t\t\t0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n\t\t\t0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n\t\t\t0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n\t\t\t0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n\t\t\t0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n\t\t\t0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n\t\t\t0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n\t\t\t0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n\t\t\t0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n\t\t\t0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n\t\t\t0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n\t\t\t0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n\t\t\t0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n\t\t\t0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n\t\t\t0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n\t\t\t0xf9,0xfa\n\t\t]);\n\t\t\n\t\tprivate function initHuffmanTbl():void\n\t\t{\n\t\t\tYDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n\t\t\tUVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n\t\t\tYAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n\t\t\tUVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n\t\t}\n\t\t\n\t\tprivate var bitcode:Vector.<BitString> = new Vector.<BitString>(65535, true);\n\t\tprivate var category:Vector.<int> = new Vector.<int>(65535, true);\n\t\t\n\t\tprivate function initCategoryNumber():void\n\t\t{\n\t\t\tvar nrlower:int = 1;\n\t\t\tvar nrupper:int = 2;\n\t\t\tvar bitString:BitString;\n\t\t\tconst I15:int = 15;\n\t\t\tvar pos:int;\n\t\t\tfor (var cat:int=1; cat<=I15; ++cat)\n\t\t\t{\n\t\t\t\t//Positive numbers\n\t\t\t\tfor (var nr:int=nrlower; nr<nrupper; ++nr)\n\t\t\t\t{\n\t\t\t\t\tpos = int(32767+nr);\n\t\t\t\t\tcategory[pos] = cat;\n\t\t\t\t\tbitcode[pos] = bitString = new BitString();\n\t\t\t\t\tbitString.len = cat;\n\t\t\t\t\tbitString.val = nr;\n\t\t\t\t}\n\t\t\t\t//Negative numbers\n\t\t\t\tfor (var nrneg:int=-(nrupper-1); nrneg<=-nrlower; ++nrneg)\n\t\t\t\t{\n\t\t\t\t\tpos = int(32767+nrneg);\n\t\t\t\t\tcategory[pos] = cat;\n\t\t\t\t\tbitcode[pos] = bitString = new BitString();\n\t\t\t\t\tbitString.len = cat;\n\t\t\t\t\tbitString.val = nrupper-1+nrneg;\n\t\t\t\t}\n\t\t\t\tnrlower <<= 1;\n\t\t\t\tnrupper <<= 1;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// IO functions\n\t\t\n\t\tprivate var byteout:ByteArray;\n\t\tprivate var bytenew:int = 0;\n\t\tprivate var bytepos:int = 7;\n\t\t\n\t\tprivate function writeBits(bs:BitString):void\n\t\t{\n\t\t\tvar value:int = bs.val;\n\t\t\tvar posval:int = bs.len-1;\n\t\t\twhile ( posval >= 0 )\n\t\t\t{\n\t\t\t\tif (value & uint(1 << posval) )\n\t\t\t\t\tbytenew |= uint(1 << bytepos);\n\t\t\t\tposval--;\n\t\t\t\tbytepos--;\n\t\t\t\tif (bytepos < 0)\n\t\t\t\t{\n\t\t\t\t\tif (bytenew == 0xFF)\n\t\t\t\t\t{\n\t\t\t\t\t\tbyteout.writeByte(0xFF);\n\t\t\t\t\t\tbyteout.writeByte(0);\n\t\t\t\t\t}\n\t\t\t\t\telse byteout.writeByte(bytenew);\n\t\t\t\t\tbytepos=7;\n\t\t\t\t\tbytenew=0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// DCT & quantization core\n\t\t\n\t\tprivate function fDCTQuant(data:Vector.<Number>, fdtbl:Vector.<Number>):Vector.<int>\n\t\t{\n\t\t\t/* Pass 1: process rows. */\n\t\t\tvar dataOff:int=0;\n\t\t\tvar d0:Number, d1:Number, d2:Number, d3:Number, d4:Number, d5:Number, d6:Number, d7:Number;\n\t\t\tvar i:int;\n\t\t\tconst I8:int = 8;\n\t\t\tconst I64:int = 64;\n\t\t\tfor (i=0; i<I8; ++i)\n\t\t\t{\t\n\t\t\t\td0 = data[int(dataOff)];\n\t\t\t\td1 = data[int(dataOff+1)];\n\t\t\t\td2 = data[int(dataOff+2)];\n\t\t\t\td3 = data[int(dataOff+3)];\n\t\t\t\td4 = data[int(dataOff+4)];\n\t\t\t\td5 = data[int(dataOff+5)];\n\t\t\t\td6 = data[int(dataOff+6)];\n\t\t\t\td7 = data[int(dataOff+7)];\n\t\t\t\t\n\t\t\t\tvar tmp0:Number = d0 + d7;\n\t\t\t\tvar tmp7:Number = d0 - d7;\n\t\t\t\tvar tmp1:Number = d1 + d6;\n\t\t\t\tvar tmp6:Number = d1 - d6;\n\t\t\t\tvar tmp2:Number = d2 + d5;\n\t\t\t\tvar tmp5:Number = d2 - d5;\n\t\t\t\tvar tmp3:Number = d3 + d4;\n\t\t\t\tvar tmp4:Number = d3 - d4;\n\t\t\t\t\n\t\t\t\t/* Even part */\n\t\t\t\tvar tmp10:Number = tmp0 + tmp3;\t/* phase 2 */\n\t\t\t\tvar tmp13:Number = tmp0 - tmp3;\n\t\t\t\tvar tmp11:Number = tmp1 + tmp2;\n\t\t\t\tvar tmp12:Number = tmp1 - tmp2;\n\t\t\t\t\n\t\t\t\tdata[int(dataOff)] = tmp10 + tmp11; /* phase 3 */\n\t\t\t\tdata[int(dataOff+4)] = tmp10 - tmp11;\n\t\t\t\t\n\t\t\t\tvar z1:Number = (tmp12 + tmp13) * 0.707106781; /* c4 */\n\t\t\t\tdata[int(dataOff+2)] = tmp13 + z1; /* phase 5 */\n\t\t\t\tdata[int(dataOff+6)] = tmp13 - z1;\n\t\t\t\t\n\t\t\t\t/* Odd part */\n\t\t\t\ttmp10 = tmp4 + tmp5; /* phase 2 */\n\t\t\t\ttmp11 = tmp5 + tmp6;\n\t\t\t\ttmp12 = tmp6 + tmp7;\n\t\t\t\t\n\t\t\t\t/* The rotator is modified from fig 4-8 to avoid extra negations. */\n\t\t\t\tvar z5:Number = (tmp10 - tmp12) * 0.382683433; /* c6 */\n\t\t\t\tvar z2:Number = 0.541196100 * tmp10 + z5; /* c2-c6 */\n\t\t\t\tvar z4:Number = 1.306562965 * tmp12 + z5; /* c2+c6 */\n\t\t\t\tvar z3:Number = tmp11 * 0.707106781; /* c4 */\n\t\t\t\t\n\t\t\t\tvar z11:Number = tmp7 + z3;\t/* phase 5 */\n\t\t\t\tvar z13:Number = tmp7 - z3;\n\t\t\t\t\n\t\t\t\tdata[int(dataOff+5)] = z13 + z2;\t/* phase 6 */\n\t\t\t\tdata[int(dataOff+3)] = z13 - z2;\n\t\t\t\tdata[int(dataOff+1)] = z11 + z4;\n\t\t\t\tdata[int(dataOff+7)] = z11 - z4;\n\t\t\t\t\n\t\t\t\tdataOff += 8; /* advance pointer to next row */\n\t\t\t}\n\t\t\t\n\t\t\t/* Pass 2: process columns. */\n\t\t\tdataOff = 0;\n\t\t\tfor (i=0; i<I8; ++i)\n\t\t\t{\n\t\t\t\td0 = data[int(dataOff)];\n\t\t\t\td1 = data[int(dataOff + 8)];\n\t\t\t\td2 = data[int(dataOff + 16)];\n\t\t\t\td3 = data[int(dataOff + 24)];\n\t\t\t\td4 = data[int(dataOff + 32)];\n\t\t\t\td5 = data[int(dataOff + 40)];\n\t\t\t\td6 = data[int(dataOff + 48)];\n\t\t\t\td7 = data[int(dataOff + 56)];\n\t\t\t\t\n\t\t\t\tvar tmp0p2:Number = d0 + d7;\n\t\t\t\tvar tmp7p2:Number = d0 - d7;\n\t\t\t\tvar tmp1p2:Number = d1 + d6;\n\t\t\t\tvar tmp6p2:Number = d1 - d6;\n\t\t\t\tvar tmp2p2:Number = d2 + d5;\n\t\t\t\tvar tmp5p2:Number = d2 - d5;\n\t\t\t\tvar tmp3p2:Number = d3 + d4;\n\t\t\t\tvar tmp4p2:Number = d3 - d4;\n\t\t\t\t\n\t\t\t\t/* Even part */\n\t\t\t\tvar tmp10p2:Number = tmp0p2 + tmp3p2;\t/* phase 2 */\n\t\t\t\tvar tmp13p2:Number = tmp0p2 - tmp3p2;\n\t\t\t\tvar tmp11p2:Number = tmp1p2 + tmp2p2;\n\t\t\t\tvar tmp12p2:Number = tmp1p2 - tmp2p2;\n\t\t\t\t\n\t\t\t\tdata[int(dataOff)] = tmp10p2 + tmp11p2; /* phase 3 */\n\t\t\t\tdata[int(dataOff+32)] = tmp10p2 - tmp11p2;\n\t\t\t\t\n\t\t\t\tvar z1p2:Number = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n\t\t\t\tdata[int(dataOff+16)] = tmp13p2 + z1p2; /* phase 5 */\n\t\t\t\tdata[int(dataOff+48)] = tmp13p2 - z1p2;\n\t\t\t\t\n\t\t\t\t/* Odd part */\n\t\t\t\ttmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n\t\t\t\ttmp11p2 = tmp5p2 + tmp6p2;\n\t\t\t\ttmp12p2 = tmp6p2 + tmp7p2;\n\t\t\t\t\n\t\t\t\t/* The rotator is modified from fig 4-8 to avoid extra negations. */\n\t\t\t\tvar z5p2:Number = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n\t\t\t\tvar z2p2:Number = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n\t\t\t\tvar z4p2:Number = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n\t\t\t\tvar z3p2:Number= tmp11p2 * 0.707106781; /* c4 */\n\t\t\t\t\n\t\t\t\tvar z11p2:Number = tmp7p2 + z3p2;\t/* phase 5 */\n\t\t\t\tvar z13p2:Number = tmp7p2 - z3p2;\n\t\t\t\t\n\t\t\t\tdata[int(dataOff+40)] = z13p2 + z2p2; /* phase 6 */\n\t\t\t\tdata[int(dataOff+24)] = z13p2 - z2p2;\n\t\t\t\tdata[int(dataOff+ 8)] = z11p2 + z4p2;\n\t\t\t\tdata[int(dataOff+56)] = z11p2 - z4p2;\n\t\t\t\t\n\t\t\t\tdataOff++; /* advance pointer to next column */\n\t\t\t}\n\t\t\t\n\t\t\t// Quantize/descale the coefficients\n\t\t\tvar fDCTQuant:Number;\n\t\t\tfor (i=0; i<I64; ++i)\n\t\t\t{\n\t\t\t\t// Apply the quantization and scaling factor & Round to nearest integer\n\t\t\t\tfDCTQuant = data[int(i)]*fdtbl[int(i)];\n\t\t\t\toutputfDCTQuant[int(i)] = (fDCTQuant > 0.0) ? int(fDCTQuant + 0.5) : int(fDCTQuant - 0.5);\n\t\t\t}\n\t\t\treturn outputfDCTQuant;\n\t\t}\n\t\t\n\t\t// Chunk writing\n\t\tprivate function writeAPP0():void\n\t\t{\n\t\t\tbyteout.writeShort(0xFFE0); // marker\n\t\t\tbyteout.writeShort(16); // length\n\t\t\tbyteout.writeByte(0x4A); // J\n\t\t\tbyteout.writeByte(0x46); // F\n\t\t\tbyteout.writeByte(0x49); // I\n\t\t\tbyteout.writeByte(0x46); // F\n\t\t\tbyteout.writeByte(0); // = \"JFIF\",'\\0'\n\t\t\tbyteout.writeByte(1); // versionhi\n\t\t\tbyteout.writeByte(1); // versionlo\n\t\t\tbyteout.writeByte(0); // xyunits\n\t\t\tbyteout.writeShort(1); // xdensity\n\t\t\tbyteout.writeShort(1); // ydensity\n\t\t\tbyteout.writeByte(0); // thumbnwidth\n\t\t\tbyteout.writeByte(0); // thumbnheight\n\t\t}\n\t\t\n\t\tprivate function writeSOF0(width:int, height:int):void\n\t\t{\n\t\t\tbyteout.writeShort(0xFFC0); // marker\n\t\t\tbyteout.writeShort(17);   // length, truecolor YUV JPG\n\t\t\tbyteout.writeByte(8);    // precision\n\t\t\tbyteout.writeShort(height);\n\t\t\tbyteout.writeShort(width);\n\t\t\tbyteout.writeByte(3);    // nrofcomponents\n\t\t\tbyteout.writeByte(1);    // IdY\n\t\t\tbyteout.writeByte(0x11); // HVY\n\t\t\tbyteout.writeByte(0);    // QTY\n\t\t\tbyteout.writeByte(2);    // IdU\n\t\t\tbyteout.writeByte(0x11); // HVU\n\t\t\tbyteout.writeByte(1);    // QTU\n\t\t\tbyteout.writeByte(3);    // IdV\n\t\t\tbyteout.writeByte(0x11); // HVV\n\t\t\tbyteout.writeByte(1);    // QTV\n\t\t}\n\t\t\n\t\tprivate function writeDQT():void\n\t\t{\n\t\t\tbyteout.writeShort(0xFFDB); // marker\n\t\t\tbyteout.writeShort(132);\t   // length\n\t\t\tbyteout.writeByte(0);\n\t\t\t\n\t\t\tvar i:int;\n\t\t\tconst I64:int = 64;\n\t\t\tfor (i=0; i<I64; ++i)\n\t\t\t\tbyteout.writeByte(YTable[i]);\n\t\t\t\n\t\t\tbyteout.writeByte(1);\n\t\t\t\n\t\t\tfor (i=0; i<I64; ++i)\n\t\t\t\tbyteout.writeByte(UVTable[i]);\n\t\t}\n\t\t\n\t\tprivate function writeDHT():void\n\t\t{\n\t\t\tbyteout.writeShort(0xFFC4); // marker\n\t\t\tbyteout.writeShort(0x01A2); // length\n\t\t\t\n\t\t\tbyteout.writeByte(0); // HTYDCinfo\n\t\t\tvar i:int;\n\t\t\tconst I11:int = 11;\n\t\t\tconst I16:int = 16;\n\t\t\tconst I161:int = 161;\n\t\t\tfor (i=0; i<I16; ++i)\n\t\t\t\tbyteout.writeByte(std_dc_luminance_nrcodes[int(i+1)]);\n\t\t\t\n\t\t\tfor (i=0; i<=I11; ++i)\n\t\t\t\tbyteout.writeByte(std_dc_luminance_values[int(i)]);\n\t\t\t\n\t\t\tbyteout.writeByte(0x10); // HTYACinfo\n\t\t\t\n\t\t\tfor (i=0; i<I16; ++i)\n\t\t\t\tbyteout.writeByte(std_ac_luminance_nrcodes[int(i+1)]);\n\t\t\t\n\t\t\tfor (i=0; i<=I161; ++i)\n\t\t\t\tbyteout.writeByte(std_ac_luminance_values[int(i)]);\n\t\t\t\n\t\t\tbyteout.writeByte(1); // HTUDCinfo\n\t\t\t\n\t\t\tfor (i=0; i<I16; ++i)\n\t\t\t\tbyteout.writeByte(std_dc_chrominance_nrcodes[int(i+1)]);\n\t\t\t\n\t\t\tfor (i=0; i<=I11; ++i)\n\t\t\t\tbyteout.writeByte(std_dc_chrominance_values[int(i)]);\n\t\t\t\n\t\t\tbyteout.writeByte(0x11); // HTUACinfo\n\t\t\t\n\t\t\tfor (i=0; i<I16; ++i)\n\t\t\t\tbyteout.writeByte(std_ac_chrominance_nrcodes[int(i+1)]);\n\t\t\t\n\t\t\tfor (i=0; i<=I161; ++i)\n\t\t\t\tbyteout.writeByte(std_ac_chrominance_values[int(i)]);\n\t\t}\n\t\t\n\t\tprivate function writeSOS():void\n\t\t{\n\t\t\tbyteout.writeShort(0xFFDA); // marker\n\t\t\tbyteout.writeShort(12); // length\n\t\t\tbyteout.writeByte(3); // nrofcomponents\n\t\t\tbyteout.writeByte(1); // IdY\n\t\t\tbyteout.writeByte(0); // HTY\n\t\t\tbyteout.writeByte(2); // IdU\n\t\t\tbyteout.writeByte(0x11); // HTU\n\t\t\tbyteout.writeByte(3); // IdV\n\t\t\tbyteout.writeByte(0x11); // HTV\n\t\t\tbyteout.writeByte(0); // Ss\n\t\t\tbyteout.writeByte(0x3f); // Se\n\t\t\tbyteout.writeByte(0); // Bf\n\t\t}\n\t\t\n\t\t// Core processing\n\t\tinternal var DU:Vector.<int> = new Vector.<int>(64, true);\n\t\t\n\t\tprivate function processDU(CDU:Vector.<Number>, fdtbl:Vector.<Number>, DC:Number, HTDC:Vector.<BitString>, HTAC:Vector.<BitString>):Number\n\t\t{\n\t\t\tvar EOB:BitString = HTAC[0x00];\n\t\t\tvar M16zeroes:BitString = HTAC[0xF0];\n\t\t\tvar pos:int;\n\t\t\tconst I16:int = 16;\n\t\t\tconst I63:int = 63;\n\t\t\tconst I64:int = 64;\n\t\t\tvar DU_DCT:Vector.<int> = fDCTQuant(CDU, fdtbl);\n\t\t\t//ZigZag reorder\n\t\t\tfor (var j:int=0;j<I64;++j) {\n\t\t\t\tDU[ZigZag[j]]=DU_DCT[j];\n\t\t\t}\n\t\t\tvar Diff:int = DU[0] - DC; DC = DU[0];\n\t\t\t//Encode DC\n\t\t\tif (Diff==0) {\n\t\t\t\twriteBits(HTDC[0]); // Diff might be 0\n\t\t\t} else {\n\t\t\t\tpos = int(32767+Diff);\n\t\t\t\twriteBits(HTDC[category[pos]]);\n\t\t\t\twriteBits(bitcode[pos]);\n\t\t\t}\n\t\t\t//Encode ACs\n\t\t\tvar end0pos:int = 63;\n\t\t\tfor (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n\t\t\t//end0pos = first element in reverse order !=0\n\t\t\tif ( end0pos == 0) {\n\t\t\t\twriteBits(EOB);\n\t\t\t\treturn DC;\n\t\t\t}\n\t\t\tvar i:int = 1;\n\t\t\tvar lng:int;\n\t\t\twhile ( i <= end0pos ) {\n\t\t\t\tvar startpos:int = i;\n\t\t\t\tfor (; (DU[i]==0) && (i<=end0pos); ++i) {}\n\t\t\t\tvar nrzeroes:int = i-startpos;\n\t\t\t\tif ( nrzeroes >= I16 ) {\n\t\t\t\t\tlng = nrzeroes>>4;\n\t\t\t\t\tfor (var nrmarker:int=1; nrmarker <= lng; ++nrmarker)\n\t\t\t\t\t\twriteBits(M16zeroes);\n\t\t\t\t\tnrzeroes = int(nrzeroes&0xF);\n\t\t\t\t}\n\t\t\t\tpos = int(32767+DU[i]);\n\t\t\t\twriteBits(HTAC[int((nrzeroes<<4)+category[pos])]);\n\t\t\t\twriteBits(bitcode[pos]);\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tif ( end0pos != I63 ) {\n\t\t\t\twriteBits(EOB);\n\t\t\t}\n\t\t\treturn DC;\n\t\t}\n\t\t\n\t\tprivate var YDU:Vector.<Number> = new Vector.<Number>(64, true);\n\t\tprivate var UDU:Vector.<Number> = new Vector.<Number>(64, true);\n\t\tprivate var VDU:Vector.<Number> = new Vector.<Number>(64, true);\n\t\t\n\t\tprivate function RGB2YUV(img:BitmapData, xpos:int, ypos:int):void\n\t\t{\n\t\t\tvar pos:int=0;\n\t\t\tconst I8:int = 8;\n\t\t\tfor (var y:int=0; y<I8; ++y) {\n\t\t\t\tfor (var x:int=0; x<I8; ++x) {\n\t\t\t\t\tvar P:uint = img.getPixel32(xpos+x,ypos+y);\n\t\t\t\t\tvar R:int = (P>>16)&0xFF;\n\t\t\t\t\tvar G:int = (P>> 8)&0xFF;\n\t\t\t\t\tvar B:int = (P    )&0xFF;\n\t\t\t\t\tYDU[int(pos)]=((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-0x80;\n\t\t\t\t\tUDU[int(pos)]=(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B));\n\t\t\t\t\tVDU[int(pos)]=((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B));\n\t\t\t\t\t++pos;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function JPEGEncoder(quality:int=50)\n\t\t{\n\t\t\tif (quality <= 0)\n\t\t\t\tquality = 1;\n\t\t\t\n\t\t\tif (quality > 100)\n\t\t\t\tquality = 100;\n\t\t\t\n\t\t\tsf = quality < 50 ? int(5000 / quality) : int(200 - (quality<<1));\n\t\t\tinit();\n\t\t}\n\t\t\n\t\tprivate function init():void\n\t\t{\n\t\t\tZigZag.fixed = true;\n\t\t\taasf.fixed = true;\n\t\t\tYQT.fixed = true;\n\t\t\tUVQT.fixed = true;\n\t\t\tstd_ac_chrominance_nrcodes.fixed = true;\n\t\t\tstd_ac_chrominance_values.fixed = true;\n\t\t\tstd_ac_luminance_nrcodes.fixed = true;\n\t\t\tstd_ac_luminance_values.fixed = true;\n\t\t\tstd_dc_chrominance_nrcodes.fixed = true;\n\t\t\tstd_dc_chrominance_values.fixed = true;\n\t\t\tstd_dc_luminance_nrcodes.fixed = true;\n\t\t\tstd_dc_luminance_values.fixed = true;\n\t\t\t// Create tables\n\t\t\tinitHuffmanTbl();\n\t\t\tinitCategoryNumber();\n\t\t\tinitQuantTables(sf);\n\t\t}\n\t\t\n\t\tpublic function encode(image:BitmapData):ByteArray\n\t\t{\n\t\t\t// Initialize bit writer\n\t\t\tbyteout = new ByteArray();\n\t\t\t\n\t\t\tbytenew=0;\n\t\t\tbytepos=7;\n\t\t\t\n\t\t\t// Add JPEG headers\n\t\t\tbyteout.writeShort(0xFFD8); // SOI\n\t\t\twriteAPP0();\n\t\t\twriteDQT();\n\t\t\twriteSOF0(image.width,image.height);\n\t\t\twriteDHT();\n\t\t\twriteSOS();\n\t\t\t\n\t\t\t// Encode 8x8 macroblocks\n\t\t\tvar DCY:Number=0;\n\t\t\tvar DCU:Number=0;\n\t\t\tvar DCV:Number=0;\n\t\t\tbytenew=0;\n\t\t\tbytepos=7;\n\t\t\t\n\t\t\tvar width:int = image.width;\n\t\t\tvar height:int = image.height;\n\t\t\t\n\t\t\tfor (var ypos:int=0; ypos<height; ypos+=8)\n\t\t\t{\n\t\t\t\tfor (var xpos:int=0; xpos<width; xpos+=8)\n\t\t\t\t{\n\t\t\t\t\tRGB2YUV(image, xpos, ypos);\n\t\t\t\t\tDCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n\t\t\t\t\tDCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n\t\t\t\t\tDCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// Do the bit alignment of the EOI marker\n\t\t\tif ( bytepos >= 0 )\n\t\t\t{\n\t\t\t\tvar fillbits:BitString = new BitString();\n\t\t\t\tfillbits.len = bytepos+1;\n\t\t\t\tfillbits.val = (1<<(bytepos+1))-1;\n\t\t\t\twriteBits(fillbits);\n\t\t\t}\n\t\t\tbyteout.writeShort(0xFFD9); //EOI\n\t\t\treturn byteout;\n\t\t}\n\t}\n}\n\nfinal class BitString\n{\n\tpublic var len:int = 0;\n\tpublic var val:int = 0;\n}"
  },
  {
    "path": "flash/src/com/image/PNG.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image\n{\n\timport com.utils.BinaryReader;\n\timport flash.utils.ByteArray;\n\t\n\tpublic class PNG \n\t{\t\t\n\t\tpublic static const MIME:String = 'image/png';\n\t\t\n\t\tprotected var _br:BinaryReader;\n\t\t\n\t\tpublic function PNG(binData:ByteArray)\n\t\t{\n\t\t\t_br = new BinaryReader;\n\t\t\t_br.init(binData);\n\t\t}\n\t\t\n\t\tstatic public function test(binData:ByteArray) : Boolean\n\t\t{\n\t\t\tvar sign:Array = [ 137, 80, 78, 71, 13, 10, 26, 10 ];\n\t\t\t\n\t\t\tfor (var i:int = sign.length - 1; i >= 0 ; i--) {\n\t\t\t\tif (binData[i] != sign[i]) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function info() : Object\n\t\t{\n\t\t\tvar chunk:Object, idx:uint;\n\t\t\t\n\t\t\tchunk = _getChunkAt(8);\n\t\t\t\n\t\t\tif (chunk.type == 'IHDR') {\n\t\t\t\tidx = chunk.start;\n\t\t\t\treturn {\n\t\t\t\t\twidth: _br.LONG(idx),\n\t\t\t\t\theight: _br.LONG(idx += 4),\n\t\t\t\t\ttype: PNG.MIME\n\t\t\t\t};\n\t\t\t}\n\t\t\t\t\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function metaInfo() : Object\n\t\t{\n\t\t\treturn {};\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _getChunkAt(idx:uint) : Object\n\t\t{\n\t\t\tvar length:uint, type:String, start:uint, CRC:uint;\n\t\t\t\n\t\t\tlength = _br.LONG(idx);\n\t\t\ttype = _br.STRING(idx += 4, 4);\n\t\t\tstart = idx += 4;\t\n\t\t\tCRC = _br.LONG(idx + length);\n\t\t\t\n\t\t\treturn {\n\t\t\t\tlength: length,\n\t\t\t\ttype: type,\n\t\t\t\tstart: start,\n\t\t\t\tCRC: CRC\n\t\t\t};\n\t\t}\n\t\t\n\t\tpublic function purge() : void\n\t\t{\n\t\t\t_br.clear();\n\t\t}\n\t\t\n\t}\n\n}"
  },
  {
    "path": "flash/src/com/image/ascb/filters/ColorMatrixArrays.as",
    "content": "package com.image.ascb.filters {\n\n\tpublic class ColorMatrixArrays {\n\n\t\tpublic static const DIGITAL_NEGATIVE:Array = [-1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0];\n\t\tpublic static const GRAYSCALE:Array = [0.3086, 0.6094, 0.0820, 0, 0, 0.3086, 0.6094, 0.0820, 0, 0, 0.3086, 0.6094, 0.0820, 0, 0, 0, 0, 0, 1, 0];\n\t\tpublic static const SEPIA:Array = [0.3930000066757202, 0.7689999938011169, 0.1889999955892563, 0, 0, 0.3490000069141388, 0.6859999895095825, 0.1679999977350235, 0, 0, 0.2720000147819519, 0.5339999794960022, 0.1309999972581863, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1];\n\n\t  \tpublic static function getSaturationArray(nValue:Number):Array {\n\t\t\tvar nRed:Number = 0.3086,\n\t\t  \t\tnGreen:Number = 0.6094,\n\t\t\t\tnBlue:Number = 0.0820,\n\t\t\t\tnA:Number = (1 - nValue) * nRed + nValue,\n\t\t\t\tnB:Number = (1 - nValue) * nGreen,\n\t\t\t\tnC:Number = (1 - nValue) * nBlue,\n\t\t\t\tnD:Number = (1 - nValue) * nRed,\n\t\t\t\tnE:Number = (1 - nValue) * nGreen + nValue,\n\t\t\t\tnF:Number = (1 - nValue) * nBlue,\n\t\t\t\tnG:Number = (1 - nValue) * nRed,\n\t\t\t\tnH:Number = (1 - nValue) * nGreen,\n\t\t\t\tnI:Number = (1 - nValue) * nBlue + nValue;\n\n\t      return [nA, nB, nC, 0, 0, nD, nE, nF, 0, 0, nG, nH, nI, 0, 0, 0, 0, 0, 1, 0];\n\t \t}\n\n\t  \tpublic static function getContrastArray(nValue:Number) : Array\n\t\t{\n\t  \t  var nScale:Number = nValue * 11,\n\t\t\t  nOffset:Number = 63.6 - (nValue * 698.5);\n\n\t  \t  return [nScale, 0, 0, 0, nOffset, 0, nScale, 0, 0, nOffset, 0, 0, nScale, 0, nOffset, 0, 0, 0, 1, 0];\n\t  \t}\n\n\t\tpublic static function getBrightnessArray(nValue:Number) : Array\n\t\t{\n\t\t\treturn [1, 0, 0, 0, nValue, 0, 1, 0, 0, nValue, 0, 0, 1, 0, nValue, 0, 0, 0, 1, 0];\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/ascb/filters/ConvolutionMatrixArrays.as",
    "content": "package com.image.ascb.filters {\n\n\tpublic class ConvolutionMatrixArrays {\n\n\t\tpublic static const EMBOSS:Array = [-2, -1, 0, -1, 1, 1, 0, 1, 2];\n\t\tpublic static const EDGE_DETECT:Array = [0, 1, 0, 1, -4, 1, 0, 1, 0];\n\t\tpublic static const SHARPEN:Array = [0, -1, 0, -1, 5, -1, 0, -1, 0];\n\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/events/ExifParserEvent.as",
    "content": "/**\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.image.events {\n\timport flash.events.Event;\n\n\tpublic class ExifParserEvent extends Event {\n\n\t\tpublic var data:Object;\n\n\t\tpublic static const EXIF_DATA:String = 'exifdata';\n\t\tpublic static const GPS_DATA:String = 'gpsdata';\n\n\t\tfunction ExifParserEvent(type:String, data:Object) {\n\t\t\tthis.data = data;\n\t\t\tsuper(type);\n\t\t}\n\n\t\toverride public function clone() : Event\n\t\t{\n\t\t\treturn new ExifParserEvent(type, data);\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/formatlos/BitmapDataUnlimited.as",
    "content": "/*\nCopyright (c) 2008 Martin Raedlinger (mr@formatlos.de)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\npackage com.image.formatlos\n{\n\timport flash.display.Bitmap;\n\timport flash.display.BitmapData;\n\timport flash.display.DisplayObject;\n\timport flash.display.IBitmapDrawable;\n\timport flash.display.Loader;\n\timport flash.events.Event;\n\timport flash.events.EventDispatcher;\n\timport flash.geom.ColorTransform;\n\timport flash.geom.Matrix;\n\timport flash.geom.Point;\n\timport flash.geom.Rectangle;\n\n\timport com.image.formatlos.events.BitmapDataUnlimitedEvent;\n\n\t/**\n \t * Dispatched when the BitmapData is ready\n \t *\n \t * @eventType com.formatlos.events.BitmapDataUnlimitedEvent\n \t */\n\t[Event(name='COMPLETE', type='com.image.formatlos.events.BitmapDataUnlimitedEvent')]\n\n\t/**\n \t * Dispatched when the BitmapData can't be created due to memory issues.\n \t * watch your system memory and/or System.totalMemory\n \t *\n \t * @eventType com.formatlos.events.BitmapDataUnlimitedEvent\n \t */\n\t[Event(name='ERROR', type='com.image.formatlos.events.BitmapDataUnlimitedEvent')]\n\n\t// ---------------------------------------------------------------------------\n\n\t/**\n\t * The BitmapDataUnlimited Class creates an empty gif image\n\t *\n\t * @author Martin Raedlinger\n\t *\n\t * @example\n\t * The example shows how to use the BitmapDataUnlimited\n\t * <div class=\"listing\">\n\t * <pre>\n\t *\n\t * var bdu:BitmapDataUnlimited = new BitmapDataUnlimited();\n\t * bdu.addEventListener(BitmapDataUnlimitedEvent.COMPLETE, onBmpReady);\n\t * bdu.create(5000, 5000, true);\n\t *\n\t * var hugeBitmapData : BitmapData;\n\t *\n\t * function onBmpReady(event : BitmapDataUnlimitedEvent) : void\n\t * {\n\t * \t  hugeBitmapData = bdu.bitmapData;\n\t *\n\t * \t  var rect : Rectangle = new Rectangle(10, 10, 10, 10);\n\t *\n\t *    hugeBitmapData.fillRect(rect, 0xffff0000);\n\t * \t  addChild(new Bitmap(hugeBitmapData));\n\t *\n\t * \t  trace(\"BitmapData: w=\" + hugeBitmapData.width + \" h=\" + hugeBitmapData.height);\n\t * }\n\t *\n\t * </pre>\n\t * </div>\n\t *\n\t */\n\tpublic class BitmapDataUnlimited extends EventDispatcher\n\t{\n\t\t// basically this value is 4096, but take 4000 to have some buffer\n\t\tstatic private const DRAW_LIMIT : uint = 4000;\n\n\t\tprivate var _loader : Loader;\n\t\tprivate var _gif : Gif;\n\t\tprivate var _fillColor : uint;\n\t\tprivate var _transparent : Boolean;\n\n\n\t\t// created bitmapData\n\t\tprivate var _bitmapData : BitmapData;\n\n\t\t/**\n\t\t * Returns the created BitmapData Object\n\t\t *\n\t\t * @return Huge BitmapData\n\t\t */\n\t\tpublic function get bitmapData() : BitmapData\n\t\t{\n\t\t\treturn _bitmapData;\n\t\t}\n\n\n\t\t/**\n\t\t * Creates a huge BitmapData object.\n\t\t *\n\t\t * @param width The width of the huge BitmapData\n\t\t * @param height The height of the huge BitmapData\n\t\t * @param transparent transparent BitmapData or not\n\t\t * @param fillColor Fill the BitmapData with color\n\t\t */\n\t\tpublic function create(width_ : int, height_ : int, transparent_ : Boolean = true, fillColor_ : uint = 0xFFFFFF) : void\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t_bitmapData = new BitmapData(width_, height_, transparent_, fillColor_);\n\t\t\t\tdispatchComplete();\n\t\t\t}\n\t\t\tcatch(error : ArgumentError)\n\t\t\t{\n\t\t\t\tif(width_ <= 0 || height_ <= 0)\n\t\t\t\t{\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t_transparent = transparent_;\n\t\t\t\t\t_fillColor = fillColor_;\n\n\t\t\t\t\t_gif = new Gif(width_, height_, transparent_, fillColor_);\n\t\t\t\t\t_loader = new Loader();\n\t\t\t\t\t_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);\n\t\t\t\t\t_loader.loadBytes(_gif.bytes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Bypasses the 4096px limit in BitmapData.draw() and draws the source display object onto the bitmap image.\n\t\t *\n\t\t * @see http://blog.formatlos.de/2008/12/11/bitmapdatadraw-is-limited-to-4096px/\n\t\t *\n\t\t * @param source_ The display object or BitmapData object to draw to the BitmapData object. (The DisplayObject and BitmapData classes implement the IBitmapDrawable interface.)\n\t\t * @param colorTransform_ A ColorTransform object that you use to adjust the color values of the bitmap. If no object is supplied, the bitmap image's colors are not transformed\n\t\t * @param blendMode_ A string value, from the flash.display.BlendMode class, specifying the blend mode to be applied to the resulting bitmap.\n\t\t * @param clipRect_ A Rectangle object that defines the area of the source object to draw. If you do not supply this value, no clipping occurs and the entire source object is drawn.\n\t\t * @param smoothing_ A Boolean value that determines whether a BitmapData object is smoothed\n\t\t */\n\t\tpublic function draw(source_ : IBitmapDrawable, matrix_ : Matrix = null, colorTransform_ : ColorTransform = null, blendMode_ : String = null, clipRect_:Rectangle = null, smoothing_ : Boolean = false) : void\n\t\t{\n\t\t\tvar srcRect : Rectangle;\n\n\t\t\tif (source_ is BitmapData) srcRect = (source_ as BitmapData).rect.clone();\n\t\t\telse if (source_ is DisplayObject) srcRect = (source_ as DisplayObject).getBounds(source_ as DisplayObject);\n\n\n\t\t\tif(srcRect)\n\t\t\t{\n\t\t\t\tvar x : int = (clipRect_) ? clipRect_.x : 0;\n\t\t\t\tvar y : int = (clipRect_) ? clipRect_.y : 0;\n\t\t\t\tvar clipWidth : int = (clipRect_) ? clipRect_.right : _bitmapData.width;\n\t\t\t\tvar clipHeight : int = (clipRect_) ? clipRect_.bottom : _bitmapData.height;\n\t\t\t\tvar xMax : int = Math.min(srcRect.right, clipWidth);\n\t\t\t\tvar yMax : int = Math.min(srcRect.bottom, clipHeight);\n\t\t\t\tvar matrix : Matrix;\n\t\t\t\tvar chunk : BitmapData;\n\t\t\t\tvar clip : Rectangle = new Rectangle();\n\n\t\t\t\tif(matrix_) {\n\t\t\t\t\txMax *= matrix_.a;\n\t\t\t\t\tyMax *= matrix_.d;\n\t\t\t\t}\n\n\t\t\t\twhile(x < xMax)\n\t\t\t\t{\n\t\t\t\t\twhile(y < yMax)\n\t\t\t\t\t{\n\t\t\t\t\t\tmatrix = new Matrix();\n\t\t\t\t\t\tif(matrix_) {\n\t\t\t\t\t\t\tmatrix.a = matrix_.a;\n\t\t\t\t\t\t\tmatrix.d = matrix_.d;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatrix.translate(-x, -y);\n\t\t\t\t\t\tclip.width = (xMax - x >= DRAW_LIMIT) ? DRAW_LIMIT : xMax - x;\n\t\t\t\t\t\tclip.height = (yMax - y >= DRAW_LIMIT) ? DRAW_LIMIT : yMax - y\t;\n\n\t\t\t\t\t\t// use source\n\t\t\t\t\t\tif(x == 0 && y == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_bitmapData.draw(source_, matrix, colorTransform_, blendMode_, clip, smoothing_);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// copy to chunk first\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(!chunk) chunk = _bitmapData.clone();\n\t\t\t\t\t\t\tchunk.fillRect(chunk.rect, (!_transparent) ? _fillColor : 0x00000000 );\n\t\t\t\t\t\t\tchunk.draw(source_, matrix, colorTransform_, blendMode_, clip, smoothing_);\n\t\t\t\t\t\t\t_bitmapData.copyPixels(chunk, chunk.rect, new Point(x, y), null, null, true);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ty += DRAW_LIMIT;\n\t\t\t\t\t}\n\n\t\t\t\t\tx += DRAW_LIMIT;\n\t\t\t\t\ty = 0;\n\t\t\t\t}\n\n\t\t\t\tif(chunk)\n\t\t\t\t{\n\t\t\t\t\tchunk.dispose();\n\t\t\t\t\tchunk = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tprivate function onLoaderComplete(event : Event) : void\n\t\t{\n\t\t\tvar ok:Boolean = true;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\t_bitmapData = Bitmap(_loader.content).bitmapData.clone();\n\t\t\t\tif(!_transparent) _bitmapData.fillRect(_bitmapData.rect, _fillColor);\n\t\t\t}\n\t\t\tcatch(error : ArgumentError)\n\t\t\t{\n\t\t\t\tok = false;\n\t\t\t\tdispatchEvent(new BitmapDataUnlimitedEvent(BitmapDataUnlimitedEvent.ERROR));\n\t\t\t}\n\n\t\t\t_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoaderComplete);\n\t\t\t_loader = null;\n\n\t\t\tif(ok) dispatchComplete();\n\t\t}\n\n\t\tprivate function dispatchComplete() : void\n\t\t{\n\t\t\tdispatchEvent(new BitmapDataUnlimitedEvent(BitmapDataUnlimitedEvent.COMPLETE));\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/formatlos/Gif.as",
    "content": "/*\nCopyright (c) 2008 Martin Raedlinger (mr@formatlos.de)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n */\n\npackage com.image.formatlos\n{\n\timport flash.utils.ByteArray;\n\n\t/**\n\t * The Gif Class creates an empty gif image\n\t *\n\t * @author Martin Raedlinger\n\t */\n\tpublic class Gif\n\t{\n\t\tprivate var _width : int;\n\t\tprivate var _height : int;\n\t\tprivate var _colorTable : ByteArray;\n\t\tprivate var _colorTableSize : int = 7;\n\t\tprivate var _transparent : Boolean;\n\t\tprivate var _transparentIndex : int = 0;\n\t\tprivate var _fillColor : uint;\n\n\t\t// binary gif data\n\t\tprivate var _binaryGif : ByteArray;\n\n\t\t/**\n\t\t * Returns the created binary gif data\n\t\t *\n\t\t * @return binary gif data\n\t\t */\n\t\tpublic function get bytes() : ByteArray\n\t\t{\n\t\t\treturn _binaryGif;\n\t\t}\n\n\n\t\tpublic function Gif(width_ : int, height_ : int, transparent_ : Boolean = false, fillColor_ : uint = 4.294967295E9)\n\t\t{\n\t\t\t_width = width_;\n\t\t\t_height = height_;\n\t\t\t_transparent = transparent_;\n\t\t\t_fillColor = fillColor_;\n\n\t\t\tinitialize();\n\t\t}\n\n\t\tprivate function initialize() : void\n\t\t{\n\t\t\t_binaryGif = new ByteArray();\n\n\t\t\twriteHeader();\n\t\t\twriteLogicalScreenDescriptor();\n\t\t\twriteColorTable();\n\t\t\twriteGraphicControlExtensionBlock();\n\t\t\twriteImageBlock();\n\t\t\twriteTrailer();\n\t\t}\n\n\t\tprivate function writeHeader() : void\n\t\t{\n\t\t\t_binaryGif.writeUTFBytes(\"GIF89a\");\n\t\t}\n\n\t\tprivate function writeLogicalScreenDescriptor() : void\n\t\t{\n\t\t\t// size\n\t\t\twriteShort(_width);\n\t\t\twriteShort(_height);\n\n\t\t\t// Packed Fields\n\t\t\t// bit 0:    Global Color Table Flag (GCTF)\n\t\t\t// bit 1..3: Color Resolution\n\t\t\t// bit 4:    Sort Flag to Global Color Table\n\t\t\t// bit 5..7: Size of Global Color Table: 2^(1+n)\n\t\t\t_binaryGif.writeByte((0x80 | 0x70 | 0x00 | _colorTableSize));\n\t\t\t// Background Color Index\n\t\t\t_binaryGif.writeByte(0);\n\t\t\t// Pixel Aspect Ratio\n\t\t\t_binaryGif.writeByte(0); //\n\t\t}\n\n\n\t\tprivate function writeColorTable() : void\n\t\t{\n\t\t\t_colorTable = new ByteArray();\n\t\t\t//_colorTable[0] = 0xFF0000 >> 16;\n\t\t\t//_colorTable[1] = 0x00FF00 >> 8;\n\t\t\t//_colorTable[2] = 0x0000FF;\n\t\t\t_colorTable[0] = _fillColor >> 16 & 0xFF;\n\t\t\t_colorTable[1] = _fillColor >> 8 & 0xFF;\n\t\t\t_colorTable[2] = _fillColor & 0xFF;\n\n\t\t\t_binaryGif.writeBytes(_colorTable, 0, _colorTable.length);\n\n\t\t\tvar i : int = 0;\n\t\t\tvar n : int = (3 * 256) - _colorTable.length;\n\n\t\t\twhile(i < n)\n\t\t\t{\n\t\t\t\t_binaryGif.writeByte(0);\n\t\t\t\t++i;\n\t\t\t}\n\t\t}\n\n\t\tprivate function writeGraphicControlExtensionBlock() : void\n\t\t{\n\t\t\t// Extension Introducer\n\t\t\t_binaryGif.writeByte(0x21);\n\t\t\t// Graphic Control Label\n\t\t\t_binaryGif.writeByte(0xf9);\n\t\t\t// Block Size\n\t\t\t_binaryGif.writeByte(4);\n\n\n\t\t\tvar transparent : int;\n\t\t\tvar dispose : int;\n\n\t\t\tif (_transparent)\n\t\t\t{\n\t\t\t\ttransparent = 1;\n\t\t\t\tdispose = 2;\n\t\t\t}\n\t\t    else\n\t\t\t{\n\t\t\t\ttransparent = 0;\n\t\t\t\tdispose = 0;\n\t\t\t}\n\n\t\t\tdispose <<= 2;\n\n\t\t\t// Packed Fields\n\t\t\t// bit 0..2: Reserved\n\t\t\t// bit 3..5: Disposal Method\n\t\t\t// bit 6:    User Input Flag\n\t\t\t// bit 7:    Transparent Color Flag\n\t\t\t_binaryGif.writeByte(0 | dispose | 0 | transparent);\n\n\t\t\t// Delay Time\n\t\t\twriteShort(0);\n\t\t\t// Transparent Color Index\n\t\t\t_binaryGif.writeByte(_transparentIndex);\n\t\t\t// Block Terminator\n\t\t\t_binaryGif.writeByte(0);\n\t\t}\n\n\t\tprivate function writeImageBlock() : void\n\t\t{\n\t\t\t//Image Separator\n\t\t\t_binaryGif.writeByte(0x2c);\n\t\t\t// Image Left Position\n\t\t\twriteShort(0);\n\t\t\t// image position x,y = 0,0\n\t\t\t// Image Top Position\n\t\t\twriteShort(0);\n\t\t\t// Image Width\n\t\t\twriteShort(_width);\n\t\t\t// Image Height\n\t\t\twriteShort(_height);\n\t\t\t// Packed Fields\n\t\t\t_binaryGif.writeByte(0);\n\t\t}\n\n\n\t\tprivate function writeTrailer() : void\n\t\t{\n\t\t\t_binaryGif.writeByte(0x3b);\n\t\t}\n\n\t\tprivate function writeShort(pValue : int) : void\n\t\t{\n\t\t\t_binaryGif.writeByte(pValue & 0xFF);\n\t\t\t_binaryGif.writeByte((pValue >> 8) & 0xFF);\n\t\t}\n\n\n\n\t}\n}"
  },
  {
    "path": "flash/src/com/image/formatlos/events/BitmapDataUnlimitedEvent.as",
    "content": "/*\rCopyright (c) 2008 Martin Raedlinger (mr@formatlos.de)\r\rPermission is hereby granted, free of charge, to any person obtaining a copy\rof this software and associated documentation files (the \"Software\"), to deal\rin the Software without restriction, including without limitation the rights\rto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\rcopies of the Software, and to permit persons to whom the Software is\rfurnished to do so, subject to the following conditions:\r\rThe above copyright notice and this permission notice shall be included in\rall copies or substantial portions of the Software.\r\rTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\rIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\rFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\rAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\rLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\rOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\rTHE SOFTWARE.\r*/\r\rpackage com.image.formatlos.events\r{\r\timport flash.events.Event;\r\r\t/**\r\t * @author Martin Raedlinger\r\t */\r\tpublic class BitmapDataUnlimitedEvent extends Event\r\t{\r\t\tpublic static const COMPLETE : String = \"bitmapDataComplete\";\r\t\tpublic static const ERROR : String = \"bitmapDataError\";\r\r\r\t\tpublic function BitmapDataUnlimitedEvent(type : String, bubbles : Boolean = false, cancelable : Boolean = false)\r\t\t{\r\t\t\tsuper(type, bubbles, cancelable);\r\t\t}\r\r\t\toverride public function clone() : Event\r\t\t{\r\t\t\treturn new BitmapDataUnlimitedEvent(type, bubbles, cancelable);\r\t\t}\r\t}\r}"
  },
  {
    "path": "flash/src/com/utils/BMPDecoder.as",
    "content": "package com.utils {\n\timport flash.display.BitmapData;\n\timport flash.errors.IOError;\n\timport flash.utils.ByteArray;\n\timport flash.utils.Endian;\n\t\n\tpublic class BMPDecoder {\n\t\t//___________________________________________________________ const\n\t\t\n\t\tprivate const BITMAP_HEADER_TYPE:String = \"BM\";\n\t\t\n\t\tprivate const BITMAP_FILE_HEADER_SIZE:int = 14;\n\t\tprivate const BITMAP_CORE_HEADER_SIZE:int = 12;\n\t\tprivate const BITMAP_INFO_HEADER_SIZE:int = 40;\n\t\t\n\t\tprivate const COMP_RGB      :int = 0;\n\t\tprivate const COMP_RLE8     :int = 1;\n\t\tprivate const COMP_RLE4     :int = 2;\n\t\tprivate const COMP_BITFIELDS:int = 3;\n\t\t\n\t\tprivate const BIT1 :int = 1;\n\t\tprivate const BIT4 :int = 4;\n\t\tprivate const BIT8 :int = 8;\n\t\tprivate const BIT16:int = 16;\n\t\tprivate const BIT24:int = 24;\n\t\tprivate const BIT32:int = 32;\n\t\t\n\t\t\n\t\t//___________________________________________________________ vars\n\t\t\n\t\tprivate var bytes:ByteArray;\n\t\tprivate var palette:Array;\n\t\tprivate var bd:BitmapData;\n\t\t\n\t\tprivate var nFileSize:uint;\n\t\tprivate var nReserved1:uint;\n\t\tprivate var nReserved2:uint;\n\t\tprivate var nOffbits:uint;\n\t\t\n\t\tprivate var nInfoSize:uint;\n\t\tprivate var nWidth:int;\n\t\tprivate var nHeight:int;\n\t\tprivate var nPlains:uint;\n\t\tprivate var nBitsPerPixel:uint;\n\t\tprivate var nCompression:uint;\n\t\tprivate var nSizeImage:uint;\n\t\tprivate var nXPixPerMeter:int;\n\t\tprivate var nYPixPerMeter:int;\n\t\tprivate var nColorUsed:uint;\n\t\tprivate var nColorImportant:uint;\n\t\t\n\t\tprivate var nRMask:uint;\n\t\tprivate var nGMask:uint;\n\t\tprivate var nBMask:uint;\n\t\tprivate var nRPos:uint;\n\t\tprivate var nGPos:uint;\n\t\tprivate var nBPos:uint;\n\t\tprivate var nRMax:uint;\n\t\tprivate var nGMax:uint;\n\t\tprivate var nBMax:uint;\n\t\t\n\t\t\n\t\t/**\n\t\t * コンストラクタ\n\t\t */\n\t\tpublic function BMPDecoder() {\n\t\t\tnRPos = 0;\n\t\t\tnGPos = 0;\n\t\t\tnBPos = 0;\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * デコード\n\t\t * \n\t\t * @param デコードしたいBMPファイルのバイナリデータ\n\t\t */\n\t\tpublic function decode( data:ByteArray ):BitmapData {\n\t\t\tbytes = data;\n\t\t\tbytes.endian = Endian.LITTLE_ENDIAN;\n\t\t\tbytes.position = 0;\n\t\t\t\n\t\t\treadFileHeader();\n\t\t\t\n\t\t\tnInfoSize = bytes.readUnsignedInt();\n\t\t\t\n\t\t\tswitch ( nInfoSize ) {\n\t\t\t\tcase BITMAP_CORE_HEADER_SIZE:\n\t\t\t\t\treadCoreHeader();\n\t\t\t\t\tbreak;\n\t\t\t\tcase BITMAP_INFO_HEADER_SIZE:\n\t\t\t\t\treadInfoHeader();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treadExtendedInfoHeader();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tbd = new BitmapData( nWidth, nHeight );\n\t\t\t\n\t\t\tswitch ( nBitsPerPixel ){\n\t\t\t\tcase BIT1:\n\t\t\t\t\treadColorPalette();\n\t\t\t\t\tdecode1BitBMP();\n\t\t\t\t\tbreak;\n\t\t\t\tcase BIT4:\n\t\t\t\t\treadColorPalette();\n\t\t\t\t\tif ( nCompression == COMP_RLE4 ){\n\t\t\t\t\t\tdecode4bitRLE();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdecode4BitBMP();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase BIT8:\n\t\t\t\t\treadColorPalette();\n\t\t\t\t\tif ( nCompression == COMP_RLE8 ){\n\t\t\t\t\t\tdecode8BitRLE();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdecode8BitBMP();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase BIT16:\n\t\t\t\t\treadBitFields();\n\t\t\t\t\tcheckColorMask();\n\t\t\t\t\tdecode16BitBMP();\n\t\t\t\t\tbreak;\n\t\t\t\tcase BIT24:\n\t\t\t\t\tdecode24BitBMP();\n\t\t\t\t\tbreak;\n\t\t\t\tcase BIT32:\n\t\t\t\t\treadBitFields();\n\t\t\t\t\tcheckColorMask();\n\t\t\t\t\tdecode32BitBMP();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new VerifyError(\"invalid bits per pixel : \" + nBitsPerPixel );\n\t\t\t}\n\t\t\t\n\t\t\treturn bd;\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * BITMAP FILE HEADER 読み込み\n\t\t */\n\t\tprivate function readFileHeader():void {\n\t\t\tvar fileHeader:ByteArray = new ByteArray();\n\t\t\tfileHeader.endian = Endian.LITTLE_ENDIAN;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbytes.readBytes( fileHeader, 0, BITMAP_FILE_HEADER_SIZE );\n\t\t\t\t\n\t\t\t\tif ( fileHeader.readUTFBytes( 2 ) != BITMAP_HEADER_TYPE ){\n\t\t\t\t\tthrow new VerifyError(\"invalid bitmap header type\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tnFileSize  = fileHeader.readUnsignedInt();\n\t\t\t\tnReserved1 = fileHeader.readUnsignedShort();\n\t\t\t\tnReserved2 = fileHeader.readUnsignedShort();\n\t\t\t\tnOffbits   = fileHeader.readUnsignedInt();\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid file header\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * BITMAP CORE HEADER 読み込み \n\t\t */\n\t\tprivate function readCoreHeader():void {\n\t\t\tvar coreHeader:ByteArray = new ByteArray();\n\t\t\tcoreHeader.endian = Endian.LITTLE_ENDIAN;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbytes.readBytes( coreHeader, 0, BITMAP_CORE_HEADER_SIZE - 4 );\n\t\t\t\t\n\t\t\t\tnWidth  = coreHeader.readShort();\n\t\t\t\tnHeight = coreHeader.readShort();\n\t\t\t\tnPlains = coreHeader.readUnsignedShort();\n\t\t\t\tnBitsPerPixel = coreHeader.readUnsignedShort();\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid core header\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * BITMAP INFO HEADER 読み込み\n\t\t */\n\t\tprivate function readInfoHeader():void {\n\t\t\tvar infoHeader:ByteArray = new ByteArray();\n\t\t\tinfoHeader.endian = Endian.LITTLE_ENDIAN;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbytes.readBytes( infoHeader, 0, BITMAP_INFO_HEADER_SIZE - 4 );\n\t\t\t\t\n\t\t\t\tnWidth  = infoHeader.readInt();\n\t\t\t\tnHeight = infoHeader.readInt();\n\t\t\t\tnPlains = infoHeader.readUnsignedShort();\n\t\t\t\tnBitsPerPixel = infoHeader.readUnsignedShort();\n\t\t\t\t\n\t\t\t\tnCompression = infoHeader.readUnsignedInt();\n\t\t\t\tnSizeImage = infoHeader.readUnsignedInt();\n\t\t\t\tnXPixPerMeter = infoHeader.readInt();\n\t\t\t\tnYPixPerMeter = infoHeader.readInt();\n\t\t\t\tnColorUsed = infoHeader.readUnsignedInt();\n\t\t\t\tnColorImportant = infoHeader.readUnsignedInt();\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid info header\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t * 拡張 BITMAP INFO HEADER 読み込み\n\t\t */\n\t\tprivate function readExtendedInfoHeader():void {\n\t\t\tvar infoHeader:ByteArray = new ByteArray();\n\t\t\tinfoHeader.endian = Endian.LITTLE_ENDIAN;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tbytes.readBytes( infoHeader, 0, nInfoSize - 4 );\n\t\t\t\t\n\t\t\t\tnWidth  = infoHeader.readInt();\n\t\t\t\tnHeight = infoHeader.readInt();\n\t\t\t\tnPlains = infoHeader.readUnsignedShort();\n\t\t\t\tnBitsPerPixel = infoHeader.readUnsignedShort();\n\t\t\t\t\n\t\t\t\tnCompression = infoHeader.readUnsignedInt();\n\t\t\t\tnSizeImage = infoHeader.readUnsignedInt();\n\t\t\t\tnXPixPerMeter = infoHeader.readInt();\n\t\t\t\tnYPixPerMeter = infoHeader.readInt();\n\t\t\t\tnColorUsed = infoHeader.readUnsignedInt();\n\t\t\t\tnColorImportant = infoHeader.readUnsignedInt();\n\t\t\t\t\n\t\t\t\tif ( infoHeader.bytesAvailable >= 4 ) nRMask = infoHeader.readUnsignedInt();\n\t\t\t\tif ( infoHeader.bytesAvailable >= 4 ) nGMask = infoHeader.readUnsignedInt();\n\t\t\t\tif ( infoHeader.bytesAvailable >= 4 ) nBMask = infoHeader.readUnsignedInt();\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid info header\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * ビットフィールド読み込み\n\t\t */\n\t\tprivate function readBitFields():void {\n\t\t\tif ( nCompression == COMP_RGB ){\n\t\t\t\tif ( nBitsPerPixel == BIT16 ){\n\t\t\t\t\t// RGB555\n\t\t\t\t\tnRMask = 0x00007c00;\n\t\t\t\t\tnGMask = 0x000003e0;\n\t\t\t\t\tnBMask = 0x0000001f;\n\t\t\t\t} else {\n\t\t\t\t\t//RGB888;\n\t\t\t\t\tnRMask = 0x00ff0000;\n\t\t\t\t\tnGMask = 0x0000ff00;\n\t\t\t\t\tnBMask = 0x000000ff;\n\t\t\t\t}\n\t\t\t} else if ( ( nCompression == COMP_BITFIELDS ) && ( nInfoSize < 52 ) ){\n\t\t\t\ttry {\n\t\t\t\t\tnRMask = bytes.readUnsignedInt();\n\t\t\t\t\tnGMask = bytes.readUnsignedInt();\n\t\t\t\t\tnBMask = bytes.readUnsignedInt();\n\t\t\t\t} catch ( e:IOError ) {\n\t\t\t\t\tthrow new VerifyError(\"invalid bit fields\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * カラーパレット読み込み\n\t\t */\n\t\tprivate function readColorPalette():void {\n\t\t\tvar i:int;\n\t\t\tvar len:int = ( nColorUsed > 0 ) ? nColorUsed : Math.pow( 2, nBitsPerPixel );\n\t\t\tpalette = new Array( len );\n\t\t\t\n\t\t\tfor ( i = 0; i < len; ++i ){\n\t\t\t\tpalette[ i ] = bytes.readUnsignedInt();\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * 1bitのBMPデコード\n\t\t */\n\t\tprivate function decode1BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar i:int;\n\t\t\tvar col:int;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\tvar line:int = nWidth / 8;\n\t\t\t\n\t\t\tif ( line % 4 > 0 ){\n\t\t\t\tline = ( ( line / 4 | 0 ) + 1 ) * 4;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\tbytes.readBytes( buf, 0, line );\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; x += 8 ){\n\t\t\t\t\t\tcol = buf.readUnsignedByte();\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor ( i = 0; i < 8; ++i ){\n\t\t\t\t\t\t\tbd.setPixel( x + i, y, palette[ col >> ( 7 - i ) & 0x01 ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * 4bitのRLE圧縮BMPデコード\n\t\t */\n\t\tprivate function decode4bitRLE():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar i:int;\n\t\t\tvar n:int;\n\t\t\tvar col:int;\n\t\t\tvar data:uint;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\t\n\t\t\t\t\twhile ( bytes.bytesAvailable > 0 ){\n\t\t\t\t\t\tn = bytes.readUnsignedByte();\n\t\t\t\t\t\t\n\t\t\t\t\t\tif ( n > 0 ){\n\t\t\t\t\t\t\t// エンコードデータ\n\t\t\t\t\t\t\tdata = bytes.readUnsignedByte();\n\t\t\t\t\t\t\tfor ( i = 0; i < n/2; ++i ){\n\t\t\t\t\t\t\t\tbuf.writeByte( data );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tn = bytes.readUnsignedByte();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif ( n > 0 ){\n\t\t\t\t\t\t\t\t// 絶対モードデータ\n\t\t\t\t\t\t\t\tbytes.readBytes( buf, buf.length, n/2 );\n\t\t\t\t\t\t\t\tbuf.position += n/2;\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif ( n/2 + 1 >> 1 << 1 != n/2 ){\n\t\t\t\t\t\t\t\t\tbytes.readUnsignedByte();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// EOL\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbuf.position = 0;\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; x += 2 ){\n\t\t\t\t\t\tcol = buf.readUnsignedByte();\n\t\t\t\t\t\t\n\t\t\t\t\t\tbd.setPixel( x, y, palette[ col >> 4 ] );\n\t\t\t\t\t\tbd.setPixel( x + 1, y, palette[ col & 0x0f ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * 4bitの非圧縮BMPデコード\n\t\t */\n\t\tprivate function decode4BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar i:int;\n\t\t\tvar col:int;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\tvar line:int = nWidth / 2;\n\t\t\t\n\t\t\tif ( line % 4 > 0 ){\n\t\t\t\tline = ( ( line / 4 | 0 ) + 1 ) * 4;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\tbytes.readBytes( buf, 0, line );\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; x += 2 ){\n\t\t\t\t\t\tcol = buf.readUnsignedByte();\n\t\t\t\t\t\t\n\t\t\t\t\t\tbd.setPixel( x, y, palette[ col >> 4 ] );\n\t\t\t\t\t\tbd.setPixel( x + 1, y, palette[ col & 0x0f ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * 8bitのRLE圧縮BMPデコード\n\t\t */\n\t\tprivate function decode8BitRLE():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar i:int;\n\t\t\tvar n:int;\n\t\t\tvar col:int;\n\t\t\tvar data:uint;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\t\n\t\t\t\t\twhile ( bytes.bytesAvailable > 0 ){\n\t\t\t\t\t\tn = bytes.readUnsignedByte();\n\t\t\t\t\t\t\n\t\t\t\t\t\tif ( n > 0 ){\n\t\t\t\t\t\t\t// エンコードデータ\n\t\t\t\t\t\t\tdata = bytes.readUnsignedByte();\n\t\t\t\t\t\t\tfor ( i = 0; i < n; ++i ){\n\t\t\t\t\t\t\t\tbuf.writeByte( data );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tn = bytes.readUnsignedByte();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif ( n > 0 ){\n\t\t\t\t\t\t\t\t// 絶対モードデータ\n\t\t\t\t\t\t\t\tbytes.readBytes( buf, buf.length, n );\n\t\t\t\t\t\t\t\tbuf.position += n;\n\t\t\t\t\t\t\t\tif ( n + 1 >> 1 << 1 != n ){\n\t\t\t\t\t\t\t\t\tbytes.readUnsignedByte();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// EOL\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbuf.position = 0;\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; ++x ){\n\t\t\t\t\t\tbd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t * 8bitの非圧縮BMPデコード\n\t\t */\n\t\tprivate function decode8BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar i:int;\n\t\t\tvar col:int;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\tvar line:int = nWidth;\n\t\t\t\n\t\t\tif ( line % 4 > 0 ){\n\t\t\t\tline = ( ( line / 4 | 0 ) + 1 ) * 4;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\tbytes.readBytes( buf, 0, line );\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; ++x ){\n\t\t\t\t\t\tbd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t * 16bitのBMPデコード\n\t\t */\n\t\tprivate function decode16BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar col:int;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tfor ( x = 0; x < nWidth; ++x ){\n\t\t\t\t\t\tcol = bytes.readUnsignedShort();\n\t\t\t\t\t\tbd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t * 24bitのBMPデコード\n\t\t */\n\t\tprivate function decode24BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar col:int;\n\t\t\tvar buf:ByteArray = new ByteArray();\n\t\t\tvar line:int = nWidth * 3;\n\t\t\t\n\t\t\tif ( line % 4 > 0 ){\n\t\t\t\tline = ( ( line / 4 | 0 ) + 1 ) * 4;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tbuf.length = 0;\n\t\t\t\t\tbytes.readBytes( buf, 0, line );\n\t\t\t\t\t\n\t\t\t\t\tfor ( x = 0; x < nWidth; ++x ){\n\t\t\t\t\t\tbd.setPixel( x, y, buf.readUnsignedByte() + ( buf.readUnsignedByte() << 8 ) + ( buf.readUnsignedByte() << 16 ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t/**\n\t\t * 32bitのBMPデコード\n\t\t */\n\t\tprivate function decode32BitBMP():void {\n\t\t\tvar x:int;\n\t\t\tvar y:int;\n\t\t\tvar col:int;\n\t\t\t\n\t\t\ttry {\n\t\t\t\tfor ( y = nHeight - 1; y >= 0; --y ){\n\t\t\t\t\tfor ( x = 0; x < nWidth; ++x ){\n\t\t\t\t\t\tcol = bytes.readUnsignedInt();\n\t\t\t\t\t\tbd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( e:IOError ) {\n\t\t\t\tthrow new VerifyError(\"invalid image data\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * カラーマスクチェック\n\t\t */\n\t\tprivate function checkColorMask():void {\n\t\t\tif ( ( nRMask & nGMask ) | ( nGMask & nBMask ) | ( nBMask & nRMask ) ){\n\t\t\t\tthrow new VerifyError(\"invalid bit fields\");\n\t\t\t}\n\t\t\t\n\t\t\twhile ( ( ( nRMask >> nRPos ) & 0x00000001 ) == 0 ){\n\t\t\t\tnRPos++;\n\t\t\t}\n\t\t\twhile ( ( ( nGMask >> nGPos ) & 0x00000001 ) == 0 ){\n\t\t\t\tnGPos++;\n\t\t\t}\n\t\t\twhile ( ( ( nBMask >> nBPos ) & 0x00000001 ) == 0 ){\n\t\t\t\tnBPos++;\n\t\t\t}\n\t\t\t\n\t\t\tnRMax = nRMask >> nRPos;\n\t\t\tnGMax = nGMask >> nGPos;\n\t\t\tnBMax = nBMask >> nBPos;\n\t\t}\n\t\t\n\t\t\n\t\t/**\n\t\t * 情報出力\n\t\t */\n\t\tpublic function traceInfo():void {\n\t\t\ttrace(\"---- FILE HEADER ----\");\n\t\t\ttrace(\"nFileSize: \" + nFileSize );\n\t\t\ttrace(\"nReserved1: \" + nReserved1 );\n\t\t\ttrace(\"nReserved2: \" + nReserved2 );\n\t\t\ttrace(\"nOffbits: \" + nOffbits );\n\t\t\t\n\t\t\ttrace(\"---- INFO HEADER ----\");\n\t\t\ttrace(\"nWidth: \" + nWidth );\n\t\t\ttrace(\"nHeight: \" + nHeight );\n\t\t\ttrace(\"nPlains: \" + nPlains );\n\t\t\ttrace(\"nBitsPerPixel: \" + nBitsPerPixel );\n\t\t\t\n\t\t\tif ( nInfoSize >= 40 ){\n\t\t\t\ttrace(\"nCompression: \" + nCompression );\n\t\t\t\ttrace(\"nSizeImage: \" + nSizeImage );\n\t\t\t\ttrace(\"nXPixPerMeter: \" + nXPixPerMeter );\n\t\t\t\ttrace(\"nYPixPerMeter: \" + nYPixPerMeter );\n\t\t\t\ttrace(\"nColorUsed: \" + nColorUsed );\n\t\t\t\ttrace(\"nColorUsed: \" + nColorImportant );\n\t\t\t}\n\t\t\t\n\t\t\tif ( nInfoSize >= 52 ){\n\t\t\t\ttrace(\"nRMask: \" + nRMask.toString( 2 ) );\n\t\t\t\ttrace(\"nGMask: \" + nGMask.toString( 2 ) );\n\t\t\t\ttrace(\"nBMask: \" + nBMask.toString( 2 ) );\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/utils/Base64.as",
    "content": "/* \n* Copyright (C) 2012 Jean-Philippe Auclair \n* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php \n* Base64 library for ActionScript 3.0. \n* By: Jean-Philippe Auclair : http://jpauclair.net \n* Based on article: http://jpauclair.net/2010/01/09/base64-optimized-as3-lib/ \n* Benchmark: \n* This version: encode: 260ms decode: 255ms \n* Blog version: encode: 322ms decode: 694ms \n* as3Crypto encode: 6728ms decode: 4098ms \n* \n* Encode: com.sociodox.utils.Base64 is 25.8x faster than as3Crypto Base64 \n* Decode: com.sociodox.utils.Base64 is 16x faster than as3Crypto Base64 \n* \n* Optimize & Profile any Flash content with TheMiner ( http://www.sociodox.com/theminer ) \n*/  \npackage com.utils  \n{  \n\timport flash.utils.ByteArray;  \n\t\n\tpublic class Base64  \n\t{  \n\t\tprivate static const _encodeChars:Vector.<int> = InitEncoreChar();  \n\t\tprivate static const _decodeChars:Vector.<int> = InitDecodeChar();  \n\t\t\n\t\tpublic static function encode(data:ByteArray):String  \n\t\t{  \n\t\t\tvar out:ByteArray = new ByteArray();  \n\t\t\t//Presetting the length keep the memory smaller and optimize speed since there is no \"grow\" needed  \n\t\t\tout.length = (2 + data.length - ((data.length + 2) % 3)) * 4 / 3; //Preset length //1.6 to 1.5 ms  \n\t\t\tvar i:int = 0;  \n\t\t\tvar r:int = data.length % 3;  \n\t\t\tvar len:int = data.length - r;  \n\t\t\tvar c:uint; //read (3) character AND write (4) characters  \n\t\t\tvar outPos:int = 0;  \n\t\t\twhile (i < len)  \n\t\t\t{  \n\t\t\t\t//Read 3 Characters (8bit * 3 = 24 bits)  \n\t\t\t\tc = data[int(i++)] << 16 | data[int(i++)] << 8 | data[int(i++)];  \n\t\t\t\t\n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 18)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 12 & 0x3f)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 6 & 0x3f)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c & 0x3f)];  \n\t\t\t}  \n\t\t\t\n\t\t\tif (r == 1) //Need two \"=\" padding  \n\t\t\t{  \n\t\t\t\t//Read one char, write two chars, write padding  \n\t\t\t\tc = data[int(i)];  \n\t\t\t\t\n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 2)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int((c & 0x03) << 4)];  \n\t\t\t\tout[int(outPos++)] = 61;  \n\t\t\t\tout[int(outPos++)] = 61;  \n\t\t\t}  \n\t\t\telse if (r == 2) //Need one \"=\" padding  \n\t\t\t{  \n\t\t\t\tc = data[int(i++)] << 8 | data[int(i)];  \n\t\t\t\t\n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 10)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int(c >>> 4 & 0x3f)];  \n\t\t\t\tout[int(outPos++)] = _encodeChars[int((c & 0x0f) << 2)];  \n\t\t\t\tout[int(outPos++)] = 61;  \n\t\t\t}  \n\t\t\t\n\t\t\treturn out.readUTFBytes(out.length);  \n\t\t}  \n\t\t\n\t\tpublic static function decode(str:String):ByteArray  \n\t\t{  \n\t\t\tvar c1:int;  \n\t\t\tvar c2:int;  \n\t\t\tvar c3:int;  \n\t\t\tvar c4:int;  \n\t\t\tvar i:int = 0;  \n\t\t\tvar len:int = str.length;  \n\t\t\t\n\t\t\tvar byteString:ByteArray = new ByteArray();  \n\t\t\tbyteString.writeUTFBytes(str);  \n\t\t\tvar outPos:int = 0;  \n\t\t\twhile (i < len)  \n\t\t\t{  \n\t\t\t\t//c1  \n\t\t\t\tc1 = _decodeChars[int(byteString[i++])];  \n\t\t\t\tif (c1 == -1)  \n\t\t\t\t\tbreak;  \n\t\t\t\t\n\t\t\t\t//c2  \n\t\t\t\tc2 = _decodeChars[int(byteString[i++])];  \n\t\t\t\tif (c2 == -1)  \n\t\t\t\t\tbreak;  \n\t\t\t\t\n\t\t\t\tbyteString[int(outPos++)] = (c1 << 2) | ((c2 & 0x30) >> 4);  \n\t\t\t\t\n\t\t\t\t//c3  \n\t\t\t\tc3 = byteString[int(i++)];  \n\t\t\t\tif (c3 == 61)  \n\t\t\t\t{  \n\t\t\t\t\tbyteString.length = outPos  \n\t\t\t\t\treturn byteString;  \n\t\t\t\t}  \n\t\t\t\t\n\t\t\t\tc3 = _decodeChars[int(c3)];  \n\t\t\t\tif (c3 == -1)  \n\t\t\t\t\tbreak;  \n\t\t\t\t\n\t\t\t\tbyteString[int(outPos++)] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);  \n\t\t\t\t\n\t\t\t\t//c4  \n\t\t\t\tc4 = byteString[int(i++)];  \n\t\t\t\tif (c4 == 61)  \n\t\t\t\t{  \n\t\t\t\t\tbyteString.length = outPos  \n\t\t\t\t\treturn byteString;  \n\t\t\t\t}  \n\t\t\t\t\n\t\t\t\tc4 = _decodeChars[int(c4)];  \n\t\t\t\tif (c4 == -1)  \n\t\t\t\t\tbreak;  \n\t\t\t\t\n\t\t\t\tbyteString[int(outPos++)] = ((c3 & 0x03) << 6) | c4;  \n\t\t\t}  \n\t\t\tbyteString.length = outPos  \n\t\t\treturn byteString;  \n\t\t}  \n\t\t\n\t\tpublic static function InitEncoreChar():Vector.<int>  \n\t\t{  \n\t\t\tvar encodeChars:Vector.<int> = new Vector.<int>(64, true);  \n\t\t\t\n\t\t\t// We could push the number directly  \n\t\t\t// but I think it's nice to see the characters (with no overhead on encode/decode)  \n\t\t\tvar chars:String = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";  \n\t\t\tfor (var i:int = 0; i < 64; i++)  \n\t\t\t{  \n\t\t\t\tencodeChars[i] = chars.charCodeAt(i);  \n\t\t\t}  \n\t\t\t\n\t\t\treturn encodeChars;  \n\t\t}  \n\t\t\n\t\tpublic static function InitDecodeChar():Vector.<int>  \n\t\t{  \n\t\t\t\n\t\t\tvar decodeChars:Vector.<int> = new <int>[  \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,   \n\t\t\t\t52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,   \n\t\t\t\t15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,   \n\t\t\t\t41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   \n\t\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];  \n\t\t\t\n\t\t\treturn decodeChars;  \n\t\t}  \n\t\t\n\t}  \n}  "
  },
  {
    "path": "flash/src/com/utils/BinaryReader.as",
    "content": "/**\n *\n * Copyright 2011, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\npackage com.utils {\n\timport flash.utils.ByteArray;\n\timport flash.utils.Endian;\n\t\n\tpublic class BinaryReader extends ByteArray {\n\t\t\n\t\tpublic function init(binData:ByteArray):void {\n\t\t\tclear();\n\t\t\tendian = Endian.BIG_ENDIAN;\n\t\t\twriteBytes(binData);\n\t\t}\n\t\t\n\t\tpublic function II(... args):* {\n\t\t\tif (!args.length)\n\t\t\t\treturn endian == Endian.LITTLE_ENDIAN ? true : false;\n\t\t\telse\n\t\t\t\tendian = args[0] == true ? Endian.LITTLE_ENDIAN : Endian.BIG_ENDIAN;\n\t\t}\n\t\t\n\t\tpublic function SEGMENT(... args):ByteArray {\n\t\t\tvar seg:ByteArray = new ByteArray();\n\t\t\t\n\t\t\tswitch (args.length) {\n\t\t\t\t\n\t\t\t\tcase 1: \n\t\t\t\t\tposition = args[0];\n\t\t\t\t\treadBytes(seg, 0);\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tcase 2:\n\t\t\t\t\tposition = args[0];\n\t\t\t\t\treadBytes(seg, 0, args[1]);\n\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\tcase 3:\n\t\t\t\t\tseg.writeBytes(this, 0, args[0]);\n\t\t\t\t\tif (args[2] is ByteArray) {\n\t\t\t\t\t\tseg.writeBytes(args[2]);\n\t\t\t\t\t}\n\t\t\t\t\tseg.writeBytes(this, args[0] + args[1]);\n\t\t\t\t\tclear();\n\t\t\t\t\twriteBytes(seg);\n\t\t\t\t\tseg.clear();\n\t\t\t\t\treturn null;\n\t\t\t\t\n\t\t\t\tdefault:\n\t\t\t\t\tseg.writeBytes(this);\n\t\t\t}\n\t\t\t\n\t\t\treturn seg;\n\t\t}\n\t\t\n\t\tpublic function BYTE(idx:int):uint {\n\t\t\tposition = idx;\n\t\t\treturn readUnsignedByte();\n\t\t}\n\t\t\n\t\tpublic function SHORT(idx:int):uint {\n\t\t\tposition = idx;\n\t\t\treturn readUnsignedShort();\n\t\t}\n\t\t\n\t\tpublic function LONG(idx:int, ... args):* {\n\t\t\tposition = idx;\n\t\t\tif (!args.length) \n\t\t\t\treturn readUnsignedInt();\n\t\t\telse\n\t\t\t\twriteUnsignedInt(args[0]);\n\t\t}\n\t\t\n\t\tpublic function SLONG(idx:uint):int { \n\t\t\tposition = idx;\n\t\t\treturn readInt(); \n\t\t}\n\t\t\n\t\tpublic function STRING(idx:uint, size:uint):String {\n\t\t\tposition = idx;\n\t\t\treturn readUTFBytes(size);\n\t\t}\n\t\t\n\t\t\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "flash/src/com/utils/Buffer.as",
    "content": "package com.utils\n{\n\timport flash.net.FileReference;\n\timport flash.utils.ByteArray;\n\n\tpublic class Buffer\n\t{\t\t\n\t\tprivate var _data:ByteArray;\n\t\tpublic function get data() : ByteArray {\n\t\t\tif (_fileRef && _fileRef.data) {\n\t\t\t\t_data = _fileRef.data\n\t\t\t}\n\t\t\treturn _data;\n\t\t}\n\t\t\t\t\n\t\tprivate var _fileRef:FileReference;\n\t\tpublic function get fileRef() : FileReference {\n\t\t\treturn _fileRef;\n\t\t}\n\t\t\n\t\tpublic function get size() : uint {\n\t\t\tif (_fileRef) {\n\t\t\t\treturn _fileRef.size;\n\t\t\t} else if (_data) {\n\t\t\t\treturn _data.length;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\t\n\t\tpublic var refs:uint = 1; // counting references to this object\n\t\t\n\t\tpublic function Buffer(source:*)\n\t\t{\t\t\t\n\t\t\tif (source is FileReference) {\n\t\t\t\t_fileRef = source;\n\t\t\t} else if (source is ByteArray) {\n\t\t\t\t_data = source;\n\t\t\t} else {\n\t\t\t\t// throw wrong type exception\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function isEmpty() : Boolean \n\t\t{\n\t\t\treturn !_data || !_data.length;\n\t\t}\n\t\t\n\t\t\n\t\tpublic function purge() : void {\n\t\t\tif (_fileRef && _fileRef.data.length) {\n\t\t\t\t_fileRef.data.clear();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic function destroy() : void {\n\t\t\tif (_data && _data.length) {\n\t\t\t\t_data.clear();\n\t\t\t}\n\t\t\tpurge();\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/utils/OEventDispatcher.as",
    "content": "package com.utils\n{\n\timport flash.events.EventDispatcher;\n\t\n\tpublic class OEventDispatcher extends EventDispatcher\n\t{\n\t\tprivate var _events:Object = [];\n\t\t\n\t\tpublic override function addEventListener(type:String, callback:Function, useCapture:Boolean = false, priority:int = 0, useWeak:Boolean = false) : void\n\t\t{\n\t\t\tif (hasListener(type, callback, useCapture)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t_events.push({\n\t\t\t\ttype: type,\n\t\t\t\tcallback: callback,\n\t\t\t\tuseCapture: useCapture\n\t\t\t});\n\t\t\t\n\t\t\tsuper.addEventListener(type, callback, useCapture, priority, useWeak); \n\t\t}\n\t\t\n\t\tpublic override function removeEventListener(type:String, callback:Function, useCapture:Boolean=false):void\n\t\t{\t\t\t\n\t\t\tvar index:uint, e:Object;\n\t\t\t\n\t\t\tindex = hasListener(type, callback, useCapture);\n\t\t\tif (index === false) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\te = _events.splice(index, 1);\n\t\t\tsuper.removeEventListener(e.type, e.callback, e.useCapture);\n\t\t}\n\t\t\n\t\t\n\t\tpublic function hasListener(type:String, callback:Function = null, useCapture:Boolean = false): *\n\t\t{\n\t\t\tfor (var i:uint = 0, length:uint = _events.length; i < length; i++) {\n\t\t\t\tif (type == _events[i].type && callback == _events[i].callback && _events[i].useCapture == useCapture) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t} \n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t/* anyone any idea why Flash doesn't have a call like this?.. */\n\t\tpublic function removeAllEventsListeners() : void\n\t\t{\n\t\t\tfor (var i:uint = 0, length:uint = _events.length; i < length; i++) { \n\t\t\t\tsuper.removeEventListener(_events[i].type, _events[i].callback, _events[i].useCapture); // call super method directly\n\t\t\t}\n\t\t\t_events = [];\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/utils/URLStreamProgress.as",
    "content": "package com.utils\n{\n\timport com.events.URLStreamProgressEvent;\n\t\n\timport flash.events.Event;\n\timport flash.events.EventDispatcher;\n\timport flash.events.IEventDispatcher;\n\timport flash.events.IOErrorEvent;\n\timport flash.events.ProgressEvent;\n\timport flash.events.SecurityErrorEvent;\n\timport flash.events.TimerEvent;\n\timport flash.external.ExternalInterface;\n\timport flash.net.URLRequest;\n\timport flash.net.URLRequestHeader;\n\timport flash.net.URLRequestMethod;\n\timport flash.net.URLStream;\n\timport flash.utils.ByteArray;\n\timport flash.utils.Timer;\n\t\n\t\n\tpublic class URLStreamProgress extends OEventDispatcher\n\t{\n\t\tprivate var _options:Object = {};\n\t\t\n\t\tpublic static var speed:uint = 512000; // number of bytes per 500ms\n\t\t\n\t\tpublic static var rate:uint = 50;\n\t\t\n\t\tprivate var _progressTimer:Timer;\n\t\t\n\t\tprivate var _bytesLoaded:uint = 0;\n\t\t\n\t\tprivate var _bytesTotal:uint = 0;\n\t\t\n\t\t\n\t\tpublic function URLStreamProgress(options:Object = null) \n\t\t{\n\t\t\t_options = Utils.extend({\n\t\t\t\turl: 'webuploader' + new Date().getTime(),\n\t\t\t\tsize: 307200\n\t\t\t}, options);\t\n\t\t}\n\t\t\n\t\t\n\t\tpublic static function calculateSpeed(size:uint, time:uint) : void\n\t\t{\t\t\t\n\t\t\tURLStreamProgress.speed = Math.ceil(size * URLStreamProgress.rate / Math.max(time - 30, 3));\t\t\t\n\t\t}\n\t\t\n\t\t\n\t\tpublic function start(bytesTotal:uint) : void\n\t\t{\t\t\t\n\t\t\tvar onProgress:Function;\n\t\t\t\n\t\t\tif (!URLStreamProgress.speed) {\n\t\t\t\taddEventListener(URLStreamProgressEvent.PROBE_COMPLETE, function() : void { \n\t\t\t\t\tstart(bytesTotal); \n\t\t\t\t}, false, 0, true);\n\t\t\t\tprobe();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tdispatchEvent(new Event(Event.OPEN));\n\t\t\t\n\t\t\t_bytesTotal = bytesTotal;\n\t\t\t\n\t\t\tonProgress = function() : void {\n\t\t\t\t_bytesLoaded += URLStreamProgress.speed;\n\t\t\t\tif (_bytesLoaded < _bytesTotal) {\n\t\t\t\t\tdispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, _bytesLoaded, _bytesTotal));\n\t\t\t\t} else {\n\t\t\t\t\tdispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, _bytesTotal, _bytesTotal));\n\t\t\t\t\tstop();\n\t\t\t\t\tdispatchEvent(new Event(Event.COMPLETE));\n\t\t\t\t}\n\t\t\t};\n\t\t\t_progressTimer = new Timer(URLStreamProgress.rate);\n\t\t\t_progressTimer.addEventListener(TimerEvent.TIMER, onProgress, false, 0, true);\n\t\t\t_progressTimer.start();\n\t\t}\n\t\t\n\t\t\n\t\tpublic function stop() : void \n\t\t{\n\t\t\tif (_progressTimer) {\n\t\t\t\t_progressTimer.stop();\t\n\t\t\t\t_bytesLoaded = _bytesTotal = 0;\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t\tpublic function probe() : void\n\t\t{\n\t\t\tvar stream:URLStream, request:URLRequest,\n\t\t\t\tonComplete:Function,\n\t\t\t\tstart:Date, end:Date;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\trequest = new URLRequest(_options.url);\n\t\t\trequest.method = URLRequestMethod.POST;\n\t\t\trequest.data = _generateByteArray(_options.size);\n\t\t\t\n\t\t\tstart = new Date;\n\t\t\t\n\t\t\tonComplete = function(e:* = null) : void {\n\t\t\t\tend = new Date;\n\t\t\t\tURLStreamProgress.calculateSpeed(_options.size, end.time - start.time);\n\t\t\t\tdispatchEvent(new URLStreamProgressEvent(URLStreamProgressEvent.PROBE_COMPLETE));\n\t\t\t};\n\t\t\t\n\t\t\tstream = new URLStream;\n\t\t\tstream.addEventListener(Event.COMPLETE, onComplete, false, 0, true);\n\t\t\tstream.addEventListener(IOErrorEvent.IO_ERROR, onComplete, false, 0, true);\n\t\t\tstream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onComplete, false, 0, true);\n\t\t\tstream.load(request);\n\t\t\t\n\t\t}\n\t\t\n\t\t\n\t\tprivate function _generateByteArray(size:uint = 307200) : ByteArray\n\t\t{\n\t\t\tvar ba:ByteArray, replicator:ByteArray;\n\t\t\t\n\t\t\tba = new ByteArray;\n\t\t\t\n\t\t\treplicator = new ByteArray;\n\t\t\treplicator.writeByte(0xE0);\n\t\t\treplicator.writeByte(0xA5);\n\t\t\treplicator.writeByte(0x90);\n\t\t\t\n\t\t\tsize = Math.ceil(size / replicator.length);\n\t\t\t\t\t\t\n\t\t\tfor (var i:uint = 0; i < size; i++) {\n\t\t\t\tba.writeBytes(replicator);\n\t\t\t}\n\t\t\t\n\t\t\treturn ba;\n\t\t}\n\t}\n}"
  },
  {
    "path": "flash/src/com/utils/Utils.as",
    "content": "package com.utils\n{\n    public class Utils\n    {\n        static public function trim(str:String) : String\n        {\n            if (!str) return '';\n\n            return str.replace(/^\\s+/, '').replace(/\\s+$/, '');\n        }\n\n\n        static public function sanitize(str:String) : String\n        {\n            // allow only [a-zA-Z0-9_]\n            return str.replace(/[^\\w]/g, '');\n        }\n\n\n        static private var _guidCounter:uint = 0;\n\n        static public function guid(prefix:String = '') : String\n        {\n            var guid:String = new Date().getTime().toString(32), i:int;\n\n            for (i = 0; i < 5; i++) {\n                guid += Math.floor(Math.random() * 65535).toString(32);\n            }\n\n            return (prefix || 'o_') + guid + (_guidCounter++).toString(32);\n        }\n\n\n        static public function extend(obj1:*, obj2:*, strict:Boolean = false, propsOnly:Boolean = true) : *\n        {\n            if (!obj1)\n                obj1 = {};\n\n            for (var key:String in obj2) {\n                if (propsOnly && obj2[key] is Function)\n                    continue;\n\n                if (strict) {\n                    if (obj1.hasOwnProperty(key))\n                        obj1[key] = obj2[key];\n                } else {\n                    obj1[key] = obj2[key];\n                }\n            }\n            return obj1;\n        }\n\n\n        static public function isTrue(value:String) : Boolean\n        {\n            if (!value) return false;\n\n            return ['1', 'true'].indexOf(value.toLowerCase()) !== -1 ? true : false;\n        }\n\n\n        static public function isFalse(value:String) : Boolean\n        {\n            return !Utils.isTrue(value);\n        }\n\n\n        static public function toHHIISS(seconds:Number) : String\n        {\n            var h:Number, i:Number, s:Number,\n            HHIISS:String,\n\n            padZero:Function = function(number:uint) : String {\n                return number > 9 ? number.toString() : '0' + number.toString();\n            };\n\n            s = seconds % 60;\n            i = Math.floor((seconds % 3600) / 60);\n            h = Math.floor(seconds / (3600));\n\n            HHIISS = padZero(s);\n            HHIISS = (i > 0 ? padZero(i) : '00') + ':' + HHIISS;\n            if (h > 0)\n                HHIISS = padZero(h) + ':' + HHIISS;\n\n            return HHIISS;\n        }\n\n        static public function isEmptyObj(o:Object) : Boolean {\n            var prop:*;\n\n            if (!o) {\n                return true;\n            }\n\n            for (prop in o) {\n                return false;\n            }\n            return true;\n        }\n\n    }\n}"
  },
  {
    "path": "flash/src/com/utils/Zhenpin.as",
    "content": "package com.utils\n{\n\timport flash.events.Event;\n\timport flash.events.IOErrorEvent;\n\timport flash.media.Sound;\n\timport flash.media.SoundChannel;\n\t\n\tpublic class Zhenpin\n\t{\n\t\tprivate static var sound:Sound;\n\t\tprivate static var st:SoundChannel;\n\t\tpublic static var hasStart:Boolean = false;\n\t\tpublic function Zhenpin()\n\t\t{\n\t\t}\n\t\t\n\t\tpublic static function start():void{\n\t\t\tsound = new BlankSound();\n\t\t\tsound.addEventListener(IOErrorEvent.IO_ERROR, onError);\n\t\t\tst = sound.play();\n\t\t\tst.addEventListener(Event.SOUND_COMPLETE, onComp);\n\t\t\t\n\t\t\thasStart = true;\n\t\t}\n\t\t\n\t\tprivate static function onComp(e:Event):void{\n\t\t\tst.removeEventListener(Event.SOUND_COMPLETE, onComp);\n\t\t\tst = sound.play();\n\t\t\tst.addEventListener(Event.SOUND_COMPLETE, onComp);\n\t\t}\n\t\t\n\t\tprivate static function onError(e:Object):void{\n\t\t}\n\t}\n}"
  },
  {
    "path": "jekyll/_config.yml",
    "content": "name: Web Uploader\nmarkdown: redcarpet\nredcarpet:\n  extensions:\n    - with_toc_data\n    - html_toc\nhighlighter: true\nbaseurl: /webuploader\nexclude:\n  - node_modules\n  - README.md\n"
  },
  {
    "path": "jekyll/_includes/comments.html",
    "content": "{% if page.commentIssueId %}\n\n<div id=\"ghComments\" class=\"comments\" data-issue-id=\"{{page.commentIssueId}}\">\n    <h2>评论</h2>\n    <div id=\"header\">想要留下评论，请在<a href=\"https://github.com/fex-team/webuploader/issues/{{page.commentIssueId}}\">此Github issue</a> 页面完成操作，谢谢！</div>\n\n    <div class=\"alert alert-warning\">\n        <p>如果是讨论技术问题或者报告bug，请最好还是新建一个issue展开讨论，以免你提出的问题石沉大海。</p>\n        <p>点此进入<a href=\"https://github.com/fex-team/webuploader/issues\">issues列表页</a>。</p></div>\n</div>\n\n{% endif %}"
  },
  {
    "path": "jekyll/_includes/footer.html",
    "content": "<div id=\"footer\" class=\"footer\">\n    <div class=\"footer-inner container\">\n        <div class=\"row\">\n            <div class=\"col-md-4\">\n                <p class=\"copyright\">Webuploader由<a href=\"https://github.com/fex-team\">fex-team</a>团队负责维护</p>\n                <p>&copy;2013-2018 Baidu Fex Team</p>\n            </div>\n            <div class=\"col-md-4\">\n                <!-- <p>友情链接</p> -->\n                <ul class=\"friends-links\">\n                    <li><a href=\"http://fis.baidu.com/\" title=\"前端集成解决方案\">Fis</a></li>\n                    <li><a href=\"http://gmu.baidu.com\" title=\"基于zepto的mobile UI组件库\">GMU</a></li>\n                    <li><a href=\"http://ueditor.baidu.com/website/\" title=\"UEditor是由百度web前端研发部开发所见即所得富文本web编辑器，具有轻量，可定制，注重用户体验等特点，开源基于MIT协议，允许自由使用和修改代码...\">Ueditor</a></li>\n                </ul>\n            </div>\n            <div class=\"col-md-4\">\n                <div class=\"weixin\">\n                    <img src=\"{{ site.baseurl }}/images/qrcode.jpg\" alt=\"...\" class=\"img-rounded weixin-img\" />\n                    <p>微信公共帐号</p>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>"
  },
  {
    "path": "jekyll/_includes/header.html",
    "content": "<div class=\"navbar navbar-fixed-top navbar-inverse\" role=\"navigation\">\n    <div class=\"container\">\n        <div class=\"navbar-header\">\n            <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-collapse\">\n                <span class=\"sr-only\">Toggle navigation</span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n            </button>\n            <a class=\"navbar-brand logo{% if page.url == '/index.html' %} active{% endif%}\" href=\"{{ site.baseurl }}/\">\n                <span class=\"fa fa-cloud-upload\"></span>WebUploader</a>\n        </div>\n        <div class=\"collapse navbar-collapse\">\n            <ul class=\"nav navbar-nav\">{% for weight in (1..10) %} {% for post in site.pages %} {% if post.group == 'nav' and post.weight == weight %} {% if page.url == post.url %}<li class=\"active\"><a href=\"{{ site.baseurl }}{{post.url}}\" class=\"active\">{{post.navName}}</a></li>{% else %}<li><a href=\"{{ site.baseurl }}{{ post.url }}\">{{ post.navName }}</a></li>{% endif %}{% endif %}{% endfor %}{% endfor %}\n                <li><a target=\"_blank\" href=\"https://github.com/fex-team/webuploader/issues?labels=faq&amp;page=1&amp;state=open\">FAQ</a></li>\n            </ul>\n\n            <ul class=\"nav navbar-nav navbar-right\">\n                <li><a target=\"_blank\" href=\"https://github.com/fex-team/webuploader\">Github</a></li>\n            </ul>\n        </div>\n        <!-- /.nav-collapse -->\n    </div>\n    <!-- /.container -->\n</div>\n<!-- /.navbar -->"
  },
  {
    "path": "jekyll/_layouts/default.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{% if page.url == '/index.html' %}\n            {{ page.title }}\n        {% else %}\n            {{ page.title }} - {{ site.name }}\n        {% endif %}</title>\n\n    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->\n    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\n    <!--[if lt IE 9]>\n        <script src=\"https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js\"></script>\n        <script src=\"https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js\"></script>\n    <![endif]-->\n    <link rel=\"shortcut icon\" href=\"{{ site.baseurl }}/images/favicon.ico\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}/css/bootstrap.min.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}/css/bootstrap-theme.min.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}/css/font-awesome.min.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}/css/syntax.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}/css/style.css\">\n    {% for link in page.styles %}\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.baseurl }}{{link}}\">\n    {% endfor %}\n</head>\n\n<body>\n    <div id=\"wrapper\">\n        {% include header.html %} <div class=\"page-body\">{{ content }}</div> {% include footer.html %}\n    </div>\n    <script type=\"text/javascript\">\n    // 添加全局站点信息\n    var BASE_URL = '{{site.baseurl}}';\n    </script>\n    <script type=\"text/javascript\" src=\"{{ site.baseurl }}/js/jquery-1.10.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"{{ site.baseurl }}/js/bootstrap.min.js\"></script>\n    <script type=\"text/javascript\" src=\"{{ site.baseurl }}/js/global.js\"></script>\n    {% for script in page.scripts %}\n        <script type=\"text/javascript\" src=\"{{ site.baseurl }}{{script}}\"></script>\n    {% endfor %}\n    <script type=\"text/javascript\">\n    var _bdhmProtocol = ((\"https:\" == document.location.protocol) ? \" https://\" : \" http://\");\n    document.write(unescape(\"%3Cscript src='\" + _bdhmProtocol + \"hm.baidu.com/h.js%3F67c4841095cbee8275705e1f6224a3c7' type='text/javascript'%3E%3C/script%3E\"));\n    </script>\n</body>\n</html>\n"
  },
  {
    "path": "jekyll/_layouts/post.html",
    "content": "---\nlayout: default\n---\n\n{% if page.bannerTitle %}\n<!-- Main jumbotron for a primary marketing message or call to action -->\n<div class=\"jumbotron\">\n    <div class=\"container\">\n        <h1>{{ page.bannerTitle }}</h1>\n        {% for paragraph in page.bannerContents %}\n        <p>{{ paragraph }}</p>\n        {% endfor %}\n    </div>\n</div>\n{% endif %}\n\n<div id=\"post-container\" class=\"container\">\n    {% capture toc %}{{ page.content | fancytoc }}{% endcapture %}\n\n    {% if page.noToc == nil and toc != '' %}\n\n    <div class=\"row\">\n        <div class=\"col-md-3\">\n            <div class=\"post-toc\">{{ toc }}</div>\n        </div>\n        <div class=\"col-md-9\">\n\n            {% if page.hideTitle == nil %}\n            <div class=\"page-header\">\n              <h1>{{ page.title }}</h1>\n            </div>\n            {% endif %}\n\n            <div class=\"page-container\">\n                {{ content }}\n            </div>\n\n            {% include comments.html %}\n        </div>\n    </div>\n\n    {% else %}\n\n    {% if page.hideTitle == nil %}\n    <div class=\"page-header\">\n      <h1>{{ page.title }}</h1>\n    </div>\n    {% endif %}\n\n    <div class=\"page-container\">\n        {{ content }}\n    </div>\n\n    {% include comments.html %}\n\n    {% endif %}\n</div>"
  },
  {
    "path": "jekyll/_plugins/fancytoc.rb",
    "content": "\nmodule Jekyll\n  module FancyToCFilter\n    def fancytoc(input)\n      converter = Redcarpet::Markdown.new(Redcarpet::Render::HTML_TOC)\n      toc = converter.render(input)\n      toc.gsub(/<ul>/, '<ul class=\"nav\">') unless toc.empty?\n    end\n  end\nend\n\nLiquid::Template.register_filter(Jekyll::FancyToCFilter)"
  },
  {
    "path": "jekyll/css/bootstrap-theme.css",
    "content": "/*!\n * Bootstrap v3.0.3 (http://getbootstrap.com)\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn:active,\n.btn.active {\n  background-image: none;\n}\n\n.btn-default {\n  text-shadow: 0 1px 0 #fff;\n  background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n  background-repeat: repeat-x;\n  border-color: #dbdbdb;\n  border-color: #ccc;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-default:hover,\n.btn-default:focus {\n  background-color: #e0e0e0;\n  background-position: 0 -15px;\n}\n\n.btn-default:active,\n.btn-default.active {\n  background-color: #e0e0e0;\n  border-color: #dbdbdb;\n}\n\n.btn-primary {\n  background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);\n  background-repeat: repeat-x;\n  border-color: #2b669a;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-primary:hover,\n.btn-primary:focus {\n  background-color: #2d6ca2;\n  background-position: 0 -15px;\n}\n\n.btn-primary:active,\n.btn-primary.active {\n  background-color: #2d6ca2;\n  border-color: #2b669a;\n}\n\n.btn-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n  background-repeat: repeat-x;\n  border-color: #3e8f3e;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-success:hover,\n.btn-success:focus {\n  background-color: #419641;\n  background-position: 0 -15px;\n}\n\n.btn-success:active,\n.btn-success.active {\n  background-color: #419641;\n  border-color: #3e8f3e;\n}\n\n.btn-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n  background-repeat: repeat-x;\n  border-color: #e38d13;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-warning:hover,\n.btn-warning:focus {\n  background-color: #eb9316;\n  background-position: 0 -15px;\n}\n\n.btn-warning:active,\n.btn-warning.active {\n  background-color: #eb9316;\n  border-color: #e38d13;\n}\n\n.btn-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n  background-repeat: repeat-x;\n  border-color: #b92c28;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-danger:hover,\n.btn-danger:focus {\n  background-color: #c12e2a;\n  background-position: 0 -15px;\n}\n\n.btn-danger:active,\n.btn-danger.active {\n  background-color: #c12e2a;\n  border-color: #b92c28;\n}\n\n.btn-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n  background-repeat: repeat-x;\n  border-color: #28a4c9;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.btn-info:hover,\n.btn-info:focus {\n  background-color: #2aabd2;\n  background-position: 0 -15px;\n}\n\n.btn-info:active,\n.btn-info.active {\n  background-color: #2aabd2;\n  border-color: #28a4c9;\n}\n\n.thumbnail,\n.img-thumbnail {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  background-color: #e8e8e8;\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  background-color: #357ebd;\n  background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);\n}\n\n.navbar-default {\n  background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n  background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n  background-repeat: repeat-x;\n  border-radius: 4px;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n\n.navbar-default .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);\n  background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n\n.navbar-inverse {\n  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n  background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.navbar-inverse .navbar-nav > .active > a {\n  background-image: -webkit-linear-gradient(top, #222222 0%, #282828 100%);\n  background-image: linear-gradient(to bottom, #222222 0%, #282828 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);\n  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n          box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n.alert {\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.alert-success {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n  background-repeat: repeat-x;\n  border-color: #b2dba1;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n}\n\n.alert-info {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n  background-repeat: repeat-x;\n  border-color: #9acfea;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n}\n\n.alert-warning {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n  background-repeat: repeat-x;\n  border-color: #f5e79e;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n}\n\n.alert-danger {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n  background-repeat: repeat-x;\n  border-color: #dca7a7;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n}\n\n.progress {\n  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n\n.progress-bar {\n  background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);\n}\n\n.progress-bar-success {\n  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n  background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n\n.progress-bar-info {\n  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n  background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n\n.progress-bar-warning {\n  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n  background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n\n.progress-bar-danger {\n  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n  background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n\n.list-group {\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 #3071a9;\n  background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);\n  background-repeat: repeat-x;\n  border-color: #3278b3;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);\n}\n\n.panel {\n  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.panel-default > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n  background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n\n.panel-primary > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);\n  background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);\n}\n\n.panel-success > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n  background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n\n.panel-info > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n  background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n\n.panel-warning > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n  background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n\n.panel-danger > .panel-heading {\n  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n  background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n\n.well {\n  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n  background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n  background-repeat: repeat-x;\n  border-color: #dcdcdc;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n          box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}"
  },
  {
    "path": "jekyll/css/bootstrap.css",
    "content": "/*!\n * Bootstrap v3.0.3 (http://getbootstrap.com)\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.org/licenses/LICENSE-2.0\n */\n\n/*! normalize.css v2.1.3 | MIT License | git.io/normalize */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\naudio,\ncanvas,\nvideo {\n  display: inline-block;\n}\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n[hidden],\ntemplate {\n  display: none;\n}\n\nhtml {\n  font-family: sans-serif;\n  -webkit-text-size-adjust: 100%;\n      -ms-text-size-adjust: 100%;\n}\n\nbody {\n  margin: 0;\n}\n\na {\n  background: transparent;\n}\n\na:focus {\n  outline: thin dotted;\n}\n\na:active,\na:hover {\n  outline: 0;\n}\n\nh1 {\n  margin: 0.67em 0;\n  font-size: 2em;\n}\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\nb,\nstrong {\n  font-weight: bold;\n}\n\ndfn {\n  font-style: italic;\n}\n\nhr {\n  height: 0;\n  -moz-box-sizing: content-box;\n       box-sizing: content-box;\n}\n\nmark {\n  color: #000;\n  background: #ff0;\n}\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, serif;\n  font-size: 1em;\n}\n\npre {\n  white-space: pre-wrap;\n}\n\nq {\n  quotes: \"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\";\n}\n\nsmall {\n  font-size: 80%;\n}\n\nsub,\nsup {\n  position: relative;\n  font-size: 75%;\n  line-height: 0;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nimg {\n  border: 0;\n}\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\nfigure {\n  margin: 0;\n}\n\nfieldset {\n  padding: 0.35em 0.625em 0.75em;\n  margin: 0 2px;\n  border: 1px solid #c0c0c0;\n}\n\nlegend {\n  padding: 0;\n  border: 0;\n}\n\nbutton,\ninput,\nselect,\ntextarea {\n  margin: 0;\n  font-family: inherit;\n  font-size: 100%;\n}\n\nbutton,\ninput {\n  line-height: normal;\n}\n\nbutton,\nselect {\n  text-transform: none;\n}\n\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  cursor: pointer;\n  -webkit-appearance: button;\n}\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  padding: 0;\n  box-sizing: border-box;\n}\n\ninput[type=\"search\"] {\n  -webkit-box-sizing: content-box;\n     -moz-box-sizing: content-box;\n          box-sizing: content-box;\n  -webkit-appearance: textfield;\n}\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  padding: 0;\n  border: 0;\n}\n\ntextarea {\n  overflow: auto;\n  vertical-align: top;\n}\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\n@media print {\n  * {\n    color: #000 !important;\n    text-shadow: none !important;\n    background: transparent !important;\n    box-shadow: none !important;\n  }\n  a,\n  a:visited {\n    text-decoration: underline;\n  }\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n  a[href^=\"javascript:\"]:after,\n  a[href^=\"#\"]:after {\n    content: \"\";\n  }\n  pre,\n  blockquote {\n    border: 1px solid #999;\n    page-break-inside: avoid;\n  }\n  thead {\n    display: table-header-group;\n  }\n  tr,\n  img {\n    page-break-inside: avoid;\n  }\n  img {\n    max-width: 100% !important;\n  }\n  @page  {\n    margin: 2cm .5cm;\n  }\n  p,\n  h2,\n  h3 {\n    orphans: 3;\n    widows: 3;\n  }\n  h2,\n  h3 {\n    page-break-after: avoid;\n  }\n  select {\n    background: #fff !important;\n  }\n  .navbar {\n    display: none;\n  }\n  .table td,\n  .table th {\n    background-color: #fff !important;\n  }\n  .btn > .caret,\n  .dropup > .btn > .caret {\n    border-top-color: #000 !important;\n  }\n  .label {\n    border: 1px solid #000;\n  }\n  .table {\n    border-collapse: collapse !important;\n  }\n  .table-bordered th,\n  .table-bordered td {\n    border: 1px solid #ddd !important;\n  }\n}\n\n*,\n*:before,\n*:after {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\nhtml {\n  font-size: 62.5%;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.428571429;\n  color: #333333;\n  background-color: #ffffff;\n}\n\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\na {\n  color: #428bca;\n  text-decoration: none;\n}\n\na:hover,\na:focus {\n  color: #2a6496;\n  text-decoration: underline;\n}\n\na:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\nimg {\n  vertical-align: middle;\n}\n\n.img-responsive {\n  display: block;\n  height: auto;\n  max-width: 100%;\n}\n\n.img-rounded {\n  border-radius: 6px;\n}\n\n.img-thumbnail {\n  display: inline-block;\n  height: auto;\n  max-width: 100%;\n  padding: 4px;\n  line-height: 1.428571429;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n          transition: all 0.2s ease-in-out;\n}\n\n.img-circle {\n  border-radius: 50%;\n}\n\nhr {\n  margin-top: 20px;\n  margin-bottom: 20px;\n  border: 0;\n  border-top: 1px solid #eeeeee;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\n\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n  font-weight: normal;\n  line-height: 1;\n  color: #999999;\n}\n\nh1,\nh2,\nh3 {\n  margin-top: 20px;\n  margin-bottom: 10px;\n}\n\nh1 small,\nh2 small,\nh3 small,\nh1 .small,\nh2 .small,\nh3 .small {\n  font-size: 65%;\n}\n\nh4,\nh5,\nh6 {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n\nh4 small,\nh5 small,\nh6 small,\nh4 .small,\nh5 .small,\nh6 .small {\n  font-size: 75%;\n}\n\nh1,\n.h1 {\n  font-size: 36px;\n}\n\nh2,\n.h2 {\n  font-size: 30px;\n}\n\nh3,\n.h3 {\n  font-size: 24px;\n}\n\nh4,\n.h4 {\n  font-size: 18px;\n}\n\nh5,\n.h5 {\n  font-size: 14px;\n}\n\nh6,\n.h6 {\n  font-size: 12px;\n}\n\np {\n  margin: 0 0 10px;\n}\n\n.lead {\n  margin-bottom: 20px;\n  font-size: 16px;\n  font-weight: 200;\n  line-height: 1.4;\n}\n\n@media (min-width: 768px) {\n  .lead {\n    font-size: 21px;\n  }\n}\n\nsmall,\n.small {\n  font-size: 85%;\n}\n\ncite {\n  font-style: normal;\n}\n\n.text-muted {\n  color: #999999;\n}\n\n.text-primary {\n  color: #428bca;\n}\n\n.text-primary:hover {\n  color: #3071a9;\n}\n\n.text-warning {\n  color: #8a6d3b;\n}\n\n.text-warning:hover {\n  color: #66512c;\n}\n\n.text-danger {\n  color: #a94442;\n}\n\n.text-danger:hover {\n  color: #843534;\n}\n\n.text-success {\n  color: #3c763d;\n}\n\n.text-success:hover {\n  color: #2b542c;\n}\n\n.text-info {\n  color: #31708f;\n}\n\n.text-info:hover {\n  color: #245269;\n}\n\n.text-left {\n  text-align: left;\n}\n\n.text-right {\n  text-align: right;\n}\n\n.text-center {\n  text-align: center;\n}\n\n.page-header {\n  padding-bottom: 9px;\n  margin: 40px 0 20px;\n  border-bottom: 1px solid #eeeeee;\n}\n\nul,\nol {\n  margin-top: 0;\n  margin-bottom: 10px;\n}\n\nul ul,\nol ul,\nul ol,\nol ol {\n  margin-bottom: 0;\n}\n\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-inline > li {\n  display: inline-block;\n  padding-right: 5px;\n  padding-left: 5px;\n}\n\n.list-inline > li:first-child {\n  padding-left: 0;\n}\n\ndl {\n  margin-top: 0;\n  margin-bottom: 20px;\n}\n\ndt,\ndd {\n  line-height: 1.428571429;\n}\n\ndt {\n  font-weight: bold;\n}\n\ndd {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  .dl-horizontal dt {\n    float: left;\n    width: 160px;\n    overflow: hidden;\n    clear: left;\n    text-align: right;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .dl-horizontal dd {\n    margin-left: 180px;\n  }\n  .dl-horizontal dd:before,\n  .dl-horizontal dd:after {\n    display: table;\n    content: \" \";\n  }\n  .dl-horizontal dd:after {\n    clear: both;\n  }\n  .dl-horizontal dd:before,\n  .dl-horizontal dd:after {\n    display: table;\n    content: \" \";\n  }\n  .dl-horizontal dd:after {\n    clear: both;\n  }\n}\n\nabbr[title],\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted #999999;\n}\n\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\nblockquote {\n  padding: 10px 20px;\n  margin: 0 0 20px;\n  border-left: 5px solid #eeeeee;\n}\n\nblockquote p {\n  font-size: 17.5px;\n  font-weight: 300;\n  line-height: 1.25;\n}\n\nblockquote p:last-child {\n  margin-bottom: 0;\n}\n\nblockquote small,\nblockquote .small {\n  display: block;\n  line-height: 1.428571429;\n  color: #999999;\n}\n\nblockquote small:before,\nblockquote .small:before {\n  content: '\\2014 \\00A0';\n}\n\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid #eeeeee;\n  border-left: 0;\n}\n\nblockquote.pull-right p,\nblockquote.pull-right small,\nblockquote.pull-right .small {\n  text-align: right;\n}\n\nblockquote.pull-right small:before,\nblockquote.pull-right .small:before {\n  content: '';\n}\n\nblockquote.pull-right small:after,\nblockquote.pull-right .small:after {\n  content: '\\00A0 \\2014';\n}\n\nblockquote:before,\nblockquote:after {\n  content: \"\";\n}\n\naddress {\n  margin-bottom: 20px;\n  font-style: normal;\n  line-height: 1.428571429;\n}\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\n\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: #c7254e;\n  white-space: nowrap;\n  background-color: #f9f2f4;\n  border-radius: 4px;\n}\n\npre {\n  display: block;\n  padding: 9.5px;\n  margin: 0 0 10px;\n  font-size: 13px;\n  line-height: 1.428571429;\n  color: #333333;\n  word-break: break-all;\n  word-wrap: break-word;\n  background-color: #f5f5f5;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n\npre code {\n  padding: 0;\n  font-size: inherit;\n  color: inherit;\n  white-space: pre-wrap;\n  background-color: transparent;\n  border-radius: 0;\n}\n\n.pre-scrollable {\n  max-height: 340px;\n  overflow-y: scroll;\n}\n\n.container {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.container:before,\n.container:after {\n  display: table;\n  content: \" \";\n}\n\n.container:after {\n  clear: both;\n}\n\n.container:before,\n.container:after {\n  display: table;\n  content: \" \";\n}\n\n.container:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .container {\n    width: 750px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container {\n    width: 970px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container {\n    width: 1170px;\n  }\n}\n\n.row {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n.row:before,\n.row:after {\n  display: table;\n  content: \" \";\n}\n\n.row:after {\n  clear: both;\n}\n\n.row:before,\n.row:after {\n  display: table;\n  content: \" \";\n}\n\n.row:after {\n  clear: both;\n}\n\n.col-xs-1,\n.col-sm-1,\n.col-md-1,\n.col-lg-1,\n.col-xs-2,\n.col-sm-2,\n.col-md-2,\n.col-lg-2,\n.col-xs-3,\n.col-sm-3,\n.col-md-3,\n.col-lg-3,\n.col-xs-4,\n.col-sm-4,\n.col-md-4,\n.col-lg-4,\n.col-xs-5,\n.col-sm-5,\n.col-md-5,\n.col-lg-5,\n.col-xs-6,\n.col-sm-6,\n.col-md-6,\n.col-lg-6,\n.col-xs-7,\n.col-sm-7,\n.col-md-7,\n.col-lg-7,\n.col-xs-8,\n.col-sm-8,\n.col-md-8,\n.col-lg-8,\n.col-xs-9,\n.col-sm-9,\n.col-md-9,\n.col-lg-9,\n.col-xs-10,\n.col-sm-10,\n.col-md-10,\n.col-lg-10,\n.col-xs-11,\n.col-sm-11,\n.col-md-11,\n.col-lg-11,\n.col-xs-12,\n.col-sm-12,\n.col-md-12,\n.col-lg-12 {\n  position: relative;\n  min-height: 1px;\n  padding-right: 15px;\n  padding-left: 15px;\n}\n\n.col-xs-1,\n.col-xs-2,\n.col-xs-3,\n.col-xs-4,\n.col-xs-5,\n.col-xs-6,\n.col-xs-7,\n.col-xs-8,\n.col-xs-9,\n.col-xs-10,\n.col-xs-11,\n.col-xs-12 {\n  float: left;\n}\n\n.col-xs-12 {\n  width: 100%;\n}\n\n.col-xs-11 {\n  width: 91.66666666666666%;\n}\n\n.col-xs-10 {\n  width: 83.33333333333334%;\n}\n\n.col-xs-9 {\n  width: 75%;\n}\n\n.col-xs-8 {\n  width: 66.66666666666666%;\n}\n\n.col-xs-7 {\n  width: 58.333333333333336%;\n}\n\n.col-xs-6 {\n  width: 50%;\n}\n\n.col-xs-5 {\n  width: 41.66666666666667%;\n}\n\n.col-xs-4 {\n  width: 33.33333333333333%;\n}\n\n.col-xs-3 {\n  width: 25%;\n}\n\n.col-xs-2 {\n  width: 16.666666666666664%;\n}\n\n.col-xs-1 {\n  width: 8.333333333333332%;\n}\n\n.col-xs-pull-12 {\n  right: 100%;\n}\n\n.col-xs-pull-11 {\n  right: 91.66666666666666%;\n}\n\n.col-xs-pull-10 {\n  right: 83.33333333333334%;\n}\n\n.col-xs-pull-9 {\n  right: 75%;\n}\n\n.col-xs-pull-8 {\n  right: 66.66666666666666%;\n}\n\n.col-xs-pull-7 {\n  right: 58.333333333333336%;\n}\n\n.col-xs-pull-6 {\n  right: 50%;\n}\n\n.col-xs-pull-5 {\n  right: 41.66666666666667%;\n}\n\n.col-xs-pull-4 {\n  right: 33.33333333333333%;\n}\n\n.col-xs-pull-3 {\n  right: 25%;\n}\n\n.col-xs-pull-2 {\n  right: 16.666666666666664%;\n}\n\n.col-xs-pull-1 {\n  right: 8.333333333333332%;\n}\n\n.col-xs-pull-0 {\n  right: 0;\n}\n\n.col-xs-push-12 {\n  left: 100%;\n}\n\n.col-xs-push-11 {\n  left: 91.66666666666666%;\n}\n\n.col-xs-push-10 {\n  left: 83.33333333333334%;\n}\n\n.col-xs-push-9 {\n  left: 75%;\n}\n\n.col-xs-push-8 {\n  left: 66.66666666666666%;\n}\n\n.col-xs-push-7 {\n  left: 58.333333333333336%;\n}\n\n.col-xs-push-6 {\n  left: 50%;\n}\n\n.col-xs-push-5 {\n  left: 41.66666666666667%;\n}\n\n.col-xs-push-4 {\n  left: 33.33333333333333%;\n}\n\n.col-xs-push-3 {\n  left: 25%;\n}\n\n.col-xs-push-2 {\n  left: 16.666666666666664%;\n}\n\n.col-xs-push-1 {\n  left: 8.333333333333332%;\n}\n\n.col-xs-push-0 {\n  left: 0;\n}\n\n.col-xs-offset-12 {\n  margin-left: 100%;\n}\n\n.col-xs-offset-11 {\n  margin-left: 91.66666666666666%;\n}\n\n.col-xs-offset-10 {\n  margin-left: 83.33333333333334%;\n}\n\n.col-xs-offset-9 {\n  margin-left: 75%;\n}\n\n.col-xs-offset-8 {\n  margin-left: 66.66666666666666%;\n}\n\n.col-xs-offset-7 {\n  margin-left: 58.333333333333336%;\n}\n\n.col-xs-offset-6 {\n  margin-left: 50%;\n}\n\n.col-xs-offset-5 {\n  margin-left: 41.66666666666667%;\n}\n\n.col-xs-offset-4 {\n  margin-left: 33.33333333333333%;\n}\n\n.col-xs-offset-3 {\n  margin-left: 25%;\n}\n\n.col-xs-offset-2 {\n  margin-left: 16.666666666666664%;\n}\n\n.col-xs-offset-1 {\n  margin-left: 8.333333333333332%;\n}\n\n.col-xs-offset-0 {\n  margin-left: 0;\n}\n\n@media (min-width: 768px) {\n  .col-sm-1,\n  .col-sm-2,\n  .col-sm-3,\n  .col-sm-4,\n  .col-sm-5,\n  .col-sm-6,\n  .col-sm-7,\n  .col-sm-8,\n  .col-sm-9,\n  .col-sm-10,\n  .col-sm-11,\n  .col-sm-12 {\n    float: left;\n  }\n  .col-sm-12 {\n    width: 100%;\n  }\n  .col-sm-11 {\n    width: 91.66666666666666%;\n  }\n  .col-sm-10 {\n    width: 83.33333333333334%;\n  }\n  .col-sm-9 {\n    width: 75%;\n  }\n  .col-sm-8 {\n    width: 66.66666666666666%;\n  }\n  .col-sm-7 {\n    width: 58.333333333333336%;\n  }\n  .col-sm-6 {\n    width: 50%;\n  }\n  .col-sm-5 {\n    width: 41.66666666666667%;\n  }\n  .col-sm-4 {\n    width: 33.33333333333333%;\n  }\n  .col-sm-3 {\n    width: 25%;\n  }\n  .col-sm-2 {\n    width: 16.666666666666664%;\n  }\n  .col-sm-1 {\n    width: 8.333333333333332%;\n  }\n  .col-sm-pull-12 {\n    right: 100%;\n  }\n  .col-sm-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-sm-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-sm-pull-9 {\n    right: 75%;\n  }\n  .col-sm-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-sm-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-sm-pull-6 {\n    right: 50%;\n  }\n  .col-sm-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-sm-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-sm-pull-3 {\n    right: 25%;\n  }\n  .col-sm-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-sm-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-sm-pull-0 {\n    right: 0;\n  }\n  .col-sm-push-12 {\n    left: 100%;\n  }\n  .col-sm-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-sm-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-sm-push-9 {\n    left: 75%;\n  }\n  .col-sm-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-sm-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-sm-push-6 {\n    left: 50%;\n  }\n  .col-sm-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-sm-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-sm-push-3 {\n    left: 25%;\n  }\n  .col-sm-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-sm-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-sm-push-0 {\n    left: 0;\n  }\n  .col-sm-offset-12 {\n    margin-left: 100%;\n  }\n  .col-sm-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n  .col-sm-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-sm-offset-9 {\n    margin-left: 75%;\n  }\n  .col-sm-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-sm-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-sm-offset-6 {\n    margin-left: 50%;\n  }\n  .col-sm-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-sm-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-sm-offset-3 {\n    margin-left: 25%;\n  }\n  .col-sm-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-sm-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-sm-offset-0 {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 992px) {\n  .col-md-1,\n  .col-md-2,\n  .col-md-3,\n  .col-md-4,\n  .col-md-5,\n  .col-md-6,\n  .col-md-7,\n  .col-md-8,\n  .col-md-9,\n  .col-md-10,\n  .col-md-11,\n  .col-md-12 {\n    float: left;\n  }\n  .col-md-12 {\n    width: 100%;\n  }\n  .col-md-11 {\n    width: 91.66666666666666%;\n  }\n  .col-md-10 {\n    width: 83.33333333333334%;\n  }\n  .col-md-9 {\n    width: 75%;\n  }\n  .col-md-8 {\n    width: 66.66666666666666%;\n  }\n  .col-md-7 {\n    width: 58.333333333333336%;\n  }\n  .col-md-6 {\n    width: 50%;\n  }\n  .col-md-5 {\n    width: 41.66666666666667%;\n  }\n  .col-md-4 {\n    width: 33.33333333333333%;\n  }\n  .col-md-3 {\n    width: 25%;\n  }\n  .col-md-2 {\n    width: 16.666666666666664%;\n  }\n  .col-md-1 {\n    width: 8.333333333333332%;\n  }\n  .col-md-pull-12 {\n    right: 100%;\n  }\n  .col-md-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-md-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-md-pull-9 {\n    right: 75%;\n  }\n  .col-md-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-md-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-md-pull-6 {\n    right: 50%;\n  }\n  .col-md-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-md-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-md-pull-3 {\n    right: 25%;\n  }\n  .col-md-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-md-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-md-pull-0 {\n    right: 0;\n  }\n  .col-md-push-12 {\n    left: 100%;\n  }\n  .col-md-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-md-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-md-push-9 {\n    left: 75%;\n  }\n  .col-md-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-md-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-md-push-6 {\n    left: 50%;\n  }\n  .col-md-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-md-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-md-push-3 {\n    left: 25%;\n  }\n  .col-md-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-md-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-md-push-0 {\n    left: 0;\n  }\n  .col-md-offset-12 {\n    margin-left: 100%;\n  }\n  .col-md-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n  .col-md-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-md-offset-9 {\n    margin-left: 75%;\n  }\n  .col-md-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-md-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-md-offset-6 {\n    margin-left: 50%;\n  }\n  .col-md-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-md-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-md-offset-3 {\n    margin-left: 25%;\n  }\n  .col-md-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-md-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-md-offset-0 {\n    margin-left: 0;\n  }\n}\n\n@media (min-width: 1200px) {\n  .col-lg-1,\n  .col-lg-2,\n  .col-lg-3,\n  .col-lg-4,\n  .col-lg-5,\n  .col-lg-6,\n  .col-lg-7,\n  .col-lg-8,\n  .col-lg-9,\n  .col-lg-10,\n  .col-lg-11,\n  .col-lg-12 {\n    float: left;\n  }\n  .col-lg-12 {\n    width: 100%;\n  }\n  .col-lg-11 {\n    width: 91.66666666666666%;\n  }\n  .col-lg-10 {\n    width: 83.33333333333334%;\n  }\n  .col-lg-9 {\n    width: 75%;\n  }\n  .col-lg-8 {\n    width: 66.66666666666666%;\n  }\n  .col-lg-7 {\n    width: 58.333333333333336%;\n  }\n  .col-lg-6 {\n    width: 50%;\n  }\n  .col-lg-5 {\n    width: 41.66666666666667%;\n  }\n  .col-lg-4 {\n    width: 33.33333333333333%;\n  }\n  .col-lg-3 {\n    width: 25%;\n  }\n  .col-lg-2 {\n    width: 16.666666666666664%;\n  }\n  .col-lg-1 {\n    width: 8.333333333333332%;\n  }\n  .col-lg-pull-12 {\n    right: 100%;\n  }\n  .col-lg-pull-11 {\n    right: 91.66666666666666%;\n  }\n  .col-lg-pull-10 {\n    right: 83.33333333333334%;\n  }\n  .col-lg-pull-9 {\n    right: 75%;\n  }\n  .col-lg-pull-8 {\n    right: 66.66666666666666%;\n  }\n  .col-lg-pull-7 {\n    right: 58.333333333333336%;\n  }\n  .col-lg-pull-6 {\n    right: 50%;\n  }\n  .col-lg-pull-5 {\n    right: 41.66666666666667%;\n  }\n  .col-lg-pull-4 {\n    right: 33.33333333333333%;\n  }\n  .col-lg-pull-3 {\n    right: 25%;\n  }\n  .col-lg-pull-2 {\n    right: 16.666666666666664%;\n  }\n  .col-lg-pull-1 {\n    right: 8.333333333333332%;\n  }\n  .col-lg-pull-0 {\n    right: 0;\n  }\n  .col-lg-push-12 {\n    left: 100%;\n  }\n  .col-lg-push-11 {\n    left: 91.66666666666666%;\n  }\n  .col-lg-push-10 {\n    left: 83.33333333333334%;\n  }\n  .col-lg-push-9 {\n    left: 75%;\n  }\n  .col-lg-push-8 {\n    left: 66.66666666666666%;\n  }\n  .col-lg-push-7 {\n    left: 58.333333333333336%;\n  }\n  .col-lg-push-6 {\n    left: 50%;\n  }\n  .col-lg-push-5 {\n    left: 41.66666666666667%;\n  }\n  .col-lg-push-4 {\n    left: 33.33333333333333%;\n  }\n  .col-lg-push-3 {\n    left: 25%;\n  }\n  .col-lg-push-2 {\n    left: 16.666666666666664%;\n  }\n  .col-lg-push-1 {\n    left: 8.333333333333332%;\n  }\n  .col-lg-push-0 {\n    left: 0;\n  }\n  .col-lg-offset-12 {\n    margin-left: 100%;\n  }\n  .col-lg-offset-11 {\n    margin-left: 91.66666666666666%;\n  }\n  .col-lg-offset-10 {\n    margin-left: 83.33333333333334%;\n  }\n  .col-lg-offset-9 {\n    margin-left: 75%;\n  }\n  .col-lg-offset-8 {\n    margin-left: 66.66666666666666%;\n  }\n  .col-lg-offset-7 {\n    margin-left: 58.333333333333336%;\n  }\n  .col-lg-offset-6 {\n    margin-left: 50%;\n  }\n  .col-lg-offset-5 {\n    margin-left: 41.66666666666667%;\n  }\n  .col-lg-offset-4 {\n    margin-left: 33.33333333333333%;\n  }\n  .col-lg-offset-3 {\n    margin-left: 25%;\n  }\n  .col-lg-offset-2 {\n    margin-left: 16.666666666666664%;\n  }\n  .col-lg-offset-1 {\n    margin-left: 8.333333333333332%;\n  }\n  .col-lg-offset-0 {\n    margin-left: 0;\n  }\n}\n\ntable {\n  max-width: 100%;\n  background-color: transparent;\n}\n\nth {\n  text-align: left;\n}\n\n.table {\n  width: 100%;\n  margin-bottom: 20px;\n}\n\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.428571429;\n  vertical-align: top;\n  border-top: 1px solid #dddddd;\n}\n\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #dddddd;\n}\n\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n\n.table > tbody + tbody {\n  border-top: 2px solid #dddddd;\n}\n\n.table .table {\n  background-color: #ffffff;\n}\n\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\n\n.table-bordered {\n  border: 1px solid #dddddd;\n}\n\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n  border: 1px solid #dddddd;\n}\n\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n  border-bottom-width: 2px;\n}\n\n.table-striped > tbody > tr:nth-child(odd) > td,\n.table-striped > tbody > tr:nth-child(odd) > th {\n  background-color: #f9f9f9;\n}\n\n.table-hover > tbody > tr:hover > td,\n.table-hover > tbody > tr:hover > th {\n  background-color: #f5f5f5;\n}\n\ntable col[class*=\"col-\"] {\n  position: static;\n  display: table-column;\n  float: none;\n}\n\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  display: table-cell;\n  float: none;\n}\n\n.table > thead > tr > .active,\n.table > tbody > tr > .active,\n.table > tfoot > tr > .active,\n.table > thead > .active > td,\n.table > tbody > .active > td,\n.table > tfoot > .active > td,\n.table > thead > .active > th,\n.table > tbody > .active > th,\n.table > tfoot > .active > th {\n  background-color: #f5f5f5;\n}\n\n.table-hover > tbody > tr > .active:hover,\n.table-hover > tbody > .active:hover > td,\n.table-hover > tbody > .active:hover > th {\n  background-color: #e8e8e8;\n}\n\n.table > thead > tr > .success,\n.table > tbody > tr > .success,\n.table > tfoot > tr > .success,\n.table > thead > .success > td,\n.table > tbody > .success > td,\n.table > tfoot > .success > td,\n.table > thead > .success > th,\n.table > tbody > .success > th,\n.table > tfoot > .success > th {\n  background-color: #dff0d8;\n}\n\n.table-hover > tbody > tr > .success:hover,\n.table-hover > tbody > .success:hover > td,\n.table-hover > tbody > .success:hover > th {\n  background-color: #d0e9c6;\n}\n\n.table > thead > tr > .danger,\n.table > tbody > tr > .danger,\n.table > tfoot > tr > .danger,\n.table > thead > .danger > td,\n.table > tbody > .danger > td,\n.table > tfoot > .danger > td,\n.table > thead > .danger > th,\n.table > tbody > .danger > th,\n.table > tfoot > .danger > th {\n  background-color: #f2dede;\n}\n\n.table-hover > tbody > tr > .danger:hover,\n.table-hover > tbody > .danger:hover > td,\n.table-hover > tbody > .danger:hover > th {\n  background-color: #ebcccc;\n}\n\n.table > thead > tr > .warning,\n.table > tbody > tr > .warning,\n.table > tfoot > tr > .warning,\n.table > thead > .warning > td,\n.table > tbody > .warning > td,\n.table > tfoot > .warning > td,\n.table > thead > .warning > th,\n.table > tbody > .warning > th,\n.table > tfoot > .warning > th {\n  background-color: #fcf8e3;\n}\n\n.table-hover > tbody > tr > .warning:hover,\n.table-hover > tbody > .warning:hover > td,\n.table-hover > tbody > .warning:hover > th {\n  background-color: #faf2cc;\n}\n\n@media (max-width: 767px) {\n  .table-responsive {\n    width: 100%;\n    margin-bottom: 15px;\n    overflow-x: scroll;\n    overflow-y: hidden;\n    border: 1px solid #dddddd;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    -webkit-overflow-scrolling: touch;\n  }\n  .table-responsive > .table {\n    margin-bottom: 0;\n  }\n  .table-responsive > .table > thead > tr > th,\n  .table-responsive > .table > tbody > tr > th,\n  .table-responsive > .table > tfoot > tr > th,\n  .table-responsive > .table > thead > tr > td,\n  .table-responsive > .table > tbody > tr > td,\n  .table-responsive > .table > tfoot > tr > td {\n    white-space: nowrap;\n  }\n  .table-responsive > .table-bordered {\n    border: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:first-child,\n  .table-responsive > .table-bordered > tbody > tr > th:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n  .table-responsive > .table-bordered > thead > tr > td:first-child,\n  .table-responsive > .table-bordered > tbody > tr > td:first-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n    border-left: 0;\n  }\n  .table-responsive > .table-bordered > thead > tr > th:last-child,\n  .table-responsive > .table-bordered > tbody > tr > th:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n  .table-responsive > .table-bordered > thead > tr > td:last-child,\n  .table-responsive > .table-bordered > tbody > tr > td:last-child,\n  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n    border-right: 0;\n  }\n  .table-responsive > .table-bordered > tbody > tr:last-child > th,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n  .table-responsive > .table-bordered > tbody > tr:last-child > td,\n  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n    border-bottom: 0;\n  }\n}\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: 20px;\n  font-size: 21px;\n  line-height: inherit;\n  color: #333333;\n  border: 0;\n  border-bottom: 1px solid #e5e5e5;\n}\n\nlabel {\n  display: inline-block;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\ninput[type=\"search\"] {\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9;\n  /* IE8-9 */\n\n  line-height: normal;\n}\n\ninput[type=\"file\"] {\n  display: block;\n}\n\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\nselect optgroup {\n  font-family: inherit;\n  font-size: inherit;\n  font-style: inherit;\n}\n\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n  height: auto;\n}\n\noutput {\n  display: block;\n  padding-top: 7px;\n  font-size: 14px;\n  line-height: 1.428571429;\n  color: #555555;\n  vertical-align: middle;\n}\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: 34px;\n  padding: 6px 12px;\n  font-size: 14px;\n  line-height: 1.428571429;\n  color: #555555;\n  vertical-align: middle;\n  background-color: #ffffff;\n  background-image: none;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n          transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n}\n\n.form-control:focus {\n  border-color: #66afe9;\n  outline: 0;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n\n.form-control:-moz-placeholder {\n  color: #999999;\n}\n\n.form-control::-moz-placeholder {\n  color: #999999;\n  opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n  color: #999999;\n}\n\n.form-control::-webkit-input-placeholder {\n  color: #999999;\n}\n\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n  cursor: not-allowed;\n  background-color: #eeeeee;\n}\n\ntextarea.form-control {\n  height: auto;\n}\n\n.form-group {\n  margin-bottom: 15px;\n}\n\n.radio,\n.checkbox {\n  display: block;\n  min-height: 20px;\n  padding-left: 20px;\n  margin-top: 10px;\n  margin-bottom: 10px;\n  vertical-align: middle;\n}\n\n.radio label,\n.checkbox label {\n  display: inline;\n  margin-bottom: 0;\n  font-weight: normal;\n  cursor: pointer;\n}\n\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  float: left;\n  margin-left: -20px;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px;\n}\n\n.radio-inline,\n.checkbox-inline {\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  font-weight: normal;\n  vertical-align: middle;\n  cursor: pointer;\n}\n\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px;\n}\n\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\n.radio[disabled],\n.radio-inline[disabled],\n.checkbox[disabled],\n.checkbox-inline[disabled],\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"],\nfieldset[disabled] .radio,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox,\nfieldset[disabled] .checkbox-inline {\n  cursor: not-allowed;\n}\n\n.input-sm {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\nselect.input-sm {\n  height: 30px;\n  line-height: 30px;\n}\n\ntextarea.input-sm {\n  height: auto;\n}\n\n.input-lg {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\nselect.input-lg {\n  height: 46px;\n  line-height: 46px;\n}\n\ntextarea.input-lg {\n  height: auto;\n}\n\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline {\n  color: #8a6d3b;\n}\n\n.has-warning .form-control {\n  border-color: #8a6d3b;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-warning .form-control:focus {\n  border-color: #66512c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n\n.has-warning .input-group-addon {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #8a6d3b;\n}\n\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline {\n  color: #a94442;\n}\n\n.has-error .form-control {\n  border-color: #a94442;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-error .form-control:focus {\n  border-color: #843534;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n\n.has-error .input-group-addon {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #a94442;\n}\n\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline {\n  color: #3c763d;\n}\n\n.has-success .form-control {\n  border-color: #3c763d;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n\n.has-success .form-control:focus {\n  border-color: #2b542c;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n\n.has-success .input-group-addon {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #3c763d;\n}\n\n.form-control-static {\n  margin-bottom: 0;\n}\n\n.help-block {\n  display: block;\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: #737373;\n}\n\n@media (min-width: 768px) {\n  .form-inline .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .form-inline .form-control {\n    display: inline-block;\n  }\n  .form-inline select.form-control {\n    width: auto;\n  }\n  .form-inline .radio,\n  .form-inline .checkbox {\n    display: inline-block;\n    padding-left: 0;\n    margin-top: 0;\n    margin-bottom: 0;\n  }\n  .form-inline .radio input[type=\"radio\"],\n  .form-inline .checkbox input[type=\"checkbox\"] {\n    float: none;\n    margin-left: 0;\n  }\n}\n\n.form-horizontal .control-label,\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n  padding-top: 7px;\n  margin-top: 0;\n  margin-bottom: 0;\n}\n\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n  min-height: 27px;\n}\n\n.form-horizontal .form-group {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after {\n  display: table;\n  content: \" \";\n}\n\n.form-horizontal .form-group:after {\n  clear: both;\n}\n\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after {\n  display: table;\n  content: \" \";\n}\n\n.form-horizontal .form-group:after {\n  clear: both;\n}\n\n.form-horizontal .form-control-static {\n  padding-top: 7px;\n}\n\n@media (min-width: 768px) {\n  .form-horizontal .control-label {\n    text-align: right;\n  }\n}\n\n.btn {\n  display: inline-block;\n  padding: 6px 12px;\n  margin-bottom: 0;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1.428571429;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: middle;\n  cursor: pointer;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n       -o-user-select: none;\n          user-select: none;\n}\n\n.btn:focus {\n  outline: thin dotted;\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n.btn:hover,\n.btn:focus {\n  color: #333333;\n  text-decoration: none;\n}\n\n.btn:active,\n.btn.active {\n  background-image: none;\n  outline: 0;\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n  pointer-events: none;\n  cursor: not-allowed;\n  opacity: 0.65;\n  filter: alpha(opacity=65);\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn-default {\n  color: #333333;\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n\n.btn-default:hover,\n.btn-default:focus,\n.btn-default:active,\n.btn-default.active,\n.open .dropdown-toggle.btn-default {\n  color: #333333;\n  background-color: #ebebeb;\n  border-color: #adadad;\n}\n\n.btn-default:active,\n.btn-default.active,\n.open .dropdown-toggle.btn-default {\n  background-image: none;\n}\n\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n  background-color: #ffffff;\n  border-color: #cccccc;\n}\n\n.btn-default .badge {\n  color: #ffffff;\n  background-color: #fff;\n}\n\n.btn-primary {\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #357ebd;\n}\n\n.btn-primary:hover,\n.btn-primary:focus,\n.btn-primary:active,\n.btn-primary.active,\n.open .dropdown-toggle.btn-primary {\n  color: #ffffff;\n  background-color: #3276b1;\n  border-color: #285e8e;\n}\n\n.btn-primary:active,\n.btn-primary.active,\n.open .dropdown-toggle.btn-primary {\n  background-image: none;\n}\n\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n  background-color: #428bca;\n  border-color: #357ebd;\n}\n\n.btn-primary .badge {\n  color: #428bca;\n  background-color: #fff;\n}\n\n.btn-warning {\n  color: #ffffff;\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n\n.btn-warning:hover,\n.btn-warning:focus,\n.btn-warning:active,\n.btn-warning.active,\n.open .dropdown-toggle.btn-warning {\n  color: #ffffff;\n  background-color: #ed9c28;\n  border-color: #d58512;\n}\n\n.btn-warning:active,\n.btn-warning.active,\n.open .dropdown-toggle.btn-warning {\n  background-image: none;\n}\n\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n  background-color: #f0ad4e;\n  border-color: #eea236;\n}\n\n.btn-warning .badge {\n  color: #f0ad4e;\n  background-color: #fff;\n}\n\n.btn-danger {\n  color: #ffffff;\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n\n.btn-danger:hover,\n.btn-danger:focus,\n.btn-danger:active,\n.btn-danger.active,\n.open .dropdown-toggle.btn-danger {\n  color: #ffffff;\n  background-color: #d2322d;\n  border-color: #ac2925;\n}\n\n.btn-danger:active,\n.btn-danger.active,\n.open .dropdown-toggle.btn-danger {\n  background-image: none;\n}\n\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n  background-color: #d9534f;\n  border-color: #d43f3a;\n}\n\n.btn-danger .badge {\n  color: #d9534f;\n  background-color: #fff;\n}\n\n.btn-success {\n  color: #ffffff;\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n\n.btn-success:hover,\n.btn-success:focus,\n.btn-success:active,\n.btn-success.active,\n.open .dropdown-toggle.btn-success {\n  color: #ffffff;\n  background-color: #47a447;\n  border-color: #398439;\n}\n\n.btn-success:active,\n.btn-success.active,\n.open .dropdown-toggle.btn-success {\n  background-image: none;\n}\n\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n  background-color: #5cb85c;\n  border-color: #4cae4c;\n}\n\n.btn-success .badge {\n  color: #5cb85c;\n  background-color: #fff;\n}\n\n.btn-info {\n  color: #ffffff;\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n\n.btn-info:hover,\n.btn-info:focus,\n.btn-info:active,\n.btn-info.active,\n.open .dropdown-toggle.btn-info {\n  color: #ffffff;\n  background-color: #39b3d7;\n  border-color: #269abc;\n}\n\n.btn-info:active,\n.btn-info.active,\n.open .dropdown-toggle.btn-info {\n  background-image: none;\n}\n\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n  background-color: #5bc0de;\n  border-color: #46b8da;\n}\n\n.btn-info .badge {\n  color: #5bc0de;\n  background-color: #fff;\n}\n\n.btn-link {\n  font-weight: normal;\n  color: #428bca;\n  cursor: pointer;\n  border-radius: 0;\n}\n\n.btn-link,\n.btn-link:active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n  background-color: transparent;\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n  border-color: transparent;\n}\n\n.btn-link:hover,\n.btn-link:focus {\n  color: #2a6496;\n  text-decoration: underline;\n  background-color: transparent;\n}\n\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n  color: #999999;\n  text-decoration: none;\n}\n\n.btn-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\n.btn-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-xs {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-block {\n  display: block;\n  width: 100%;\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n  width: 100%;\n}\n\n.fade {\n  opacity: 0;\n  -webkit-transition: opacity 0.15s linear;\n          transition: opacity 0.15s linear;\n}\n\n.fade.in {\n  opacity: 1;\n}\n\n.collapse {\n  display: none;\n}\n\n.collapse.in {\n  display: block;\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  -webkit-transition: height 0.35s ease;\n          transition: height 0.35s ease;\n}\n\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('../fonts/glyphicons-halflings-regular.eot');\n  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');\n}\n\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  -webkit-font-smoothing: antialiased;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.glyphicon:empty {\n  width: 1em;\n}\n\n.glyphicon-asterisk:before {\n  content: \"\\2a\";\n}\n\n.glyphicon-plus:before {\n  content: \"\\2b\";\n}\n\n.glyphicon-euro:before {\n  content: \"\\20ac\";\n}\n\n.glyphicon-minus:before {\n  content: \"\\2212\";\n}\n\n.glyphicon-cloud:before {\n  content: \"\\2601\";\n}\n\n.glyphicon-envelope:before {\n  content: \"\\2709\";\n}\n\n.glyphicon-pencil:before {\n  content: \"\\270f\";\n}\n\n.glyphicon-glass:before {\n  content: \"\\e001\";\n}\n\n.glyphicon-music:before {\n  content: \"\\e002\";\n}\n\n.glyphicon-search:before {\n  content: \"\\e003\";\n}\n\n.glyphicon-heart:before {\n  content: \"\\e005\";\n}\n\n.glyphicon-star:before {\n  content: \"\\e006\";\n}\n\n.glyphicon-star-empty:before {\n  content: \"\\e007\";\n}\n\n.glyphicon-user:before {\n  content: \"\\e008\";\n}\n\n.glyphicon-film:before {\n  content: \"\\e009\";\n}\n\n.glyphicon-th-large:before {\n  content: \"\\e010\";\n}\n\n.glyphicon-th:before {\n  content: \"\\e011\";\n}\n\n.glyphicon-th-list:before {\n  content: \"\\e012\";\n}\n\n.glyphicon-ok:before {\n  content: \"\\e013\";\n}\n\n.glyphicon-remove:before {\n  content: \"\\e014\";\n}\n\n.glyphicon-zoom-in:before {\n  content: \"\\e015\";\n}\n\n.glyphicon-zoom-out:before {\n  content: \"\\e016\";\n}\n\n.glyphicon-off:before {\n  content: \"\\e017\";\n}\n\n.glyphicon-signal:before {\n  content: \"\\e018\";\n}\n\n.glyphicon-cog:before {\n  content: \"\\e019\";\n}\n\n.glyphicon-trash:before {\n  content: \"\\e020\";\n}\n\n.glyphicon-home:before {\n  content: \"\\e021\";\n}\n\n.glyphicon-file:before {\n  content: \"\\e022\";\n}\n\n.glyphicon-time:before {\n  content: \"\\e023\";\n}\n\n.glyphicon-road:before {\n  content: \"\\e024\";\n}\n\n.glyphicon-download-alt:before {\n  content: \"\\e025\";\n}\n\n.glyphicon-download:before {\n  content: \"\\e026\";\n}\n\n.glyphicon-upload:before {\n  content: \"\\e027\";\n}\n\n.glyphicon-inbox:before {\n  content: \"\\e028\";\n}\n\n.glyphicon-play-circle:before {\n  content: \"\\e029\";\n}\n\n.glyphicon-repeat:before {\n  content: \"\\e030\";\n}\n\n.glyphicon-refresh:before {\n  content: \"\\e031\";\n}\n\n.glyphicon-list-alt:before {\n  content: \"\\e032\";\n}\n\n.glyphicon-lock:before {\n  content: \"\\e033\";\n}\n\n.glyphicon-flag:before {\n  content: \"\\e034\";\n}\n\n.glyphicon-headphones:before {\n  content: \"\\e035\";\n}\n\n.glyphicon-volume-off:before {\n  content: \"\\e036\";\n}\n\n.glyphicon-volume-down:before {\n  content: \"\\e037\";\n}\n\n.glyphicon-volume-up:before {\n  content: \"\\e038\";\n}\n\n.glyphicon-qrcode:before {\n  content: \"\\e039\";\n}\n\n.glyphicon-barcode:before {\n  content: \"\\e040\";\n}\n\n.glyphicon-tag:before {\n  content: \"\\e041\";\n}\n\n.glyphicon-tags:before {\n  content: \"\\e042\";\n}\n\n.glyphicon-book:before {\n  content: \"\\e043\";\n}\n\n.glyphicon-bookmark:before {\n  content: \"\\e044\";\n}\n\n.glyphicon-print:before {\n  content: \"\\e045\";\n}\n\n.glyphicon-camera:before {\n  content: \"\\e046\";\n}\n\n.glyphicon-font:before {\n  content: \"\\e047\";\n}\n\n.glyphicon-bold:before {\n  content: \"\\e048\";\n}\n\n.glyphicon-italic:before {\n  content: \"\\e049\";\n}\n\n.glyphicon-text-height:before {\n  content: \"\\e050\";\n}\n\n.glyphicon-text-width:before {\n  content: \"\\e051\";\n}\n\n.glyphicon-align-left:before {\n  content: \"\\e052\";\n}\n\n.glyphicon-align-center:before {\n  content: \"\\e053\";\n}\n\n.glyphicon-align-right:before {\n  content: \"\\e054\";\n}\n\n.glyphicon-align-justify:before {\n  content: \"\\e055\";\n}\n\n.glyphicon-list:before {\n  content: \"\\e056\";\n}\n\n.glyphicon-indent-left:before {\n  content: \"\\e057\";\n}\n\n.glyphicon-indent-right:before {\n  content: \"\\e058\";\n}\n\n.glyphicon-facetime-video:before {\n  content: \"\\e059\";\n}\n\n.glyphicon-picture:before {\n  content: \"\\e060\";\n}\n\n.glyphicon-map-marker:before {\n  content: \"\\e062\";\n}\n\n.glyphicon-adjust:before {\n  content: \"\\e063\";\n}\n\n.glyphicon-tint:before {\n  content: \"\\e064\";\n}\n\n.glyphicon-edit:before {\n  content: \"\\e065\";\n}\n\n.glyphicon-share:before {\n  content: \"\\e066\";\n}\n\n.glyphicon-check:before {\n  content: \"\\e067\";\n}\n\n.glyphicon-move:before {\n  content: \"\\e068\";\n}\n\n.glyphicon-step-backward:before {\n  content: \"\\e069\";\n}\n\n.glyphicon-fast-backward:before {\n  content: \"\\e070\";\n}\n\n.glyphicon-backward:before {\n  content: \"\\e071\";\n}\n\n.glyphicon-play:before {\n  content: \"\\e072\";\n}\n\n.glyphicon-pause:before {\n  content: \"\\e073\";\n}\n\n.glyphicon-stop:before {\n  content: \"\\e074\";\n}\n\n.glyphicon-forward:before {\n  content: \"\\e075\";\n}\n\n.glyphicon-fast-forward:before {\n  content: \"\\e076\";\n}\n\n.glyphicon-step-forward:before {\n  content: \"\\e077\";\n}\n\n.glyphicon-eject:before {\n  content: \"\\e078\";\n}\n\n.glyphicon-chevron-left:before {\n  content: \"\\e079\";\n}\n\n.glyphicon-chevron-right:before {\n  content: \"\\e080\";\n}\n\n.glyphicon-plus-sign:before {\n  content: \"\\e081\";\n}\n\n.glyphicon-minus-sign:before {\n  content: \"\\e082\";\n}\n\n.glyphicon-remove-sign:before {\n  content: \"\\e083\";\n}\n\n.glyphicon-ok-sign:before {\n  content: \"\\e084\";\n}\n\n.glyphicon-question-sign:before {\n  content: \"\\e085\";\n}\n\n.glyphicon-info-sign:before {\n  content: \"\\e086\";\n}\n\n.glyphicon-screenshot:before {\n  content: \"\\e087\";\n}\n\n.glyphicon-remove-circle:before {\n  content: \"\\e088\";\n}\n\n.glyphicon-ok-circle:before {\n  content: \"\\e089\";\n}\n\n.glyphicon-ban-circle:before {\n  content: \"\\e090\";\n}\n\n.glyphicon-arrow-left:before {\n  content: \"\\e091\";\n}\n\n.glyphicon-arrow-right:before {\n  content: \"\\e092\";\n}\n\n.glyphicon-arrow-up:before {\n  content: \"\\e093\";\n}\n\n.glyphicon-arrow-down:before {\n  content: \"\\e094\";\n}\n\n.glyphicon-share-alt:before {\n  content: \"\\e095\";\n}\n\n.glyphicon-resize-full:before {\n  content: \"\\e096\";\n}\n\n.glyphicon-resize-small:before {\n  content: \"\\e097\";\n}\n\n.glyphicon-exclamation-sign:before {\n  content: \"\\e101\";\n}\n\n.glyphicon-gift:before {\n  content: \"\\e102\";\n}\n\n.glyphicon-leaf:before {\n  content: \"\\e103\";\n}\n\n.glyphicon-fire:before {\n  content: \"\\e104\";\n}\n\n.glyphicon-eye-open:before {\n  content: \"\\e105\";\n}\n\n.glyphicon-eye-close:before {\n  content: \"\\e106\";\n}\n\n.glyphicon-warning-sign:before {\n  content: \"\\e107\";\n}\n\n.glyphicon-plane:before {\n  content: \"\\e108\";\n}\n\n.glyphicon-calendar:before {\n  content: \"\\e109\";\n}\n\n.glyphicon-random:before {\n  content: \"\\e110\";\n}\n\n.glyphicon-comment:before {\n  content: \"\\e111\";\n}\n\n.glyphicon-magnet:before {\n  content: \"\\e112\";\n}\n\n.glyphicon-chevron-up:before {\n  content: \"\\e113\";\n}\n\n.glyphicon-chevron-down:before {\n  content: \"\\e114\";\n}\n\n.glyphicon-retweet:before {\n  content: \"\\e115\";\n}\n\n.glyphicon-shopping-cart:before {\n  content: \"\\e116\";\n}\n\n.glyphicon-folder-close:before {\n  content: \"\\e117\";\n}\n\n.glyphicon-folder-open:before {\n  content: \"\\e118\";\n}\n\n.glyphicon-resize-vertical:before {\n  content: \"\\e119\";\n}\n\n.glyphicon-resize-horizontal:before {\n  content: \"\\e120\";\n}\n\n.glyphicon-hdd:before {\n  content: \"\\e121\";\n}\n\n.glyphicon-bullhorn:before {\n  content: \"\\e122\";\n}\n\n.glyphicon-bell:before {\n  content: \"\\e123\";\n}\n\n.glyphicon-certificate:before {\n  content: \"\\e124\";\n}\n\n.glyphicon-thumbs-up:before {\n  content: \"\\e125\";\n}\n\n.glyphicon-thumbs-down:before {\n  content: \"\\e126\";\n}\n\n.glyphicon-hand-right:before {\n  content: \"\\e127\";\n}\n\n.glyphicon-hand-left:before {\n  content: \"\\e128\";\n}\n\n.glyphicon-hand-up:before {\n  content: \"\\e129\";\n}\n\n.glyphicon-hand-down:before {\n  content: \"\\e130\";\n}\n\n.glyphicon-circle-arrow-right:before {\n  content: \"\\e131\";\n}\n\n.glyphicon-circle-arrow-left:before {\n  content: \"\\e132\";\n}\n\n.glyphicon-circle-arrow-up:before {\n  content: \"\\e133\";\n}\n\n.glyphicon-circle-arrow-down:before {\n  content: \"\\e134\";\n}\n\n.glyphicon-globe:before {\n  content: \"\\e135\";\n}\n\n.glyphicon-wrench:before {\n  content: \"\\e136\";\n}\n\n.glyphicon-tasks:before {\n  content: \"\\e137\";\n}\n\n.glyphicon-filter:before {\n  content: \"\\e138\";\n}\n\n.glyphicon-briefcase:before {\n  content: \"\\e139\";\n}\n\n.glyphicon-fullscreen:before {\n  content: \"\\e140\";\n}\n\n.glyphicon-dashboard:before {\n  content: \"\\e141\";\n}\n\n.glyphicon-paperclip:before {\n  content: \"\\e142\";\n}\n\n.glyphicon-heart-empty:before {\n  content: \"\\e143\";\n}\n\n.glyphicon-link:before {\n  content: \"\\e144\";\n}\n\n.glyphicon-phone:before {\n  content: \"\\e145\";\n}\n\n.glyphicon-pushpin:before {\n  content: \"\\e146\";\n}\n\n.glyphicon-usd:before {\n  content: \"\\e148\";\n}\n\n.glyphicon-gbp:before {\n  content: \"\\e149\";\n}\n\n.glyphicon-sort:before {\n  content: \"\\e150\";\n}\n\n.glyphicon-sort-by-alphabet:before {\n  content: \"\\e151\";\n}\n\n.glyphicon-sort-by-alphabet-alt:before {\n  content: \"\\e152\";\n}\n\n.glyphicon-sort-by-order:before {\n  content: \"\\e153\";\n}\n\n.glyphicon-sort-by-order-alt:before {\n  content: \"\\e154\";\n}\n\n.glyphicon-sort-by-attributes:before {\n  content: \"\\e155\";\n}\n\n.glyphicon-sort-by-attributes-alt:before {\n  content: \"\\e156\";\n}\n\n.glyphicon-unchecked:before {\n  content: \"\\e157\";\n}\n\n.glyphicon-expand:before {\n  content: \"\\e158\";\n}\n\n.glyphicon-collapse-down:before {\n  content: \"\\e159\";\n}\n\n.glyphicon-collapse-up:before {\n  content: \"\\e160\";\n}\n\n.glyphicon-log-in:before {\n  content: \"\\e161\";\n}\n\n.glyphicon-flash:before {\n  content: \"\\e162\";\n}\n\n.glyphicon-log-out:before {\n  content: \"\\e163\";\n}\n\n.glyphicon-new-window:before {\n  content: \"\\e164\";\n}\n\n.glyphicon-record:before {\n  content: \"\\e165\";\n}\n\n.glyphicon-save:before {\n  content: \"\\e166\";\n}\n\n.glyphicon-open:before {\n  content: \"\\e167\";\n}\n\n.glyphicon-saved:before {\n  content: \"\\e168\";\n}\n\n.glyphicon-import:before {\n  content: \"\\e169\";\n}\n\n.glyphicon-export:before {\n  content: \"\\e170\";\n}\n\n.glyphicon-send:before {\n  content: \"\\e171\";\n}\n\n.glyphicon-floppy-disk:before {\n  content: \"\\e172\";\n}\n\n.glyphicon-floppy-saved:before {\n  content: \"\\e173\";\n}\n\n.glyphicon-floppy-remove:before {\n  content: \"\\e174\";\n}\n\n.glyphicon-floppy-save:before {\n  content: \"\\e175\";\n}\n\n.glyphicon-floppy-open:before {\n  content: \"\\e176\";\n}\n\n.glyphicon-credit-card:before {\n  content: \"\\e177\";\n}\n\n.glyphicon-transfer:before {\n  content: \"\\e178\";\n}\n\n.glyphicon-cutlery:before {\n  content: \"\\e179\";\n}\n\n.glyphicon-header:before {\n  content: \"\\e180\";\n}\n\n.glyphicon-compressed:before {\n  content: \"\\e181\";\n}\n\n.glyphicon-earphone:before {\n  content: \"\\e182\";\n}\n\n.glyphicon-phone-alt:before {\n  content: \"\\e183\";\n}\n\n.glyphicon-tower:before {\n  content: \"\\e184\";\n}\n\n.glyphicon-stats:before {\n  content: \"\\e185\";\n}\n\n.glyphicon-sd-video:before {\n  content: \"\\e186\";\n}\n\n.glyphicon-hd-video:before {\n  content: \"\\e187\";\n}\n\n.glyphicon-subtitles:before {\n  content: \"\\e188\";\n}\n\n.glyphicon-sound-stereo:before {\n  content: \"\\e189\";\n}\n\n.glyphicon-sound-dolby:before {\n  content: \"\\e190\";\n}\n\n.glyphicon-sound-5-1:before {\n  content: \"\\e191\";\n}\n\n.glyphicon-sound-6-1:before {\n  content: \"\\e192\";\n}\n\n.glyphicon-sound-7-1:before {\n  content: \"\\e193\";\n}\n\n.glyphicon-copyright-mark:before {\n  content: \"\\e194\";\n}\n\n.glyphicon-registration-mark:before {\n  content: \"\\e195\";\n}\n\n.glyphicon-cloud-download:before {\n  content: \"\\e197\";\n}\n\n.glyphicon-cloud-upload:before {\n  content: \"\\e198\";\n}\n\n.glyphicon-tree-conifer:before {\n  content: \"\\e199\";\n}\n\n.glyphicon-tree-deciduous:before {\n  content: \"\\e200\";\n}\n\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top: 4px solid;\n  border-right: 4px solid transparent;\n  border-left: 4px solid transparent;\n}\n\n.dropdown {\n  position: relative;\n}\n\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: 1000;\n  display: none;\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0;\n  font-size: 14px;\n  list-style: none;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n          box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n  background-clip: padding-box;\n}\n\n.dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.dropdown-menu .divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n\n.dropdown-menu > li > a {\n  display: block;\n  padding: 3px 20px;\n  clear: both;\n  font-weight: normal;\n  line-height: 1.428571429;\n  color: #333333;\n  white-space: nowrap;\n}\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  color: #262626;\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\n\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  color: #ffffff;\n  text-decoration: none;\n  background-color: #428bca;\n  outline: 0;\n}\n\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  color: #999999;\n}\n\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n  background-image: none;\n  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);\n}\n\n.open > .dropdown-menu {\n  display: block;\n}\n\n.open > a {\n  outline: 0;\n}\n\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: 12px;\n  line-height: 1.428571429;\n  color: #999999;\n}\n\n.dropdown-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 990;\n}\n\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n  border-top: 0;\n  border-bottom: 4px solid;\n  content: \"\";\n}\n\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n  top: auto;\n  bottom: 100%;\n  margin-bottom: 1px;\n}\n\n@media (min-width: 768px) {\n  .navbar-right .dropdown-menu {\n    right: 0;\n    left: auto;\n  }\n}\n\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n  position: relative;\n  float: left;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n  z-index: 2;\n}\n\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus {\n  outline: none;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n  margin-left: -1px;\n}\n\n.btn-toolbar:before,\n.btn-toolbar:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-toolbar:after {\n  clear: both;\n}\n\n.btn-toolbar:before,\n.btn-toolbar:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-toolbar:after {\n  clear: both;\n}\n\n.btn-toolbar .btn-group {\n  float: left;\n}\n\n.btn-toolbar > .btn + .btn,\n.btn-toolbar > .btn-group + .btn,\n.btn-toolbar > .btn + .btn-group,\n.btn-toolbar > .btn-group + .btn-group {\n  margin-left: 5px;\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n.btn-group > .btn:first-child {\n  margin-left: 0;\n}\n\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group > .btn-group {\n  float: left;\n}\n\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n\n.btn-group > .btn-group:first-child > .btn:last-child,\n.btn-group > .btn-group:first-child > .dropdown-toggle {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn-group:last-child > .btn:first-child {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n.btn-group-xs > .btn {\n  padding: 1px 5px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-group-sm > .btn {\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\n.btn-group-lg > .btn {\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\n.btn-group > .btn + .dropdown-toggle {\n  padding-right: 8px;\n  padding-left: 8px;\n}\n\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-right: 12px;\n  padding-left: 12px;\n}\n\n.btn-group.open .dropdown-toggle {\n  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n          box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.btn-group.open .dropdown-toggle.btn-link {\n  -webkit-box-shadow: none;\n          box-shadow: none;\n}\n\n.btn .caret {\n  margin-left: 0;\n}\n\n.btn-lg .caret {\n  border-width: 5px 5px 0;\n  border-bottom-width: 0;\n}\n\n.dropup .btn-lg .caret {\n  border-width: 0 5px 5px;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n  display: block;\n  float: none;\n  width: 100%;\n  max-width: 100%;\n}\n\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-group-vertical > .btn-group:after {\n  clear: both;\n}\n\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after {\n  display: table;\n  content: \" \";\n}\n\n.btn-group-vertical > .btn-group:after {\n  clear: both;\n}\n\n.btn-group-vertical > .btn-group > .btn {\n  float: none;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n  margin-top: -1px;\n  margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n  border-top-right-radius: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:first-child > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child > .dropdown-toggle {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn-group:last-child > .btn:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  border-collapse: separate;\n  table-layout: fixed;\n}\n\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n  display: table-cell;\n  float: none;\n  width: 1%;\n}\n\n.btn-group-justified > .btn-group .btn {\n  width: 100%;\n}\n\n[data-toggle=\"buttons\"] > .btn > input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn > input[type=\"checkbox\"] {\n  display: none;\n}\n\n.input-group {\n  position: relative;\n  display: table;\n  border-collapse: separate;\n}\n\n.input-group[class*=\"col-\"] {\n  float: none;\n  padding-right: 0;\n  padding-left: 0;\n}\n\n.input-group .form-control {\n  width: 100%;\n  margin-bottom: 0;\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  padding: 10px 16px;\n  font-size: 18px;\n  line-height: 1.33;\n  border-radius: 6px;\n}\n\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n  height: 46px;\n  line-height: 46px;\n}\n\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn {\n  height: auto;\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  padding: 5px 10px;\n  font-size: 12px;\n  line-height: 1.5;\n  border-radius: 3px;\n}\n\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n  height: 30px;\n  line-height: 30px;\n}\n\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn {\n  height: auto;\n}\n\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n}\n\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n  border-radius: 0;\n}\n\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle;\n}\n\n.input-group-addon {\n  padding: 6px 12px;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 1;\n  color: #555555;\n  text-align: center;\n  background-color: #eeeeee;\n  border: 1px solid #cccccc;\n  border-radius: 4px;\n}\n\n.input-group-addon.input-sm {\n  padding: 5px 10px;\n  font-size: 12px;\n  border-radius: 3px;\n}\n\n.input-group-addon.input-lg {\n  padding: 10px 16px;\n  font-size: 18px;\n  border-radius: 6px;\n}\n\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n  margin-top: 0;\n}\n\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.input-group-addon:first-child {\n  border-right: 0;\n}\n\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child) {\n  border-bottom-left-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n.input-group-btn {\n  position: relative;\n  white-space: nowrap;\n}\n\n.input-group-btn:first-child > .btn {\n  margin-right: -1px;\n}\n\n.input-group-btn:last-child > .btn {\n  margin-left: -1px;\n}\n\n.input-group-btn > .btn {\n  position: relative;\n}\n\n.input-group-btn > .btn + .btn {\n  margin-left: -4px;\n}\n\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:active {\n  z-index: 2;\n}\n\n.nav {\n  padding-left: 0;\n  margin-bottom: 0;\n  list-style: none;\n}\n\n.nav:before,\n.nav:after {\n  display: table;\n  content: \" \";\n}\n\n.nav:after {\n  clear: both;\n}\n\n.nav:before,\n.nav:after {\n  display: table;\n  content: \" \";\n}\n\n.nav:after {\n  clear: both;\n}\n\n.nav > li {\n  position: relative;\n  display: block;\n}\n\n.nav > li > a {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n}\n\n.nav > li > a:hover,\n.nav > li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n\n.nav > li.disabled > a {\n  color: #999999;\n}\n\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n  color: #999999;\n  text-decoration: none;\n  cursor: not-allowed;\n  background-color: transparent;\n}\n\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n  background-color: #eeeeee;\n  border-color: #428bca;\n}\n\n.nav .nav-divider {\n  height: 1px;\n  margin: 9px 0;\n  overflow: hidden;\n  background-color: #e5e5e5;\n}\n\n.nav > li > a > img {\n  max-width: none;\n}\n\n.nav-tabs {\n  border-bottom: 1px solid #dddddd;\n}\n\n.nav-tabs > li {\n  float: left;\n  margin-bottom: -1px;\n}\n\n.nav-tabs > li > a {\n  margin-right: 2px;\n  line-height: 1.428571429;\n  border: 1px solid transparent;\n  border-radius: 4px 4px 0 0;\n}\n\n.nav-tabs > li > a:hover {\n  border-color: #eeeeee #eeeeee #dddddd;\n}\n\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n  color: #555555;\n  cursor: default;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-bottom-color: transparent;\n}\n\n.nav-tabs.nav-justified {\n  width: 100%;\n  border-bottom: 0;\n}\n\n.nav-tabs.nav-justified > li {\n  float: none;\n}\n\n.nav-tabs.nav-justified > li > a {\n  margin-bottom: 5px;\n  text-align: center;\n}\n\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-tabs.nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n\n.nav-tabs.nav-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n\n@media (min-width: 768px) {\n  .nav-tabs.nav-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs.nav-justified > .active > a,\n  .nav-tabs.nav-justified > .active > a:hover,\n  .nav-tabs.nav-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n\n.nav-pills > li {\n  float: left;\n}\n\n.nav-pills > li > a {\n  border-radius: 4px;\n}\n\n.nav-pills > li + li {\n  margin-left: 2px;\n}\n\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n  color: #ffffff;\n  background-color: #428bca;\n}\n\n.nav-stacked > li {\n  float: none;\n}\n\n.nav-stacked > li + li {\n  margin-top: 2px;\n  margin-left: 0;\n}\n\n.nav-justified {\n  width: 100%;\n}\n\n.nav-justified > li {\n  float: none;\n}\n\n.nav-justified > li > a {\n  margin-bottom: 5px;\n  text-align: center;\n}\n\n.nav-justified > .dropdown .dropdown-menu {\n  top: auto;\n  left: auto;\n}\n\n@media (min-width: 768px) {\n  .nav-justified > li {\n    display: table-cell;\n    width: 1%;\n  }\n  .nav-justified > li > a {\n    margin-bottom: 0;\n  }\n}\n\n.nav-tabs-justified {\n  border-bottom: 0;\n}\n\n.nav-tabs-justified > li > a {\n  margin-right: 0;\n  border-radius: 4px;\n}\n\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n  border: 1px solid #dddddd;\n}\n\n@media (min-width: 768px) {\n  .nav-tabs-justified > li > a {\n    border-bottom: 1px solid #dddddd;\n    border-radius: 4px 4px 0 0;\n  }\n  .nav-tabs-justified > .active > a,\n  .nav-tabs-justified > .active > a:hover,\n  .nav-tabs-justified > .active > a:focus {\n    border-bottom-color: #ffffff;\n  }\n}\n\n.tab-content > .tab-pane {\n  display: none;\n}\n\n.tab-content > .active {\n  display: block;\n}\n\n.nav-tabs .dropdown-menu {\n  margin-top: -1px;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.navbar {\n  position: relative;\n  min-height: 50px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n}\n\n.navbar:before,\n.navbar:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar:after {\n  clear: both;\n}\n\n.navbar:before,\n.navbar:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .navbar {\n    border-radius: 4px;\n  }\n}\n\n.navbar-header:before,\n.navbar-header:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-header:after {\n  clear: both;\n}\n\n.navbar-header:before,\n.navbar-header:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-header:after {\n  clear: both;\n}\n\n@media (min-width: 768px) {\n  .navbar-header {\n    float: left;\n  }\n}\n\n.navbar-collapse {\n  max-height: 340px;\n  padding-right: 15px;\n  padding-left: 15px;\n  overflow-x: visible;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n  -webkit-overflow-scrolling: touch;\n}\n\n.navbar-collapse:before,\n.navbar-collapse:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-collapse:after {\n  clear: both;\n}\n\n.navbar-collapse:before,\n.navbar-collapse:after {\n  display: table;\n  content: \" \";\n}\n\n.navbar-collapse:after {\n  clear: both;\n}\n\n.navbar-collapse.in {\n  overflow-y: auto;\n}\n\n@media (min-width: 768px) {\n  .navbar-collapse {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n  }\n  .navbar-collapse.collapse {\n    display: block !important;\n    height: auto !important;\n    padding-bottom: 0;\n    overflow: visible !important;\n  }\n  .navbar-collapse.in {\n    overflow-y: visible;\n  }\n  .navbar-fixed-top .navbar-collapse,\n  .navbar-static-top .navbar-collapse,\n  .navbar-fixed-bottom .navbar-collapse {\n    padding-right: 0;\n    padding-left: 0;\n  }\n}\n\n.container > .navbar-header,\n.container > .navbar-collapse {\n  margin-right: -15px;\n  margin-left: -15px;\n}\n\n@media (min-width: 768px) {\n  .container > .navbar-header,\n  .container > .navbar-collapse {\n    margin-right: 0;\n    margin-left: 0;\n  }\n}\n\n.navbar-static-top {\n  z-index: 1000;\n  border-width: 0 0 1px;\n}\n\n@media (min-width: 768px) {\n  .navbar-static-top {\n    border-radius: 0;\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: 1030;\n}\n\n@media (min-width: 768px) {\n  .navbar-fixed-top,\n  .navbar-fixed-bottom {\n    border-radius: 0;\n  }\n}\n\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0;\n  border-width: 1px 0 0;\n}\n\n.navbar-brand {\n  float: left;\n  padding: 15px 15px;\n  font-size: 18px;\n  line-height: 20px;\n}\n\n.navbar-brand:hover,\n.navbar-brand:focus {\n  text-decoration: none;\n}\n\n@media (min-width: 768px) {\n  .navbar > .container .navbar-brand {\n    margin-left: -15px;\n  }\n}\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  padding: 9px 10px;\n  margin-top: 8px;\n  margin-right: 15px;\n  margin-bottom: 8px;\n  background-color: transparent;\n  background-image: none;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n\n.navbar-toggle .icon-bar {\n  display: block;\n  width: 22px;\n  height: 2px;\n  border-radius: 1px;\n}\n\n.navbar-toggle .icon-bar + .icon-bar {\n  margin-top: 4px;\n}\n\n@media (min-width: 768px) {\n  .navbar-toggle {\n    display: none;\n  }\n}\n\n.navbar-nav {\n  margin: 7.5px -15px;\n}\n\n.navbar-nav > li > a {\n  padding-top: 10px;\n  padding-bottom: 10px;\n  line-height: 20px;\n}\n\n@media (max-width: 767px) {\n  .navbar-nav .open .dropdown-menu {\n    position: static;\n    float: none;\n    width: auto;\n    margin-top: 0;\n    background-color: transparent;\n    border: 0;\n    box-shadow: none;\n  }\n  .navbar-nav .open .dropdown-menu > li > a,\n  .navbar-nav .open .dropdown-menu .dropdown-header {\n    padding: 5px 15px 5px 25px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a {\n    line-height: 20px;\n  }\n  .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-nav .open .dropdown-menu > li > a:focus {\n    background-image: none;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-nav {\n    float: left;\n    margin: 0;\n  }\n  .navbar-nav > li {\n    float: left;\n  }\n  .navbar-nav > li > a {\n    padding-top: 15px;\n    padding-bottom: 15px;\n  }\n  .navbar-nav.navbar-right:last-child {\n    margin-right: -15px;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-left {\n    float: left !important;\n  }\n  .navbar-right {\n    float: right !important;\n  }\n}\n\n.navbar-form {\n  padding: 10px 15px;\n  margin-top: 8px;\n  margin-right: -15px;\n  margin-bottom: 8px;\n  margin-left: -15px;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n\n@media (min-width: 768px) {\n  .navbar-form .form-group {\n    display: inline-block;\n    margin-bottom: 0;\n    vertical-align: middle;\n  }\n  .navbar-form .form-control {\n    display: inline-block;\n  }\n  .navbar-form select.form-control {\n    width: auto;\n  }\n  .navbar-form .radio,\n  .navbar-form .checkbox {\n    display: inline-block;\n    padding-left: 0;\n    margin-top: 0;\n    margin-bottom: 0;\n  }\n  .navbar-form .radio input[type=\"radio\"],\n  .navbar-form .checkbox input[type=\"checkbox\"] {\n    float: none;\n    margin-left: 0;\n  }\n}\n\n@media (max-width: 767px) {\n  .navbar-form .form-group {\n    margin-bottom: 5px;\n  }\n}\n\n@media (min-width: 768px) {\n  .navbar-form {\n    width: auto;\n    padding-top: 0;\n    padding-bottom: 0;\n    margin-right: 0;\n    margin-left: 0;\n    border: 0;\n    -webkit-box-shadow: none;\n            box-shadow: none;\n  }\n  .navbar-form.navbar-right:last-child {\n    margin-right: -15px;\n  }\n}\n\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  border-bottom-right-radius: 0;\n  border-bottom-left-radius: 0;\n}\n\n.navbar-nav.pull-right > li > .dropdown-menu,\n.navbar-nav > li > .dropdown-menu.pull-right {\n  right: 0;\n  left: auto;\n}\n\n.navbar-btn {\n  margin-top: 8px;\n  margin-bottom: 8px;\n}\n\n.navbar-btn.btn-sm {\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n\n.navbar-btn.btn-xs {\n  margin-top: 14px;\n  margin-bottom: 14px;\n}\n\n.navbar-text {\n  margin-top: 15px;\n  margin-bottom: 15px;\n}\n\n@media (min-width: 768px) {\n  .navbar-text {\n    float: left;\n    margin-right: 15px;\n    margin-left: 15px;\n  }\n  .navbar-text.navbar-right:last-child {\n    margin-right: 0;\n  }\n}\n\n.navbar-default {\n  background-color: #f8f8f8;\n  border-color: #e7e7e7;\n}\n\n.navbar-default .navbar-brand {\n  color: #777777;\n}\n\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n  color: #5e5e5e;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-text {\n  color: #777777;\n}\n\n.navbar-default .navbar-nav > li > a {\n  color: #777777;\n}\n\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n  color: #333333;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n  color: #cccccc;\n  background-color: transparent;\n}\n\n.navbar-default .navbar-toggle {\n  border-color: #dddddd;\n}\n\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n  background-color: #dddddd;\n}\n\n.navbar-default .navbar-toggle .icon-bar {\n  background-color: #cccccc;\n}\n\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n  border-color: #e7e7e7;\n}\n\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n  color: #555555;\n  background-color: #e7e7e7;\n}\n\n@media (max-width: 767px) {\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n    color: #777777;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #333333;\n    background-color: transparent;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #555555;\n    background-color: #e7e7e7;\n  }\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #cccccc;\n    background-color: transparent;\n  }\n}\n\n.navbar-default .navbar-link {\n  color: #777777;\n}\n\n.navbar-default .navbar-link:hover {\n  color: #333333;\n}\n\n.navbar-inverse {\n  background-color: #222222;\n  border-color: #080808;\n}\n\n.navbar-inverse .navbar-brand {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-text {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-nav > li > a {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n  color: #ffffff;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n  color: #444444;\n  background-color: transparent;\n}\n\n.navbar-inverse .navbar-toggle {\n  border-color: #333333;\n}\n\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n  background-color: #333333;\n}\n\n.navbar-inverse .navbar-toggle .icon-bar {\n  background-color: #ffffff;\n}\n\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n  border-color: #101010;\n}\n\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n  color: #ffffff;\n  background-color: #080808;\n}\n\n@media (max-width: 767px) {\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n    border-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n    color: #999999;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n    color: #ffffff;\n    background-color: transparent;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n    color: #ffffff;\n    background-color: #080808;\n  }\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n    color: #444444;\n    background-color: transparent;\n  }\n}\n\n.navbar-inverse .navbar-link {\n  color: #999999;\n}\n\n.navbar-inverse .navbar-link:hover {\n  color: #ffffff;\n}\n\n.breadcrumb {\n  padding: 8px 15px;\n  margin-bottom: 20px;\n  list-style: none;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n}\n\n.breadcrumb > li {\n  display: inline-block;\n}\n\n.breadcrumb > li + li:before {\n  padding: 0 5px;\n  color: #cccccc;\n  content: \"/\\00a0\";\n}\n\n.breadcrumb > .active {\n  color: #999999;\n}\n\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: 20px 0;\n  border-radius: 4px;\n}\n\n.pagination > li {\n  display: inline;\n}\n\n.pagination > li > a,\n.pagination > li > span {\n  position: relative;\n  float: left;\n  padding: 6px 12px;\n  margin-left: -1px;\n  line-height: 1.428571429;\n  text-decoration: none;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n  margin-left: 0;\n  border-bottom-left-radius: 4px;\n  border-top-left-radius: 4px;\n}\n\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n}\n\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n  background-color: #eeeeee;\n}\n\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n  z-index: 2;\n  color: #ffffff;\n  cursor: default;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n  color: #999999;\n  cursor: not-allowed;\n  background-color: #ffffff;\n  border-color: #dddddd;\n}\n\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n  padding: 10px 16px;\n  font-size: 18px;\n}\n\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n  border-bottom-left-radius: 6px;\n  border-top-left-radius: 6px;\n}\n\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n  border-top-right-radius: 6px;\n  border-bottom-right-radius: 6px;\n}\n\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n  padding: 5px 10px;\n  font-size: 12px;\n}\n\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n  border-bottom-left-radius: 3px;\n  border-top-left-radius: 3px;\n}\n\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n  border-top-right-radius: 3px;\n  border-bottom-right-radius: 3px;\n}\n\n.pager {\n  padding-left: 0;\n  margin: 20px 0;\n  text-align: center;\n  list-style: none;\n}\n\n.pager:before,\n.pager:after {\n  display: table;\n  content: \" \";\n}\n\n.pager:after {\n  clear: both;\n}\n\n.pager:before,\n.pager:after {\n  display: table;\n  content: \" \";\n}\n\n.pager:after {\n  clear: both;\n}\n\n.pager li {\n  display: inline;\n}\n\n.pager li > a,\n.pager li > span {\n  display: inline-block;\n  padding: 5px 14px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 15px;\n}\n\n.pager li > a:hover,\n.pager li > a:focus {\n  text-decoration: none;\n  background-color: #eeeeee;\n}\n\n.pager .next > a,\n.pager .next > span {\n  float: right;\n}\n\n.pager .previous > a,\n.pager .previous > span {\n  float: left;\n}\n\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n  color: #999999;\n  cursor: not-allowed;\n  background-color: #ffffff;\n}\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n}\n\n.label[href]:hover,\n.label[href]:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.label:empty {\n  display: none;\n}\n\n.btn .label {\n  position: relative;\n  top: -1px;\n}\n\n.label-default {\n  background-color: #999999;\n}\n\n.label-default[href]:hover,\n.label-default[href]:focus {\n  background-color: #808080;\n}\n\n.label-primary {\n  background-color: #428bca;\n}\n\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n  background-color: #3071a9;\n}\n\n.label-success {\n  background-color: #5cb85c;\n}\n\n.label-success[href]:hover,\n.label-success[href]:focus {\n  background-color: #449d44;\n}\n\n.label-info {\n  background-color: #5bc0de;\n}\n\n.label-info[href]:hover,\n.label-info[href]:focus {\n  background-color: #31b0d5;\n}\n\n.label-warning {\n  background-color: #f0ad4e;\n}\n\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n  background-color: #ec971f;\n}\n\n.label-danger {\n  background-color: #d9534f;\n}\n\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n  background-color: #c9302c;\n}\n\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: 12px;\n  font-weight: bold;\n  line-height: 1;\n  color: #ffffff;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  background-color: #999999;\n  border-radius: 10px;\n}\n\n.badge:empty {\n  display: none;\n}\n\n.btn .badge {\n  position: relative;\n  top: -1px;\n}\n\na.badge:hover,\na.badge:focus {\n  color: #ffffff;\n  text-decoration: none;\n  cursor: pointer;\n}\n\na.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n  color: #428bca;\n  background-color: #ffffff;\n}\n\n.nav-pills > li > a > .badge {\n  margin-left: 3px;\n}\n\n.jumbotron {\n  padding: 30px;\n  margin-bottom: 30px;\n  font-size: 21px;\n  font-weight: 200;\n  line-height: 2.1428571435;\n  color: inherit;\n  background-color: #eeeeee;\n}\n\n.jumbotron h1,\n.jumbotron .h1 {\n  line-height: 1;\n  color: inherit;\n}\n\n.jumbotron p {\n  line-height: 1.4;\n}\n\n.container .jumbotron {\n  border-radius: 6px;\n}\n\n.jumbotron .container {\n  max-width: 100%;\n}\n\n@media screen and (min-width: 768px) {\n  .jumbotron {\n    padding-top: 48px;\n    padding-bottom: 48px;\n  }\n  .container .jumbotron {\n    padding-right: 60px;\n    padding-left: 60px;\n  }\n  .jumbotron h1,\n  .jumbotron .h1 {\n    font-size: 63px;\n  }\n}\n\n.thumbnail {\n  display: block;\n  padding: 4px;\n  margin-bottom: 20px;\n  line-height: 1.428571429;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n  border-radius: 4px;\n  -webkit-transition: all 0.2s ease-in-out;\n          transition: all 0.2s ease-in-out;\n}\n\n.thumbnail > img,\n.thumbnail a > img {\n  display: block;\n  height: auto;\n  max-width: 100%;\n  margin-right: auto;\n  margin-left: auto;\n}\n\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n  border-color: #428bca;\n}\n\n.thumbnail .caption {\n  padding: 9px;\n  color: #333333;\n}\n\n.alert {\n  padding: 15px;\n  margin-bottom: 20px;\n  border: 1px solid transparent;\n  border-radius: 4px;\n}\n\n.alert h4 {\n  margin-top: 0;\n  color: inherit;\n}\n\n.alert .alert-link {\n  font-weight: bold;\n}\n\n.alert > p,\n.alert > ul {\n  margin-bottom: 0;\n}\n\n.alert > p + p {\n  margin-top: 5px;\n}\n\n.alert-dismissable {\n  padding-right: 35px;\n}\n\n.alert-dismissable .close {\n  position: relative;\n  top: -2px;\n  right: -21px;\n  color: inherit;\n}\n\n.alert-success {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.alert-success hr {\n  border-top-color: #c9e2b3;\n}\n\n.alert-success .alert-link {\n  color: #2b542c;\n}\n\n.alert-info {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n\n.alert-info hr {\n  border-top-color: #a6e1ec;\n}\n\n.alert-info .alert-link {\n  color: #245269;\n}\n\n.alert-warning {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n\n.alert-warning hr {\n  border-top-color: #f7e1b5;\n}\n\n.alert-warning .alert-link {\n  color: #66512c;\n}\n\n.alert-danger {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n\n.alert-danger hr {\n  border-top-color: #e4b9c0;\n}\n\n.alert-danger .alert-link {\n  color: #843534;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n@keyframes progress-bar-stripes {\n  from {\n    background-position: 40px 0;\n  }\n  to {\n    background-position: 0 0;\n  }\n}\n\n.progress {\n  height: 20px;\n  margin-bottom: 20px;\n  overflow: hidden;\n  background-color: #f5f5f5;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n\n.progress-bar {\n  float: left;\n  width: 0;\n  height: 100%;\n  font-size: 12px;\n  line-height: 20px;\n  color: #ffffff;\n  text-align: center;\n  background-color: #428bca;\n  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n  -webkit-transition: width 0.6s ease;\n          transition: width 0.6s ease;\n}\n\n.progress-striped .progress-bar {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-size: 40px 40px;\n}\n\n.progress.active .progress-bar {\n  -webkit-animation: progress-bar-stripes 2s linear infinite;\n          animation: progress-bar-stripes 2s linear infinite;\n}\n\n.progress-bar-success {\n  background-color: #5cb85c;\n}\n\n.progress-striped .progress-bar-success {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-info {\n  background-color: #5bc0de;\n}\n\n.progress-striped .progress-bar-info {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-warning {\n  background-color: #f0ad4e;\n}\n\n.progress-striped .progress-bar-warning {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.progress-bar-danger {\n  background-color: #d9534f;\n}\n\n.progress-striped .progress-bar-danger {\n  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n\n.media,\n.media-body {\n  overflow: hidden;\n  zoom: 1;\n}\n\n.media,\n.media .media {\n  margin-top: 15px;\n}\n\n.media:first-child {\n  margin-top: 0;\n}\n\n.media-object {\n  display: block;\n}\n\n.media-heading {\n  margin: 0 0 5px;\n}\n\n.media > .pull-left {\n  margin-right: 10px;\n}\n\n.media > .pull-right {\n  margin-left: 10px;\n}\n\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n\n.list-group {\n  padding-left: 0;\n  margin-bottom: 20px;\n}\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  margin-bottom: -1px;\n  background-color: #ffffff;\n  border: 1px solid #dddddd;\n}\n\n.list-group-item:first-child {\n  border-top-right-radius: 4px;\n  border-top-left-radius: 4px;\n}\n\n.list-group-item:last-child {\n  margin-bottom: 0;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n\n.list-group-item > .badge {\n  float: right;\n}\n\n.list-group-item > .badge + .badge {\n  margin-right: 5px;\n}\n\na.list-group-item {\n  color: #555555;\n}\n\na.list-group-item .list-group-item-heading {\n  color: #333333;\n}\n\na.list-group-item:hover,\na.list-group-item:focus {\n  text-decoration: none;\n  background-color: #f5f5f5;\n}\n\na.list-group-item.active,\na.list-group-item.active:hover,\na.list-group-item.active:focus {\n  z-index: 2;\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\na.list-group-item.active .list-group-item-heading,\na.list-group-item.active:hover .list-group-item-heading,\na.list-group-item.active:focus .list-group-item-heading {\n  color: inherit;\n}\n\na.list-group-item.active .list-group-item-text,\na.list-group-item.active:hover .list-group-item-text,\na.list-group-item.active:focus .list-group-item-text {\n  color: #e1edf7;\n}\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n\n.panel {\n  margin-bottom: 20px;\n  background-color: #ffffff;\n  border: 1px solid transparent;\n  border-radius: 4px;\n  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n\n.panel-body {\n  padding: 15px;\n}\n\n.panel-body:before,\n.panel-body:after {\n  display: table;\n  content: \" \";\n}\n\n.panel-body:after {\n  clear: both;\n}\n\n.panel-body:before,\n.panel-body:after {\n  display: table;\n  content: \" \";\n}\n\n.panel-body:after {\n  clear: both;\n}\n\n.panel > .list-group {\n  margin-bottom: 0;\n}\n\n.panel > .list-group .list-group-item {\n  border-width: 1px 0;\n}\n\n.panel > .list-group .list-group-item:first-child {\n  border-top-right-radius: 0;\n  border-top-left-radius: 0;\n}\n\n.panel > .list-group .list-group-item:last-child {\n  border-bottom: 0;\n}\n\n.panel-heading + .list-group .list-group-item:first-child {\n  border-top-width: 0;\n}\n\n.panel > .table,\n.panel > .table-responsive > .table {\n  margin-bottom: 0;\n}\n\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive {\n  border-top: 1px solid #dddddd;\n}\n\n.panel > .table > tbody:first-child th,\n.panel > .table > tbody:first-child td {\n  border-top: 0;\n}\n\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n  border: 0;\n}\n\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n  border-left: 0;\n}\n\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n  border-right: 0;\n}\n\n.panel > .table-bordered > thead > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:last-child > th,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-bordered > thead > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n  border-bottom: 0;\n}\n\n.panel > .table-responsive {\n  margin-bottom: 0;\n  border: 0;\n}\n\n.panel-heading {\n  padding: 10px 15px;\n  border-bottom: 1px solid transparent;\n  border-top-right-radius: 3px;\n  border-top-left-radius: 3px;\n}\n\n.panel-heading > .dropdown .dropdown-toggle {\n  color: inherit;\n}\n\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: 16px;\n  color: inherit;\n}\n\n.panel-title > a {\n  color: inherit;\n}\n\n.panel-footer {\n  padding: 10px 15px;\n  background-color: #f5f5f5;\n  border-top: 1px solid #dddddd;\n  border-bottom-right-radius: 3px;\n  border-bottom-left-radius: 3px;\n}\n\n.panel-group .panel {\n  margin-bottom: 0;\n  overflow: hidden;\n  border-radius: 4px;\n}\n\n.panel-group .panel + .panel {\n  margin-top: 5px;\n}\n\n.panel-group .panel-heading {\n  border-bottom: 0;\n}\n\n.panel-group .panel-heading + .panel-collapse .panel-body {\n  border-top: 1px solid #dddddd;\n}\n\n.panel-group .panel-footer {\n  border-top: 0;\n}\n\n.panel-group .panel-footer + .panel-collapse .panel-body {\n  border-bottom: 1px solid #dddddd;\n}\n\n.panel-default {\n  border-color: #dddddd;\n}\n\n.panel-default > .panel-heading {\n  color: #333333;\n  background-color: #f5f5f5;\n  border-color: #dddddd;\n}\n\n.panel-default > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #dddddd;\n}\n\n.panel-default > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #dddddd;\n}\n\n.panel-primary {\n  border-color: #428bca;\n}\n\n.panel-primary > .panel-heading {\n  color: #ffffff;\n  background-color: #428bca;\n  border-color: #428bca;\n}\n\n.panel-primary > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #428bca;\n}\n\n.panel-primary > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #428bca;\n}\n\n.panel-success {\n  border-color: #d6e9c6;\n}\n\n.panel-success > .panel-heading {\n  color: #3c763d;\n  background-color: #dff0d8;\n  border-color: #d6e9c6;\n}\n\n.panel-success > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #d6e9c6;\n}\n\n.panel-success > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #d6e9c6;\n}\n\n.panel-warning {\n  border-color: #faebcc;\n}\n\n.panel-warning > .panel-heading {\n  color: #8a6d3b;\n  background-color: #fcf8e3;\n  border-color: #faebcc;\n}\n\n.panel-warning > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #faebcc;\n}\n\n.panel-warning > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #faebcc;\n}\n\n.panel-danger {\n  border-color: #ebccd1;\n}\n\n.panel-danger > .panel-heading {\n  color: #a94442;\n  background-color: #f2dede;\n  border-color: #ebccd1;\n}\n\n.panel-danger > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #ebccd1;\n}\n\n.panel-danger > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #ebccd1;\n}\n\n.panel-info {\n  border-color: #bce8f1;\n}\n\n.panel-info > .panel-heading {\n  color: #31708f;\n  background-color: #d9edf7;\n  border-color: #bce8f1;\n}\n\n.panel-info > .panel-heading + .panel-collapse .panel-body {\n  border-top-color: #bce8f1;\n}\n\n.panel-info > .panel-footer + .panel-collapse .panel-body {\n  border-bottom-color: #bce8f1;\n}\n\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: #f5f5f5;\n  border: 1px solid #e3e3e3;\n  border-radius: 4px;\n  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n\n.well blockquote {\n  border-color: #ddd;\n  border-color: rgba(0, 0, 0, 0.15);\n}\n\n.well-lg {\n  padding: 24px;\n  border-radius: 6px;\n}\n\n.well-sm {\n  padding: 9px;\n  border-radius: 3px;\n}\n\n.close {\n  float: right;\n  font-size: 21px;\n  font-weight: bold;\n  line-height: 1;\n  color: #000000;\n  text-shadow: 0 1px 0 #ffffff;\n  opacity: 0.2;\n  filter: alpha(opacity=20);\n}\n\n.close:hover,\n.close:focus {\n  color: #000000;\n  text-decoration: none;\n  cursor: pointer;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\nbutton.close {\n  padding: 0;\n  cursor: pointer;\n  background: transparent;\n  border: 0;\n  -webkit-appearance: none;\n}\n\n.modal-open {\n  overflow: hidden;\n}\n\n.modal {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1040;\n  display: none;\n  overflow: auto;\n  overflow-y: scroll;\n}\n\n.modal.fade .modal-dialog {\n  -webkit-transform: translate(0, -25%);\n      -ms-transform: translate(0, -25%);\n          transform: translate(0, -25%);\n  -webkit-transition: -webkit-transform 0.3s ease-out;\n     -moz-transition: -moz-transform 0.3s ease-out;\n       -o-transition: -o-transform 0.3s ease-out;\n          transition: transform 0.3s ease-out;\n}\n\n.modal.in .modal-dialog {\n  -webkit-transform: translate(0, 0);\n      -ms-transform: translate(0, 0);\n          transform: translate(0, 0);\n}\n\n.modal-dialog {\n  position: relative;\n  z-index: 1050;\n  width: auto;\n  margin: 10px;\n}\n\n.modal-content {\n  position: relative;\n  background-color: #ffffff;\n  border: 1px solid #999999;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  outline: none;\n  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n          box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n  background-clip: padding-box;\n}\n\n.modal-backdrop {\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 1030;\n  background-color: #000000;\n}\n\n.modal-backdrop.fade {\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n\n.modal-backdrop.in {\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\n.modal-header {\n  min-height: 16.428571429px;\n  padding: 15px;\n  border-bottom: 1px solid #e5e5e5;\n}\n\n.modal-header .close {\n  margin-top: -2px;\n}\n\n.modal-title {\n  margin: 0;\n  line-height: 1.428571429;\n}\n\n.modal-body {\n  position: relative;\n  padding: 20px;\n}\n\n.modal-footer {\n  padding: 19px 20px 20px;\n  margin-top: 15px;\n  text-align: right;\n  border-top: 1px solid #e5e5e5;\n}\n\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \" \";\n}\n\n.modal-footer:after {\n  clear: both;\n}\n\n.modal-footer:before,\n.modal-footer:after {\n  display: table;\n  content: \" \";\n}\n\n.modal-footer:after {\n  clear: both;\n}\n\n.modal-footer .btn + .btn {\n  margin-bottom: 0;\n  margin-left: 5px;\n}\n\n.modal-footer .btn-group .btn + .btn {\n  margin-left: -1px;\n}\n\n.modal-footer .btn-block + .btn-block {\n  margin-left: 0;\n}\n\n@media screen and (min-width: 768px) {\n  .modal-dialog {\n    width: 600px;\n    margin: 30px auto;\n  }\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n  }\n}\n\n.tooltip {\n  position: absolute;\n  z-index: 1030;\n  display: block;\n  font-size: 12px;\n  line-height: 1.4;\n  opacity: 0;\n  filter: alpha(opacity=0);\n  visibility: visible;\n}\n\n.tooltip.in {\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n\n.tooltip.top {\n  padding: 5px 0;\n  margin-top: -3px;\n}\n\n.tooltip.right {\n  padding: 0 5px;\n  margin-left: 3px;\n}\n\n.tooltip.bottom {\n  padding: 5px 0;\n  margin-top: 3px;\n}\n\n.tooltip.left {\n  padding: 0 5px;\n  margin-left: -3px;\n}\n\n.tooltip-inner {\n  max-width: 200px;\n  padding: 3px 8px;\n  color: #ffffff;\n  text-align: center;\n  text-decoration: none;\n  background-color: #000000;\n  border-radius: 4px;\n}\n\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n\n.tooltip.top .tooltip-arrow {\n  bottom: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.top-left .tooltip-arrow {\n  bottom: 0;\n  left: 5px;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.top-right .tooltip-arrow {\n  right: 5px;\n  bottom: 0;\n  border-top-color: #000000;\n  border-width: 5px 5px 0;\n}\n\n.tooltip.right .tooltip-arrow {\n  top: 50%;\n  left: 0;\n  margin-top: -5px;\n  border-right-color: #000000;\n  border-width: 5px 5px 5px 0;\n}\n\n.tooltip.left .tooltip-arrow {\n  top: 50%;\n  right: 0;\n  margin-top: -5px;\n  border-left-color: #000000;\n  border-width: 5px 0 5px 5px;\n}\n\n.tooltip.bottom .tooltip-arrow {\n  top: 0;\n  left: 50%;\n  margin-left: -5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.tooltip.bottom-left .tooltip-arrow {\n  top: 0;\n  left: 5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.tooltip.bottom-right .tooltip-arrow {\n  top: 0;\n  right: 5px;\n  border-bottom-color: #000000;\n  border-width: 0 5px 5px;\n}\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 1010;\n  display: none;\n  max-width: 276px;\n  padding: 1px;\n  text-align: left;\n  white-space: normal;\n  background-color: #ffffff;\n  border: 1px solid #cccccc;\n  border: 1px solid rgba(0, 0, 0, 0.2);\n  border-radius: 6px;\n  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  background-clip: padding-box;\n}\n\n.popover.top {\n  margin-top: -10px;\n}\n\n.popover.right {\n  margin-left: 10px;\n}\n\n.popover.bottom {\n  margin-top: 10px;\n}\n\n.popover.left {\n  margin-left: -10px;\n}\n\n.popover-title {\n  padding: 8px 14px;\n  margin: 0;\n  font-size: 14px;\n  font-weight: normal;\n  line-height: 18px;\n  background-color: #f7f7f7;\n  border-bottom: 1px solid #ebebeb;\n  border-radius: 5px 5px 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n.popover .arrow,\n.popover .arrow:after {\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n\n.popover .arrow {\n  border-width: 11px;\n}\n\n.popover .arrow:after {\n  border-width: 10px;\n  content: \"\";\n}\n\n.popover.top .arrow {\n  bottom: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-top-color: #999999;\n  border-top-color: rgba(0, 0, 0, 0.25);\n  border-bottom-width: 0;\n}\n\n.popover.top .arrow:after {\n  bottom: 1px;\n  margin-left: -10px;\n  border-top-color: #ffffff;\n  border-bottom-width: 0;\n  content: \" \";\n}\n\n.popover.right .arrow {\n  top: 50%;\n  left: -11px;\n  margin-top: -11px;\n  border-right-color: #999999;\n  border-right-color: rgba(0, 0, 0, 0.25);\n  border-left-width: 0;\n}\n\n.popover.right .arrow:after {\n  bottom: -10px;\n  left: 1px;\n  border-right-color: #ffffff;\n  border-left-width: 0;\n  content: \" \";\n}\n\n.popover.bottom .arrow {\n  top: -11px;\n  left: 50%;\n  margin-left: -11px;\n  border-bottom-color: #999999;\n  border-bottom-color: rgba(0, 0, 0, 0.25);\n  border-top-width: 0;\n}\n\n.popover.bottom .arrow:after {\n  top: 1px;\n  margin-left: -10px;\n  border-bottom-color: #ffffff;\n  border-top-width: 0;\n  content: \" \";\n}\n\n.popover.left .arrow {\n  top: 50%;\n  right: -11px;\n  margin-top: -11px;\n  border-left-color: #999999;\n  border-left-color: rgba(0, 0, 0, 0.25);\n  border-right-width: 0;\n}\n\n.popover.left .arrow:after {\n  right: 1px;\n  bottom: -10px;\n  border-left-color: #ffffff;\n  border-right-width: 0;\n  content: \" \";\n}\n\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  width: 100%;\n  overflow: hidden;\n}\n\n.carousel-inner > .item {\n  position: relative;\n  display: none;\n  -webkit-transition: 0.6s ease-in-out left;\n          transition: 0.6s ease-in-out left;\n}\n\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n  display: block;\n  height: auto;\n  max-width: 100%;\n  line-height: 1;\n}\n\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  display: block;\n}\n\n.carousel-inner > .active {\n  left: 0;\n}\n\n.carousel-inner > .next,\n.carousel-inner > .prev {\n  position: absolute;\n  top: 0;\n  width: 100%;\n}\n\n.carousel-inner > .next {\n  left: 100%;\n}\n\n.carousel-inner > .prev {\n  left: -100%;\n}\n\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n  left: 0;\n}\n\n.carousel-inner > .active.left {\n  left: -100%;\n}\n\n.carousel-inner > .active.right {\n  left: 100%;\n}\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  width: 15%;\n  font-size: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n}\n\n.carousel-control.left {\n  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%));\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n\n.carousel-control.right {\n  right: 0;\n  left: auto;\n  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%));\n  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);\n  background-repeat: repeat-x;\n  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n\n.carousel-control:hover,\n.carousel-control:focus {\n  color: #ffffff;\n  text-decoration: none;\n  outline: none;\n  opacity: 0.9;\n  filter: alpha(opacity=90);\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n  position: absolute;\n  top: 50%;\n  z-index: 5;\n  display: inline-block;\n}\n\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n  left: 50%;\n}\n\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n  right: 50%;\n}\n\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n  width: 20px;\n  height: 20px;\n  margin-top: -10px;\n  margin-left: -10px;\n  font-family: serif;\n}\n\n.carousel-control .icon-prev:before {\n  content: '\\2039';\n}\n\n.carousel-control .icon-next:before {\n  content: '\\203a';\n}\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  padding-left: 0;\n  margin-left: -30%;\n  text-align: center;\n  list-style: none;\n}\n\n.carousel-indicators li {\n  display: inline-block;\n  width: 10px;\n  height: 10px;\n  margin: 1px;\n  text-indent: -999px;\n  cursor: pointer;\n  background-color: #000 \\9;\n  background-color: rgba(0, 0, 0, 0);\n  border: 1px solid #ffffff;\n  border-radius: 10px;\n}\n\n.carousel-indicators .active {\n  width: 12px;\n  height: 12px;\n  margin: 0;\n  background-color: #ffffff;\n}\n\n.carousel-caption {\n  position: absolute;\n  right: 15%;\n  bottom: 20px;\n  left: 15%;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: #ffffff;\n  text-align: center;\n  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n\n.carousel-caption .btn {\n  text-shadow: none;\n}\n\n@media screen and (min-width: 768px) {\n  .carousel-control .glyphicons-chevron-left,\n  .carousel-control .glyphicons-chevron-right,\n  .carousel-control .icon-prev,\n  .carousel-control .icon-next {\n    width: 30px;\n    height: 30px;\n    margin-top: -15px;\n    margin-left: -15px;\n    font-size: 30px;\n  }\n  .carousel-caption {\n    right: 20%;\n    left: 20%;\n    padding-bottom: 30px;\n  }\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n\n.clearfix:before,\n.clearfix:after {\n  display: table;\n  content: \" \";\n}\n\n.clearfix:after {\n  clear: both;\n}\n\n.center-block {\n  display: block;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n.pull-right {\n  float: right !important;\n}\n\n.pull-left {\n  float: left !important;\n}\n\n.hide {\n  display: none !important;\n}\n\n.show {\n  display: block !important;\n}\n\n.invisible {\n  visibility: hidden;\n}\n\n.text-hide {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n.hidden {\n  display: none !important;\n  visibility: hidden !important;\n}\n\n.affix {\n  position: fixed;\n}\n\n@-ms-viewport {\n  width: device-width;\n}\n\n.visible-xs,\ntr.visible-xs,\nth.visible-xs,\ntd.visible-xs {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-xs {\n    display: block !important;\n  }\n  table.visible-xs {\n    display: table;\n  }\n  tr.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-xs,\n  td.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-xs.visible-sm {\n    display: block !important;\n  }\n  table.visible-xs.visible-sm {\n    display: table;\n  }\n  tr.visible-xs.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-sm,\n  td.visible-xs.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-xs.visible-md {\n    display: block !important;\n  }\n  table.visible-xs.visible-md {\n    display: table;\n  }\n  tr.visible-xs.visible-md {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-md,\n  td.visible-xs.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-xs.visible-lg {\n    display: block !important;\n  }\n  table.visible-xs.visible-lg {\n    display: table;\n  }\n  tr.visible-xs.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-xs.visible-lg,\n  td.visible-xs.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-sm,\ntr.visible-sm,\nth.visible-sm,\ntd.visible-sm {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-sm.visible-xs {\n    display: block !important;\n  }\n  table.visible-sm.visible-xs {\n    display: table;\n  }\n  tr.visible-sm.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-xs,\n  td.visible-sm.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-sm {\n    display: block !important;\n  }\n  table.visible-sm {\n    display: table;\n  }\n  tr.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-sm,\n  td.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-sm.visible-md {\n    display: block !important;\n  }\n  table.visible-sm.visible-md {\n    display: table;\n  }\n  tr.visible-sm.visible-md {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-md,\n  td.visible-sm.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-sm.visible-lg {\n    display: block !important;\n  }\n  table.visible-sm.visible-lg {\n    display: table;\n  }\n  tr.visible-sm.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-sm.visible-lg,\n  td.visible-sm.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-md,\ntr.visible-md,\nth.visible-md,\ntd.visible-md {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-md.visible-xs {\n    display: block !important;\n  }\n  table.visible-md.visible-xs {\n    display: table;\n  }\n  tr.visible-md.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-md.visible-xs,\n  td.visible-md.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-md.visible-sm {\n    display: block !important;\n  }\n  table.visible-md.visible-sm {\n    display: table;\n  }\n  tr.visible-md.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-md.visible-sm,\n  td.visible-md.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-md {\n    display: block !important;\n  }\n  table.visible-md {\n    display: table;\n  }\n  tr.visible-md {\n    display: table-row !important;\n  }\n  th.visible-md,\n  td.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-md.visible-lg {\n    display: block !important;\n  }\n  table.visible-md.visible-lg {\n    display: table;\n  }\n  tr.visible-md.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-md.visible-lg,\n  td.visible-md.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.visible-lg,\ntr.visible-lg,\nth.visible-lg,\ntd.visible-lg {\n  display: none !important;\n}\n\n@media (max-width: 767px) {\n  .visible-lg.visible-xs {\n    display: block !important;\n  }\n  table.visible-lg.visible-xs {\n    display: table;\n  }\n  tr.visible-lg.visible-xs {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-xs,\n  td.visible-lg.visible-xs {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .visible-lg.visible-sm {\n    display: block !important;\n  }\n  table.visible-lg.visible-sm {\n    display: table;\n  }\n  tr.visible-lg.visible-sm {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-sm,\n  td.visible-lg.visible-sm {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .visible-lg.visible-md {\n    display: block !important;\n  }\n  table.visible-lg.visible-md {\n    display: table;\n  }\n  tr.visible-lg.visible-md {\n    display: table-row !important;\n  }\n  th.visible-lg.visible-md,\n  td.visible-lg.visible-md {\n    display: table-cell !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .visible-lg {\n    display: block !important;\n  }\n  table.visible-lg {\n    display: table;\n  }\n  tr.visible-lg {\n    display: table-row !important;\n  }\n  th.visible-lg,\n  td.visible-lg {\n    display: table-cell !important;\n  }\n}\n\n.hidden-xs {\n  display: block !important;\n}\n\ntable.hidden-xs {\n  display: table;\n}\n\ntr.hidden-xs {\n  display: table-row !important;\n}\n\nth.hidden-xs,\ntd.hidden-xs {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-xs,\n  tr.hidden-xs,\n  th.hidden-xs,\n  td.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-xs.hidden-sm,\n  tr.hidden-xs.hidden-sm,\n  th.hidden-xs.hidden-sm,\n  td.hidden-xs.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-xs.hidden-md,\n  tr.hidden-xs.hidden-md,\n  th.hidden-xs.hidden-md,\n  td.hidden-xs.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-xs.hidden-lg,\n  tr.hidden-xs.hidden-lg,\n  th.hidden-xs.hidden-lg,\n  td.hidden-xs.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-sm {\n  display: block !important;\n}\n\ntable.hidden-sm {\n  display: table;\n}\n\ntr.hidden-sm {\n  display: table-row !important;\n}\n\nth.hidden-sm,\ntd.hidden-sm {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-sm.hidden-xs,\n  tr.hidden-sm.hidden-xs,\n  th.hidden-sm.hidden-xs,\n  td.hidden-sm.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-sm,\n  tr.hidden-sm,\n  th.hidden-sm,\n  td.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-sm.hidden-md,\n  tr.hidden-sm.hidden-md,\n  th.hidden-sm.hidden-md,\n  td.hidden-sm.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-sm.hidden-lg,\n  tr.hidden-sm.hidden-lg,\n  th.hidden-sm.hidden-lg,\n  td.hidden-sm.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-md {\n  display: block !important;\n}\n\ntable.hidden-md {\n  display: table;\n}\n\ntr.hidden-md {\n  display: table-row !important;\n}\n\nth.hidden-md,\ntd.hidden-md {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-md.hidden-xs,\n  tr.hidden-md.hidden-xs,\n  th.hidden-md.hidden-xs,\n  td.hidden-md.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-md.hidden-sm,\n  tr.hidden-md.hidden-sm,\n  th.hidden-md.hidden-sm,\n  td.hidden-md.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-md,\n  tr.hidden-md,\n  th.hidden-md,\n  td.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-md.hidden-lg,\n  tr.hidden-md.hidden-lg,\n  th.hidden-md.hidden-lg,\n  td.hidden-md.hidden-lg {\n    display: none !important;\n  }\n}\n\n.hidden-lg {\n  display: block !important;\n}\n\ntable.hidden-lg {\n  display: table;\n}\n\ntr.hidden-lg {\n  display: table-row !important;\n}\n\nth.hidden-lg,\ntd.hidden-lg {\n  display: table-cell !important;\n}\n\n@media (max-width: 767px) {\n  .hidden-lg.hidden-xs,\n  tr.hidden-lg.hidden-xs,\n  th.hidden-lg.hidden-xs,\n  td.hidden-lg.hidden-xs {\n    display: none !important;\n  }\n}\n\n@media (min-width: 768px) and (max-width: 991px) {\n  .hidden-lg.hidden-sm,\n  tr.hidden-lg.hidden-sm,\n  th.hidden-lg.hidden-sm,\n  td.hidden-lg.hidden-sm {\n    display: none !important;\n  }\n}\n\n@media (min-width: 992px) and (max-width: 1199px) {\n  .hidden-lg.hidden-md,\n  tr.hidden-lg.hidden-md,\n  th.hidden-lg.hidden-md,\n  td.hidden-lg.hidden-md {\n    display: none !important;\n  }\n}\n\n@media (min-width: 1200px) {\n  .hidden-lg,\n  tr.hidden-lg,\n  th.hidden-lg,\n  td.hidden-lg {\n    display: none !important;\n  }\n}\n\n.visible-print,\ntr.visible-print,\nth.visible-print,\ntd.visible-print {\n  display: none !important;\n}\n\n@media print {\n  .visible-print {\n    display: block !important;\n  }\n  table.visible-print {\n    display: table;\n  }\n  tr.visible-print {\n    display: table-row !important;\n  }\n  th.visible-print,\n  td.visible-print {\n    display: table-cell !important;\n  }\n  .hidden-print,\n  tr.hidden-print,\n  th.hidden-print,\n  td.hidden-print {\n    display: none !important;\n  }\n}"
  },
  {
    "path": "jekyll/css/demo.css",
    "content": "#container {\n    color: #838383;\n    font-size: 12px;\n}\n\n#uploader .queueList {\n    margin: 20px;\n    border: 3px dashed #e6e6e6;\n}\n#uploader .queueList.filled {\n    padding: 17px;\n    margin: 0;\n    border: 3px dashed transparent;\n}\n#uploader .queueList.webuploader-dnd-over {\n    border: 3px dashed #999999;\n}\n\n#uploader p {margin: 0;}\n\n.element-invisible {\n    position: absolute !important;\n    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */\n    clip: rect(1px,1px,1px,1px);\n}\n\n#uploader .placeholder {\n    min-height: 350px;\n    padding-top: 178px;\n    text-align: center;\n    background: url(../images/image.png) center 93px no-repeat;\n    color: #cccccc;\n    font-size: 18px;\n    position: relative;\n}\n\n#uploader .placeholder .webuploader-pick {\n    font-size: 18px;\n    background: #00b7ee;\n    border-radius: 3px;\n    line-height: 44px;\n    padding: 0 30px;\n    *width: 120px;\n    color: #fff;\n    display: inline-block;\n    margin: 0 auto 20px auto;\n    cursor: pointer;\n    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);\n}\n\n#uploader .placeholder .webuploader-pick-hover {\n    background: #00a2d4;\n}\n\n#uploader .placeholder .flashTip {\n    color: #666666;\n    font-size: 12px;\n    position: absolute;\n    width: 100%;\n    text-align: center;\n    bottom: 20px;\n}\n#uploader .placeholder .flashTip a {\n    color: #0785d1;\n    text-decoration: none;\n}\n#uploader .placeholder .flashTip a:hover {\n    text-decoration: underline;\n}\n\n#uploader .filelist {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n}\n\n#uploader .filelist:after {\n    content: '';\n    display: block;\n    width: 0;\n    height: 0;\n    overflow: hidden;\n    clear: both;\n}\n\n#uploader .filelist li {\n    width: 110px;\n    height: 110px;\n    background: url(../images/bg.png) no-repeat;\n    text-align: center;\n    margin: 0 8px 20px 0;\n    position: relative;\n    display: inline;\n    float: left;\n    overflow: hidden;\n    font-size: 12px;\n}\n\n#uploader .filelist li p.log {\n    position: relative;\n    top: -45px;\n}\n\n#uploader .filelist li p.title {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow : ellipsis;\n    top: 5px;\n    text-indent: 5px;\n    text-align: left;\n}\n\n#uploader .filelist li p.progress {\n    position: absolute;\n    width: 100%;\n    bottom: 0;\n    left: 0;\n    height: 8px;\n    overflow: hidden;\n    z-index: 50;\n    margin: 0;\n    border-radius: 0;\n    background: none;\n    -webkit-box-shadow: 0 0 0;\n}\n#uploader .filelist li p.progress span {\n    display: none;\n    overflow: hidden;\n    width: 0;\n    height: 100%;\n    background: #1483d8 url(../images/progress.png) repeat-x;\n\n    -webit-transition: width 200ms linear;\n    -moz-transition: width 200ms linear;\n    -o-transition: width 200ms linear;\n    -ms-transition: width 200ms linear;\n    transition: width 200ms linear;\n\n    -webkit-animation: progressmove 2s linear infinite;\n    -moz-animation: progressmove 2s linear infinite;\n    -o-animation: progressmove 2s linear infinite;\n    -ms-animation: progressmove 2s linear infinite;\n    animation: progressmove 2s linear infinite;\n\n    -webkit-transform: translateZ(0);\n}\n\n@-webkit-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@-moz-keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n@keyframes progressmove {\n    0% {\n       background-position: 0 0;\n    }\n    100% {\n       background-position: 17px 0;\n    }\n}\n\n#uploader .filelist li p.imgWrap {\n    position: relative;\n    z-index: 2;\n    line-height: 110px;\n    vertical-align: middle;\n    overflow: hidden;\n    width: 110px;\n    height: 110px;\n\n    -webkit-transform-origin: 50% 50%;\n    -moz-transform-origin: 50% 50%;\n    -o-transform-origin: 50% 50%;\n    -ms-transform-origin: 50% 50%;\n    transform-origin: 50% 50%;\n\n    -webit-transition: 200ms ease-out;\n    -moz-transition: 200ms ease-out;\n    -o-transition: 200ms ease-out;\n    -ms-transition: 200ms ease-out;\n    transition: 200ms ease-out;\n}\n\n#uploader .filelist li img {\n    width: 100%;\n}\n\n#uploader .filelist li p.error {\n    background: #f43838;\n    color: #fff;\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    height: 28px;\n    line-height: 28px;\n    width: 100%;\n    z-index: 100;\n}\n\n#uploader .filelist li .success {\n    display: block;\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    height: 40px;\n    width: 100%;\n    z-index: 200;\n    background: url(../images/success.png) no-repeat right bottom;\n}\n\n#uploader .filelist div.file-panel {\n    position: absolute;\n    height: 0;\n    filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\\0;\n    background: rgba( 0, 0, 0, 0.5 );\n    width: 100%;\n    top: 0;\n    left: 0;\n    overflow: hidden;\n    z-index: 300;\n}\n\n#uploader .filelist div.file-panel span {\n    width: 24px;\n    height: 24px;\n    display: inline;\n    float: right;\n    text-indent: -9999px;\n    overflow: hidden;\n    background: url(../images/icons.png) no-repeat;\n    margin: 5px 1px 1px;\n    cursor: pointer;\n}\n\n#uploader .filelist div.file-panel span.rotateLeft {\n    background-position: 0 -24px;\n}\n#uploader .filelist div.file-panel span.rotateLeft:hover {\n    background-position: 0 0;\n}\n\n#uploader .filelist div.file-panel span.rotateRight {\n    background-position: -24px -24px;\n}\n#uploader .filelist div.file-panel span.rotateRight:hover {\n    background-position: -24px 0;\n}\n\n#uploader .filelist div.file-panel span.cancel {\n    background-position: -48px -24px;\n}\n#uploader .filelist div.file-panel span.cancel:hover {\n    background-position: -48px 0;\n}\n\n#uploader .statusBar {\n    height: 63px;\n    border-top: 1px solid #dadada;\n    padding: 0 20px;\n    line-height: 63px;\n    vertical-align: middle;\n    position: relative;\n}\n\n#uploader .statusBar .progress {\n    border: 1px solid #1483d8;\n    width: 198px;\n    background: #fff;\n    height: 18px;\n    position: relative;\n    display: inline-block;\n    text-align: center;\n    line-height: 20px;\n    color: #6dbfff;\n    position: relative;\n    margin: 0 10px 0 0;\n}\n#uploader .statusBar .progress span.percentage {\n    width: 0;\n    height: 100%;\n    left: 0;\n    top: 0;\n    background: #1483d8;\n    position: absolute;\n}\n#uploader .statusBar .progress span.text {\n    position: relative;\n    z-index: 10;\n}\n\n#uploader .statusBar .info {\n    display: inline-block;\n    font-size: 14px;\n    color: #666666;\n}\n\n#uploader .statusBar .btns {\n    position: absolute;\n    top: 10px;\n    right: 20px;\n    line-height: 40px;\n}\n\n#filePicker2 {\n    display: inline-block;\n    float: left;\n}\n\n#uploader .statusBar .btns .webuploader-pick,\n#uploader .statusBar .btns .uploadBtn,\n#uploader .statusBar .btns .uploadBtn.state-uploading,\n#uploader .statusBar .btns .uploadBtn.state-paused {\n    background: #ffffff;\n    border: 1px solid #cfcfcf;\n    color: #565656;\n    padding: 0 18px;\n    display: inline-block;\n    border-radius: 3px;\n    margin-left: 10px;\n    cursor: pointer;\n    font-size: 14px;\n    float: left;\n}\n#uploader .statusBar .btns .webuploader-pick-hover,\n#uploader .statusBar .btns .uploadBtn:hover,\n#uploader .statusBar .btns .uploadBtn.state-uploading:hover,\n#uploader .statusBar .btns .uploadBtn.state-paused:hover {\n    background: #f0f0f0;\n}\n\n#uploader .statusBar .btns .uploadBtn {\n    background: #00b7ee;\n    color: #fff;\n    border-color: transparent;\n}\n#uploader .statusBar .btns .uploadBtn:hover {\n    background: #00a2d4;\n}\n\n#uploader .statusBar .btns .uploadBtn.disabled {\n    pointer-events: none;\n    opacity: 0.6;\n}"
  },
  {
    "path": "jekyll/css/font-awesome.css",
    "content": "/*!\n *  Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n */\n/* FONT PATH\n * -------------------------- */\n@font-face {\n  font-family: 'FontAwesome';\n  src: url('../fonts/fontawesome-webfont.eot?v=4.0.3');\n  src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');\n  font-weight: normal;\n  font-style: normal;\n}\n.fa {\n  display: inline-block;\n  font-family: FontAwesome;\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n/* makes the font 33% larger relative to the icon container */\n.fa-lg {\n  font-size: 1.3333333333333333em;\n  line-height: 0.75em;\n  vertical-align: -15%;\n}\n.fa-2x {\n  font-size: 2em;\n}\n.fa-3x {\n  font-size: 3em;\n}\n.fa-4x {\n  font-size: 4em;\n}\n.fa-5x {\n  font-size: 5em;\n}\n.fa-fw {\n  width: 1.2857142857142858em;\n  text-align: center;\n}\n.fa-ul {\n  padding-left: 0;\n  margin-left: 2.142857142857143em;\n  list-style-type: none;\n}\n.fa-ul > li {\n  position: relative;\n}\n.fa-li {\n  position: absolute;\n  left: -2.142857142857143em;\n  width: 2.142857142857143em;\n  top: 0.14285714285714285em;\n  text-align: center;\n}\n.fa-li.fa-lg {\n  left: -1.8571428571428572em;\n}\n.fa-border {\n  padding: .2em .25em .15em;\n  border: solid 0.08em #eeeeee;\n  border-radius: .1em;\n}\n.pull-right {\n  float: right;\n}\n.pull-left {\n  float: left;\n}\n.fa.pull-left {\n  margin-right: .3em;\n}\n.fa.pull-right {\n  margin-left: .3em;\n}\n.fa-spin {\n  -webkit-animation: spin 2s infinite linear;\n  -moz-animation: spin 2s infinite linear;\n  -o-animation: spin 2s infinite linear;\n  animation: spin 2s infinite linear;\n}\n@-moz-keyframes spin {\n  0% {\n    -moz-transform: rotate(0deg);\n  }\n  100% {\n    -moz-transform: rotate(359deg);\n  }\n}\n@-webkit-keyframes spin {\n  0% {\n    -webkit-transform: rotate(0deg);\n  }\n  100% {\n    -webkit-transform: rotate(359deg);\n  }\n}\n@-o-keyframes spin {\n  0% {\n    -o-transform: rotate(0deg);\n  }\n  100% {\n    -o-transform: rotate(359deg);\n  }\n}\n@-ms-keyframes spin {\n  0% {\n    -ms-transform: rotate(0deg);\n  }\n  100% {\n    -ms-transform: rotate(359deg);\n  }\n}\n@keyframes spin {\n  0% {\n    transform: rotate(0deg);\n  }\n  100% {\n    transform: rotate(359deg);\n  }\n}\n.fa-rotate-90 {\n  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);\n  -webkit-transform: rotate(90deg);\n  -moz-transform: rotate(90deg);\n  -ms-transform: rotate(90deg);\n  -o-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n.fa-rotate-180 {\n  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);\n  -webkit-transform: rotate(180deg);\n  -moz-transform: rotate(180deg);\n  -ms-transform: rotate(180deg);\n  -o-transform: rotate(180deg);\n  transform: rotate(180deg);\n}\n.fa-rotate-270 {\n  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);\n  -webkit-transform: rotate(270deg);\n  -moz-transform: rotate(270deg);\n  -ms-transform: rotate(270deg);\n  -o-transform: rotate(270deg);\n  transform: rotate(270deg);\n}\n.fa-flip-horizontal {\n  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);\n  -webkit-transform: scale(-1, 1);\n  -moz-transform: scale(-1, 1);\n  -ms-transform: scale(-1, 1);\n  -o-transform: scale(-1, 1);\n  transform: scale(-1, 1);\n}\n.fa-flip-vertical {\n  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);\n  -webkit-transform: scale(1, -1);\n  -moz-transform: scale(1, -1);\n  -ms-transform: scale(1, -1);\n  -o-transform: scale(1, -1);\n  transform: scale(1, -1);\n}\n.fa-stack {\n  position: relative;\n  display: inline-block;\n  width: 2em;\n  height: 2em;\n  line-height: 2em;\n  vertical-align: middle;\n}\n.fa-stack-1x,\n.fa-stack-2x {\n  position: absolute;\n  left: 0;\n  width: 100%;\n  text-align: center;\n}\n.fa-stack-1x {\n  line-height: inherit;\n}\n.fa-stack-2x {\n  font-size: 2em;\n}\n.fa-inverse {\n  color: #ffffff;\n}\n/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen\n   readers do not read off random characters that represent icons */\n.fa-glass:before {\n  content: \"\\f000\";\n}\n.fa-music:before {\n  content: \"\\f001\";\n}\n.fa-search:before {\n  content: \"\\f002\";\n}\n.fa-envelope-o:before {\n  content: \"\\f003\";\n}\n.fa-heart:before {\n  content: \"\\f004\";\n}\n.fa-star:before {\n  content: \"\\f005\";\n}\n.fa-star-o:before {\n  content: \"\\f006\";\n}\n.fa-user:before {\n  content: \"\\f007\";\n}\n.fa-film:before {\n  content: \"\\f008\";\n}\n.fa-th-large:before {\n  content: \"\\f009\";\n}\n.fa-th:before {\n  content: \"\\f00a\";\n}\n.fa-th-list:before {\n  content: \"\\f00b\";\n}\n.fa-check:before {\n  content: \"\\f00c\";\n}\n.fa-times:before {\n  content: \"\\f00d\";\n}\n.fa-search-plus:before {\n  content: \"\\f00e\";\n}\n.fa-search-minus:before {\n  content: \"\\f010\";\n}\n.fa-power-off:before {\n  content: \"\\f011\";\n}\n.fa-signal:before {\n  content: \"\\f012\";\n}\n.fa-gear:before,\n.fa-cog:before {\n  content: \"\\f013\";\n}\n.fa-trash-o:before {\n  content: \"\\f014\";\n}\n.fa-home:before {\n  content: \"\\f015\";\n}\n.fa-file-o:before {\n  content: \"\\f016\";\n}\n.fa-clock-o:before {\n  content: \"\\f017\";\n}\n.fa-road:before {\n  content: \"\\f018\";\n}\n.fa-download:before {\n  content: \"\\f019\";\n}\n.fa-arrow-circle-o-down:before {\n  content: \"\\f01a\";\n}\n.fa-arrow-circle-o-up:before {\n  content: \"\\f01b\";\n}\n.fa-inbox:before {\n  content: \"\\f01c\";\n}\n.fa-play-circle-o:before {\n  content: \"\\f01d\";\n}\n.fa-rotate-right:before,\n.fa-repeat:before {\n  content: \"\\f01e\";\n}\n.fa-refresh:before {\n  content: \"\\f021\";\n}\n.fa-list-alt:before {\n  content: \"\\f022\";\n}\n.fa-lock:before {\n  content: \"\\f023\";\n}\n.fa-flag:before {\n  content: \"\\f024\";\n}\n.fa-headphones:before {\n  content: \"\\f025\";\n}\n.fa-volume-off:before {\n  content: \"\\f026\";\n}\n.fa-volume-down:before {\n  content: \"\\f027\";\n}\n.fa-volume-up:before {\n  content: \"\\f028\";\n}\n.fa-qrcode:before {\n  content: \"\\f029\";\n}\n.fa-barcode:before {\n  content: \"\\f02a\";\n}\n.fa-tag:before {\n  content: \"\\f02b\";\n}\n.fa-tags:before {\n  content: \"\\f02c\";\n}\n.fa-book:before {\n  content: \"\\f02d\";\n}\n.fa-bookmark:before {\n  content: \"\\f02e\";\n}\n.fa-print:before {\n  content: \"\\f02f\";\n}\n.fa-camera:before {\n  content: \"\\f030\";\n}\n.fa-font:before {\n  content: \"\\f031\";\n}\n.fa-bold:before {\n  content: \"\\f032\";\n}\n.fa-italic:before {\n  content: \"\\f033\";\n}\n.fa-text-height:before {\n  content: \"\\f034\";\n}\n.fa-text-width:before {\n  content: \"\\f035\";\n}\n.fa-align-left:before {\n  content: \"\\f036\";\n}\n.fa-align-center:before {\n  content: \"\\f037\";\n}\n.fa-align-right:before {\n  content: \"\\f038\";\n}\n.fa-align-justify:before {\n  content: \"\\f039\";\n}\n.fa-list:before {\n  content: \"\\f03a\";\n}\n.fa-dedent:before,\n.fa-outdent:before {\n  content: \"\\f03b\";\n}\n.fa-indent:before {\n  content: \"\\f03c\";\n}\n.fa-video-camera:before {\n  content: \"\\f03d\";\n}\n.fa-picture-o:before {\n  content: \"\\f03e\";\n}\n.fa-pencil:before {\n  content: \"\\f040\";\n}\n.fa-map-marker:before {\n  content: \"\\f041\";\n}\n.fa-adjust:before {\n  content: \"\\f042\";\n}\n.fa-tint:before {\n  content: \"\\f043\";\n}\n.fa-edit:before,\n.fa-pencil-square-o:before {\n  content: \"\\f044\";\n}\n.fa-share-square-o:before {\n  content: \"\\f045\";\n}\n.fa-check-square-o:before {\n  content: \"\\f046\";\n}\n.fa-arrows:before {\n  content: \"\\f047\";\n}\n.fa-step-backward:before {\n  content: \"\\f048\";\n}\n.fa-fast-backward:before {\n  content: \"\\f049\";\n}\n.fa-backward:before {\n  content: \"\\f04a\";\n}\n.fa-play:before {\n  content: \"\\f04b\";\n}\n.fa-pause:before {\n  content: \"\\f04c\";\n}\n.fa-stop:before {\n  content: \"\\f04d\";\n}\n.fa-forward:before {\n  content: \"\\f04e\";\n}\n.fa-fast-forward:before {\n  content: \"\\f050\";\n}\n.fa-step-forward:before {\n  content: \"\\f051\";\n}\n.fa-eject:before {\n  content: \"\\f052\";\n}\n.fa-chevron-left:before {\n  content: \"\\f053\";\n}\n.fa-chevron-right:before {\n  content: \"\\f054\";\n}\n.fa-plus-circle:before {\n  content: \"\\f055\";\n}\n.fa-minus-circle:before {\n  content: \"\\f056\";\n}\n.fa-times-circle:before {\n  content: \"\\f057\";\n}\n.fa-check-circle:before {\n  content: \"\\f058\";\n}\n.fa-question-circle:before {\n  content: \"\\f059\";\n}\n.fa-info-circle:before {\n  content: \"\\f05a\";\n}\n.fa-crosshairs:before {\n  content: \"\\f05b\";\n}\n.fa-times-circle-o:before {\n  content: \"\\f05c\";\n}\n.fa-check-circle-o:before {\n  content: \"\\f05d\";\n}\n.fa-ban:before {\n  content: \"\\f05e\";\n}\n.fa-arrow-left:before {\n  content: \"\\f060\";\n}\n.fa-arrow-right:before {\n  content: \"\\f061\";\n}\n.fa-arrow-up:before {\n  content: \"\\f062\";\n}\n.fa-arrow-down:before {\n  content: \"\\f063\";\n}\n.fa-mail-forward:before,\n.fa-share:before {\n  content: \"\\f064\";\n}\n.fa-expand:before {\n  content: \"\\f065\";\n}\n.fa-compress:before {\n  content: \"\\f066\";\n}\n.fa-plus:before {\n  content: \"\\f067\";\n}\n.fa-minus:before {\n  content: \"\\f068\";\n}\n.fa-asterisk:before {\n  content: \"\\f069\";\n}\n.fa-exclamation-circle:before {\n  content: \"\\f06a\";\n}\n.fa-gift:before {\n  content: \"\\f06b\";\n}\n.fa-leaf:before {\n  content: \"\\f06c\";\n}\n.fa-fire:before {\n  content: \"\\f06d\";\n}\n.fa-eye:before {\n  content: \"\\f06e\";\n}\n.fa-eye-slash:before {\n  content: \"\\f070\";\n}\n.fa-warning:before,\n.fa-exclamation-triangle:before {\n  content: \"\\f071\";\n}\n.fa-plane:before {\n  content: \"\\f072\";\n}\n.fa-calendar:before {\n  content: \"\\f073\";\n}\n.fa-random:before {\n  content: \"\\f074\";\n}\n.fa-comment:before {\n  content: \"\\f075\";\n}\n.fa-magnet:before {\n  content: \"\\f076\";\n}\n.fa-chevron-up:before {\n  content: \"\\f077\";\n}\n.fa-chevron-down:before {\n  content: \"\\f078\";\n}\n.fa-retweet:before {\n  content: \"\\f079\";\n}\n.fa-shopping-cart:before {\n  content: \"\\f07a\";\n}\n.fa-folder:before {\n  content: \"\\f07b\";\n}\n.fa-folder-open:before {\n  content: \"\\f07c\";\n}\n.fa-arrows-v:before {\n  content: \"\\f07d\";\n}\n.fa-arrows-h:before {\n  content: \"\\f07e\";\n}\n.fa-bar-chart-o:before {\n  content: \"\\f080\";\n}\n.fa-twitter-square:before {\n  content: \"\\f081\";\n}\n.fa-facebook-square:before {\n  content: \"\\f082\";\n}\n.fa-camera-retro:before {\n  content: \"\\f083\";\n}\n.fa-key:before {\n  content: \"\\f084\";\n}\n.fa-gears:before,\n.fa-cogs:before {\n  content: \"\\f085\";\n}\n.fa-comments:before {\n  content: \"\\f086\";\n}\n.fa-thumbs-o-up:before {\n  content: \"\\f087\";\n}\n.fa-thumbs-o-down:before {\n  content: \"\\f088\";\n}\n.fa-star-half:before {\n  content: \"\\f089\";\n}\n.fa-heart-o:before {\n  content: \"\\f08a\";\n}\n.fa-sign-out:before {\n  content: \"\\f08b\";\n}\n.fa-linkedin-square:before {\n  content: \"\\f08c\";\n}\n.fa-thumb-tack:before {\n  content: \"\\f08d\";\n}\n.fa-external-link:before {\n  content: \"\\f08e\";\n}\n.fa-sign-in:before {\n  content: \"\\f090\";\n}\n.fa-trophy:before {\n  content: \"\\f091\";\n}\n.fa-github-square:before {\n  content: \"\\f092\";\n}\n.fa-upload:before {\n  content: \"\\f093\";\n}\n.fa-lemon-o:before {\n  content: \"\\f094\";\n}\n.fa-phone:before {\n  content: \"\\f095\";\n}\n.fa-square-o:before {\n  content: \"\\f096\";\n}\n.fa-bookmark-o:before {\n  content: \"\\f097\";\n}\n.fa-phone-square:before {\n  content: \"\\f098\";\n}\n.fa-twitter:before {\n  content: \"\\f099\";\n}\n.fa-facebook:before {\n  content: \"\\f09a\";\n}\n.fa-github:before {\n  content: \"\\f09b\";\n}\n.fa-unlock:before {\n  content: \"\\f09c\";\n}\n.fa-credit-card:before {\n  content: \"\\f09d\";\n}\n.fa-rss:before {\n  content: \"\\f09e\";\n}\n.fa-hdd-o:before {\n  content: \"\\f0a0\";\n}\n.fa-bullhorn:before {\n  content: \"\\f0a1\";\n}\n.fa-bell:before {\n  content: \"\\f0f3\";\n}\n.fa-certificate:before {\n  content: \"\\f0a3\";\n}\n.fa-hand-o-right:before {\n  content: \"\\f0a4\";\n}\n.fa-hand-o-left:before {\n  content: \"\\f0a5\";\n}\n.fa-hand-o-up:before {\n  content: \"\\f0a6\";\n}\n.fa-hand-o-down:before {\n  content: \"\\f0a7\";\n}\n.fa-arrow-circle-left:before {\n  content: \"\\f0a8\";\n}\n.fa-arrow-circle-right:before {\n  content: \"\\f0a9\";\n}\n.fa-arrow-circle-up:before {\n  content: \"\\f0aa\";\n}\n.fa-arrow-circle-down:before {\n  content: \"\\f0ab\";\n}\n.fa-globe:before {\n  content: \"\\f0ac\";\n}\n.fa-wrench:before {\n  content: \"\\f0ad\";\n}\n.fa-tasks:before {\n  content: \"\\f0ae\";\n}\n.fa-filter:before {\n  content: \"\\f0b0\";\n}\n.fa-briefcase:before {\n  content: \"\\f0b1\";\n}\n.fa-arrows-alt:before {\n  content: \"\\f0b2\";\n}\n.fa-group:before,\n.fa-users:before {\n  content: \"\\f0c0\";\n}\n.fa-chain:before,\n.fa-link:before {\n  content: \"\\f0c1\";\n}\n.fa-cloud:before {\n  content: \"\\f0c2\";\n}\n.fa-flask:before {\n  content: \"\\f0c3\";\n}\n.fa-cut:before,\n.fa-scissors:before {\n  content: \"\\f0c4\";\n}\n.fa-copy:before,\n.fa-files-o:before {\n  content: \"\\f0c5\";\n}\n.fa-paperclip:before {\n  content: \"\\f0c6\";\n}\n.fa-save:before,\n.fa-floppy-o:before {\n  content: \"\\f0c7\";\n}\n.fa-square:before {\n  content: \"\\f0c8\";\n}\n.fa-bars:before {\n  content: \"\\f0c9\";\n}\n.fa-list-ul:before {\n  content: \"\\f0ca\";\n}\n.fa-list-ol:before {\n  content: \"\\f0cb\";\n}\n.fa-strikethrough:before {\n  content: \"\\f0cc\";\n}\n.fa-underline:before {\n  content: \"\\f0cd\";\n}\n.fa-table:before {\n  content: \"\\f0ce\";\n}\n.fa-magic:before {\n  content: \"\\f0d0\";\n}\n.fa-truck:before {\n  content: \"\\f0d1\";\n}\n.fa-pinterest:before {\n  content: \"\\f0d2\";\n}\n.fa-pinterest-square:before {\n  content: \"\\f0d3\";\n}\n.fa-google-plus-square:before {\n  content: \"\\f0d4\";\n}\n.fa-google-plus:before {\n  content: \"\\f0d5\";\n}\n.fa-money:before {\n  content: \"\\f0d6\";\n}\n.fa-caret-down:before {\n  content: \"\\f0d7\";\n}\n.fa-caret-up:before {\n  content: \"\\f0d8\";\n}\n.fa-caret-left:before {\n  content: \"\\f0d9\";\n}\n.fa-caret-right:before {\n  content: \"\\f0da\";\n}\n.fa-columns:before {\n  content: \"\\f0db\";\n}\n.fa-unsorted:before,\n.fa-sort:before {\n  content: \"\\f0dc\";\n}\n.fa-sort-down:before,\n.fa-sort-asc:before {\n  content: \"\\f0dd\";\n}\n.fa-sort-up:before,\n.fa-sort-desc:before {\n  content: \"\\f0de\";\n}\n.fa-envelope:before {\n  content: \"\\f0e0\";\n}\n.fa-linkedin:before {\n  content: \"\\f0e1\";\n}\n.fa-rotate-left:before,\n.fa-undo:before {\n  content: \"\\f0e2\";\n}\n.fa-legal:before,\n.fa-gavel:before {\n  content: \"\\f0e3\";\n}\n.fa-dashboard:before,\n.fa-tachometer:before {\n  content: \"\\f0e4\";\n}\n.fa-comment-o:before {\n  content: \"\\f0e5\";\n}\n.fa-comments-o:before {\n  content: \"\\f0e6\";\n}\n.fa-flash:before,\n.fa-bolt:before {\n  content: \"\\f0e7\";\n}\n.fa-sitemap:before {\n  content: \"\\f0e8\";\n}\n.fa-umbrella:before {\n  content: \"\\f0e9\";\n}\n.fa-paste:before,\n.fa-clipboard:before {\n  content: \"\\f0ea\";\n}\n.fa-lightbulb-o:before {\n  content: \"\\f0eb\";\n}\n.fa-exchange:before {\n  content: \"\\f0ec\";\n}\n.fa-cloud-download:before {\n  content: \"\\f0ed\";\n}\n.fa-cloud-upload:before {\n  content: \"\\f0ee\";\n}\n.fa-user-md:before {\n  content: \"\\f0f0\";\n}\n.fa-stethoscope:before {\n  content: \"\\f0f1\";\n}\n.fa-suitcase:before {\n  content: \"\\f0f2\";\n}\n.fa-bell-o:before {\n  content: \"\\f0a2\";\n}\n.fa-coffee:before {\n  content: \"\\f0f4\";\n}\n.fa-cutlery:before {\n  content: \"\\f0f5\";\n}\n.fa-file-text-o:before {\n  content: \"\\f0f6\";\n}\n.fa-building-o:before {\n  content: \"\\f0f7\";\n}\n.fa-hospital-o:before {\n  content: \"\\f0f8\";\n}\n.fa-ambulance:before {\n  content: \"\\f0f9\";\n}\n.fa-medkit:before {\n  content: \"\\f0fa\";\n}\n.fa-fighter-jet:before {\n  content: \"\\f0fb\";\n}\n.fa-beer:before {\n  content: \"\\f0fc\";\n}\n.fa-h-square:before {\n  content: \"\\f0fd\";\n}\n.fa-plus-square:before {\n  content: \"\\f0fe\";\n}\n.fa-angle-double-left:before {\n  content: \"\\f100\";\n}\n.fa-angle-double-right:before {\n  content: \"\\f101\";\n}\n.fa-angle-double-up:before {\n  content: \"\\f102\";\n}\n.fa-angle-double-down:before {\n  content: \"\\f103\";\n}\n.fa-angle-left:before {\n  content: \"\\f104\";\n}\n.fa-angle-right:before {\n  content: \"\\f105\";\n}\n.fa-angle-up:before {\n  content: \"\\f106\";\n}\n.fa-angle-down:before {\n  content: \"\\f107\";\n}\n.fa-desktop:before {\n  content: \"\\f108\";\n}\n.fa-laptop:before {\n  content: \"\\f109\";\n}\n.fa-tablet:before {\n  content: \"\\f10a\";\n}\n.fa-mobile-phone:before,\n.fa-mobile:before {\n  content: \"\\f10b\";\n}\n.fa-circle-o:before {\n  content: \"\\f10c\";\n}\n.fa-quote-left:before {\n  content: \"\\f10d\";\n}\n.fa-quote-right:before {\n  content: \"\\f10e\";\n}\n.fa-spinner:before {\n  content: \"\\f110\";\n}\n.fa-circle:before {\n  content: \"\\f111\";\n}\n.fa-mail-reply:before,\n.fa-reply:before {\n  content: \"\\f112\";\n}\n.fa-github-alt:before {\n  content: \"\\f113\";\n}\n.fa-folder-o:before {\n  content: \"\\f114\";\n}\n.fa-folder-open-o:before {\n  content: \"\\f115\";\n}\n.fa-smile-o:before {\n  content: \"\\f118\";\n}\n.fa-frown-o:before {\n  content: \"\\f119\";\n}\n.fa-meh-o:before {\n  content: \"\\f11a\";\n}\n.fa-gamepad:before {\n  content: \"\\f11b\";\n}\n.fa-keyboard-o:before {\n  content: \"\\f11c\";\n}\n.fa-flag-o:before {\n  content: \"\\f11d\";\n}\n.fa-flag-checkered:before {\n  content: \"\\f11e\";\n}\n.fa-terminal:before {\n  content: \"\\f120\";\n}\n.fa-code:before {\n  content: \"\\f121\";\n}\n.fa-reply-all:before {\n  content: \"\\f122\";\n}\n.fa-mail-reply-all:before {\n  content: \"\\f122\";\n}\n.fa-star-half-empty:before,\n.fa-star-half-full:before,\n.fa-star-half-o:before {\n  content: \"\\f123\";\n}\n.fa-location-arrow:before {\n  content: \"\\f124\";\n}\n.fa-crop:before {\n  content: \"\\f125\";\n}\n.fa-code-fork:before {\n  content: \"\\f126\";\n}\n.fa-unlink:before,\n.fa-chain-broken:before {\n  content: \"\\f127\";\n}\n.fa-question:before {\n  content: \"\\f128\";\n}\n.fa-info:before {\n  content: \"\\f129\";\n}\n.fa-exclamation:before {\n  content: \"\\f12a\";\n}\n.fa-superscript:before {\n  content: \"\\f12b\";\n}\n.fa-subscript:before {\n  content: \"\\f12c\";\n}\n.fa-eraser:before {\n  content: \"\\f12d\";\n}\n.fa-puzzle-piece:before {\n  content: \"\\f12e\";\n}\n.fa-microphone:before {\n  content: \"\\f130\";\n}\n.fa-microphone-slash:before {\n  content: \"\\f131\";\n}\n.fa-shield:before {\n  content: \"\\f132\";\n}\n.fa-calendar-o:before {\n  content: \"\\f133\";\n}\n.fa-fire-extinguisher:before {\n  content: \"\\f134\";\n}\n.fa-rocket:before {\n  content: \"\\f135\";\n}\n.fa-maxcdn:before {\n  content: \"\\f136\";\n}\n.fa-chevron-circle-left:before {\n  content: \"\\f137\";\n}\n.fa-chevron-circle-right:before {\n  content: \"\\f138\";\n}\n.fa-chevron-circle-up:before {\n  content: \"\\f139\";\n}\n.fa-chevron-circle-down:before {\n  content: \"\\f13a\";\n}\n.fa-html5:before {\n  content: \"\\f13b\";\n}\n.fa-css3:before {\n  content: \"\\f13c\";\n}\n.fa-anchor:before {\n  content: \"\\f13d\";\n}\n.fa-unlock-alt:before {\n  content: \"\\f13e\";\n}\n.fa-bullseye:before {\n  content: \"\\f140\";\n}\n.fa-ellipsis-h:before {\n  content: \"\\f141\";\n}\n.fa-ellipsis-v:before {\n  content: \"\\f142\";\n}\n.fa-rss-square:before {\n  content: \"\\f143\";\n}\n.fa-play-circle:before {\n  content: \"\\f144\";\n}\n.fa-ticket:before {\n  content: \"\\f145\";\n}\n.fa-minus-square:before {\n  content: \"\\f146\";\n}\n.fa-minus-square-o:before {\n  content: \"\\f147\";\n}\n.fa-level-up:before {\n  content: \"\\f148\";\n}\n.fa-level-down:before {\n  content: \"\\f149\";\n}\n.fa-check-square:before {\n  content: \"\\f14a\";\n}\n.fa-pencil-square:before {\n  content: \"\\f14b\";\n}\n.fa-external-link-square:before {\n  content: \"\\f14c\";\n}\n.fa-share-square:before {\n  content: \"\\f14d\";\n}\n.fa-compass:before {\n  content: \"\\f14e\";\n}\n.fa-toggle-down:before,\n.fa-caret-square-o-down:before {\n  content: \"\\f150\";\n}\n.fa-toggle-up:before,\n.fa-caret-square-o-up:before {\n  content: \"\\f151\";\n}\n.fa-toggle-right:before,\n.fa-caret-square-o-right:before {\n  content: \"\\f152\";\n}\n.fa-euro:before,\n.fa-eur:before {\n  content: \"\\f153\";\n}\n.fa-gbp:before {\n  content: \"\\f154\";\n}\n.fa-dollar:before,\n.fa-usd:before {\n  content: \"\\f155\";\n}\n.fa-rupee:before,\n.fa-inr:before {\n  content: \"\\f156\";\n}\n.fa-cny:before,\n.fa-rmb:before,\n.fa-yen:before,\n.fa-jpy:before {\n  content: \"\\f157\";\n}\n.fa-ruble:before,\n.fa-rouble:before,\n.fa-rub:before {\n  content: \"\\f158\";\n}\n.fa-won:before,\n.fa-krw:before {\n  content: \"\\f159\";\n}\n.fa-bitcoin:before,\n.fa-btc:before {\n  content: \"\\f15a\";\n}\n.fa-file:before {\n  content: \"\\f15b\";\n}\n.fa-file-text:before {\n  content: \"\\f15c\";\n}\n.fa-sort-alpha-asc:before {\n  content: \"\\f15d\";\n}\n.fa-sort-alpha-desc:before {\n  content: \"\\f15e\";\n}\n.fa-sort-amount-asc:before {\n  content: \"\\f160\";\n}\n.fa-sort-amount-desc:before {\n  content: \"\\f161\";\n}\n.fa-sort-numeric-asc:before {\n  content: \"\\f162\";\n}\n.fa-sort-numeric-desc:before {\n  content: \"\\f163\";\n}\n.fa-thumbs-up:before {\n  content: \"\\f164\";\n}\n.fa-thumbs-down:before {\n  content: \"\\f165\";\n}\n.fa-youtube-square:before {\n  content: \"\\f166\";\n}\n.fa-youtube:before {\n  content: \"\\f167\";\n}\n.fa-xing:before {\n  content: \"\\f168\";\n}\n.fa-xing-square:before {\n  content: \"\\f169\";\n}\n.fa-youtube-play:before {\n  content: \"\\f16a\";\n}\n.fa-dropbox:before {\n  content: \"\\f16b\";\n}\n.fa-stack-overflow:before {\n  content: \"\\f16c\";\n}\n.fa-instagram:before {\n  content: \"\\f16d\";\n}\n.fa-flickr:before {\n  content: \"\\f16e\";\n}\n.fa-adn:before {\n  content: \"\\f170\";\n}\n.fa-bitbucket:before {\n  content: \"\\f171\";\n}\n.fa-bitbucket-square:before {\n  content: \"\\f172\";\n}\n.fa-tumblr:before {\n  content: \"\\f173\";\n}\n.fa-tumblr-square:before {\n  content: \"\\f174\";\n}\n.fa-long-arrow-down:before {\n  content: \"\\f175\";\n}\n.fa-long-arrow-up:before {\n  content: \"\\f176\";\n}\n.fa-long-arrow-left:before {\n  content: \"\\f177\";\n}\n.fa-long-arrow-right:before {\n  content: \"\\f178\";\n}\n.fa-apple:before {\n  content: \"\\f179\";\n}\n.fa-windows:before {\n  content: \"\\f17a\";\n}\n.fa-android:before {\n  content: \"\\f17b\";\n}\n.fa-linux:before {\n  content: \"\\f17c\";\n}\n.fa-dribbble:before {\n  content: \"\\f17d\";\n}\n.fa-skype:before {\n  content: \"\\f17e\";\n}\n.fa-foursquare:before {\n  content: \"\\f180\";\n}\n.fa-trello:before {\n  content: \"\\f181\";\n}\n.fa-female:before {\n  content: \"\\f182\";\n}\n.fa-male:before {\n  content: \"\\f183\";\n}\n.fa-gittip:before {\n  content: \"\\f184\";\n}\n.fa-sun-o:before {\n  content: \"\\f185\";\n}\n.fa-moon-o:before {\n  content: \"\\f186\";\n}\n.fa-archive:before {\n  content: \"\\f187\";\n}\n.fa-bug:before {\n  content: \"\\f188\";\n}\n.fa-vk:before {\n  content: \"\\f189\";\n}\n.fa-weibo:before {\n  content: \"\\f18a\";\n}\n.fa-renren:before {\n  content: \"\\f18b\";\n}\n.fa-pagelines:before {\n  content: \"\\f18c\";\n}\n.fa-stack-exchange:before {\n  content: \"\\f18d\";\n}\n.fa-arrow-circle-o-right:before {\n  content: \"\\f18e\";\n}\n.fa-arrow-circle-o-left:before {\n  content: \"\\f190\";\n}\n.fa-toggle-left:before,\n.fa-caret-square-o-left:before {\n  content: \"\\f191\";\n}\n.fa-dot-circle-o:before {\n  content: \"\\f192\";\n}\n.fa-wheelchair:before {\n  content: \"\\f193\";\n}\n.fa-vimeo-square:before {\n  content: \"\\f194\";\n}\n.fa-turkish-lira:before,\n.fa-try:before {\n  content: \"\\f195\";\n}\n.fa-plus-square-o:before {\n  content: \"\\f196\";\n}\n"
  },
  {
    "path": "jekyll/css/style.css",
    "content": "body {\n    margin-top: 50px;\n    font-size: 16px;\n    font-family: \"Myriad Pro\", \"Hiragino Sans GB\",\"Microsoft YaHei\",\"微软雅黑\", Calibri, Helvetica, tahoma,arial,simsun,\"宋体\", sans-serif;\n    line-height: 1.5;\n    -webkit-font-smoothing: antialiased;\n}\n\n.jumbotron {\n    background: transparent url(../images/banner.jpg) repeat-x 50% 0%;\n    color: #fff;\n    text-shadow: 1px 1px 1px #3b3262;\n    margin-bottom: 0;\n}\n\n.jumbotron .container {\n   position: relative;\n}\n\n.jumbotron .github-btns {\n    position: absolute;\n    bottom: 0;\n    right: 0;\n}\n\n.fetature {\n    margin-top: 30px;\n}\n\n.page-body {\n    min-height: 450px;\n}\n\n.page-container {\n    margin-top: 10px;\n}\n\n.page-container h1,\n.page-container h2,\n.page-container h3 {\n    padding-top: 70px;\n    margin-top: -50px;\n}\n\n.logo {\n    position: relative;\n    padding-left: 60px;\n}\n.logo span {\n    position: absolute;\n    left: 15px;\n    top: 8px;\n    font-size: 2em;\n    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.logo.active {\n  color: #fff;\n}\n@media (min-width: 768px) {\n    .fetature .row .col-lg-4 {\n        min-height: 250px;\n    }\n}\n.footer {\n    width: 100%;\n    overflow: hidden;\n    color: #f2f2f2;\n    background: #212121;\n    margin-top: 50px;\n}\n.footer-fixed-bottom {\n    position: fixed;\n    bottom: 0;\n}\n.footer a {\n    color: #f2f2f2;\n}\n.footer .footer-inner {\n    margin: 15px;\n}\n.wu-example {\n    position: relative;\n    padding: 45px 15px 15px;\n    margin: 15px 0;\n    background-color: #fafafa;\n    box-shadow: inset 0 3px 6px rgba(0, 0, 0, .05);\n    border-color: #e5e5e5 #eee #eee;\n    border-style: solid;\n    border-width: 1px 0;\n}\n.wu-example:after {\n    content:\"示例\";\n    position: absolute;\n    top: 15px;\n    left: 15px;\n    font-size: 12px;\n    font-weight: bold;\n    color: #bbb;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n}\n@media (min-width: 768px) {\n    .bs-example {\n        margin-left: 0;\n        margin-right: 0;\n        background-color: #fff;\n        border-width: 1px;\n        border-color: #ddd;\n        border-radius: 4px 4px 0 0;\n        box-shadow: none;\n    }\n}\n.post-toc {\n    margin-top: 30px;\n    margin-bottom: 30px;\n    padding-top: 10px;\n    padding-bottom: 10px;\n    text-shadow: 0 1px 0 #fff;\n    background-color: #f7f5fa;\n    border-radius: 5px;\n}\n\n.post-toc .nav > li > a {\n    display: block;\n    color: #716b7a;\n    padding: 5px 20px;\n}\n\n.post-toc .nav .nav > li > a {\n    padding-top: 3px;\n    padding-bottom: 3px;\n    padding-left: 30px;\n    font-size: 90%;\n}\n\n.post-toc.affix {\n    position: static;\n}\n\n.post-toc .nav .nav {\n  display: none;\n  margin-bottom: 8px;\n}\n\n\n@media (min-width: 992px) {\n    .post-toc.affix {\n        position: fixed;\n        width: 213px;\n        top: 50px;\n    }\n\n    .post-toc .nav > .active > ul {\n      display: block;\n    }\n}\n\n@media (min-width: 1200px) {\n    .post-toc.affix {\n        width: 263px;\n    }\n\n    .post-toc .nav > .active > ul {\n      display: block;\n    }\n}\n\n.post-toc .nav > .active > a,\n.post-toc .nav > .active:hover > a,\n.post-toc .nav > .active:focus > a {\n    font-weight: bold;\n    color: #563d7c;\n    background-color: transparent;\n    border-right: 1px solid #563d7c;\n}\n\n.friends-links {\n    margin: 0;\n    padding: 0;\n    list-style: none;\n}\n\n.weixin {\n  text-align: center;\n  display: inline-block;\n}\n.weixin img {\n  width: 80px;\n}\n\n\n/********************************\n*\n*  COMMENTS\n*\n********************************/\n\n\n.comment {\n    background-color: transparent;\n    border-color: #CACACA;\n    border-style: solid;\n    border-width: 1px;\n    color: black;\n    display: block;\n    margin-bottom: 10px;\n    margin-top: 10px;\n    padding: 0px;\n    width: 100%;\n  }\n\n.comment .commentheader {\n  border-bottom-color: #CACACA;\n  border-bottom-style: solid;\n  border-bottom-width: 1px;\n  color: black;\n  background-image: -webkit-linear-gradient(#F8F8F8,#E1E1E1);\n  background-image: -moz-linear-gradient(#F8F8F8,#E1E1E1);\n  color: black;\n  display: block;\n  float: left;\n  font-family: helvetica, arial, freesans, clean, sans-serif;\n  font-size: 12px;\n  font-style: normal;\n  font-variant: normal;\n  font-weight: normal;\n  height: 33px;\n  line-height: 33px;\n  margin: 0px;\n  overflow-x: hidden;\n  overflow-y: hidden;\n  padding: 0px;\n  text-overflow: ellipsis;\n  text-shadow: rgba(255, 255, 255, 0.699219) 1px 1px 0px;\n  white-space: nowrap;\n  width: 100%;\n}\n\n.comment .commentheader .commentgravatar {\n  background-attachment: scroll;\n  background-clip: border-box;\n  background-color: white;\n  background-image: none;\n  background-origin: padding-box;\n  border-color: #C8C8C8;\n  border-style: solid;\n  border-width: 1px;\n  color: black;\n  display: inline-block;\n  float: none;\n  font-family: helvetica, arial, freesans, clean, sans-serif;\n  font-size: 1px;\n  font-style: normal;\n  font-variant: normal;\n  font-weight: normal;\n  height: 24px;\n  line-height: 1px;\n  margin-left: 5px;\n  margin-right: 3px;\n  margin-top: -2px;\n  overflow-x: visible;\n  overflow-y: visible;\n  padding: 1px;\n  text-overflow: clip;\n  text-shadow: rgba(255, 255, 255, 0.699219) 1px 1px 0px;\n  vertical-align: middle;\n  white-space: nowrap;\n  width: 24px;\n}\n\n.comment .commentheader a:link {\n  text-decoration: none;\n}\n\n.comment .commentheader a:hover {\n  border-bottom:1px solid;\n}\n\n\n.comment .commentheader .commentuser {\n  background-color: transparent;\n  color: black;\n  display: inline;\n  float: none;\n  font-family: helvetica, arial, freesans, clean, sans-serif;\n  font-size: 12px;\n  font-style: normal;\n  font-variant: normal;\n  font-weight: bold;\n  height: 0px;\n  line-height: 16px;\n  margin-left: 5px;\n  margin-right: 10px;\n  overflow-x: visible;\n  overflow-y: visible;\n  padding: 0px;\n  text-overflow: clip;\n  text-shadow: rgba(255, 255, 255, 0.699219) 1px 1px 0px;\n  white-space: nowrap;\n  width: 0px;\n}\n\n.comment .commentheader .commentdate {\n  background-color: transparent;\n  color: #777;\n  display: inline;\n  float: none;\n  font-family: helvetica, arial, freesans, clean, sans-serif;\n  font-size: 11px;\n  font-style: normal;\n  font-variant: normal;\n  font-weight: normal;\n  height: 0px;\n  line-height: 33px;\n  margin: 0px;\n  overflow-x: visible;\n  overflow-y: visible;\n  padding: 0px;\n  text-overflow: clip;\n  text-shadow: rgba(255, 255, 255, 0.699219) 1px 1px 0px;\n  white-space: nowrap;\n  width: 20em;\n}\n\n.comment .commentbody {\n  background-attachment: scroll;\n  background-clip: border-box;\n  background-color: transparent;\n  background-image: none;\n  background-origin: padding-box;\n  color: #333;\n  display: block;\n  margin-bottom: 1em;\n  margin-left: 1em;\n  margin-right: 1em;\n  margin-top: 40px;\n  overflow-x: visible;\n  overflow-y: visible;\n  padding: 0em;\n  position: static;\n  width: 96%;\n  word-wrap: break-word;\n}\n\n.comment .commentbody p {\n  margin-bottom: 0.5em;\n  margin-top: 0.5em;\n  margin-left: 0em;\n  margin-right: 0em;\n}\n\n.comment .commentbody pre {\n  border: 0px solid #ddd;\n  background-color: #eef;\n  padding: 0 .4em;\n}\n\n.comment .commentbody pre code {\n  border: 0px solid #ddd;\n}\n\n.comment .commentbody code {\n  border: 1px solid #ddd;\n  background-color: #eef;\n  font-size: 85%;\n  padding: 0 .2em;\n}\n\n\n\n\n/*demo样式*/\n#picker {\n    display: inline-block;\n    line-height: 1.428571429;\n    vertical-align: middle;\n    margin: 0 12px 0 0;\n}\n#picker .webuploader-pick {\n    padding: 6px 12px;\n    display: block;\n}\n\n\n#uploader-demo .thumbnail {\n    width: 110px;\n    height: 110px;\n}\n#uploader-demo .thumbnail img {\n    width: 100%;\n}\n.uploader-list {\n    width: 100%;\n    overflow: hidden;\n}\n.file-item {\n    float: left;\n    position: relative;\n    margin: 0 20px 20px 0;\n    padding: 4px;\n}\n.file-item .error {\n    position: absolute;\n    top: 4px;\n    left: 4px;\n    right: 4px;\n    background: red;\n    color: white;\n    text-align: center;\n    height: 20px;\n    font-size: 14px;\n    line-height: 23px;\n}\n.file-item .info {\n    position: absolute;\n    left: 4px;\n    bottom: 4px;\n    right: 4px;\n    height: 20px;\n    line-height: 20px;\n    text-indent: 5px;\n    background: rgba(0, 0, 0, 0.6);\n    color: white;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow : ellipsis;\n    font-size: 12px;\n    z-index: 10;\n}\n.upload-state-done:after {\n    content:\"\\f00c\";\n    font-family: FontAwesome;\n    font-style: normal;\n    font-weight: normal;\n    line-height: 1;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n    font-size: 32px;\n    position: absolute;\n    bottom: 0;\n    right: 4px;\n    color: #4cae4c;\n    z-index: 99;\n}\n.file-item .progress {\n    position: absolute;\n    right: 4px;\n    bottom: 4px;\n    height: 3px;\n    left: 4px;\n    height: 4px;\n    overflow: hidden;\n    z-index: 15;\n    margin:0;\n    padding: 0;\n    border-radius: 0;\n    background: transparent;\n}\n.file-item .progress span {\n    display: block;\n    overflow: hidden;\n    width: 0;\n    height: 100%;\n    background: #d14 url(../images/progress.png) repeat-x;\n    -webit-transition: width 200ms linear;\n    -moz-transition: width 200ms linear;\n    -o-transition: width 200ms linear;\n    -ms-transition: width 200ms linear;\n    transition: width 200ms linear;\n    -webkit-animation: progressmove 2s linear infinite;\n    -moz-animation: progressmove 2s linear infinite;\n    -o-animation: progressmove 2s linear infinite;\n    -ms-animation: progressmove 2s linear infinite;\n    animation: progressmove 2s linear infinite;\n    -webkit-transform: translateZ(0);\n}\n@-webkit-keyframes progressmove {\n    0% {\n        background-position: 0 0;\n    }\n    100% {\n        background-position: 17px 0;\n    }\n}\n@-moz-keyframes progressmove {\n    0% {\n        background-position: 0 0;\n    }\n    100% {\n        background-position: 17px 0;\n    }\n}\n@keyframes progressmove {\n    0% {\n        background-position: 0 0;\n    }\n    100% {\n        background-position: 17px 0;\n    }\n}\n\na.travis {\n  position: relative;\n  top: -4px;\n  right: 15px;\n}"
  },
  {
    "path": "jekyll/css/syntax.css",
    "content": ".highlight  { background: #ffffff; }\n.highlight .c { color: #999988; font-style: italic } /* Comment */\n.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */\n.highlight .k { font-weight: bold } /* Keyword */\n.highlight .o { font-weight: bold } /* Operator */\n.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */\n.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */\n.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */\n.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */\n.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */\n.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */\n.highlight .ge { font-style: italic } /* Generic.Emph */\n.highlight .gr { color: #aa0000 } /* Generic.Error */\n.highlight .gh { color: #999999 } /* Generic.Heading */\n.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */\n.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */\n.highlight .go { color: #888888 } /* Generic.Output */\n.highlight .gp { color: #555555 } /* Generic.Prompt */\n.highlight .gs { font-weight: bold } /* Generic.Strong */\n.highlight .gu { color: #aaaaaa } /* Generic.Subheading */\n.highlight .gt { color: #aa0000 } /* Generic.Traceback */\n.highlight .kc { font-weight: bold } /* Keyword.Constant */\n.highlight .kd { font-weight: bold } /* Keyword.Declaration */\n.highlight .kp { font-weight: bold } /* Keyword.Pseudo */\n.highlight .kr { font-weight: bold } /* Keyword.Reserved */\n.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */\n.highlight .m { color: #009999 } /* Literal.Number */\n.highlight .s { color: #d14 } /* Literal.String */\n.highlight .na { color: #008080 } /* Name.Attribute */\n.highlight .nb { color: #0086B3 } /* Name.Builtin */\n.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */\n.highlight .no { color: #008080 } /* Name.Constant */\n.highlight .ni { color: #800080 } /* Name.Entity */\n.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */\n.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */\n.highlight .nn { color: #555555 } /* Name.Namespace */\n.highlight .nt { color: #000080 } /* Name.Tag */\n.highlight .nv { color: #008080 } /* Name.Variable */\n.highlight .ow { font-weight: bold } /* Operator.Word */\n.highlight .w { color: #bbbbbb } /* Text.Whitespace */\n.highlight .mf { color: #009999 } /* Literal.Number.Float */\n.highlight .mh { color: #009999 } /* Literal.Number.Hex */\n.highlight .mi { color: #009999 } /* Literal.Number.Integer */\n.highlight .mo { color: #009999 } /* Literal.Number.Oct */\n.highlight .sb { color: #d14 } /* Literal.String.Backtick */\n.highlight .sc { color: #d14 } /* Literal.String.Char */\n.highlight .sd { color: #d14 } /* Literal.String.Doc */\n.highlight .s2 { color: #d14 } /* Literal.String.Double */\n.highlight .se { color: #d14 } /* Literal.String.Escape */\n.highlight .sh { color: #d14 } /* Literal.String.Heredoc */\n.highlight .si { color: #d14 } /* Literal.String.Interpol */\n.highlight .sx { color: #d14 } /* Literal.String.Other */\n.highlight .sr { color: #009926 } /* Literal.String.Regex */\n.highlight .s1 { color: #d14 } /* Literal.String.Single */\n.highlight .ss { color: #990073 } /* Literal.String.Symbol */\n.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */\n.highlight .vc { color: #008080 } /* Name.Variable.Class */\n.highlight .vg { color: #008080 } /* Name.Variable.Global */\n.highlight .vi { color: #008080 } /* Name.Variable.Instance */\n.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */\n"
  },
  {
    "path": "jekyll/css/webuploader.css",
    "content": ".webuploader-container {\n\tposition: relative;\n}\n.webuploader-element-invisible {\n\tposition: absolute !important;\n\tclip: rect(1px 1px 1px 1px); /* IE6, IE7 */\n    clip: rect(1px,1px,1px,1px);\n}\n.webuploader-pick {\n\tposition: relative;\n\tdisplay: inline-block;\n\tcursor: pointer;\n\tbackground: #00b7ee;\n\tpadding: 10px 15px;\n\tcolor: #fff;\n\ttext-align: center;\n\tborder-radius: 3px;\n\toverflow: hidden;\n}\n.webuploader-pick-hover {\n\tbackground: #00a2d4;\n}\n\n.webuploader-pick-disable {\n\topacity: 0.6;\n\tpointer-events:none;\n}\n\n"
  },
  {
    "path": "jekyll/demo.md",
    "content": "---\nlayout: post\ntitle: 演示\nnavName: Demo\ngroup: 'nav'\nweight : 4\nhideTitle: true\nnoToc: true\ncommentIssueId: 81\nstyles:\n  - /css/webuploader.css\n  - /css/demo.css\nscripts:\n  - /js/webuploader.js\n  - /js/demo.js\n---\n# Demo\n\n您可以尝试文件拖拽，使用QQ截屏工具，然后激活窗口后粘贴，或者点击添加图片按钮，来体验此demo.\n\n<div id=\"uploader\" class=\"wu-example\">\n    <div class=\"queueList\">\n        <div id=\"dndArea\" class=\"placeholder\">\n            <div id=\"filePicker\"></div>\n            <p>或将照片拖到这里，单次最多可选300张</p>\n        </div>\n    </div>\n    <div class=\"statusBar\" style=\"display:none;\">\n        <div class=\"progress\">\n            <span class=\"text\">0%</span>\n            <span class=\"percentage\"></span>\n        </div><div class=\"info\"></div>\n        <div class=\"btns\">\n            <div id=\"filePicker2\"></div><div class=\"uploadBtn\">开始上传</div>\n        </div>\n    </div>\n</div>\n\n"
  },
  {
    "path": "jekyll/doc/css/doc.css",
    "content": "ul.signature {\n    margin: 1em 0;\n    padding: 0;\n    font: normal 12px/18px Monaco, Consolas, \"Lucida Console\", monospace;\n}\n\nul.signature li {\n    list-style: none;\n}\n\n.method p {\n    margin: 0.5em 0;\n}\n\n.method h3 {\n    margin-bottom: .2em;\n}\n\n.method h3.deprecated {\n    text-decoration: line-through;\n}\n\nspan.deleted {\n    text-decoration: line-through;\n}\n\n.method pre.cm-s-default {\n    display: block;\n    white-space: pre-wrap;\n    margin: 1em 0px;\n}\n\n.method .warning {\n    margin: 5px 0;\n    font: bold 12px Arial;\n    color: #700;\n}\n\n.method .version {\n    background: #CCF;\n    color: black;\n    padding: 1px 3px;\n    margin: 0 0 0 5px;\n    border-radius: 3px;\n    font: bold 11px Arial;\n    font-weight: normal;\n}\n\ndiv.notice {\n    padding: 4px 10px;\n    border-left: 2px solid #FF8;\n    background: #FFE;\n    color: black;\n    margin: 1em 0;\n}\n\nul.params-list li > span.meta + p, ol.plugin-list li > code + p {\n    display: inline;\n    padding-left: 10px;\n}\n\ntable.table .events-tb-desc {\n    min-width: 250px;\n}"
  },
  {
    "path": "jekyll/doc/index.html",
    "content": "---\nlayout: default\ntitle: WebUploader API文档\nnavName: API\ngroup: 'nav'\nweight : 3\nstyles:\n  - /doc/css/doc.css\n---\n\n    <div class=\"container\">\n    \n    <div class=\"page-header\">\n      <h1 id=\"WebUploader\">WebUploader API文档</h1>\n    </div>\n    \n    <p>Web Uploader内部类的详细说明，以下提及的功能类，都可以在<code>WebUploader</code>这个变量中访问到。</p>\n<p>As you know, Web Uploader的每个文件都是用过<a href=\"https://github.com/amdjs/amdjs-api/wiki/AMD\">AMD</a>规范中的<code>define</code>组织起来的, 每个Module都会有个module id.\n默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：</p>\n<ul>\n<li>module <code>base</code>：WebUploader.Base</li>\n<li>module <code>file</code>: WebUploader.File</li>\n<li>module <code>lib/dnd</code>: WebUploader.Lib.Dnd</li>\n<li>module <code>runtime/html5/dnd</code>: WebUploader.Runtime.Html5.Dnd</li>\n</ul>\n<p>以下文档中对类的使用可能省略掉了<code>WebUploader</code>前缀。</p>\n\n</div>\n\n\n<div id=\"post-container\" class=\"container\">\n    <div class=\"row\">\n        <div class=\"col-md-3\">\n            <div class=\"post-toc\">\n    <ul class=\"nav\">\n        \n        \n            <li>\n                <a href=\"#WebUploader_Uploader\">Uploader</a>\n                <ul class=\"nav\">\n                    \n                    \n\n                    <li class=\"options\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_options\">options</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"events\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_events\">events</a>\n                    </li>\n                    \n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_option\">option</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_getStats\">getStats</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_destroy\">destroy</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_addButton\">addButton</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_makeThumb\">makeThumb</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_md5File\">md5File</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_addFiles\">addFiles</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_removeFile\">removeFile</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_getFiles\">getFiles</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_retry\">retry</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_sort\">sort</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_reset\">reset</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_predictRuntimeType\">predictRuntimeType</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_upload\">upload</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_stop\">stop</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_cancelFile\">cancelFile</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_isInProgress\">isInProgress</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_skipFile\">skipFile</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_request\">request</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_Uploader_register\">Uploader.register</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Uploader_Uploader_unRegister\">Uploader.unRegister</a>\n                    </li>\n                    \n                </ul>\n            </li>\n        \n            <li>\n                <a href=\"#WebUploader_Base\">Base</a>\n                <ul class=\"nav\">\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_create\">create</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_Base_version\">version</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_Base_\">$</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_Base_browser\">browser</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_Base_os\">os</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_inherits\">inherits</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_noop\">noop</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_bindFn\">bindFn</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_log\">log</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_slice\">slice</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_guid\">guid</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_formatSize\">formatSize</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_Deferred\">Deferred</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_isPromise\">isPromise</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Base_when\">when</a>\n                    </li>\n                    \n                </ul>\n            </li>\n        \n            <li>\n                <a href=\"#WebUploader_Mediator\">Mediator</a>\n                <ul class=\"nav\">\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Mediator_on\">on</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Mediator_once\">once</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Mediator_off\">off</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Mediator_trigger\">trigger</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Mediator_installTo\">installTo</a>\n                    </li>\n                    \n                </ul>\n            </li>\n        \n            <li>\n                <a href=\"#WebUploader_File\">File</a>\n                <ul class=\"nav\">\n                    \n                    \n\n                    <li class=\"events\">\n                        <a class=\"\" href=\"#WebUploader_File_events\">events</a>\n                    </li>\n                    \n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_name\">name</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_size\">size</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_type\">type</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_lastModifiedDate\">lastModifiedDate</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_id\">id</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_ext\">ext</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_statusText\">statusText</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_File_setStatus\">setStatus</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_File_File_Status\">Status</a>\n                    </li>\n                    \n                </ul>\n            </li>\n        \n            <li>\n                <a href=\"#WebUploader_Queue\">Queue</a>\n                <ul class=\"nav\">\n                    \n                    \n\n                    <li class=\"property\">\n                        <a class=\"\" href=\"#WebUploader_Queue_stats\">stats</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_append\">append</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_prepend\">prepend</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_getFile\">getFile</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_fetch\">fetch</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_sort\">sort</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_getFiles\">getFiles</a>\n                    </li>\n                    \n                    \n\n                    <li class=\"method\">\n                        <a class=\"\" href=\"#WebUploader_Queue_removeFile\">removeFile</a>\n                    </li>\n                    \n                </ul>\n            </li>\n        \n        \n    </ul>\n</div>\n        </div>\n        <div class=\"col-md-9\">\n            <div class=\"page-container\">\n\n                \n                <a id=\"WebUploader_WebUploader\" ></a>\n                \n\n                \n\n                \n                <div class=\"clearfix category\">\n\n\n    \n    <article class=\"clearfix constructor\">\n        <h3 id=\"WebUploader_Uploader\"  >Uploader\n            \n        </h3>\n        \n\n        \n\n        \n        <ul class=\"signature\">\n            \n            <li>new Uploader( opts ) \n                <span class=\"return\">⇒  Uploader</span>\n                \n            </li>\n        </ul>\n        \n        \n        \n\n        <p>上传入口类。</p>\n\n        \n            \n{% highlight javascript %}\nvar uploader = WebUploader.Uploader({\n    swf: 'path_of_swf/Uploader.swf',\n\n    // 开起分片上传。\n    chunked: true\n});\n{% endhighlight %}\n\n\n        \n    </article>\n\n    \n\n     <div>\n    <h3 id=\"WebUploader_Uploader_options\">参数说明</h3>\n    <ul class=\"params-list\">\n        \n        <li>\n            <span class=\"meta\">\n                <code>dnd</code>\n                 {Selector} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>  指定Drag And Drop拖拽的容器，如果不指定，则不启动。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>disableGlobalDnd</code>\n                 {Selector} \n                 [可选] \n                 [默认值：false] \n            </span>\n            <p>  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>paste</code>\n                 {Selector} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为<code>document.body</code>.</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>pick</code>\n                 {Selector, Object} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>指定选择文件的按钮容器，不指定则不创建按钮。</p>\n<ul>\n<li><code>id</code> {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。<strong>注意</strong> 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。</li>\n<li><code>label</code> {String} 请采用 <code>innerHTML</code> 代替</li>\n<li><code>innerHTML</code> {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。</li>\n<li><code>multiple</code> {Boolean} 是否开起同时选择多个文件能力。</li>\n</ul>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>accept</code>\n                 {Array} \n                 [可选] \n                 [默认值：null] \n            </span>\n            <p>指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。</p>\n<ul>\n<li><code>title</code> {String} 文字描述</li>\n<li><code>extensions</code> {String} 允许的文件后缀，不带点，多个用逗号分割。</li>\n<li><code>mimeTypes</code> {String} 多个用逗号分割。</li>\n</ul>\n<p>如：</p>\n<pre><code>{\n    title: &#39;Images&#39;,\n    extensions: &#39;gif,jpg,jpeg,bmp,png&#39;,\n    mimeTypes: &#39;image/*&#39;\n}</code></pre>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>thumb</code>\n                 {Object} \n                 [可选] \n                \n            </span>\n            <p>配置生成缩略图的选项。</p>\n<p>默认为：</p>\n\n{% highlight javascript %}\n{\n    width: 110,\n    height: 110,\n\n    // 图片质量，只有type为`image/jpeg`的时候才有效。\n    quality: 70,\n\n    // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n    allowMagnify: true,\n\n    // 是否允许裁剪。\n    crop: true,\n\n    // 为空的话则保留原有图片格式。\n    // 否则强制转换成指定的类型。\n    type: 'image/jpeg'\n}\n{% endhighlight %}\n\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>compress</code>\n                 {Object} \n                 [可选] \n                \n            </span>\n            <p>配置压缩的图片的选项。如果此选项为<code>false</code>, 则图片在上传前不进行压缩。</p>\n<p>默认为：</p>\n\n{% highlight javascript %}\n{\n    width: 1600,\n    height: 1600,\n\n    // 图片质量，只有type为`image/jpeg`的时候才有效。\n    quality: 90,\n\n    // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n    allowMagnify: false,\n\n    // 是否允许裁剪。\n    crop: false,\n\n    // 是否保留头部meta信息。\n    preserveHeaders: true,\n\n    // 如果发现压缩后文件大小比原来还大，则使用原来图片\n    // 此属性可能会影响图片自动纠正功能\n    noCompressIfLarger: false,\n\n    // 单位字节，如果图片大小小于此值，不会采用压缩。\n    compressSize: 0\n}\n{% endhighlight %}\n\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>auto</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：false] \n            </span>\n            <p>设置为 true 后，不需要手动调用上传，有文件选择即开始上传。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>runtimeOrder</code>\n                 {Object} \n                 [可选] \n                 [默认值：html5,flash] \n            </span>\n            <p>指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.</p>\n<p>可以将此值设置成 <code>flash</code>，来强制使用 flash 运行时。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>prepareNextFile</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：false] \n            </span>\n            <p>是否允许在文件传输时提前把下一个文件准备好。\n某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n如果能提前在当前文件传输期处理，可以节省总体耗时。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>chunked</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：false] \n            </span>\n            <p>是否要分片处理大文件上传。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>chunkSize</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：5242880] \n            </span>\n            <p>如果要分片，分多大一片？ 默认大小为5M.</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>chunkRetry</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：2] \n            </span>\n            <p>如果某个分片由于网络问题出错，允许自动重传多少次？</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>chunkRetryDelay</code>\n                 {Number} \n                 [可选] \n                 [默认值：1000] \n            </span>\n            <p>开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>threads</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：3] \n            </span>\n            <p>上传并发数。允许同时最大上传进程数。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>formData</code>\n                 {Object} \n                 [可选] \n                 [默认值：{}] \n            </span>\n            <p>文件上传请求的参数表，每次发送都会发送此对象中的参数。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>fileVal</code>\n                 {Object} \n                 [可选] \n                 [默认值：&#39;file&#39;] \n            </span>\n            <p>设置文件上传域的name。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>method</code>\n                 {Object} \n                 [可选] \n                 [默认值：POST] \n            </span>\n            <p>文件上传方式，<code>POST</code> 或者 <code>GET</code>。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>sendAsBinary</code>\n                 {Object} \n                 [可选] \n                 [默认值：false] \n            </span>\n            <p>是否已二进制的流的方式发送文件，这样整个上传内容<code>php://input</code>都为文件内容，\n其他参数在$_GET数组中。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>fileNumLimit</code>\n                 {int} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>验证文件总数量, 超出则不允许加入队列。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>fileSizeLimit</code>\n                 {int} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>验证文件总大小是否超出限制, 超出则不允许加入队列。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>fileSingleSizeLimit</code>\n                 {int} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>验证单个文件大小是否超出限制, 超出则不允许加入队列。</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>duplicate</code>\n                 {Boolean} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.</p>\n\n            \n        </li>\n        \n        <li>\n            <span class=\"meta\">\n                <code>disableWidgets</code>\n                 {String, Array} \n                 [可选] \n                 [默认值：undefined] \n            </span>\n            <p>默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。</p>\n\n            \n        </li>\n        \n    </ul>\n</div>  \n      <div>\n    <h3 id=\"WebUploader_Uploader_events\">事件说明</h3>\n    <table class=\"table table-bordered\">\n        <tr>\n            <th>事件名</th>\n            <th class=\"events-tb-desc\">参数说明</th>\n            <th>描述</th>\n        </tr>\n\n        \n        <tr>\n            <td><code>dndAccept</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>items</code> {DataTransferItemList}</span><p>DataTransferItem</p>\n</li></ul>\n                \n            </td>\n            <td><p>阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>beforeFileQueued</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li></ul>\n                \n            </td>\n            <td><p>当文件被加入队列之前触发。如果此事件handler的返回值为<code>false</code>，则此文件不会被添加进入队列。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>fileQueued</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li></ul>\n                \n            </td>\n            <td><p>当文件被加入队列以后触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>filesQueued</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>files</code> {File}</span><p>数组，内容为原始File(lib/File）对象。</p>\n</li></ul>\n                \n            </td>\n            <td><p>当一批文件添加进队列以后触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>fileDequeued</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li></ul>\n                \n            </td>\n            <td><p>当文件被移除队列后触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>reset</code></td>\n            <td class=\"events-tb-desc\">\n                \n            </td>\n            <td><p>当 uploader 被重置的时候触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>startUpload</code></td>\n            <td class=\"events-tb-desc\">\n                \n            </td>\n            <td><p>当开始上传流程时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>stopUpload</code></td>\n            <td class=\"events-tb-desc\">\n                \n            </td>\n            <td><p>当开始上传流程暂停时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadFinished</code></td>\n            <td class=\"events-tb-desc\">\n                \n            </td>\n            <td><p>当所有文件上传结束时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadStart</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li></ul>\n                \n            </td>\n            <td><p>某个文件开始上传前触发，一个文件只会触发一次。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadBeforeSend</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>object</code> {Object}</span></li><li><span class=\"meta\"><code>data</code> {Object}</span><p>默认的上传参数，可以扩展此对象来控制上传参数。</p>\n</li><li><span class=\"meta\"><code>headers</code> {Object}</span><p>可以扩展此对象来控制上传头部。</p>\n</li></ul>\n                \n            </td>\n            <td><p>当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadAccept</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>object</code> {Object}</span></li><li><span class=\"meta\"><code>ret</code> {Object}</span><p>服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。</p>\n</li></ul>\n                \n            </td>\n            <td><p>当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为<code>false</code>, 则此文件将派送<code>server</code>类型的<code>uploadError</code>事件。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadProgress</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li><li><span class=\"meta\"><code>percentage</code> {Number}</span><p>上传进度</p>\n</li></ul>\n                \n            </td>\n            <td><p>上传过程中触发，携带上传进度。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadError</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li><li><span class=\"meta\"><code>reason</code> {String}</span><p>出错的code</p>\n</li></ul>\n                \n            </td>\n            <td><p>当文件上传出错时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadSuccess</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>File对象</p>\n</li><li><span class=\"meta\"><code>response</code> {Object}</span><p>服务端返回的数据</p>\n</li></ul>\n                \n            </td>\n            <td><p>当文件上传成功时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>uploadComplete</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File} [可选]</span><p>File对象</p>\n</li></ul>\n                \n            </td>\n            <td><p>不管成功或者失败，文件上传完成时触发。</p>\n</td>\n        </tr>\n        \n        <tr>\n            <td><code>error</code></td>\n            <td class=\"events-tb-desc\">\n                \n                <ul class=\"params-list\"><li><span class=\"meta\"><code>type</code> {String}</span><p>错误类型。</p>\n</li></ul>\n                \n            </td>\n            <td><p>当validate不通过时，会以派送错误事件的形式通知调用者。通过<code>upload.on(&#39;error&#39;, handler)</code>可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。</p>\n<ul>\n<li><code>Q_EXCEED_NUM_LIMIT</code> 在设置了<code>fileNumLimit</code>且尝试给<code>uploader</code>添加的文件数量超出这个值时派送。</li>\n<li><code>Q_EXCEED_SIZE_LIMIT</code> 在设置了<code>Q_EXCEED_SIZE_LIMIT</code>且尝试给<code>uploader</code>添加的文件总大小超出这个值时派送。</li>\n<li><code>Q_TYPE_DENIED</code> 当文件类型不满足时触发。。</li>\n</ul>\n</td>\n        </tr>\n        \n    </table>\n</div>      \n     \n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_option\"  >option\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>option( key ) \n            <span class=\"return\">⇒  *</span>\n            \n        </li>\n        <li>option( key, val ) \n            <span class=\"return\">⇒  self</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>获取或者设置Uploader配置项。</p>\n\n    \n        \n{% highlight javascript %}\n// 初始状态图片上传前不会压缩\nvar uploader = new WebUploader.Uploader({\n    compress: null;\n});\n\n// 修改后图片上传前，尝试将图片压缩到1600 * 1600\nuploader.option( 'compress', {\n    width: 1600,\n    height: 1600\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_getStats\"  >getStats\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>getStats() \n            <span class=\"return\">⇒  Object</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>获取文件统计信息。返回一个包含一下信息的对象。</p>\n<ul>\n<li><code>successNum</code> 上传成功的文件数</li>\n<li><code>progressNum</code> 上传中的文件数</li>\n<li><code>cancelNum</code> 被删除的文件数</li>\n<li><code>invalidNum</code> 无效的文件数</li>\n<li><code>uploadFailNum</code> 上传失败的文件数</li>\n<li><code>queueNum</code> 还在队列中的文件数</li>\n<li><code>interruptNum</code> 被暂停的文件数</li>\n</ul>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_destroy\"  >destroy\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>destroy() \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>销毁 webuploader 实例</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_addButton\"  >addButton\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>addButton( pick ) \n            <span class=\"return\">⇒  Promise</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟<a href=\"#WebUploader_Uploader_options\">options.pick</a>一致。</p>\n\n    \n        \n{% highlight javascript %}\nuploader.addButton({\n    id: '#btnContainer',\n    innerHTML: '选择文件'\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_makeThumb\"  >makeThumb\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>makeThumb( file, callback ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>makeThumb( file, callback, width, height ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>生成缩略图，此过程为异步，所以需要传入<code>callback</code>。\n通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。</p>\n<p>当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。</p>\n<p><code>callback</code>中可以接收到两个参数。</p>\n<ul>\n<li>第一个为error，如果生成缩略图有错误，此error将为真。</li>\n<li>第二个为ret, 缩略图的Data URL值。</li>\n</ul>\n<p><strong>注意</strong>\nDate URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。</p>\n\n    \n        \n{% highlight javascript %}\nuploader.on( 'fileQueued', function( file ) {\n    var $li = ...;\n\n    uploader.makeThumb( file, function( error, ret ) {\n        if ( error ) {\n            $li.text('预览错误');\n        } else {\n            $li.append('&lt;img alt=\"\" src=\"' + ret + '\" />');\n        }\n    });\n\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_md5File\"  >md5File\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>md5File( file[, start[, end]] ) \n            <span class=\"return\">⇒  promise</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。</p>\n\n    \n        \n{% highlight javascript %}\nuploader.on( 'fileQueued', function( file ) {\n    var $li = ...;\n\n    uploader.md5File( file )\n\n        // 及时显示进度\n        .progress(function(percentage) {\n            console.log('Percentage:', percentage);\n        })\n\n        // 完成\n        .then(function(val) {\n            console.log('md5 result:', val);\n        });\n\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_addFiles\"  >addFiles\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>addFiles( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>addFiles( [file1, file2 ...] ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>files</code> {Array of File or File} [可选]</span><p>Files 对象 数组</p>\n</li></ul>\n    \n    \n\n    <p>添加文件到队列</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_removeFile\"  >removeFile\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>removeFile( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>removeFile( id ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>removeFile( file, true ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>removeFile( id, true ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File, id}</span><p>File对象或这File对象的id</p>\n</li></ul>\n    \n    \n\n    <p>移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 <code>true</code> 则会从 queue 中移除。</p>\n\n    \n        \n{% highlight javascript %}\n$li.on('click', '.remove-this', function() {\n    uploader.removeFile( file );\n})\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_getFiles\"  >getFiles\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>getFiles() \n            <span class=\"return\">⇒  Array</span>\n            \n        </li>\n        <li>getFiles( status1, status2, status... ) \n            <span class=\"return\">⇒  Array</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>返回指定状态的文件集合，不传参数将返回所有状态的文件。</p>\n\n    \n        \n{% highlight javascript %}\nconsole.log( uploader.getFiles() );    // => all files\nconsole.log( uploader.getFiles('error') )    // => all error files.\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_retry\"  >retry\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>retry() \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>retry( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>重试上传，重试指定文件，或者从出错的文件开始重新上传。</p>\n\n    \n        \n{% highlight javascript %}\nfunction retry() {\n    uploader.retry();\n}\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_sort\"  >sort\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>sort( fn ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>排序队列中的文件，在上传之前调整可以控制上传顺序。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_reset\"  >reset\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>reset() \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>重置uploader。目前只重置了队列。</p>\n\n    \n        \n{% highlight javascript %}\nuploader.reset();\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_predictRuntimeType\"  >predictRuntimeType\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>predictRuntimeType() \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>预测Uploader将采用哪个<code>Runtime</code></p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_upload\"  >upload\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>upload() \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>upload( file | fileId) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。</p>\n<p>可以指定开始某一个文件。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_stop\"  >stop\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>stop() \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>stop( true ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>stop( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>暂停上传。第一个参数为是否中断上传当前正在上传的文件。</p>\n<p>如果第一个参数是文件，则只暂停指定文件。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_cancelFile\"  >cancelFile\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>cancelFile( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n        <li>cancelFile( id ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File, id}</span><p>File对象或这File对象的id</p>\n</li></ul>\n    \n    \n\n    <p>标记文件状态为已取消, 同时将中断文件传输。</p>\n\n    \n        \n{% highlight javascript %}\n$li.on('click', '.remove-this', function() {\n    uploader.cancelFile( file );\n})\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_isInProgress\"  >isInProgress\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>isInProgress() \n            <span class=\"return\">⇒  Boolean</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>判断<code>Uploader</code>是否正在上传中。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_skipFile\"  >skipFile\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>skipFile( file ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>跳过一个文件上传，直接标记指定文件为已上传状态。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_request\"  >request\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>request( command, args ) \n            <span class=\"return\">⇒  * | Promise</span>\n            \n        </li>\n        <li>request( command, args, callback ) \n            <span class=\"return\">⇒  Promise</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>发送命令。当传入<code>callback</code>或者<code>handler</code>中返回<code>promise</code>时。返回一个当所有<code>handler</code>中的promise都完成后完成的新<code>promise</code>。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_Uploader_register\"  >Uploader.register\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Uploader.register(proto);\n            \n            \n        </li>\n        <li>Uploader.register(map, proto);\n            \n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>responseMap</code> {object}</span><p>API 名称与函数实现的映射</p>\n</li><li><span class=\"meta\"><code>proto</code> {object}</span><p>组件原型，构造函数通过 constructor 属性定义</p>\n</li></ul>\n    \n    \n\n    <p>添加组件</p>\n\n    \n        \n{% highlight javascript %}\nUploader.register({\n    'make-thumb': 'makeThumb'\n}, {\n    init: function( options ) {},\n    makeThumb: function() {}\n});\n\nUploader.register({\n    'make-thumb': function() {\n\n    }\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Uploader_Uploader_unRegister\"  >Uploader.unRegister\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Uploader.unRegister(name);\n            \n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>name</code> {string}</span><p>组件名字</p>\n</li></ul>\n    \n    \n\n    <p>删除插件，只有在注册时指定了名字的才能被删除。</p>\n\n    \n        \n{% highlight javascript %}\nUploader.register({\n    name: 'custom',\n\n    'make-thumb': function() {\n\n    }\n});\n\nUploader.unRegister('custom');\n{% endhighlight %}\n\n\n    \n</article>\n\n\n  \n\n    \n</div>\n<hr />\n                \n                <div class=\"clearfix category\">\n\n\n    \n        <h2 id=\"WebUploader_Base\">Base</h2>\n        <p>基础类，提供一些简单常用的方法。</p>\n\n    \n\n    \n    \n     \n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_create\"  >create\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.create( opts ) \n            <span class=\"return\">⇒  Uploader</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>创建Uploader实例，等同于new Uploader( opts );</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_Base_version\"  >version\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p> 当前版本号。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_Base_\"  >$\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p> 引用依赖的jQuery或者Zepto对象。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_Base_browser\"  >browser\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>简单的浏览器检查结果。</p>\n<ul>\n<li><code>webkit</code>  webkit版本号，如果浏览器为非webkit内核，此属性为<code>undefined</code>。</li>\n<li><code>chrome</code>  chrome浏览器版本号，如果浏览器为chrome，此属性为<code>undefined</code>。</li>\n<li><code>ie</code>  ie浏览器版本号，如果浏览器为非ie，此属性为<code>undefined</code>。<strong>暂不支持ie10+</strong></li>\n<li><code>firefox</code>  firefox浏览器版本号，如果浏览器为非firefox，此属性为<code>undefined</code>。</li>\n<li><code>safari</code>  safari浏览器版本号，如果浏览器为非safari，此属性为<code>undefined</code>。</li>\n<li><code>opera</code>  opera浏览器版本号，如果浏览器为非opera，此属性为<code>undefined</code>。</li>\n</ul>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_Base_os\"  >os\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>操作系统检查结果。</p>\n<ul>\n<li><code>android</code>  如果在android浏览器环境下，此值为对应的android版本号，否则为<code>undefined</code>。</li>\n<li><code>ios</code> 如果在ios浏览器环境下，此值为对应的ios版本号，否则为<code>undefined</code>。</li>\n</ul>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_inherits\"  >inherits\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.inherits( super ) \n            <span class=\"return\">⇒  child</span>\n            \n        </li>\n        <li>Base.inherits( super, protos ) \n            <span class=\"return\">⇒  child</span>\n            \n        </li>\n        <li>Base.inherits( super, protos, statics ) \n            <span class=\"return\">⇒  child</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>super</code> {Class}</span><p>父类</p>\n</li><li><span class=\"meta\"><code>protos</code> {Object, Function} [可选]</span><p>子类或者对象。如果对象中包含constructor，子类将是用此属性值。</p>\n<ul class=\"params-list\"><li><span class=\"meta\"><code>constructor</code> {Function} [可选]</span><p>子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。</p>\n</li></ul></li><li><span class=\"meta\"><code>statics</code> {Object} [可选]</span><p>静态属性或方法。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {Class}</span><p>返回子类。</p>\n</li></ul>\n    \n\n    <p>实现类与类之间的继承。</p>\n\n    \n        \n{% highlight javascript %}\nfunction Person() {\n    console.log( 'Super' );\n}\nPerson.prototype.hello = function() {\n    console.log( 'hello' );\n};\n\nvar Manager = Base.inherits( Person, {\n    world: function() {\n        console.log( 'World' );\n    }\n});\n\n// 因为没有指定构造器，父类的构造器将会执行。\nvar instance = new Manager();    // => Super\n\n// 继承子父类的方法\ninstance.hello();    // => hello\ninstance.world();    // => World\n\n// 子类的__super__属性指向父类\nconsole.log( Manager.__super__ === Person );    // => true\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_noop\"  >noop\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>一个不做任何事情的方法。可以用来赋值给默认的callback.</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_bindFn\"  >bindFn\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.bindFn( fn, context ) \n            <span class=\"return\">⇒  Function</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>返回一个新的方法，此方法将已指定的<code>context</code>来执行。</p>\n\n    \n        \n{% highlight javascript %}\nvar doSomething = function() {\n        console.log( this.name );\n    },\n    obj = {\n        name: 'Object Name'\n    },\n    aliasFn = Base.bind( doSomething, obj );\n\n aliasFn();    // => Object Name\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_log\"  >log\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.log( args... ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>引用Console.log如果存在的话，否则引用一个<a href=\"#WebUploader_Base_noop\">空函数noop</a>。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_slice\"  >slice\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.slice( target, start[, end] ) \n            <span class=\"return\">⇒  Array</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>被<a href=\"http://www.2ality.com/2011/11/uncurrying-this.html\">uncurrythis</a>的数组slice方法。\n将用来将非数组对象转化成数组对象。</p>\n\n    \n        \n{% highlight javascript %}\nfunction doSomthing() {\n    var args = Base.slice( arguments, 1 );\n    console.log( args );\n}\n\ndoSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_guid\"  >guid\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.guid() \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n        <li>Base.guid( prefx ) \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>生成唯一的ID</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_formatSize\"  >formatSize\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.formatSize( size ) \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n        <li>Base.formatSize( size, pointLength ) \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n        <li>Base.formatSize( size, pointLength, units ) \n            <span class=\"return\">⇒  String</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>size</code> {Number}</span><p>文件大小</p>\n</li><li><span class=\"meta\"><code>pointLength</code> {Number} [可选] [默认值: 2] </span><p>精确到的小数点数。</p>\n</li><li><span class=\"meta\"><code>[units=[</code> {Array}</span><p>&#39;B&#39;, &#39;K&#39;, &#39;M&#39;, &#39;G&#39;, &#39;TB&#39; ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.</p>\n</li></ul>\n    \n    \n\n    <p>格式化文件大小, 输出成带单位的字符串</p>\n\n    \n        \n{% highlight javascript %}\nconsole.log( Base.formatSize( 100 ) );    // => 100B\nconsole.log( Base.formatSize( 1024 ) );    // => 1.00K\nconsole.log( Base.formatSize( 1024, 0 ) );    // => 1K\nconsole.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\nconsole.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\nconsole.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_Deferred\"  >Deferred\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.Deferred() \n            <span class=\"return\">⇒  Deferred</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>创建一个<a href=\"http://api.jquery.com/category/deferred-object/\">Deferred</a>对象。\n详细的Deferred用法说明，请参照jQuery的API文档。</p>\n<p>Deferred对象在钩子回掉函数中经常要用到，用来处理需要等待的异步操作。</p>\n\n    \n        \n{% highlight javascript %}\n// 在文件开始发送前做些异步操作。\n// WebUploader会等待此异步操作完成后，开始发送文件。\nUploader.register({\n    'before-send-file': 'doSomthingAsync'\n}, {\n\n    doSomthingAsync: function() {\n        var deferred = Base.Deferred();\n\n        // 模拟一次异步操作。\n        setTimeout(deferred.resolve, 2000);\n\n        return deferred.promise();\n    }\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_isPromise\"  >isPromise\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.isPromise( anything ) \n            <span class=\"return\">⇒  Boolean</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>anything</code> {*}</span><p>检测对象。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {Boolean}</span></li></ul>\n    \n\n    <p>判断传入的参数是否为一个promise对象。</p>\n\n    \n        \n{% highlight javascript %}\nconsole.log( Base.isPromise() );    // => false\nconsole.log( Base.isPromise({ key: '123' }) );    // => false\nconsole.log( Base.isPromise( Base.Deferred().promise() ) );    // => true\n\n// Deferred也是一个Promise\nconsole.log( Base.isPromise( Base.Deferred() ) );    // => true\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Base_when\"  >when\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>Base.when( promise1[, promise2[, promise3...]] ) \n            <span class=\"return\">⇒  Promise</span>\n            \n        </li>\n    </ul>\n    \n    \n    \n\n    <p>返回一个promise，此promise在所有传入的promise都完成了后完成。\n详细请查看<a href=\"http://api.jquery.com/jQuery.when/\">这里</a>。</p>\n\n    \n</article>\n\n\n  \n\n    \n</div>\n<hr />\n                \n                <div class=\"clearfix category\">\n\n\n    \n        <h2 id=\"WebUploader_Mediator\">Mediator</h2>\n        <p>中介者，它本身是个单例，但可以通过<a href=\"#WebUploader_Mediator_installTo\">installTo</a>方法，使任何对象具备事件行为。\n主要目的是负责模块与模块之间的合作，降低耦合度。</p>\n\n    \n\n    \n    \n     \n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Mediator_on\"  >on\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>on( name, callback[, context] ) \n            <span class=\"return\">⇒  self</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>name</code> {String}</span><p>事件名，支持多个事件用空格隔开</p>\n</li><li><span class=\"meta\"><code>callback</code> {Function}</span><p>事件处理器</p>\n</li><li><span class=\"meta\"><code>context</code> {Object} [可选]</span><p>事件处理器的上下文。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {self}</span><p>返回自身，方便链式</p>\n</li></ul>\n    \n\n    <p>绑定事件。</p>\n<p><code>callback</code>方法在执行时，arguments将会来源于trigger的时候携带的参数。如</p>\n\n{% highlight javascript %}\nvar obj = {};\n\n// 使得obj有事件行为\nMediator.installTo( obj );\n\nobj.on( 'testa', function( arg1, arg2 ) {\n    console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n});\n\nobj.trigger( 'testa', 'arg1', 'arg2' );\n{% endhighlight %}\n\n<p>如果<code>callback</code>中，某一个方法<code>return false</code>了，则后续的其他<code>callback</code>都不会被执行到。\n切会影响到<code>trigger</code>方法的返回值，为<code>false</code>。</p>\n<p><code>on</code>还可以用来添加一个特殊事件<code>all</code>, 这样所有的事件触发都会响应到。同时此类<code>callback</code>中的arguments有一个不同处，\n就是第一个参数为<code>type</code>，记录当前是什么事件在触发。此类<code>callback</code>的优先级比脚低，会再正常<code>callback</code>执行完后触发。</p>\n\n{% highlight javascript %}\nobj.on( 'all', function( type, arg1, arg2 ) {\n    console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n});\n{% endhighlight %}\n\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Mediator_once\"  >once\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>once( name, callback[, context] ) \n            <span class=\"return\">⇒  self</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>name</code> {String}</span><p>事件名</p>\n</li><li><span class=\"meta\"><code>callback</code> {Function}</span><p>事件处理器</p>\n</li><li><span class=\"meta\"><code>context</code> {Object} [可选]</span><p>事件处理器的上下文。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {self}</span><p>返回自身，方便链式</p>\n</li></ul>\n    \n\n    <p>绑定事件，且当handler执行完后，自动解除绑定。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Mediator_off\"  >off\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>off( [name[, callback[, context] ] ] ) \n            <span class=\"return\">⇒  self</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>name</code> {String} [可选]</span><p>事件名</p>\n</li><li><span class=\"meta\"><code>callback</code> {Function} [可选]</span><p>事件处理器</p>\n</li><li><span class=\"meta\"><code>context</code> {Object} [可选]</span><p>事件处理器的上下文。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {self}</span><p>返回自身，方便链式</p>\n</li></ul>\n    \n\n    <p>解除事件绑定</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Mediator_trigger\"  >trigger\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>trigger( name[, args...] ) \n            <span class=\"return\">⇒  self</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>type</code> {String}</span><p>事件名</p>\n</li><li><span class=\"meta\"><code>...</code> {*} [可选]</span><p>任意参数</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {Boolean}</span><p>如果handler中return false了，则返回false, 否则返回true</p>\n</li></ul>\n    \n\n    <p>触发事件</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Mediator_installTo\"  >installTo\n        \n    </h3>\n    \n\n    \n\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>obj</code> {Object}</span><p>需要具备事件行为的对象。</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {Object}</span><p>返回obj.</p>\n</li></ul>\n    \n\n    <p>可以通过这个接口，使任何对象具备事件功能。</p>\n\n    \n</article>\n\n\n  \n\n    \n</div>\n<hr />\n                \n                <div class=\"clearfix category\">\n\n\n    \n    <article class=\"clearfix constructor\">\n        <h3 id=\"WebUploader_File\"  >File\n            \n        </h3>\n        \n\n        \n\n        \n        <ul class=\"signature\">\n            \n            <li>new File( source ) \n                <span class=\"return\">⇒  File</span>\n                \n            </li>\n        </ul>\n        \n        \n            <p>参数: </p>\n            <ul class=\"params-list\"><li><span class=\"meta\"><code>source</code> {Lib.File}</span><p><a href=\"#Lib.File\">lib.File</a>实例, 此source对象是带有Runtime信息的。</p>\n</li></ul>\n        \n        \n\n        <p>构造函数</p>\n\n        \n    </article>\n\n    \n\n    \n      <div>\n    <h3 id=\"WebUploader_File_events\">事件说明</h3>\n    <table class=\"table table-bordered\">\n        <tr>\n            <th>事件名</th>\n            <th class=\"events-tb-desc\">参数说明</th>\n            <th>描述</th>\n        </tr>\n\n        \n        <tr>\n            <td><code>statuschange</code></td>\n            <td class=\"events-tb-desc\">\n                \n            </td>\n            <td><p>文件状态变化</p>\n</td>\n        </tr>\n        \n    </table>\n</div>      \n     \n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_name\"  >name\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件名，包括扩展名（后缀）</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_size\"  >size\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件体积（字节）</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_type\"  >type\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件MIMETYPE类型，与文件类型的对应关系请参考<a href=\"http://t.cn/z8ZnFny\"><a href=\"http://t.cn/z8ZnFny\">http://t.cn/z8ZnFny</a></a></p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_lastModifiedDate\"  >lastModifiedDate\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件最后修改日期</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_id\"  >id\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件ID，每个对象具有唯一ID，与文件名无关</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_ext\"  >ext\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件扩展名，通过文件名获取，例如test.png的扩展名为png</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_statusText\"  >statusText\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>状态文字说明。在不同的status语境下有不同的用途。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_File_setStatus\"  >setStatus\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>setStatus( status[, statusText] );\n            \n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>status</code> {File.Status, String}</span><p><a href=\"#WebUploader_File_File_Status\">文件状态值</a></p>\n</li><li><span class=\"meta\"><code>statusText</code> {String} [可选] [默认值: ''] </span><p>状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。</p>\n</li></ul>\n    \n    \n\n    <p>设置状态，状态变化时会触发<code>change</code>事件。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_File_File_Status\"  >File.Status\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>文件状态值，具体包括以下几种类型：</p>\n<ul>\n<li><code>inited</code> 初始状态</li>\n<li><code>queued</code> 已经进入队列, 等待上传</li>\n<li><code>progress</code> 上传中</li>\n<li><code>complete</code> 上传完成。</li>\n<li><code>error</code> 上传出错，可重试</li>\n<li><code>interrupt</code> 上传中断，可续传。</li>\n<li><code>invalid</code> 文件不合格，不能重试上传。会自动从队列中移除。</li>\n<li><code>cancelled</code> 文件被移除。</li>\n</ul>\n\n    \n</article>\n\n\n  \n\n    \n</div>\n<hr />\n                \n                <div class=\"clearfix category\">\n\n\n    \n        <h2 id=\"WebUploader_Queue\">Queue</h2>\n        <p>文件队列, 用来存储各个状态中的文件。</p>\n\n    \n\n    \n    \n     \n\n\n<article class=\"clearfix property\">\n    <h3 id=\"WebUploader_Queue_stats\"  >stats\n        \n    </h3>\n    \n\n    \n\n    \n    \n    \n\n    <p>统计文件数。</p>\n<ul>\n<li><code>numOfQueue</code> 队列中的文件数。</li>\n<li><code>numOfSuccess</code> 上传成功的文件数</li>\n<li><code>numOfCancel</code> 被取消的文件数</li>\n<li><code>numOfProgress</code> 正在上传中的文件数</li>\n<li><code>numOfUploadFailed</code> 上传错误的文件数。</li>\n<li><code>numOfInvalid</code> 无效的文件数。</li>\n<li><code>numofDeleted</code> 被移除的文件数。</li>\n</ul>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_append\"  >append\n        \n    </h3>\n    \n\n    \n\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>文件对象</p>\n</li></ul>\n    \n    \n\n    <p>将新文件加入对队列尾部</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_prepend\"  >prepend\n        \n    </h3>\n    \n\n    \n\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>file</code> {File}</span><p>文件对象</p>\n</li></ul>\n    \n    \n\n    <p>将新文件加入对队列头部</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_getFile\"  >getFile\n        \n    </h3>\n    \n\n    \n\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>fileId</code> {String}</span><p>文件ID</p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {File}</span></li></ul>\n    \n\n    <p>获取文件对象</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_fetch\"  >fetch\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>fetch( status ) \n            <span class=\"return\">⇒  File</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>status</code> {String}</span><p><a href=\"#WebUploader_File_File_Status\">文件状态值</a></p>\n</li></ul>\n    \n    \n        <p>返回值: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"> {File}</span><p><a href=\"#WebUploader_File\">File</a></p>\n</li></ul>\n    \n\n    <p>从队列中取出一个指定状态的文件。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_sort\"  >sort\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>sort( fn ) \n            <span class=\"return\">⇒  undefined</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>fn</code> {Function}</span><p>排序方法</p>\n</li></ul>\n    \n    \n\n    <p>对队列进行排序，能够控制文件上传顺序。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_getFiles\"  >getFiles\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>getFiles( [status1[, status2 ...]] ) \n            <span class=\"return\">⇒  Array</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>status</code> {String} [可选]</span><p><a href=\"#WebUploader_File_File_Status\">文件状态值</a></p>\n</li></ul>\n    \n    \n\n    <p>获取指定类型的文件列表, 列表中每一个成员为<a href=\"#WebUploader_File\">File</a>对象。</p>\n\n    \n</article>\n\n\n\n\n<article class=\"clearfix method\">\n    <h3 id=\"WebUploader_Queue_removeFile\"  >removeFile\n        \n    </h3>\n    \n\n    \n\n    \n    <ul class=\"signature\">\n        \n        <li>removeFile( file ) \n            <span class=\"return\">⇒  Array</span>\n            \n        </li>\n    </ul>\n    \n    \n        <p>参数: </p>\n        <ul class=\"params-list\"><li><span class=\"meta\"><code>文件对象。</code> {File}</span></li></ul>\n    \n    \n\n    <p>在队列中删除文件。</p>\n\n    \n</article>\n\n\n  \n\n    \n</div>\n<hr />\n                \n\n            </div>\n        </div>\n    </div>\n</div>\n\n\n"
  },
  {
    "path": "jekyll/document.md",
    "content": "---\nlayout: post\ntitle: 文档\nnavName: Document\ngroup: 'nav'\nweight : 2\nhideTitle: true\ncommentIssueId: 80\n---\n\n## 接口说明\nWeb Uploader的所有代码都在一个内部闭包中，对外暴露了唯一的一个变量`WebUploader`，所以完全不用担心此框架会与其他框架冲突。\n\n内部**所有**的类和功能都暴露在`WebUploader`名字空间下面。<br />\nDemo中使用的是`WebUploader.create`方法来初始化的，实际上可直接访问`WebUploader.Uploader`。\n\n```javascript\nvar uploader = new WebUploader.Uploader({\n    swf: 'path_of_swf/Uploader.swf'\n\n    // 其他配置项\n});\n```\n\n具体有哪些内部类，请转到[API]({{ site.baseurl }}/doc/index.html)页面。\n\n## 事件\n`Uploader`实例具有Backbone同样的事件API：`on`，`off`，`once`，`trigger`。\n\n```javascript\nuploader.on( 'fileQueued', function( file ) {\n    // do some things.\n});\n```\n\n除了通过`on`绑定事件外，`Uploader`实例还有一个更便捷的添加事件方式。\n\n```javascript\nuploader.onFileQueued = function( file ) {\n    // do some things.\n};\n```\n\n如同`Document Element`中的onEvent一样，他的执行比`on`添加的`handler`的要晚。如果那些`handler`里面，有一个`return false`了，此`onEvent`里面是不会执行到的。\n\n## Hook\n`Uploader`里面的功能被拆分成了好几个`widget`，由`command`机制来通信合作。<br />\n如下，filepicker在用户选择文件后，直接把结果`request`出去，然后负责队列的`queue` widget，监听命令，根据配置项中的`accept`来决定是否加入队列。\n\n```javascript\n// in file picker\npicker.on( 'select', function( files ) {\n    me.owner.request( 'add-file', [ files ]);\n});\n\n// in queue picker\nUploader.register({\n    'add-file': 'addFiles'\n\n    // xxxx\n}, {\n\n    addFiles: function( files ) {\n\n        // 遍历files中的文件, 过滤掉不满足规则的。\n    }\n});\n```\n\n`Uploader.regeister`方法用来说明，该`widget`要响应哪些命令，并指定由什么方法来响应。上面的例子，当`add-file`命令派送时，内部的`addFiles`成员方法将被执行到，同一个命令，可以指定多次`handler`, 各个`handler`会按添加顺序依次执行，且后续的`handler`，不能被前面的`handler`截断。\n\n`handler`里面可以是同步过程，也可以是异步过程。是异步过程时，只需要返回一个`promise`对象即可。存在异步可能的request调用者会等待此过程结束后才继续。举个例子，webuploader运行在flash模式下时，需要等待flash加载完毕后才能算ready了，此过程为一个异步过程，目前的做法是如下：\n\n```javascript\n// uploader在初始化的时候\nme.request( 'init', opts, function() {\n    me.state = 'ready';\n    me.trigger('ready');\n});\n\n// filepicker `widget`中的初始化过程。\nUploader.register({\n    'init': 'init'\n}, {\n    init: function( opts ) {\n\n        var deferred = Base.Deferred();\n\n        // 加载flash\n        // 当flash ready执行deferred.resolve方法。\n\n        return deferred.promise();\n    }\n});\n```\n\n目前webuploader内部有很多种command，在此列出比较重要的几个。\n\n<table class=\"table table-bordered\">\n    <tr>\n        <th>名称</th>\n        <th>参数</th>\n        <th>说明</th>\n    </tr>\n    <tr>\n        <td><code>add-file</code></td>\n        <td>files: File对象或者File数组</td>\n        <td>用来向队列中添加文件。</td>\n    </tr>\n    <tr>\n        <td><code>before-send-file</code></td>\n        <td>file: File对象</td>\n        <td>在文件发送之前request，此时还没有分片（如果配置了分片的话），可以用来做文件整体md5验证。</td>\n    </tr>\n    <tr>\n        <td><code>before-send</code></td>\n        <td>block: 分片对象</td>\n        <td>在分片发送之前request，可以用来做分片验证，如果此分片已经上传成功了，可返回一个rejected promise来跳过此分片上传</td>\n    </tr>\n    <tr>\n        <td><code>after-send-file</code></td>\n        <td>file: File对象</td>\n        <td>在所有分片都上传完毕后，且没有错误后request，用来做分片验证，此时如果promise被reject，当前文件上传会触发错误。</td>\n    </tr>\n</table>\n\n## 文件组织\nwebuploader由很多独立的小文件组成。每个文件都是以[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范组织的，方便类似与[RequireJS](http://requirejs.org/)之类的库直接使用。\n\n如`lib/file.js`\n\n```javascript\n/**\n * @fileOverview File\n */\ndefine([\n    '../base',\n    './blob'\n], function( Base, Blob ) {\n\n    var uid = 0,\n        rExt = /\\.([^.]+)$/;\n\n    function File( ruid, file ) {\n        var ext;\n\n        Blob.apply( this, arguments );\n        this.name = file.name || ('untitled' + uid++);\n\n        if ( !this.type ) {\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n            if ( ~'jpg,jpeg,png,gif,bmp'.indexOf( ext ) ) {\n                this.type = 'image/' + ext;\n            }\n        }\n\n        this.ext = ext;\n        this.lastModifiedDate = file.lastModifiedDate ||\n                (new Date()).toLocaleString();\n    }\n\n    return Base.inherits( Blob, File );\n});\n```\n\n下面是目前的目录结构及说明。\n\n```\n├── base.js   实现一些常用的帮助类方法，如inherits, log等等。\n├── file.js    文件类，Queue中存放的数据类。\n├── jq-bridge.js    jQuery的替代品，只实现webuploader所需的，当然，如果已经有jQuery了，此文件不用打包。\n├── lib\n│   ├── blob.js  带ruid（为了兼容flash抽象出来的，ruid为运行时id）的Blob类\n│   ├── dnd.js    文件拖拽\n│   ├── file.js   带ruid的文件类，blob的子类。\n│   ├── filepaste.js  负责图片粘贴。\n│   ├── filepicker.js    文件选择器\n│   ├── image.js    图片处理类，生成缩略图和图片压缩。\n│   └── transport.js    文件传送。\n├── mediator.js   Event类\n├── promise.js    同jq-bridge, 在没有jQuery的时候才需要。用来实现Deferred。\n├── queue.js    队列\n├── runtime\n│   ├── client.js   连接器\n│   ├── compbase.js    component的基类。\n│   ├── flash\n│   │   ├── xxx lib中flash的具体实现。\n│   ├── html5\n│   │   ├── xxx lib中html5的具体实现。\n│   └── runtime.js\n├── uploader.js    Uploader类。\n└── widgets\n    ├── filednd.js   文件拖拽应用在Uploader\n    ├── filepaste.js   图片粘贴应用在Uploader\n    ├── filepicker.js   文件上传应用在Uploader中。\n    ├── image.js     图片文件在对应的时机做图片压缩和预览\n    ├── queue.js     队列管理\n    ├── runtime.js    添加runtime信息给Uploader\n    ├── upload.js      负责具体上传逻辑\n    ├── validator.js    各种验证器\n    └── widget.js    实现command机制\n```\n"
  },
  {
    "path": "jekyll/download.md",
    "content": "---\nlayout: post\ntitle: 下载\nnavName: Download\ngroup: 'nav'\nweight : 5\nhideTitle: true\ncommentIssueId: 82\n---\n## 包内容\n\n下载包中包含以下文件\n\n```\n├── Uploader.swf                      // SWF文件，当使用Flash运行时需要引入。\n\n├── webuploader.js                    // 完全版本。\n├── webuploader.min.js                // min版本\n\n├── webuploader.flashonly.js          // 只有Flash实现的版本。\n├── webuploader.flashonly.min.js      // min版本\n\n├── webuploader.html5only.js          // 只有Html5实现的版本。\n├── webuploader.html5only.min.js      // min版本\n\n├── webuploader.withoutimage.js       // 去除图片处理的版本，包括HTML5和FLASH.\n└── webuploader.withoutimage.min.js   // min版本\n```\n\n## 下载\n\n直接下载本站默认打包版本，下载包中包含`源码版本`和`压缩版本`。\n\n<a class=\"btn btn-success\" href=\"https://github.com/fex-team/webuploader/releases\">下载最新版本</a>\n\n或者直接使用由[staticfile](http://www.staticfile.org/)提供的cdn版本，或者下载[Git项目包](https://github.com/fex-team/webuploader/zipball/master)。\n\n```html\n// SWF文件，当使用Flash运行时需要引入。\n├── http://cdn.staticfile.org/webuploader/0.1.0/Uploader.swf\n\n// 完全版本。\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.js\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.min.js\n\n// 只有Flash实现的版本。\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.flashonly.js\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.flashonly.min.js\n\n// 只有Html5实现的版本。\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.html5only.js\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.html5only.min.js\n\n// 去除图片处理的版本，包括HTML5和FLASH.\n├── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.withoutimage.js\n└── http://cdn.staticfile.org/webuploader/0.1.0/webuploader.withoutimage.min.js\n```\n\n## DIY打包\n\n想更大力度的搭配JS，实现满足需求的最小组合，请按如下步骤进行操作。 文件打包借助了[Grunt](http://gruntjs.com/getting-started)工具来实现。\n如果您还不知道什么是[Grunt](http://gruntjs.com/getting-started)，赶紧去了解一下吧。\n\n### 环境依赖\n\n1. git命令行工具\n2. node & npm命令行工具\n3. grunt (`npm install grunt-cli -g`)\n\n### 编译代码\n1. 克隆 [webuploader git仓库](https://github.com/fex-team/webuploader)，`git clone https://github.com/fex-team/webuploader.git`。\n2. 安装node依赖，`npm install`。\n3. 执行`grunt dist`，此动作会在dist目录下面创建合并版本的js, 包括通过`uglify`压缩的min版本。\n\n### 配置\n打开webuploader仓库根目录下面的`Gruntfile.js`文件, 代码合并有`build`task来完成。找到`build`配置项。\n\nGruntfile.js已经配置了一个自定义合并的demo. 打包只支持HTML5的版本。\n\n```javascript\n// 自己配置的实例\n// glob语法。\ncustom: {\n    preset: \"custom\",\n    cwd: \"src\",\n    src: [\n        'widgets/**/*.js',\n        'runtime/html5/**/*.js'\n    ],\n    dest: \"dist/webuploader.custom.js\"\n}\n```\n"
  },
  {
    "path": "jekyll/getting-started.md",
    "content": "---\nlayout: post\ntitle: 快速开始\nnavName: Getting started\ngroup: 'nav'\nweight : 1,\nstyles:\n  - /css/webuploader.css\nscripts:\n  - /js/webuploader.js\n  - /js/getting-started.js\ncommentIssueId: 71\nhideTitle: true\nbannerTitle: Getting Started\nbannerContents:\n  - 结合简单例子，快速掌握使用方法。\n---\n\n## 引入资源\n\n使用Web Uploader文件上传需要引入三种资源：JS, CSS, SWF。\n\n```html\n<!--引入CSS-->\n<link rel=\"stylesheet\" type=\"text/css\" href=\"webuploader文件夹/webuploader.css\">\n\n<!--引入JS-->\n<script type=\"text/javascript\" src=\"webuploader文件夹/webuploader.js\"></script>\n\n<!--SWF在初始化的时候指定，在后面将展示-->\n```\n## 文件上传\n\nWebUploader只包含文件上传的底层实现，不包括UI部分。所以交互方面可以自由发挥，以下将演示如何去实现一个简单的版本。\n\n请点击下面的`选择文件`按钮，然后点击`开始上传`体验此Demo。\n\n<div id=\"uploader\" class=\"wu-example\">\n    <div id=\"thelist\" class=\"uploader-list\"></div>\n    <div class=\"btns\">\n        <div id=\"picker\">选择文件</div>\n        <button id=\"ctlBtn\" class=\"btn btn-default\">开始上传</button>\n    </div>\n</div>\n\n### Html部分\n首先准备dom结构，包含存放文件信息的容器、选择按钮和上传按钮三个部分。\n\n```html\n<div id=\"uploader\" class=\"wu-example\">\n    <!--用来存放文件信息-->\n    <div id=\"thelist\" class=\"uploader-list\"></div>\n    <div class=\"btns\">\n        <div id=\"picker\">选择文件</div>\n        <button id=\"ctlBtn\" class=\"btn btn-default\">开始上传</button>\n    </div>\n</div>\n```\n\n### 初始化Web Uploader\n具体说明，请看一下代码中的注释部分。\n\n```javascript\nvar uploader = WebUploader.create({\n\n    // swf文件路径\n    swf: BASE_URL + '/js/Uploader.swf',\n\n    // 文件接收服务端。\n    server: 'http://webuploader.duapp.com/server/fileupload.php',\n\n    // 选择文件的按钮。可选。\n    // 内部根据当前运行是创建，可能是input元素，也可能是flash.\n    pick: '#picker',\n\n    // 不压缩image, 默认如果是jpeg，文件上传前会压缩一把再上传！\n    resize: false\n});\n```\n### 显示用户选择\n由于webuploader不处理UI逻辑，所以需要去监听`fileQueued`事件来实现。\n\n```javascript\n// 当有文件被添加进队列的时候\nuploader.on( 'fileQueued', function( file ) {\n    $list.append( '<div id=\"' + file.id + '\" class=\"item\">' +\n        '<h4 class=\"info\">' + file.name + '</h4>' +\n        '<p class=\"state\">等待上传...</p>' +\n    '</div>' );\n});\n```\n\n### 文件上传进度\n文件上传中，Web Uploader会对外派送`uploadProgress`事件，其中包含文件对象和该文件当前上传进度。\n\n```javascript\n// 文件上传过程中创建进度条实时显示。\nuploader.on( 'uploadProgress', function( file, percentage ) {\n    var $li = $( '#'+file.id ),\n        $percent = $li.find('.progress .progress-bar');\n\n    // 避免重复创建\n    if ( !$percent.length ) {\n        $percent = $('<div class=\"progress progress-striped active\">' +\n          '<div class=\"progress-bar\" role=\"progressbar\" style=\"width: 0%\">' +\n          '</div>' +\n        '</div>').appendTo( $li ).find('.progress-bar');\n    }\n\n    $li.find('p.state').text('上传中');\n\n    $percent.css( 'width', percentage * 100 + '%' );\n});\n```\n\n### 文件成功、失败处理\n文件上传失败会派送`uploadError`事件，成功则派送`uploadSuccess`事件。不管成功或者失败，在文件上传完后都会触发`uploadComplete`事件。\n\n```javascript\nuploader.on( 'uploadSuccess', function( file ) {\n    $( '#'+file.id ).find('p.state').text('已上传');\n});\n\nuploader.on( 'uploadError', function( file ) {\n    $( '#'+file.id ).find('p.state').text('上传出错');\n});\n\nuploader.on( 'uploadComplete', function( file ) {\n    $( '#'+file.id ).find('.progress').fadeOut();\n});\n\n```\n\n\n## 图片上传\n与普通文件上传相比，此demo演示了：文件过滤，图片预览，图片压缩上传等功能。\n\n<div id=\"uploader-demo\" class=\"wu-example\">\n    <div id=\"fileList\" class=\"uploader-list\">\n    </div>\n    <div id=\"filePicker\">选择图片</div>\n</div>\n\n### Html\n要实现如上demo，首先需要准备一个按钮，和一个用来存放添加的文件信息列表的容器。\n\n```html\n<!--dom结构部分-->\n<div id=\"uploader-demo\">\n    <!--用来存放item-->\n    <div id=\"fileList\" class=\"uploader-list\"></div>\n    <div id=\"filePicker\">选择图片</div>\n</div>\n```\n\n### Javascript\n创建Web Uploader实例\n\n```javascript\n// 初始化Web Uploader\nvar uploader = WebUploader.create({\n\n    // 选完文件后，是否自动上传。\n    auto: true,\n\n    // swf文件路径\n    swf: BASE_URL + '/js/Uploader.swf',\n\n    // 文件接收服务端。\n    server: 'http://webuploader.duapp.com/server/fileupload.php',\n\n    // 选择文件的按钮。可选。\n    // 内部根据当前运行是创建，可能是input元素，也可能是flash.\n    pick: '#filePicker',\n\n    // 只允许选择图片文件。\n    accept: {\n        title: 'Images',\n        extensions: 'gif,jpg,jpeg,bmp,png',\n        mimeTypes: 'image/*'\n    }\n});\n```\n\n监听`fileQueued`事件，通过`uploader.makeThumb`来创建图片预览图。<br />\nPS: 这里得到的是[Data URL](http://en.wikipedia.org/wiki/Data_URI_scheme)数据，IE6、IE7不支持直接预览。可以借助FLASH或者服务端来完成预览。\n\n```javascript\n// 当有文件添加进来的时候\nuploader.on( 'fileQueued', function( file ) {\n    var $li = $(\n            '<div id=\"' + file.id + '\" class=\"file-item thumbnail\">' +\n                '<img>' +\n                '<div class=\"info\">' + file.name + '</div>' +\n            '</div>'\n            ),\n        $img = $li.find('img');\n\n\n    // $list为容器jQuery实例\n    $list.append( $li );\n\n    // 创建缩略图\n    // 如果为非图片文件，可以不用调用此方法。\n    // thumbnailWidth x thumbnailHeight 为 100 x 100\n    uploader.makeThumb( file, function( error, src ) {\n        if ( error ) {\n            $img.replaceWith('<span>不能预览</span>');\n            return;\n        }\n\n        $img.attr( 'src', src );\n    }, thumbnailWidth, thumbnailHeight );\n});\n```\n\n然后剩下的就是上传状态提示了，当文件上传过程中, 上传成功，上传失败，上传完成都分别对应`uploadProgress`,\n`uploadSuccess`, `uploadError`, `uploadComplete`事件。\n\n```javascript\n// 文件上传过程中创建进度条实时显示。\nuploader.on( 'uploadProgress', function( file, percentage ) {\n    var $li = $( '#'+file.id ),\n        $percent = $li.find('.progress span');\n\n    // 避免重复创建\n    if ( !$percent.length ) {\n        $percent = $('<p class=\"progress\"><span></span></p>')\n                .appendTo( $li )\n                .find('span');\n    }\n\n    $percent.css( 'width', percentage * 100 + '%' );\n});\n\n// 文件上传成功，给item添加成功class, 用样式标记上传成功。\nuploader.on( 'uploadSuccess', function( file ) {\n    $( '#'+file.id ).addClass('upload-state-done');\n});\n\n// 文件上传失败，显示上传出错。\nuploader.on( 'uploadError', function( file ) {\n    var $li = $( '#'+file.id ),\n        $error = $li.find('div.error');\n\n    // 避免重复创建\n    if ( !$error.length ) {\n        $error = $('<div class=\"error\"></div>').appendTo( $li );\n    }\n\n    $error.text('上传失败');\n});\n\n// 完成上传完了，成功或者失败，先删除进度条。\nuploader.on( 'uploadComplete', function( file ) {\n    $( '#'+file.id ).find('.progress').remove();\n});\n```\n\n更多细节，请查看[js源码]({{site.baseurl}}/js/getting-started.js)。\n\n"
  },
  {
    "path": "jekyll/index.md",
    "content": "---\nlayout: default\ntitle: Web Uploader\n---\n\n<!-- Main jumbotron for a primary marketing message or call to action -->\n<div class=\"jumbotron\">\n    <div class=\"container\">\n        <h1>Web Uploader</h1>\n        <p>WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主，FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势，同时又不摒弃主流IE浏览器，沿用原来的FLASH运行时，兼容IE6+，iOS 6+, android 4+。两套运行时，同样的调用方式，可供用户任意选用。</p>\n\n        <p>采用大文件分片并发上传，极大的提高了文件上传效率。</p>\n        <p>\n            <a class=\"btn btn-primary btn-lg\" href=\"{{site.baseurl}}/getting-started.html\" role=\"button\">快速开始</a>\n            &nbsp;&nbsp;\n            <a class=\"btn btn-success btn-lg\" href=\"{{site.baseurl}}/download.html\" role=\"button\">&nbsp;&nbsp;&nbsp;&nbsp;下载&nbsp;&nbsp;&nbsp;&nbsp;</a>\n        </p>\n        <div class=\"github-btns\">\n            <a class=\"travis\" href=\"https://travis-ci.org/fex-team/webuploader\"><img alt=\"\" src=\"https://secure.travis-ci.org/fex-team/webuploader.png?branch=master\" /></a>\n\n            <iframe src=\"http://ghbtns.com/github-btn.html?user=fex-team&repo=webuploader&type=watch&count=true\"\n  allowtransparency=\"true\" frameborder=\"0\" scrolling=\"0\" width=\"100\" height=\"20\"></iframe>\n            <iframe src=\"http://ghbtns.com/github-btn.html?user=fex-team&repo=webuploader&type=fork&count=true\"\n  allowtransparency=\"true\" frameborder=\"0\" scrolling=\"0\" width=\"100\" height=\"20\"></iframe>\n        </div>\n\n    </div>\n</div>\n\n\n<div class=\"fetature container\">\n    <div class=\"row\">\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>分片、并发</h2>\n            <p>分片与并发结合，将一个大文件分割成多块，并发上传，极大地提高大文件的上传速度。</p>\n\n            <p>当网络问题导致传输错误时，只需要重传出错分片，而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。</p>\n        </div>\n\n\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>预览、压缩</h2>\n            <p>支持常用图片格式jpg,jpeg,gif,bmp,png预览与压缩，节省网络数据传输。</p>\n\n            <p>解析jpeg中的meta信息，对于各种orientation做了正确的处理，同时压缩后上传保留图片的所有原始meta数据。</p>\n        </div>\n\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>多途径添加文件</h2>\n            <p>支持文件多选，类型过滤，拖拽(文件&amp;文件夹)，图片粘贴功能。</p>\n            <p>粘贴功能主要体现在当有图片数据在剪切板中时（截屏工具如QQ(Ctrl + ALT + A), 网页中右击图片点击复制），Ctrl + V便可添加此图片文件。</p>\n        </div>\n\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>HTML5 &amp; FLASH</h2>\n            <p>兼容主流浏览器，接口一致，实现了两套运行时支持，用户无需关心内部用了什么内核。</p>\n            <p>同时Flash部分没有做任何UI相关的工作，方便不关心flash的用户扩展和自定义业务需求。</p>\n        </div>\n\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>MD5秒传</h2>\n            <p>当文件体积大、量比较多时，支持上传前做文件md5值验证，一致则可直接跳过。</p>\n            <p>如果服务端与前端统一修改算法，取段md5，可大大提升验证性能，耗时在20ms左右。</p>\n        </div>\n\n\n\n        <div class=\"col-6 col-sm-6 col-lg-4\">\n            <h2>易扩展、可拆分</h2>\n            <p>采用可拆分机制, 将各个功能独立成了小组件，可自由搭配。</p>\n            <p>采用AMD规范组织代码，清晰明了，方便高级玩家扩展。</p>\n        </div>\n\n    </div>\n    <!--/row-->\n</div>"
  },
  {
    "path": "jekyll/js/bootstrap.js",
    "content": "/*!\n * Bootstrap v3.0.3 (http://getbootstrap.com)\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.org/licenses/LICENSE-2.0\n */\n\nif (typeof jQuery === \"undefined\") { throw new Error(\"Bootstrap requires jQuery\") }\n\n/* ========================================================================\n * Bootstrap: transition.js v3.0.3\n * http://getbootstrap.com/javascript/#transitions\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)\n  // ============================================================\n\n  function transitionEnd() {\n    var el = document.createElement('bootstrap')\n\n    var transEndEventNames = {\n      'WebkitTransition' : 'webkitTransitionEnd'\n    , 'MozTransition'    : 'transitionend'\n    , 'OTransition'      : 'oTransitionEnd otransitionend'\n    , 'transition'       : 'transitionend'\n    }\n\n    for (var name in transEndEventNames) {\n      if (el.style[name] !== undefined) {\n        return { end: transEndEventNames[name] }\n      }\n    }\n  }\n\n  // http://blog.alexmaccaw.com/css-transitions\n  $.fn.emulateTransitionEnd = function (duration) {\n    var called = false, $el = this\n    $(this).one($.support.transition.end, function () { called = true })\n    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }\n    setTimeout(callback, duration)\n    return this\n  }\n\n  $(function () {\n    $.support.transition = transitionEnd()\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: alert.js v3.0.3\n * http://getbootstrap.com/javascript/#alerts\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // ALERT CLASS DEFINITION\n  // ======================\n\n  var dismiss = '[data-dismiss=\"alert\"]'\n  var Alert   = function (el) {\n    $(el).on('click', dismiss, this.close)\n  }\n\n  Alert.prototype.close = function (e) {\n    var $this    = $(this)\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') // strip for ie7\n    }\n\n    var $parent = $(selector)\n\n    if (e) e.preventDefault()\n\n    if (!$parent.length) {\n      $parent = $this.hasClass('alert') ? $this : $this.parent()\n    }\n\n    $parent.trigger(e = $.Event('close.bs.alert'))\n\n    if (e.isDefaultPrevented()) return\n\n    $parent.removeClass('in')\n\n    function removeElement() {\n      $parent.trigger('closed.bs.alert').remove()\n    }\n\n    $.support.transition && $parent.hasClass('fade') ?\n      $parent\n        .one($.support.transition.end, removeElement)\n        .emulateTransitionEnd(150) :\n      removeElement()\n  }\n\n\n  // ALERT PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.alert\n\n  $.fn.alert = function (option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.alert')\n\n      if (!data) $this.data('bs.alert', (data = new Alert(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  $.fn.alert.Constructor = Alert\n\n\n  // ALERT NO CONFLICT\n  // =================\n\n  $.fn.alert.noConflict = function () {\n    $.fn.alert = old\n    return this\n  }\n\n\n  // ALERT DATA-API\n  // ==============\n\n  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: button.js v3.0.3\n * http://getbootstrap.com/javascript/#buttons\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // BUTTON PUBLIC CLASS DEFINITION\n  // ==============================\n\n  var Button = function (element, options) {\n    this.$element = $(element)\n    this.options  = $.extend({}, Button.DEFAULTS, options)\n  }\n\n  Button.DEFAULTS = {\n    loadingText: 'loading...'\n  }\n\n  Button.prototype.setState = function (state) {\n    var d    = 'disabled'\n    var $el  = this.$element\n    var val  = $el.is('input') ? 'val' : 'html'\n    var data = $el.data()\n\n    state = state + 'Text'\n\n    if (!data.resetText) $el.data('resetText', $el[val]())\n\n    $el[val](data[state] || this.options[state])\n\n    // push to event loop to allow forms to submit\n    setTimeout(function () {\n      state == 'loadingText' ?\n        $el.addClass(d).attr(d, d) :\n        $el.removeClass(d).removeAttr(d);\n    }, 0)\n  }\n\n  Button.prototype.toggle = function () {\n    var $parent = this.$element.closest('[data-toggle=\"buttons\"]')\n    var changed = true\n\n    if ($parent.length) {\n      var $input = this.$element.find('input')\n      if ($input.prop('type') === 'radio') {\n        // see if clicking on current one\n        if ($input.prop('checked') && this.$element.hasClass('active'))\n          changed = false\n        else\n          $parent.find('.active').removeClass('active')\n      }\n      if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')\n    }\n\n    if (changed) this.$element.toggleClass('active')\n  }\n\n\n  // BUTTON PLUGIN DEFINITION\n  // ========================\n\n  var old = $.fn.button\n\n  $.fn.button = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.button')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.button', (data = new Button(this, options)))\n\n      if (option == 'toggle') data.toggle()\n      else if (option) data.setState(option)\n    })\n  }\n\n  $.fn.button.Constructor = Button\n\n\n  // BUTTON NO CONFLICT\n  // ==================\n\n  $.fn.button.noConflict = function () {\n    $.fn.button = old\n    return this\n  }\n\n\n  // BUTTON DATA-API\n  // ===============\n\n  $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {\n    var $btn = $(e.target)\n    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')\n    $btn.button('toggle')\n    e.preventDefault()\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: carousel.js v3.0.3\n * http://getbootstrap.com/javascript/#carousel\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // CAROUSEL CLASS DEFINITION\n  // =========================\n\n  var Carousel = function (element, options) {\n    this.$element    = $(element)\n    this.$indicators = this.$element.find('.carousel-indicators')\n    this.options     = options\n    this.paused      =\n    this.sliding     =\n    this.interval    =\n    this.$active     =\n    this.$items      = null\n\n    this.options.pause == 'hover' && this.$element\n      .on('mouseenter', $.proxy(this.pause, this))\n      .on('mouseleave', $.proxy(this.cycle, this))\n  }\n\n  Carousel.DEFAULTS = {\n    interval: 5000\n  , pause: 'hover'\n  , wrap: true\n  }\n\n  Carousel.prototype.cycle =  function (e) {\n    e || (this.paused = false)\n\n    this.interval && clearInterval(this.interval)\n\n    this.options.interval\n      && !this.paused\n      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))\n\n    return this\n  }\n\n  Carousel.prototype.getActiveIndex = function () {\n    this.$active = this.$element.find('.item.active')\n    this.$items  = this.$active.parent().children()\n\n    return this.$items.index(this.$active)\n  }\n\n  Carousel.prototype.to = function (pos) {\n    var that        = this\n    var activeIndex = this.getActiveIndex()\n\n    if (pos > (this.$items.length - 1) || pos < 0) return\n\n    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) })\n    if (activeIndex == pos) return this.pause().cycle()\n\n    return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))\n  }\n\n  Carousel.prototype.pause = function (e) {\n    e || (this.paused = true)\n\n    if (this.$element.find('.next, .prev').length && $.support.transition.end) {\n      this.$element.trigger($.support.transition.end)\n      this.cycle(true)\n    }\n\n    this.interval = clearInterval(this.interval)\n\n    return this\n  }\n\n  Carousel.prototype.next = function () {\n    if (this.sliding) return\n    return this.slide('next')\n  }\n\n  Carousel.prototype.prev = function () {\n    if (this.sliding) return\n    return this.slide('prev')\n  }\n\n  Carousel.prototype.slide = function (type, next) {\n    var $active   = this.$element.find('.item.active')\n    var $next     = next || $active[type]()\n    var isCycling = this.interval\n    var direction = type == 'next' ? 'left' : 'right'\n    var fallback  = type == 'next' ? 'first' : 'last'\n    var that      = this\n\n    if (!$next.length) {\n      if (!this.options.wrap) return\n      $next = this.$element.find('.item')[fallback]()\n    }\n\n    this.sliding = true\n\n    isCycling && this.pause()\n\n    var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })\n\n    if ($next.hasClass('active')) return\n\n    if (this.$indicators.length) {\n      this.$indicators.find('.active').removeClass('active')\n      this.$element.one('slid.bs.carousel', function () {\n        var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])\n        $nextIndicator && $nextIndicator.addClass('active')\n      })\n    }\n\n    if ($.support.transition && this.$element.hasClass('slide')) {\n      this.$element.trigger(e)\n      if (e.isDefaultPrevented()) return\n      $next.addClass(type)\n      $next[0].offsetWidth // force reflow\n      $active.addClass(direction)\n      $next.addClass(direction)\n      $active\n        .one($.support.transition.end, function () {\n          $next.removeClass([type, direction].join(' ')).addClass('active')\n          $active.removeClass(['active', direction].join(' '))\n          that.sliding = false\n          setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)\n        })\n        .emulateTransitionEnd(600)\n    } else {\n      this.$element.trigger(e)\n      if (e.isDefaultPrevented()) return\n      $active.removeClass('active')\n      $next.addClass('active')\n      this.sliding = false\n      this.$element.trigger('slid.bs.carousel')\n    }\n\n    isCycling && this.cycle()\n\n    return this\n  }\n\n\n  // CAROUSEL PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.carousel\n\n  $.fn.carousel = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.carousel')\n      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)\n      var action  = typeof option == 'string' ? option : options.slide\n\n      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))\n      if (typeof option == 'number') data.to(option)\n      else if (action) data[action]()\n      else if (options.interval) data.pause().cycle()\n    })\n  }\n\n  $.fn.carousel.Constructor = Carousel\n\n\n  // CAROUSEL NO CONFLICT\n  // ====================\n\n  $.fn.carousel.noConflict = function () {\n    $.fn.carousel = old\n    return this\n  }\n\n\n  // CAROUSEL DATA-API\n  // =================\n\n  $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {\n    var $this   = $(this), href\n    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '')) //strip for ie7\n    var options = $.extend({}, $target.data(), $this.data())\n    var slideIndex = $this.attr('data-slide-to')\n    if (slideIndex) options.interval = false\n\n    $target.carousel(options)\n\n    if (slideIndex = $this.attr('data-slide-to')) {\n      $target.data('bs.carousel').to(slideIndex)\n    }\n\n    e.preventDefault()\n  })\n\n  $(window).on('load', function () {\n    $('[data-ride=\"carousel\"]').each(function () {\n      var $carousel = $(this)\n      $carousel.carousel($carousel.data())\n    })\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: collapse.js v3.0.3\n * http://getbootstrap.com/javascript/#collapse\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // COLLAPSE PUBLIC CLASS DEFINITION\n  // ================================\n\n  var Collapse = function (element, options) {\n    this.$element      = $(element)\n    this.options       = $.extend({}, Collapse.DEFAULTS, options)\n    this.transitioning = null\n\n    if (this.options.parent) this.$parent = $(this.options.parent)\n    if (this.options.toggle) this.toggle()\n  }\n\n  Collapse.DEFAULTS = {\n    toggle: true\n  }\n\n  Collapse.prototype.dimension = function () {\n    var hasWidth = this.$element.hasClass('width')\n    return hasWidth ? 'width' : 'height'\n  }\n\n  Collapse.prototype.show = function () {\n    if (this.transitioning || this.$element.hasClass('in')) return\n\n    var startEvent = $.Event('show.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    var actives = this.$parent && this.$parent.find('> .panel > .in')\n\n    if (actives && actives.length) {\n      var hasData = actives.data('bs.collapse')\n      if (hasData && hasData.transitioning) return\n      actives.collapse('hide')\n      hasData || actives.data('bs.collapse', null)\n    }\n\n    var dimension = this.dimension()\n\n    this.$element\n      .removeClass('collapse')\n      .addClass('collapsing')\n      [dimension](0)\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.$element\n        .removeClass('collapsing')\n        .addClass('in')\n        [dimension]('auto')\n      this.transitioning = 0\n      this.$element.trigger('shown.bs.collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    var scrollSize = $.camelCase(['scroll', dimension].join('-'))\n\n    this.$element\n      .one($.support.transition.end, $.proxy(complete, this))\n      .emulateTransitionEnd(350)\n      [dimension](this.$element[0][scrollSize])\n  }\n\n  Collapse.prototype.hide = function () {\n    if (this.transitioning || !this.$element.hasClass('in')) return\n\n    var startEvent = $.Event('hide.bs.collapse')\n    this.$element.trigger(startEvent)\n    if (startEvent.isDefaultPrevented()) return\n\n    var dimension = this.dimension()\n\n    this.$element\n      [dimension](this.$element[dimension]())\n      [0].offsetHeight\n\n    this.$element\n      .addClass('collapsing')\n      .removeClass('collapse')\n      .removeClass('in')\n\n    this.transitioning = 1\n\n    var complete = function () {\n      this.transitioning = 0\n      this.$element\n        .trigger('hidden.bs.collapse')\n        .removeClass('collapsing')\n        .addClass('collapse')\n    }\n\n    if (!$.support.transition) return complete.call(this)\n\n    this.$element\n      [dimension](0)\n      .one($.support.transition.end, $.proxy(complete, this))\n      .emulateTransitionEnd(350)\n  }\n\n  Collapse.prototype.toggle = function () {\n    this[this.$element.hasClass('in') ? 'hide' : 'show']()\n  }\n\n\n  // COLLAPSE PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.collapse\n\n  $.fn.collapse = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.collapse')\n      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.collapse.Constructor = Collapse\n\n\n  // COLLAPSE NO CONFLICT\n  // ====================\n\n  $.fn.collapse.noConflict = function () {\n    $.fn.collapse = old\n    return this\n  }\n\n\n  // COLLAPSE DATA-API\n  // =================\n\n  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {\n    var $this   = $(this), href\n    var target  = $this.attr('data-target')\n        || e.preventDefault()\n        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '') //strip for ie7\n    var $target = $(target)\n    var data    = $target.data('bs.collapse')\n    var option  = data ? 'toggle' : $this.data()\n    var parent  = $this.attr('data-parent')\n    var $parent = parent && $(parent)\n\n    if (!data || !data.transitioning) {\n      if ($parent) $parent.find('[data-toggle=collapse][data-parent=\"' + parent + '\"]').not($this).addClass('collapsed')\n      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')\n    }\n\n    $target.collapse(option)\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: dropdown.js v3.0.3\n * http://getbootstrap.com/javascript/#dropdowns\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // DROPDOWN CLASS DEFINITION\n  // =========================\n\n  var backdrop = '.dropdown-backdrop'\n  var toggle   = '[data-toggle=dropdown]'\n  var Dropdown = function (element) {\n    $(element).on('click.bs.dropdown', this.toggle)\n  }\n\n  Dropdown.prototype.toggle = function (e) {\n    var $this = $(this)\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    clearMenus()\n\n    if (!isActive) {\n      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {\n        // if mobile we use a backdrop because click events don't delegate\n        $('<div class=\"dropdown-backdrop\"/>').insertAfter($(this)).on('click', clearMenus)\n      }\n\n      $parent.trigger(e = $.Event('show.bs.dropdown'))\n\n      if (e.isDefaultPrevented()) return\n\n      $parent\n        .toggleClass('open')\n        .trigger('shown.bs.dropdown')\n\n      $this.focus()\n    }\n\n    return false\n  }\n\n  Dropdown.prototype.keydown = function (e) {\n    if (!/(38|40|27)/.test(e.keyCode)) return\n\n    var $this = $(this)\n\n    e.preventDefault()\n    e.stopPropagation()\n\n    if ($this.is('.disabled, :disabled')) return\n\n    var $parent  = getParent($this)\n    var isActive = $parent.hasClass('open')\n\n    if (!isActive || (isActive && e.keyCode == 27)) {\n      if (e.which == 27) $parent.find(toggle).focus()\n      return $this.click()\n    }\n\n    var $items = $('[role=menu] li:not(.divider):visible a', $parent)\n\n    if (!$items.length) return\n\n    var index = $items.index($items.filter(':focus'))\n\n    if (e.keyCode == 38 && index > 0)                 index--                        // up\n    if (e.keyCode == 40 && index < $items.length - 1) index++                        // down\n    if (!~index)                                      index=0\n\n    $items.eq(index).focus()\n  }\n\n  function clearMenus() {\n    $(backdrop).remove()\n    $(toggle).each(function (e) {\n      var $parent = getParent($(this))\n      if (!$parent.hasClass('open')) return\n      $parent.trigger(e = $.Event('hide.bs.dropdown'))\n      if (e.isDefaultPrevented()) return\n      $parent.removeClass('open').trigger('hidden.bs.dropdown')\n    })\n  }\n\n  function getParent($this) {\n    var selector = $this.attr('data-target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\\s]*$)/, '') //strip for ie7\n    }\n\n    var $parent = selector && $(selector)\n\n    return $parent && $parent.length ? $parent : $this.parent()\n  }\n\n\n  // DROPDOWN PLUGIN DEFINITION\n  // ==========================\n\n  var old = $.fn.dropdown\n\n  $.fn.dropdown = function (option) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.dropdown')\n\n      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))\n      if (typeof option == 'string') data[option].call($this)\n    })\n  }\n\n  $.fn.dropdown.Constructor = Dropdown\n\n\n  // DROPDOWN NO CONFLICT\n  // ====================\n\n  $.fn.dropdown.noConflict = function () {\n    $.fn.dropdown = old\n    return this\n  }\n\n\n  // APPLY TO STANDARD DROPDOWN ELEMENTS\n  // ===================================\n\n  $(document)\n    .on('click.bs.dropdown.data-api', clearMenus)\n    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })\n    .on('click.bs.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)\n    .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: modal.js v3.0.3\n * http://getbootstrap.com/javascript/#modals\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // MODAL CLASS DEFINITION\n  // ======================\n\n  var Modal = function (element, options) {\n    this.options   = options\n    this.$element  = $(element)\n    this.$backdrop =\n    this.isShown   = null\n\n    if (this.options.remote) this.$element.load(this.options.remote)\n  }\n\n  Modal.DEFAULTS = {\n      backdrop: true\n    , keyboard: true\n    , show: true\n  }\n\n  Modal.prototype.toggle = function (_relatedTarget) {\n    return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)\n  }\n\n  Modal.prototype.show = function (_relatedTarget) {\n    var that = this\n    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })\n\n    this.$element.trigger(e)\n\n    if (this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = true\n\n    this.escape()\n\n    this.$element.on('click.dismiss.modal', '[data-dismiss=\"modal\"]', $.proxy(this.hide, this))\n\n    this.backdrop(function () {\n      var transition = $.support.transition && that.$element.hasClass('fade')\n\n      if (!that.$element.parent().length) {\n        that.$element.appendTo(document.body) // don't move modals dom position\n      }\n\n      that.$element.show()\n\n      if (transition) {\n        that.$element[0].offsetWidth // force reflow\n      }\n\n      that.$element\n        .addClass('in')\n        .attr('aria-hidden', false)\n\n      that.enforceFocus()\n\n      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })\n\n      transition ?\n        that.$element.find('.modal-dialog') // wait for modal to slide in\n          .one($.support.transition.end, function () {\n            that.$element.focus().trigger(e)\n          })\n          .emulateTransitionEnd(300) :\n        that.$element.focus().trigger(e)\n    })\n  }\n\n  Modal.prototype.hide = function (e) {\n    if (e) e.preventDefault()\n\n    e = $.Event('hide.bs.modal')\n\n    this.$element.trigger(e)\n\n    if (!this.isShown || e.isDefaultPrevented()) return\n\n    this.isShown = false\n\n    this.escape()\n\n    $(document).off('focusin.bs.modal')\n\n    this.$element\n      .removeClass('in')\n      .attr('aria-hidden', true)\n      .off('click.dismiss.modal')\n\n    $.support.transition && this.$element.hasClass('fade') ?\n      this.$element\n        .one($.support.transition.end, $.proxy(this.hideModal, this))\n        .emulateTransitionEnd(300) :\n      this.hideModal()\n  }\n\n  Modal.prototype.enforceFocus = function () {\n    $(document)\n      .off('focusin.bs.modal') // guard against infinite focus loop\n      .on('focusin.bs.modal', $.proxy(function (e) {\n        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {\n          this.$element.focus()\n        }\n      }, this))\n  }\n\n  Modal.prototype.escape = function () {\n    if (this.isShown && this.options.keyboard) {\n      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {\n        e.which == 27 && this.hide()\n      }, this))\n    } else if (!this.isShown) {\n      this.$element.off('keyup.dismiss.bs.modal')\n    }\n  }\n\n  Modal.prototype.hideModal = function () {\n    var that = this\n    this.$element.hide()\n    this.backdrop(function () {\n      that.removeBackdrop()\n      that.$element.trigger('hidden.bs.modal')\n    })\n  }\n\n  Modal.prototype.removeBackdrop = function () {\n    this.$backdrop && this.$backdrop.remove()\n    this.$backdrop = null\n  }\n\n  Modal.prototype.backdrop = function (callback) {\n    var that    = this\n    var animate = this.$element.hasClass('fade') ? 'fade' : ''\n\n    if (this.isShown && this.options.backdrop) {\n      var doAnimate = $.support.transition && animate\n\n      this.$backdrop = $('<div class=\"modal-backdrop ' + animate + '\" />')\n        .appendTo(document.body)\n\n      this.$element.on('click.dismiss.modal', $.proxy(function (e) {\n        if (e.target !== e.currentTarget) return\n        this.options.backdrop == 'static'\n          ? this.$element[0].focus.call(this.$element[0])\n          : this.hide.call(this)\n      }, this))\n\n      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow\n\n      this.$backdrop.addClass('in')\n\n      if (!callback) return\n\n      doAnimate ?\n        this.$backdrop\n          .one($.support.transition.end, callback)\n          .emulateTransitionEnd(150) :\n        callback()\n\n    } else if (!this.isShown && this.$backdrop) {\n      this.$backdrop.removeClass('in')\n\n      $.support.transition && this.$element.hasClass('fade')?\n        this.$backdrop\n          .one($.support.transition.end, callback)\n          .emulateTransitionEnd(150) :\n        callback()\n\n    } else if (callback) {\n      callback()\n    }\n  }\n\n\n  // MODAL PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.modal\n\n  $.fn.modal = function (option, _relatedTarget) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.modal')\n      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)\n\n      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))\n      if (typeof option == 'string') data[option](_relatedTarget)\n      else if (options.show) data.show(_relatedTarget)\n    })\n  }\n\n  $.fn.modal.Constructor = Modal\n\n\n  // MODAL NO CONFLICT\n  // =================\n\n  $.fn.modal.noConflict = function () {\n    $.fn.modal = old\n    return this\n  }\n\n\n  // MODAL DATA-API\n  // ==============\n\n  $(document).on('click.bs.modal.data-api', '[data-toggle=\"modal\"]', function (e) {\n    var $this   = $(this)\n    var href    = $this.attr('href')\n    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\\s]+$)/, ''))) //strip for ie7\n    var option  = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())\n\n    e.preventDefault()\n\n    $target\n      .modal(option, this)\n      .one('hide', function () {\n        $this.is(':visible') && $this.focus()\n      })\n  })\n\n  $(document)\n    .on('show.bs.modal',  '.modal', function () { $(document.body).addClass('modal-open') })\n    .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: tooltip.js v3.0.3\n * http://getbootstrap.com/javascript/#tooltip\n * Inspired by the original jQuery.tipsy by Jason Frame\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // TOOLTIP PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Tooltip = function (element, options) {\n    this.type       =\n    this.options    =\n    this.enabled    =\n    this.timeout    =\n    this.hoverState =\n    this.$element   = null\n\n    this.init('tooltip', element, options)\n  }\n\n  Tooltip.DEFAULTS = {\n    animation: true\n  , placement: 'top'\n  , selector: false\n  , template: '<div class=\"tooltip\"><div class=\"tooltip-arrow\"></div><div class=\"tooltip-inner\"></div></div>'\n  , trigger: 'hover focus'\n  , title: ''\n  , delay: 0\n  , html: false\n  , container: false\n  }\n\n  Tooltip.prototype.init = function (type, element, options) {\n    this.enabled  = true\n    this.type     = type\n    this.$element = $(element)\n    this.options  = this.getOptions(options)\n\n    var triggers = this.options.trigger.split(' ')\n\n    for (var i = triggers.length; i--;) {\n      var trigger = triggers[i]\n\n      if (trigger == 'click') {\n        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))\n      } else if (trigger != 'manual') {\n        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus'\n        var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'\n\n        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))\n        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))\n      }\n    }\n\n    this.options.selector ?\n      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :\n      this.fixTitle()\n  }\n\n  Tooltip.prototype.getDefaults = function () {\n    return Tooltip.DEFAULTS\n  }\n\n  Tooltip.prototype.getOptions = function (options) {\n    options = $.extend({}, this.getDefaults(), this.$element.data(), options)\n\n    if (options.delay && typeof options.delay == 'number') {\n      options.delay = {\n        show: options.delay\n      , hide: options.delay\n      }\n    }\n\n    return options\n  }\n\n  Tooltip.prototype.getDelegateOptions = function () {\n    var options  = {}\n    var defaults = this.getDefaults()\n\n    this._options && $.each(this._options, function (key, value) {\n      if (defaults[key] != value) options[key] = value\n    })\n\n    return options\n  }\n\n  Tooltip.prototype.enter = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'in'\n\n    if (!self.options.delay || !self.options.delay.show) return self.show()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'in') self.show()\n    }, self.options.delay.show)\n  }\n\n  Tooltip.prototype.leave = function (obj) {\n    var self = obj instanceof this.constructor ?\n      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)\n\n    clearTimeout(self.timeout)\n\n    self.hoverState = 'out'\n\n    if (!self.options.delay || !self.options.delay.hide) return self.hide()\n\n    self.timeout = setTimeout(function () {\n      if (self.hoverState == 'out') self.hide()\n    }, self.options.delay.hide)\n  }\n\n  Tooltip.prototype.show = function () {\n    var e = $.Event('show.bs.'+ this.type)\n\n    if (this.hasContent() && this.enabled) {\n      this.$element.trigger(e)\n\n      if (e.isDefaultPrevented()) return\n\n      var $tip = this.tip()\n\n      this.setContent()\n\n      if (this.options.animation) $tip.addClass('fade')\n\n      var placement = typeof this.options.placement == 'function' ?\n        this.options.placement.call(this, $tip[0], this.$element[0]) :\n        this.options.placement\n\n      var autoToken = /\\s?auto?\\s?/i\n      var autoPlace = autoToken.test(placement)\n      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'\n\n      $tip\n        .detach()\n        .css({ top: 0, left: 0, display: 'block' })\n        .addClass(placement)\n\n      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)\n\n      var pos          = this.getPosition()\n      var actualWidth  = $tip[0].offsetWidth\n      var actualHeight = $tip[0].offsetHeight\n\n      if (autoPlace) {\n        var $parent = this.$element.parent()\n\n        var orgPlacement = placement\n        var docScroll    = document.documentElement.scrollTop || document.body.scrollTop\n        var parentWidth  = this.options.container == 'body' ? window.innerWidth  : $parent.outerWidth()\n        var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()\n        var parentLeft   = this.options.container == 'body' ? 0 : $parent.offset().left\n\n        placement = placement == 'bottom' && pos.top   + pos.height  + actualHeight - docScroll > parentHeight  ? 'top'    :\n                    placement == 'top'    && pos.top   - docScroll   - actualHeight < 0                         ? 'bottom' :\n                    placement == 'right'  && pos.right + actualWidth > parentWidth                              ? 'left'   :\n                    placement == 'left'   && pos.left  - actualWidth < parentLeft                               ? 'right'  :\n                    placement\n\n        $tip\n          .removeClass(orgPlacement)\n          .addClass(placement)\n      }\n\n      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)\n\n      this.applyPlacement(calculatedOffset, placement)\n      this.$element.trigger('shown.bs.' + this.type)\n    }\n  }\n\n  Tooltip.prototype.applyPlacement = function(offset, placement) {\n    var replace\n    var $tip   = this.tip()\n    var width  = $tip[0].offsetWidth\n    var height = $tip[0].offsetHeight\n\n    // manually read margins because getBoundingClientRect includes difference\n    var marginTop = parseInt($tip.css('margin-top'), 10)\n    var marginLeft = parseInt($tip.css('margin-left'), 10)\n\n    // we must check for NaN for ie 8/9\n    if (isNaN(marginTop))  marginTop  = 0\n    if (isNaN(marginLeft)) marginLeft = 0\n\n    offset.top  = offset.top  + marginTop\n    offset.left = offset.left + marginLeft\n\n    $tip\n      .offset(offset)\n      .addClass('in')\n\n    // check to see if placing tip in new offset caused the tip to resize itself\n    var actualWidth  = $tip[0].offsetWidth\n    var actualHeight = $tip[0].offsetHeight\n\n    if (placement == 'top' && actualHeight != height) {\n      replace = true\n      offset.top = offset.top + height - actualHeight\n    }\n\n    if (/bottom|top/.test(placement)) {\n      var delta = 0\n\n      if (offset.left < 0) {\n        delta       = offset.left * -2\n        offset.left = 0\n\n        $tip.offset(offset)\n\n        actualWidth  = $tip[0].offsetWidth\n        actualHeight = $tip[0].offsetHeight\n      }\n\n      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')\n    } else {\n      this.replaceArrow(actualHeight - height, actualHeight, 'top')\n    }\n\n    if (replace) $tip.offset(offset)\n  }\n\n  Tooltip.prototype.replaceArrow = function(delta, dimension, position) {\n    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + \"%\") : '')\n  }\n\n  Tooltip.prototype.setContent = function () {\n    var $tip  = this.tip()\n    var title = this.getTitle()\n\n    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)\n    $tip.removeClass('fade in top bottom left right')\n  }\n\n  Tooltip.prototype.hide = function () {\n    var that = this\n    var $tip = this.tip()\n    var e    = $.Event('hide.bs.' + this.type)\n\n    function complete() {\n      if (that.hoverState != 'in') $tip.detach()\n    }\n\n    this.$element.trigger(e)\n\n    if (e.isDefaultPrevented()) return\n\n    $tip.removeClass('in')\n\n    $.support.transition && this.$tip.hasClass('fade') ?\n      $tip\n        .one($.support.transition.end, complete)\n        .emulateTransitionEnd(150) :\n      complete()\n\n    this.$element.trigger('hidden.bs.' + this.type)\n\n    return this\n  }\n\n  Tooltip.prototype.fixTitle = function () {\n    var $e = this.$element\n    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {\n      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')\n    }\n  }\n\n  Tooltip.prototype.hasContent = function () {\n    return this.getTitle()\n  }\n\n  Tooltip.prototype.getPosition = function () {\n    var el = this.$element[0]\n    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {\n      width: el.offsetWidth\n    , height: el.offsetHeight\n    }, this.$element.offset())\n  }\n\n  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {\n    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :\n           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :\n           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :\n        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }\n  }\n\n  Tooltip.prototype.getTitle = function () {\n    var title\n    var $e = this.$element\n    var o  = this.options\n\n    title = $e.attr('data-original-title')\n      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)\n\n    return title\n  }\n\n  Tooltip.prototype.tip = function () {\n    return this.$tip = this.$tip || $(this.options.template)\n  }\n\n  Tooltip.prototype.arrow = function () {\n    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')\n  }\n\n  Tooltip.prototype.validate = function () {\n    if (!this.$element[0].parentNode) {\n      this.hide()\n      this.$element = null\n      this.options  = null\n    }\n  }\n\n  Tooltip.prototype.enable = function () {\n    this.enabled = true\n  }\n\n  Tooltip.prototype.disable = function () {\n    this.enabled = false\n  }\n\n  Tooltip.prototype.toggleEnabled = function () {\n    this.enabled = !this.enabled\n  }\n\n  Tooltip.prototype.toggle = function (e) {\n    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this\n    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)\n  }\n\n  Tooltip.prototype.destroy = function () {\n    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)\n  }\n\n\n  // TOOLTIP PLUGIN DEFINITION\n  // =========================\n\n  var old = $.fn.tooltip\n\n  $.fn.tooltip = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.tooltip')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.tooltip.Constructor = Tooltip\n\n\n  // TOOLTIP NO CONFLICT\n  // ===================\n\n  $.fn.tooltip.noConflict = function () {\n    $.fn.tooltip = old\n    return this\n  }\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: popover.js v3.0.3\n * http://getbootstrap.com/javascript/#popovers\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // POPOVER PUBLIC CLASS DEFINITION\n  // ===============================\n\n  var Popover = function (element, options) {\n    this.init('popover', element, options)\n  }\n\n  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')\n\n  Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {\n    placement: 'right'\n  , trigger: 'click'\n  , content: ''\n  , template: '<div class=\"popover\"><div class=\"arrow\"></div><h3 class=\"popover-title\"></h3><div class=\"popover-content\"></div></div>'\n  })\n\n\n  // NOTE: POPOVER EXTENDS tooltip.js\n  // ================================\n\n  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)\n\n  Popover.prototype.constructor = Popover\n\n  Popover.prototype.getDefaults = function () {\n    return Popover.DEFAULTS\n  }\n\n  Popover.prototype.setContent = function () {\n    var $tip    = this.tip()\n    var title   = this.getTitle()\n    var content = this.getContent()\n\n    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)\n    $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)\n\n    $tip.removeClass('fade top bottom left right in')\n\n    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do\n    // this manually by checking the contents.\n    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()\n  }\n\n  Popover.prototype.hasContent = function () {\n    return this.getTitle() || this.getContent()\n  }\n\n  Popover.prototype.getContent = function () {\n    var $e = this.$element\n    var o  = this.options\n\n    return $e.attr('data-content')\n      || (typeof o.content == 'function' ?\n            o.content.call($e[0]) :\n            o.content)\n  }\n\n  Popover.prototype.arrow = function () {\n    return this.$arrow = this.$arrow || this.tip().find('.arrow')\n  }\n\n  Popover.prototype.tip = function () {\n    if (!this.$tip) this.$tip = $(this.options.template)\n    return this.$tip\n  }\n\n\n  // POPOVER PLUGIN DEFINITION\n  // =========================\n\n  var old = $.fn.popover\n\n  $.fn.popover = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.popover')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.popover.Constructor = Popover\n\n\n  // POPOVER NO CONFLICT\n  // ===================\n\n  $.fn.popover.noConflict = function () {\n    $.fn.popover = old\n    return this\n  }\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: scrollspy.js v3.0.3\n * http://getbootstrap.com/javascript/#scrollspy\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // SCROLLSPY CLASS DEFINITION\n  // ==========================\n\n  function ScrollSpy(element, options) {\n    var href\n    var process  = $.proxy(this.process, this)\n\n    this.$element       = $(element).is('body') ? $(window) : $(element)\n    this.$body          = $('body')\n    this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)\n    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)\n    this.selector       = (this.options.target\n      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\\s]+$)/, '')) //strip for ie7\n      || '') + ' .nav li > a'\n    this.offsets        = $([])\n    this.targets        = $([])\n    this.activeTarget   = null\n\n    this.refresh()\n    this.process()\n  }\n\n  ScrollSpy.DEFAULTS = {\n    offset: 10\n  }\n\n  ScrollSpy.prototype.refresh = function () {\n    var offsetMethod = this.$element[0] == window ? 'offset' : 'position'\n\n    this.offsets = $([])\n    this.targets = $([])\n\n    var self     = this\n    var $targets = this.$body\n      .find(this.selector)\n      .map(function () {\n        var $el   = $(this)\n        var href  = $el.data('target') || $el.attr('href')\n        var $href = /^#\\w/.test(href) && $(href)\n\n        return ($href\n          && $href.length\n          && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null\n      })\n      .sort(function (a, b) { return a[0] - b[0] })\n      .each(function () {\n        self.offsets.push(this[0])\n        self.targets.push(this[1])\n      })\n  }\n\n  ScrollSpy.prototype.process = function () {\n    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset\n    var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight\n    var maxScroll    = scrollHeight - this.$scrollElement.height()\n    var offsets      = this.offsets\n    var targets      = this.targets\n    var activeTarget = this.activeTarget\n    var i\n\n    if (scrollTop >= maxScroll) {\n      return activeTarget != (i = targets.last()[0]) && this.activate(i)\n    }\n\n    for (i = offsets.length; i--;) {\n      activeTarget != targets[i]\n        && scrollTop >= offsets[i]\n        && (!offsets[i + 1] || scrollTop <= offsets[i + 1])\n        && this.activate( targets[i] )\n    }\n  }\n\n  ScrollSpy.prototype.activate = function (target) {\n    this.activeTarget = target\n\n    $(this.selector)\n      .parents('.active')\n      .removeClass('active')\n\n    var selector = this.selector\n      + '[data-target=\"' + target + '\"],'\n      + this.selector + '[href=\"' + target + '\"]'\n\n    var active = $(selector)\n      .parents('li')\n      .addClass('active')\n\n    if (active.parent('.dropdown-menu').length)  {\n      active = active\n        .closest('li.dropdown')\n        .addClass('active')\n    }\n\n    active.trigger('activate.bs.scrollspy')\n  }\n\n\n  // SCROLLSPY PLUGIN DEFINITION\n  // ===========================\n\n  var old = $.fn.scrollspy\n\n  $.fn.scrollspy = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.scrollspy')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.scrollspy.Constructor = ScrollSpy\n\n\n  // SCROLLSPY NO CONFLICT\n  // =====================\n\n  $.fn.scrollspy.noConflict = function () {\n    $.fn.scrollspy = old\n    return this\n  }\n\n\n  // SCROLLSPY DATA-API\n  // ==================\n\n  $(window).on('load', function () {\n    $('[data-spy=\"scroll\"]').each(function () {\n      var $spy = $(this)\n      $spy.scrollspy($spy.data())\n    })\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: tab.js v3.0.3\n * http://getbootstrap.com/javascript/#tabs\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // TAB CLASS DEFINITION\n  // ====================\n\n  var Tab = function (element) {\n    this.element = $(element)\n  }\n\n  Tab.prototype.show = function () {\n    var $this    = this.element\n    var $ul      = $this.closest('ul:not(.dropdown-menu)')\n    var selector = $this.data('target')\n\n    if (!selector) {\n      selector = $this.attr('href')\n      selector = selector && selector.replace(/.*(?=#[^\\s]*$)/, '') //strip for ie7\n    }\n\n    if ($this.parent('li').hasClass('active')) return\n\n    var previous = $ul.find('.active:last a')[0]\n    var e        = $.Event('show.bs.tab', {\n      relatedTarget: previous\n    })\n\n    $this.trigger(e)\n\n    if (e.isDefaultPrevented()) return\n\n    var $target = $(selector)\n\n    this.activate($this.parent('li'), $ul)\n    this.activate($target, $target.parent(), function () {\n      $this.trigger({\n        type: 'shown.bs.tab'\n      , relatedTarget: previous\n      })\n    })\n  }\n\n  Tab.prototype.activate = function (element, container, callback) {\n    var $active    = container.find('> .active')\n    var transition = callback\n      && $.support.transition\n      && $active.hasClass('fade')\n\n    function next() {\n      $active\n        .removeClass('active')\n        .find('> .dropdown-menu > .active')\n        .removeClass('active')\n\n      element.addClass('active')\n\n      if (transition) {\n        element[0].offsetWidth // reflow for transition\n        element.addClass('in')\n      } else {\n        element.removeClass('fade')\n      }\n\n      if (element.parent('.dropdown-menu')) {\n        element.closest('li.dropdown').addClass('active')\n      }\n\n      callback && callback()\n    }\n\n    transition ?\n      $active\n        .one($.support.transition.end, next)\n        .emulateTransitionEnd(150) :\n      next()\n\n    $active.removeClass('in')\n  }\n\n\n  // TAB PLUGIN DEFINITION\n  // =====================\n\n  var old = $.fn.tab\n\n  $.fn.tab = function ( option ) {\n    return this.each(function () {\n      var $this = $(this)\n      var data  = $this.data('bs.tab')\n\n      if (!data) $this.data('bs.tab', (data = new Tab(this)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.tab.Constructor = Tab\n\n\n  // TAB NO CONFLICT\n  // ===============\n\n  $.fn.tab.noConflict = function () {\n    $.fn.tab = old\n    return this\n  }\n\n\n  // TAB DATA-API\n  // ============\n\n  $(document).on('click.bs.tab.data-api', '[data-toggle=\"tab\"], [data-toggle=\"pill\"]', function (e) {\n    e.preventDefault()\n    $(this).tab('show')\n  })\n\n}(jQuery);\n\n/* ========================================================================\n * Bootstrap: affix.js v3.0.3\n * http://getbootstrap.com/javascript/#affix\n * ========================================================================\n * Copyright 2013 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * ======================================================================== */\n\n\n+function ($) { \"use strict\";\n\n  // AFFIX CLASS DEFINITION\n  // ======================\n\n  var Affix = function (element, options) {\n    this.options = $.extend({}, Affix.DEFAULTS, options)\n    this.$window = $(window)\n      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))\n      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))\n\n    this.$element = $(element)\n    this.affixed  =\n    this.unpin    = null\n\n    this.checkPosition()\n  }\n\n  Affix.RESET = 'affix affix-top affix-bottom'\n\n  Affix.DEFAULTS = {\n    offset: 0\n  }\n\n  Affix.prototype.checkPositionWithEventLoop = function () {\n    setTimeout($.proxy(this.checkPosition, this), 1)\n  }\n\n  Affix.prototype.checkPosition = function () {\n    if (!this.$element.is(':visible')) return\n\n    var scrollHeight = $(document).height()\n    var scrollTop    = this.$window.scrollTop()\n    var position     = this.$element.offset()\n    var offset       = this.options.offset\n    var offsetTop    = offset.top\n    var offsetBottom = offset.bottom\n\n    if (typeof offset != 'object')         offsetBottom = offsetTop = offset\n    if (typeof offsetTop == 'function')    offsetTop    = offset.top()\n    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()\n\n    var affix = this.unpin   != null && (scrollTop + this.unpin <= position.top) ? false :\n                offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :\n                offsetTop    != null && (scrollTop <= offsetTop) ? 'top' : false\n\n    if (this.affixed === affix) return\n    if (this.unpin) this.$element.css('top', '')\n\n    this.affixed = affix\n    this.unpin   = affix == 'bottom' ? position.top - scrollTop : null\n\n    this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))\n\n    if (affix == 'bottom') {\n      this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })\n    }\n  }\n\n\n  // AFFIX PLUGIN DEFINITION\n  // =======================\n\n  var old = $.fn.affix\n\n  $.fn.affix = function (option) {\n    return this.each(function () {\n      var $this   = $(this)\n      var data    = $this.data('bs.affix')\n      var options = typeof option == 'object' && option\n\n      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))\n      if (typeof option == 'string') data[option]()\n    })\n  }\n\n  $.fn.affix.Constructor = Affix\n\n\n  // AFFIX NO CONFLICT\n  // =================\n\n  $.fn.affix.noConflict = function () {\n    $.fn.affix = old\n    return this\n  }\n\n\n  // AFFIX DATA-API\n  // ==============\n\n  $(window).on('load', function () {\n    $('[data-spy=\"affix\"]').each(function () {\n      var $spy = $(this)\n      var data = $spy.data()\n\n      data.offset = data.offset || {}\n\n      if (data.offsetBottom) data.offset.bottom = data.offsetBottom\n      if (data.offsetTop)    data.offset.top    = data.offsetTop\n\n      $spy.affix(data)\n    })\n  })\n\n}(jQuery);\n"
  },
  {
    "path": "jekyll/js/demo.js",
    "content": "jQuery(function() {\n    var $ = jQuery,    // just in case. Make sure it's not an other libaray.\n\n        $wrap = $('#uploader'),\n\n        // 图片容器\n        $queue = $('<ul class=\"filelist\"></ul>')\n            .appendTo( $wrap.find('.queueList') ),\n\n        // 状态栏，包括进度和控制按钮\n        $statusBar = $wrap.find('.statusBar'),\n\n        // 文件总体选择信息。\n        $info = $statusBar.find('.info'),\n\n        // 上传按钮\n        $upload = $wrap.find('.uploadBtn'),\n\n        // 没选择文件之前的内容。\n        $placeHolder = $wrap.find('.placeholder'),\n\n        // 总体进度条\n        $progress = $statusBar.find('.progress').hide(),\n\n        // 添加的文件数量\n        fileCount = 0,\n\n        // 添加的文件总大小\n        fileSize = 0,\n\n        // 优化retina, 在retina下这个值是2\n        ratio = window.devicePixelRatio || 1,\n\n        // 缩略图大小\n        thumbnailWidth = 110 * ratio,\n        thumbnailHeight = 110 * ratio,\n\n        // 可能有pedding, ready, uploading, confirm, done.\n        state = 'pedding',\n\n        // 所有文件的进度信息，key为file id\n        percentages = {},\n\n        supportTransition = (function(){\n            var s = document.createElement('p').style,\n                r = 'transition' in s ||\n                      'WebkitTransition' in s ||\n                      'MozTransition' in s ||\n                      'msTransition' in s ||\n                      'OTransition' in s;\n            s = null;\n            return r;\n        })(),\n\n        // WebUploader实例\n        uploader;\n\n    if ( !WebUploader.Uploader.support() ) {\n        alert( 'Web Uploader 不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器');\n        throw new Error( 'WebUploader does not support the browser you are using.' );\n    }\n\n    // 实例化\n    uploader = WebUploader.create({\n        pick: {\n            id: '#filePicker',\n            label: '点击选择图片'\n        },\n        dnd: '#uploader .queueList',\n        paste: document.body,\n\n        accept: {\n            title: 'Images',\n            extensions: 'gif,jpg,jpeg,bmp,png',\n            mimeTypes: 'image/*'\n        },\n\n        // swf文件路径\n        swf: BASE_URL + '/js/Uploader.swf',\n\n        disableGlobalDnd: true,\n\n        chunked: true,\n        server: 'http://webuploader.duapp.com/server/fileupload.php',\n        fileNumLimit: 300,\n        fileSizeLimit: 5 * 1024 * 1024,    // 200 M\n        fileSingleSizeLimit: 1 * 1024 * 1024    // 50 M\n    });\n\n    // 添加“添加文件”的按钮，\n    uploader.addButton({\n        id: '#filePicker2',\n        label: '继续添加'\n    });\n\n    // 当有文件添加进来时执行，负责view的创建\n    function addFile( file ) {\n        var $li = $( '<li id=\"' + file.id + '\">' +\n                '<p class=\"title\">' + file.name + '</p>' +\n                '<p class=\"imgWrap\"></p>'+\n                '<p class=\"progress\"><span></span></p>' +\n                '</li>' ),\n\n            $btns = $('<div class=\"file-panel\">' +\n                '<span class=\"cancel\">删除</span>' +\n                '<span class=\"rotateRight\">向右旋转</span>' +\n                '<span class=\"rotateLeft\">向左旋转</span></div>').appendTo( $li ),\n            $prgress = $li.find('p.progress span'),\n            $wrap = $li.find( 'p.imgWrap' ),\n            $info = $('<p class=\"error\"></p>'),\n\n            showError = function( code ) {\n                switch( code ) {\n                    case 'exceed_size':\n                        text = '文件大小超出';\n                        break;\n\n                    case 'interrupt':\n                        text = '上传暂停';\n                        break;\n\n                    default:\n                        text = '上传失败，请重试';\n                        break;\n                }\n\n                $info.text( text ).appendTo( $li );\n            };\n\n        if ( file.getStatus() === 'invalid' ) {\n            showError( file.statusText );\n        } else {\n            // @todo lazyload\n            $wrap.text( '预览中' );\n            uploader.makeThumb( file, function( error, src ) {\n                if ( error ) {\n                    $wrap.text( '不能预览' );\n                    return;\n                }\n\n                var img = $('<img src=\"'+src+'\">');\n                $wrap.empty().append( img );\n            }, thumbnailWidth, thumbnailHeight );\n\n            percentages[ file.id ] = [ file.size, 0 ];\n            file.rotation = 0;\n        }\n\n        file.on('statuschange', function( cur, prev ) {\n            if ( prev === 'progress' ) {\n                $prgress.hide().width(0);\n            } else if ( prev === 'queued' ) {\n                $li.off( 'mouseenter mouseleave' );\n                $btns.remove();\n            }\n\n            // 成功\n            if ( cur === 'error' || cur === 'invalid' ) {\n                console.log( file.statusText );\n                showError( file.statusText );\n                percentages[ file.id ][ 1 ] = 1;\n            } else if ( cur === 'interrupt' ) {\n                showError( 'interrupt' );\n            } else if ( cur === 'queued' ) {\n                percentages[ file.id ][ 1 ] = 0;\n            } else if ( cur === 'progress' ) {\n                $info.remove();\n                $prgress.css('display', 'block');\n            } else if ( cur === 'complete' ) {\n                $li.append( '<span class=\"success\"></span>' );\n            }\n\n            $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );\n        });\n\n        $li.on( 'mouseenter', function() {\n            $btns.stop().animate({height: 30});\n        });\n\n        $li.on( 'mouseleave', function() {\n            $btns.stop().animate({height: 0});\n        });\n\n        $btns.on( 'click', 'span', function() {\n            var index = $(this).index(),\n                deg;\n\n            switch ( index ) {\n                case 0:\n                    uploader.removeFile( file );\n                    return;\n\n                case 1:\n                    file.rotation += 90;\n                    break;\n\n                case 2:\n                    file.rotation -= 90;\n                    break;\n            }\n\n            if ( supportTransition ) {\n                deg = 'rotate(' + file.rotation + 'deg)';\n                $wrap.css({\n                    '-webkit-transform': deg,\n                    '-mos-transform': deg,\n                    '-o-transform': deg,\n                    'transform': deg\n                });\n            } else {\n                $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');\n                // use jquery animate to rotation\n                // $({\n                //     rotation: rotation\n                // }).animate({\n                //     rotation: file.rotation\n                // }, {\n                //     easing: 'linear',\n                //     step: function( now ) {\n                //         now = now * Math.PI / 180;\n\n                //         var cos = Math.cos( now ),\n                //             sin = Math.sin( now );\n\n                //         $wrap.css( 'filter', \"progid:DXImageTransform.Microsoft.Matrix(M11=\" + cos + \",M12=\" + (-sin) + \",M21=\" + sin + \",M22=\" + cos + \",SizingMethod='auto expand')\");\n                //     }\n                // });\n            }\n\n\n        });\n\n        $li.appendTo( $queue );\n    }\n\n    // 负责view的销毁\n    function removeFile( file ) {\n        var $li = $('#'+file.id);\n\n        delete percentages[ file.id ];\n        updateTotalProgress();\n        $li.off().find('.file-panel').off().end().remove();\n    }\n\n    function updateTotalProgress() {\n        var loaded = 0,\n            total = 0,\n            spans = $progress.children(),\n            percent;\n\n        $.each( percentages, function( k, v ) {\n            total += v[ 0 ];\n            loaded += v[ 0 ] * v[ 1 ];\n        } );\n\n        percent = total ? loaded / total : 0;\n\n        spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );\n        spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );\n        updateStatus();\n    }\n\n    function updateStatus() {\n        var text = '', stats;\n\n        if ( state === 'ready' ) {\n            text = '选中' + fileCount + '张图片，共' +\n                    WebUploader.formatSize( fileSize ) + '。';\n        } else if ( state === 'confirm' ) {\n            stats = uploader.getStats();\n            if ( stats.uploadFailNum ) {\n                text = '已成功上传' + stats.successNum+ '张照片至XX相册，'+\n                    stats.uploadFailNum + '张照片上传失败，<a class=\"retry\" href=\"#\">重新上传</a>失败图片或<a class=\"ignore\" href=\"#\">忽略</a>'\n            }\n\n        } else {\n            stats = uploader.getStats();\n            text = '共' + fileCount + '张（' +\n                    WebUploader.formatSize( fileSize )  +\n                    '），已上传' + stats.successNum + '张';\n\n            if ( stats.uploadFailNum ) {\n                text += '，失败' + stats.uploadFailNum + '张';\n            }\n        }\n\n        $info.html( text );\n    }\n\n    function setState( val ) {\n        var file, stats;\n\n        if ( val === state ) {\n            return;\n        }\n\n        $upload.removeClass( 'state-' + state );\n        $upload.addClass( 'state-' + val );\n        state = val;\n\n        switch ( state ) {\n            case 'pedding':\n                $placeHolder.removeClass( 'element-invisible' );\n                $queue.parent().removeClass('filled');\n                $queue.hide();\n                $statusBar.addClass( 'element-invisible' );\n                uploader.refresh();\n                break;\n\n            case 'ready':\n                $placeHolder.addClass( 'element-invisible' );\n                $( '#filePicker2' ).removeClass( 'element-invisible');\n                $queue.parent().addClass('filled');\n                $queue.show();\n                $statusBar.removeClass('element-invisible');\n                uploader.refresh();\n                break;\n\n            case 'uploading':\n                $( '#filePicker2' ).addClass( 'element-invisible' );\n                $progress.show();\n                $upload.text( '暂停上传' );\n                break;\n\n            case 'paused':\n                $progress.show();\n                $upload.text( '继续上传' );\n                break;\n\n            case 'confirm':\n                $progress.hide();\n                $upload.text( '开始上传' ).addClass( 'disabled' );\n\n                stats = uploader.getStats();\n                if ( stats.successNum && !stats.uploadFailNum ) {\n                    setState( 'finish' );\n                    return;\n                }\n                break;\n            case 'finish':\n                stats = uploader.getStats();\n                if ( stats.successNum ) {\n                    alert( '上传成功' );\n                } else {\n                    // 没有成功的图片，重设\n                    state = 'done';\n                    location.reload();\n                }\n                break;\n        }\n\n        updateStatus();\n    }\n\n    uploader.onUploadProgress = function( file, percentage ) {\n        var $li = $('#'+file.id),\n            $percent = $li.find('.progress span');\n\n        $percent.css( 'width', percentage * 100 + '%' );\n        percentages[ file.id ][ 1 ] = percentage;\n        updateTotalProgress();\n    };\n\n    uploader.onFileQueued = function( file ) {\n        fileCount++;\n        fileSize += file.size;\n\n        if ( fileCount === 1 ) {\n            $placeHolder.addClass( 'element-invisible' );\n            $statusBar.show();\n        }\n\n        addFile( file );\n        setState( 'ready' );\n        updateTotalProgress();\n    };\n\n    uploader.onFileDequeued = function( file ) {\n        fileCount--;\n        fileSize -= file.size;\n\n        if ( !fileCount ) {\n            setState( 'pedding' );\n        }\n\n        removeFile( file );\n        updateTotalProgress();\n\n    };\n\n    uploader.on( 'all', function( type ) {\n        var stats;\n        switch( type ) {\n            case 'uploadFinished':\n                setState( 'confirm' );\n                break;\n\n            case 'startUpload':\n                setState( 'uploading' );\n                break;\n\n            case 'stopUpload':\n                setState( 'paused' );\n                break;\n\n        }\n    });\n\n    uploader.onError = function( code ) {\n        alert( 'Eroor: ' + code );\n    };\n\n    $upload.on('click', function() {\n        if ( $(this).hasClass( 'disabled' ) ) {\n            return false;\n        }\n\n        if ( state === 'ready' ) {\n            uploader.upload();\n        } else if ( state === 'paused' ) {\n            uploader.upload();\n        } else if ( state === 'uploading' ) {\n            uploader.stop();\n        }\n    });\n\n    $info.on( 'click', '.retry', function() {\n        uploader.retry();\n    } );\n\n    $info.on( 'click', '.ignore', function() {\n        alert( 'todo' );\n    } );\n\n    $upload.addClass( 'state-' + state );\n    updateTotalProgress();\n});"
  },
  {
    "path": "jekyll/js/getting-started.js",
    "content": "// 文件上传\njQuery(function() {\n    var $ = jQuery,\n        $list = $('#thelist'),\n        $btn = $('#ctlBtn'),\n        state = 'pending',\n        uploader;\n\n    uploader = WebUploader.create({\n\n        // 不压缩image\n        resize: false,\n\n        // swf文件路径\n        swf: BASE_URL + '/js/Uploader.swf',\n\n        // 文件接收服务端。\n        server: 'http://webuploader.duapp.com/server/fileupload.php',\n\n        // 选择文件的按钮。可选。\n        // 内部根据当前运行是创建，可能是input元素，也可能是flash.\n        pick: '#picker'\n    });\n\n    // 当有文件添加进来的时候\n    uploader.on( 'fileQueued', function( file ) {\n        $list.append( '<div id=\"' + file.id + '\" class=\"item\">' +\n            '<h4 class=\"info\">' + file.name + '</h4>' +\n            '<p class=\"state\">等待上传...</p>' +\n        '</div>' );\n    });\n\n    // 文件上传过程中创建进度条实时显示。\n    uploader.on( 'uploadProgress', function( file, percentage ) {\n        var $li = $( '#'+file.id ),\n            $percent = $li.find('.progress .progress-bar');\n\n        // 避免重复创建\n        if ( !$percent.length ) {\n            $percent = $('<div class=\"progress progress-striped active\">' +\n              '<div class=\"progress-bar\" role=\"progressbar\" style=\"width: 0%\">' +\n              '</div>' +\n            '</div>').appendTo( $li ).find('.progress-bar');\n        }\n\n        $li.find('p.state').text('上传中');\n\n        $percent.css( 'width', percentage * 100 + '%' );\n    });\n\n    uploader.on( 'uploadSuccess', function( file ) {\n        $( '#'+file.id ).find('p.state').text('已上传');\n    });\n\n    uploader.on( 'uploadError', function( file ) {\n        $( '#'+file.id ).find('p.state').text('上传出错');\n    });\n\n    uploader.on( 'uploadComplete', function( file ) {\n        $( '#'+file.id ).find('.progress').fadeOut();\n    });\n\n    uploader.on( 'all', function( type ) {\n        if ( type === 'startUpload' ) {\n            state = 'uploading';\n        } else if ( type === 'stopUpload' ) {\n            state = 'paused';\n        } else if ( type === 'uploadFinished' ) {\n            state = 'done';\n        }\n\n        if ( state === 'uploading' ) {\n            $btn.text('暂停上传');\n        } else {\n            $btn.text('开始上传');\n        }\n    });\n\n    $btn.on( 'click', function() {\n        if ( state === 'uploading' ) {\n            uploader.stop();\n        } else {\n            uploader.upload();\n        }\n    });\n});\n\n\n// 图片上传demo\njQuery(function() {\n    var $ = jQuery,\n        $list = $('#fileList'),\n        // 优化retina, 在retina下这个值是2\n        ratio = window.devicePixelRatio || 1,\n\n        // 缩略图大小\n        thumbnailWidth = 100 * ratio,\n        thumbnailHeight = 100 * ratio,\n\n        // Web Uploader实例\n        uploader;\n\n    // 初始化Web Uploader\n    uploader = WebUploader.create({\n\n        // 自动上传。\n        auto: true,\n\n        // swf文件路径\n        swf: BASE_URL + '/js/Uploader.swf',\n\n        // 文件接收服务端。\n        server: 'http://webuploader.duapp.com/server/fileupload.php',\n\n        // 选择文件的按钮。可选。\n        // 内部根据当前运行是创建，可能是input元素，也可能是flash.\n        pick: '#filePicker',\n\n        // 只允许选择文件，可选。\n        accept: {\n            title: 'Images',\n            extensions: 'gif,jpg,jpeg,bmp,png',\n            mimeTypes: 'image/*'\n        }\n    });\n\n    // 当有文件添加进来的时候\n    uploader.on( 'fileQueued', function( file ) {\n        var $li = $(\n                '<div id=\"' + file.id + '\" class=\"file-item thumbnail\">' +\n                    '<img>' +\n                    '<div class=\"info\">' + file.name + '</div>' +\n                '</div>'\n                ),\n            $img = $li.find('img');\n\n        $list.append( $li );\n\n        // 创建缩略图\n        uploader.makeThumb( file, function( error, src ) {\n            if ( error ) {\n                $img.replaceWith('<span>不能预览</span>');\n                return;\n            }\n\n            $img.attr( 'src', src );\n        }, thumbnailWidth, thumbnailHeight );\n    });\n\n    // 文件上传过程中创建进度条实时显示。\n    uploader.on( 'uploadProgress', function( file, percentage ) {\n        var $li = $( '#'+file.id ),\n            $percent = $li.find('.progress span');\n\n        // 避免重复创建\n        if ( !$percent.length ) {\n            $percent = $('<p class=\"progress\"><span></span></p>')\n                    .appendTo( $li )\n                    .find('span');\n        }\n\n        $percent.css( 'width', percentage * 100 + '%' );\n    });\n\n    // 文件上传成功，给item添加成功class, 用样式标记上传成功。\n    uploader.on( 'uploadSuccess', function( file ) {\n        $( '#'+file.id ).addClass('upload-state-done');\n    });\n\n    // 文件上传失败，现实上传出错。\n    uploader.on( 'uploadError', function( file ) {\n        var $li = $( '#'+file.id ),\n            $error = $li.find('div.error');\n\n        // 避免重复创建\n        if ( !$error.length ) {\n            $error = $('<div class=\"error\"></div>').appendTo( $li );\n        }\n\n        $error.text('上传失败');\n    });\n\n    // 完成上传完了，成功或者失败，先删除进度条。\n    uploader.on( 'uploadComplete', function( file ) {\n        $( '#'+file.id ).find('.progress').remove();\n    });\n});"
  },
  {
    "path": "jekyll/js/global.js",
    "content": "jQuery(function() {\n    var $ = jQuery,\n        $toc = $('.post-toc');\n\n\n\n    $toc.each(function() {\n        var $this = $( this ),\n            postion = $this.offset();\n\n        $this.affix({\n            offset: {\n                top: postion.top - 80\n            }\n        });\n    });\n\n    if ( $toc.length ) {\n        $('body').scrollspy({ target: '.post-toc' });\n    }\n});\n\n// comments相关\njQuery(function() {\n    var $ = jQuery,\n        $el = $('#ghComments'),\n        issueId;\n\n    // 不合法\n    if ( !$el.length || !$el.attr('data-issue-id') ) {\n        return;\n    }\n\n    issueId = $el.attr('data-issue-id') ^ 0;\n\n    function formatNumber(val, len) {\n        var num = \"\" + val;\n\n        len = len || 2;\n        while (num.length < len) {\n            num = \"0\" + num;\n        }\n\n        return num;\n    }\n\n    function formatDate( str ) {\n        var date = new Date( str.replace(/T/, ' ').replace(/Z/, '') );\n\n        return date.getFullYear() + '-' +\n                formatNumber( date.getMonth() + 1 ) + '-' +\n                formatNumber( date.getDate() ) + ' ' +\n                formatNumber( date.getHours() ) + ':' +\n                formatNumber( date.getMinutes() ) + ':' +\n                formatNumber( date.getSeconds() );\n    }\n\n    function loadComments( data ) {\n        for (var i = 0; i < data.length; i++) {\n            var cuser = data[i].user.login;\n            var cuserlink = 'https://www.github.com/' + data[i].user.login;\n            var clink = 'https://github.com/fex-team/webuploader/issues/' + issueId + '#issuecomment-' + data[i].url.substring(data[i].url.lastIndexOf('/') + 1);\n            var cbody = data[i].body_html;\n            var cavatarlink = data[i].user.avatar_url;\n            var cdate = formatDate( data[i].created_at );\n            $el.append('<div class=\"comment\"><div class=\"commentheader\"><div class=\"commentgravatar\"><img src=\"' + cavatarlink + '\" alt=\"\" width=\"20\" height=\"20\"></div><a class=\"commentuser\" href=\"' + cuserlink + '\">' + cuser + '</a><a class=\"commentdate\" href=\"' + clink + '\">' + cdate + '</a></div><div class=\"commentbody\">' + cbody + '</div></div>');\n        }\n    }\n\n    $.ajax('https://api.github.com/repos/fex-team/webuploader/issues/' + issueId + '/comments?per_page=100', {\n        headers: {\n            Accept: 'application/vnd.github.full+json'\n        },\n        dataType: 'json',\n        success: loadComments\n    });\n});\n"
  },
  {
    "path": "jekyll/js/webuploader.js",
    "content": "/*! WebUploader 0.1.8-alpha */\n\n\n/**\n * @fileOverview 让内部各个部件的代码可以用[amd](https://github.com/amdjs/amdjs-api/wiki/AMD)模块定义方式组织起来。\n *\n * AMD API 内部的简单不完全实现，请忽略。只有当WebUploader被合并成一个文件的时候才会引入。\n */\n(function( root, factory ) {\n    var modules = {},\n\n        // 内部require, 简单不完全实现。\n        // https://github.com/amdjs/amdjs-api/wiki/require\n        _require = function( deps, callback ) {\n            var args, len, i;\n\n            // 如果deps不是数组，则直接返回指定module\n            if ( typeof deps === 'string' ) {\n                return getModule( deps );\n            } else {\n                args = [];\n                for( len = deps.length, i = 0; i < len; i++ ) {\n                    args.push( getModule( deps[ i ] ) );\n                }\n\n                return callback.apply( null, args );\n            }\n        },\n\n        // 内部define，暂时不支持不指定id.\n        _define = function( id, deps, factory ) {\n            if ( arguments.length === 2 ) {\n                factory = deps;\n                deps = null;\n            }\n\n            _require( deps || [], function() {\n                setModule( id, factory, arguments );\n            });\n        },\n\n        // 设置module, 兼容CommonJs写法。\n        setModule = function( id, factory, args ) {\n            var module = {\n                    exports: factory\n                },\n                returned;\n\n            if ( typeof factory === 'function' ) {\n                args.length || (args = [ _require, module.exports, module ]);\n                returned = factory.apply( null, args );\n                returned !== undefined && (module.exports = returned);\n            }\n\n            modules[ id ] = module.exports;\n        },\n\n        // 根据id获取module\n        getModule = function( id ) {\n            var module = modules[ id ] || root[ id ];\n\n            if ( !module ) {\n                throw new Error( '`' + id + '` is undefined' );\n            }\n\n            return module;\n        },\n\n        // 将所有modules，将路径ids装换成对象。\n        exportsTo = function( obj ) {\n            var key, host, parts, part, last, ucFirst;\n\n            // make the first character upper case.\n            ucFirst = function( str ) {\n                return str && (str.charAt( 0 ).toUpperCase() + str.substr( 1 ));\n            };\n\n            for ( key in modules ) {\n                host = obj;\n\n                if ( !modules.hasOwnProperty( key ) ) {\n                    continue;\n                }\n\n                parts = key.split('/');\n                last = ucFirst( parts.pop() );\n\n                while( (part = ucFirst( parts.shift() )) ) {\n                    host[ part ] = host[ part ] || {};\n                    host = host[ part ];\n                }\n\n                host[ last ] = modules[ key ];\n            }\n\n            return obj;\n        },\n\n        makeExport = function( dollar ) {\n            root.__dollar = dollar;\n\n            // exports every module.\n            return exportsTo( factory( root, _define, _require ) );\n        },\n\n        origin;\n\n    if ( typeof module === 'object' && typeof module.exports === 'object' ) {\n\n        // For CommonJS and CommonJS-like environments where a proper window is present,\n        module.exports = makeExport();\n    } else if ( typeof define === 'function' && define.amd ) {\n\n        // Allow using this built library as an AMD module\n        // in another project. That other project will only\n        // see this AMD call, not the internal modules in\n        // the closure below.\n        define([ 'jquery' ], makeExport );\n    } else {\n\n        // Browser globals case. Just assign the\n        // result to a property on the global.\n        origin = root.WebUploader;\n        root.WebUploader = makeExport();\n        root.WebUploader.noConflict = function() {\n            root.WebUploader = origin;\n        };\n    }\n})( window, function( window, define, require ) {\n\n\n    /**\n     * @fileOverview jQuery or Zepto\n     * @require \"jquery\"\n     * @require \"zepto\"\n     */\n    define('dollar-third',[],function() {\n        var req = window.require;\n        var $ = window.__dollar || \n            window.jQuery || \n            window.Zepto || \n            req('jquery') || \n            req('zepto');\n    \n        if ( !$ ) {\n            throw new Error('jQuery or Zepto not found!');\n        }\n    \n        return $;\n    });\n    \n    /**\n     * @fileOverview Dom 操作相关\n     */\n    define('dollar',[\n        'dollar-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 使用jQuery的Promise\n     */\n    define('promise-third',[\n        'dollar'\n    ], function( $ ) {\n        return {\n            Deferred: $.Deferred,\n            when: $.when,\n    \n            isPromise: function( anything ) {\n                return anything && typeof anything.then === 'function';\n            }\n        };\n    });\n    /**\n     * @fileOverview Promise/A+\n     */\n    define('promise',[\n        'promise-third'\n    ], function( _ ) {\n        return _;\n    });\n    /**\n     * @fileOverview 基础类方法。\n     */\n    \n    /**\n     * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n     *\n     * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n     * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n     *\n     * * module `base`：WebUploader.Base\n     * * module `file`: WebUploader.File\n     * * module `lib/dnd`: WebUploader.Lib.Dnd\n     * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n     *\n     *\n     * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n     * @module WebUploader\n     * @title WebUploader API文档\n     */\n    define('base',[\n        'dollar',\n        'promise'\n    ], function( $, promise ) {\n    \n        var noop = function() {},\n            call = Function.call;\n    \n        // http://jsperf.com/uncurrythis\n        // 反科里化\n        function uncurryThis( fn ) {\n            return function() {\n                return call.apply( fn, arguments );\n            };\n        }\n    \n        function bindFn( fn, context ) {\n            return function() {\n                return fn.apply( context, arguments );\n            };\n        }\n    \n        function createObject( proto ) {\n            var f;\n    \n            if ( Object.create ) {\n                return Object.create( proto );\n            } else {\n                f = function() {};\n                f.prototype = proto;\n                return new f();\n            }\n        }\n    \n    \n        /**\n         * 基础类，提供一些简单常用的方法。\n         * @class Base\n         */\n        return {\n    \n            /**\n             * @property {String} version 当前版本号。\n             */\n            version: '0.1.8-alpha',\n    \n            /**\n             * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n             */\n            $: $,\n    \n            Deferred: promise.Deferred,\n    \n            isPromise: promise.isPromise,\n    \n            when: promise.when,\n    \n            /**\n             * @description  简单的浏览器检查结果。\n             *\n             * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n             * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n             * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n             * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n             * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n             * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n             *\n             * @property {Object} [browser]\n             */\n            browser: (function( ua ) {\n                var ret = {},\n                    webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                    chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                        ua.match( /CriOS\\/([\\d.]+)/ ),\n    \n                    ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                        ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                    firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                    safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                    opera = ua.match( /OPR\\/([\\d.]+)/ );\n    \n                webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n                chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n                ie && (ret.ie = parseFloat( ie[ 1 ] ));\n                firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n                safari && (ret.safari = parseFloat( safari[ 1 ] ));\n                opera && (ret.opera = parseFloat( opera[ 1 ] ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * @description  操作系统检查结果。\n             *\n             * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n             * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n             * @property {Object} [os]\n             */\n            os: (function( ua ) {\n                var ret = {},\n    \n                    // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                    android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                    ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n    \n                // osx && (ret.osx = true);\n                android && (ret.android = parseFloat( android[ 1 ] ));\n                ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n    \n                return ret;\n            })( navigator.userAgent ),\n    \n            /**\n             * 实现类与类之间的继承。\n             * @method inherits\n             * @grammar Base.inherits( super ) => child\n             * @grammar Base.inherits( super, protos ) => child\n             * @grammar Base.inherits( super, protos, statics ) => child\n             * @param  {Class} super 父类\n             * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n             * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n             * @param  {Object} [statics] 静态属性或方法。\n             * @return {Class} 返回子类。\n             * @example\n             * function Person() {\n             *     console.log( 'Super' );\n             * }\n             * Person.prototype.hello = function() {\n             *     console.log( 'hello' );\n             * };\n             *\n             * var Manager = Base.inherits( Person, {\n             *     world: function() {\n             *         console.log( 'World' );\n             *     }\n             * });\n             *\n             * // 因为没有指定构造器，父类的构造器将会执行。\n             * var instance = new Manager();    // => Super\n             *\n             * // 继承子父类的方法\n             * instance.hello();    // => hello\n             * instance.world();    // => World\n             *\n             * // 子类的__super__属性指向父类\n             * console.log( Manager.__super__ === Person );    // => true\n             */\n            inherits: function( Super, protos, staticProtos ) {\n                var child;\n    \n                if ( typeof protos === 'function' ) {\n                    child = protos;\n                    protos = null;\n                } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                    child = protos.constructor;\n                } else {\n                    child = function() {\n                        return Super.apply( this, arguments );\n                    };\n                }\n    \n                // 复制静态方法\n                $.extend( true, child, Super, staticProtos || {} );\n    \n                /* jshint camelcase: false */\n    \n                // 让子类的__super__属性指向父类。\n                child.__super__ = Super.prototype;\n    \n                // 构建原型，添加原型方法或属性。\n                // 暂时用Object.create实现。\n                child.prototype = createObject( Super.prototype );\n                protos && $.extend( true, child.prototype, protos );\n    \n                return child;\n            },\n    \n            /**\n             * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n             * @method noop\n             */\n            noop: noop,\n    \n            /**\n             * 返回一个新的方法，此方法将已指定的`context`来执行。\n             * @grammar Base.bindFn( fn, context ) => Function\n             * @method bindFn\n             * @example\n             * var doSomething = function() {\n             *         console.log( this.name );\n             *     },\n             *     obj = {\n             *         name: 'Object Name'\n             *     },\n             *     aliasFn = Base.bind( doSomething, obj );\n             *\n             *  aliasFn();    // => Object Name\n             *\n             */\n            bindFn: bindFn,\n    \n            /**\n             * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n             * @grammar Base.log( args... ) => undefined\n             * @method log\n             */\n            log: (function() {\n                if ( window.console ) {\n                    return bindFn( console.log, console );\n                }\n                return noop;\n            })(),\n    \n            nextTick: (function() {\n    \n                return function( cb ) {\n                    setTimeout( cb, 1 );\n                };\n    \n                // @bug 当浏览器不在当前窗口时就停了。\n                // var next = window.requestAnimationFrame ||\n                //     window.webkitRequestAnimationFrame ||\n                //     window.mozRequestAnimationFrame ||\n                //     function( cb ) {\n                //         window.setTimeout( cb, 1000 / 60 );\n                //     };\n    \n                // // fix: Uncaught TypeError: Illegal invocation\n                // return bindFn( next, window );\n            })(),\n    \n            /**\n             * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n             * 将用来将非数组对象转化成数组对象。\n             * @grammar Base.slice( target, start[, end] ) => Array\n             * @method slice\n             * @example\n             * function doSomthing() {\n             *     var args = Base.slice( arguments, 1 );\n             *     console.log( args );\n             * }\n             *\n             * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n             */\n            slice: uncurryThis( [].slice ),\n    \n            /**\n             * 生成唯一的ID\n             * @method guid\n             * @grammar Base.guid() => String\n             * @grammar Base.guid( prefx ) => String\n             */\n            guid: (function() {\n                var counter = 0;\n    \n                return function( prefix ) {\n                    var guid = (+new Date()).toString( 32 ),\n                        i = 0;\n    \n                    for ( ; i < 5; i++ ) {\n                        guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                    }\n    \n                    return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n                };\n            })(),\n    \n            /**\n             * 格式化文件大小, 输出成带单位的字符串\n             * @method formatSize\n             * @grammar Base.formatSize( size ) => String\n             * @grammar Base.formatSize( size, pointLength ) => String\n             * @grammar Base.formatSize( size, pointLength, units ) => String\n             * @param {Number} size 文件大小\n             * @param {Number} [pointLength=2] 精确到的小数点数。\n             * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n             * @example\n             * console.log( Base.formatSize( 100 ) );    // => 100B\n             * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n             * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n             * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n             * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n             * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n             */\n            formatSize: function( size, pointLength, units ) {\n                var unit;\n    \n                units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n    \n                while ( (unit = units.shift()) && size > 1024 ) {\n                    size = size / 1024;\n                }\n    \n                return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                        unit;\n            }\n        };\n    });\n    /**\n     * 事件处理类，可以独立使用，也可以扩展给对象使用。\n     * @fileOverview Mediator\n     */\n    define('mediator',[\n        'base'\n    ], function( Base ) {\n        var $ = Base.$,\n            slice = [].slice,\n            separator = /\\s+/,\n            protos;\n    \n        // 根据条件过滤出事件handlers.\n        function findHandlers( arr, name, callback, context ) {\n            return $.grep( arr, function( handler ) {\n                return handler &&\n                        (!name || handler.e === name) &&\n                        (!callback || handler.cb === callback ||\n                        handler.cb._cb === callback) &&\n                        (!context || handler.ctx === context);\n            });\n        }\n    \n        function eachEvent( events, callback, iterator ) {\n            // 不支持对象，只支持多个event用空格隔开\n            $.each( (events || '').split( separator ), function( _, key ) {\n                iterator( key, callback );\n            });\n        }\n    \n        function triggerHanders( events, args ) {\n            var stoped = false,\n                i = -1,\n                len = events.length,\n                handler;\n    \n            while ( ++i < len ) {\n                handler = events[ i ];\n    \n                if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                    stoped = true;\n                    break;\n                }\n            }\n    \n            return !stoped;\n        }\n    \n        protos = {\n    \n            /**\n             * 绑定事件。\n             *\n             * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n             * ```javascript\n             * var obj = {};\n             *\n             * // 使得obj有事件行为\n             * Mediator.installTo( obj );\n             *\n             * obj.on( 'testa', function( arg1, arg2 ) {\n             *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n             * });\n             *\n             * obj.trigger( 'testa', 'arg1', 'arg2' );\n             * ```\n             *\n             * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n             * 切会影响到`trigger`方法的返回值，为`false`。\n             *\n             * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n             * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n             * ```javascript\n             * obj.on( 'all', function( type, arg1, arg2 ) {\n             *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n             * });\n             * ```\n             *\n             * @method on\n             * @grammar on( name, callback[, context] ) => self\n             * @param  {String}   name     事件名，支持多个事件用空格隔开\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             * @class Mediator\n             */\n            on: function( name, callback, context ) {\n                var me = this,\n                    set;\n    \n                if ( !callback ) {\n                    return this;\n                }\n    \n                set = this._events || (this._events = []);\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var handler = { e: name };\n    \n                    handler.cb = callback;\n                    handler.ctx = context;\n                    handler.ctx2 = context || me;\n                    handler.id = set.length;\n    \n                    set.push( handler );\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 绑定事件，且当handler执行完后，自动解除绑定。\n             * @method once\n             * @grammar once( name, callback[, context] ) => self\n             * @param  {String}   name     事件名\n             * @param  {Function} callback 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            once: function( name, callback, context ) {\n                var me = this;\n    \n                if ( !callback ) {\n                    return me;\n                }\n    \n                eachEvent( name, callback, function( name, callback ) {\n                    var once = function() {\n                            me.off( name, once );\n                            return callback.apply( context || me, arguments );\n                        };\n    \n                    once._cb = callback;\n                    me.on( name, once, context );\n                });\n    \n                return me;\n            },\n    \n            /**\n             * 解除事件绑定\n             * @method off\n             * @grammar off( [name[, callback[, context] ] ] ) => self\n             * @param  {String}   [name]     事件名\n             * @param  {Function} [callback] 事件处理器\n             * @param  {Object}   [context]  事件处理器的上下文。\n             * @return {self} 返回自身，方便链式\n             * @chainable\n             */\n            off: function( name, cb, ctx ) {\n                var events = this._events;\n    \n                if ( !events ) {\n                    return this;\n                }\n    \n                if ( !name && !cb && !ctx ) {\n                    this._events = [];\n                    return this;\n                }\n    \n                eachEvent( name, cb, function( name, cb ) {\n                    $.each( findHandlers( events, name, cb, ctx ), function() {\n                        delete events[ this.id ];\n                    });\n                });\n    \n                return this;\n            },\n    \n            /**\n             * 触发事件\n             * @method trigger\n             * @grammar trigger( name[, args...] ) => self\n             * @param  {String}   type     事件名\n             * @param  {*} [...] 任意参数\n             * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n             */\n            trigger: function( type ) {\n                var args, events, allEvents;\n    \n                if ( !this._events || !type ) {\n                    return this;\n                }\n    \n                args = slice.call( arguments, 1 );\n                events = findHandlers( this._events, type );\n                allEvents = findHandlers( this._events, 'all' );\n    \n                return triggerHanders( events, args ) &&\n                        triggerHanders( allEvents, arguments );\n            }\n        };\n    \n        /**\n         * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n         * 主要目的是负责模块与模块之间的合作，降低耦合度。\n         *\n         * @class Mediator\n         */\n        return $.extend({\n    \n            /**\n             * 可以通过这个接口，使任何对象具备事件功能。\n             * @method installTo\n             * @param  {Object} obj 需要具备事件行为的对象。\n             * @return {Object} 返回obj.\n             */\n            installTo: function( obj ) {\n                return $.extend( obj, protos );\n            }\n    \n        }, protos );\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('uploader',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$;\n    \n        /**\n         * 上传入口类。\n         * @class Uploader\n         * @constructor\n         * @grammar new Uploader( opts ) => Uploader\n         * @example\n         * var uploader = WebUploader.Uploader({\n         *     swf: 'path_of_swf/Uploader.swf',\n         *\n         *     // 开起分片上传。\n         *     chunked: true\n         * });\n         */\n        function Uploader( opts ) {\n            this.options = $.extend( true, {}, Uploader.options, opts );\n            this._init( this.options );\n        }\n    \n        // default Options\n        // widgets中有相应扩展\n        Uploader.options = {};\n        Mediator.installTo( Uploader.prototype );\n    \n        // 批量添加纯命令式方法。\n        $.each({\n            upload: 'start-upload',\n            stop: 'stop-upload',\n            getFile: 'get-file',\n            getFiles: 'get-files',\n            addFile: 'add-file',\n            addFiles: 'add-file',\n            sort: 'sort-files',\n            removeFile: 'remove-file',\n            cancelFile: 'cancel-file',\n            skipFile: 'skip-file',\n            retry: 'retry',\n            isInProgress: 'is-in-progress',\n            makeThumb: 'make-thumb',\n            md5File: 'md5-file',\n            getDimension: 'get-dimension',\n            addButton: 'add-btn',\n            predictRuntimeType: 'predict-runtime-type',\n            refresh: 'refresh',\n            disable: 'disable',\n            enable: 'enable',\n            reset: 'reset'\n        }, function( fn, command ) {\n            Uploader.prototype[ fn ] = function() {\n                return this.request( command, arguments );\n            };\n        });\n    \n        $.extend( Uploader.prototype, {\n            state: 'pending',\n    \n            _init: function( opts ) {\n                var me = this;\n    \n                me.request( 'init', opts, function() {\n                    me.state = 'ready';\n                    me.trigger('ready');\n                });\n            },\n    \n            /**\n             * 获取或者设置Uploader配置项。\n             * @method option\n             * @grammar option( key ) => *\n             * @grammar option( key, val ) => self\n             * @example\n             *\n             * // 初始状态图片上传前不会压缩\n             * var uploader = new WebUploader.Uploader({\n             *     compress: null;\n             * });\n             *\n             * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n             * uploader.option( 'compress', {\n             *     width: 1600,\n             *     height: 1600\n             * });\n             */\n            option: function( key, val ) {\n                var opts = this.options;\n    \n                // setter\n                if ( arguments.length > 1 ) {\n    \n                    if ( $.isPlainObject( val ) &&\n                            $.isPlainObject( opts[ key ] ) ) {\n                        $.extend( opts[ key ], val );\n                    } else {\n                        opts[ key ] = val;\n                    }\n    \n                } else {    // getter\n                    return key ? opts[ key ] : opts;\n                }\n            },\n    \n            /**\n             * 获取文件统计信息。返回一个包含一下信息的对象。\n             * * `successNum` 上传成功的文件数\n             * * `progressNum` 上传中的文件数\n             * * `cancelNum` 被删除的文件数\n             * * `invalidNum` 无效的文件数\n             * * `uploadFailNum` 上传失败的文件数\n             * * `queueNum` 还在队列中的文件数\n             * * `interruptNum` 被暂停的文件数\n             * @method getStats\n             * @grammar getStats() => Object\n             */\n            getStats: function() {\n                // return this._mgr.getStats.apply( this._mgr, arguments );\n                var stats = this.request('get-stats');\n    \n                return stats ? {\n                    successNum: stats.numOfSuccess,\n                    progressNum: stats.numOfProgress,\n    \n                    // who care?\n                    // queueFailNum: 0,\n                    cancelNum: stats.numOfCancel,\n                    invalidNum: stats.numOfInvalid,\n                    uploadFailNum: stats.numOfUploadFailed,\n                    queueNum: stats.numOfQueue,\n                    interruptNum: stats.numOfInterrupt\n                } : {};\n            },\n    \n            // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n            trigger: function( type/*, args...*/ ) {\n                var args = [].slice.call( arguments, 1 ),\n                    opts = this.options,\n                    name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                        type.substring( 1 );\n    \n                if (\n                        // 调用通过on方法注册的handler.\n                        Mediator.trigger.apply( this, arguments ) === false ||\n    \n                        // 调用opts.onEvent\n                        $.isFunction( opts[ name ] ) &&\n                        opts[ name ].apply( this, args ) === false ||\n    \n                        // 调用this.onEvent\n                        $.isFunction( this[ name ] ) &&\n                        this[ name ].apply( this, args ) === false ||\n    \n                        // 广播所有uploader的事件。\n                        Mediator.trigger.apply( Mediator,\n                        [ this, type ].concat( args ) ) === false ) {\n    \n                    return false;\n                }\n    \n                return true;\n            },\n    \n            /**\n             * 销毁 webuploader 实例\n             * @method destroy\n             * @grammar destroy() => undefined\n             */\n            destroy: function() {\n                this.request( 'destroy', arguments );\n                this.off();\n            },\n    \n            // widgets/widget.js将补充此方法的详细文档。\n            request: Base.noop\n        });\n    \n        /**\n         * 创建Uploader实例，等同于new Uploader( opts );\n         * @method create\n         * @class Base\n         * @static\n         * @grammar Base.create( opts ) => Uploader\n         */\n        Base.create = Uploader.create = function( opts ) {\n            return new Uploader( opts );\n        };\n    \n        // 暴露Uploader，可以通过它来扩展业务逻辑。\n        Base.Uploader = Uploader;\n    \n        return Uploader;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/runtime',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            factories = {},\n    \n            // 获取对象的第一个key\n            getFirstKey = function( obj ) {\n                for ( var key in obj ) {\n                    if ( obj.hasOwnProperty( key ) ) {\n                        return key;\n                    }\n                }\n                return null;\n            };\n    \n        // 接口类。\n        function Runtime( options ) {\n            this.options = $.extend({\n                container: document.body\n            }, options );\n            this.uid = Base.guid('rt_');\n        }\n    \n        $.extend( Runtime.prototype, {\n    \n            getContainer: function() {\n                var opts = this.options,\n                    parent, container;\n    \n                if ( this._container ) {\n                    return this._container;\n                }\n    \n                parent = $( opts.container || document.body );\n                container = $( document.createElement('div') );\n    \n                container.attr( 'id', 'rt_' + this.uid );\n                container.css({\n                    position: 'absolute',\n                    top: '0px',\n                    left: '0px',\n                    width: '1px',\n                    height: '1px',\n                    overflow: 'hidden'\n                });\n    \n                parent.append( container );\n                parent.addClass('webuploader-container');\n                this._container = container;\n                this._parent = parent;\n                return container;\n            },\n    \n            init: Base.noop,\n            exec: Base.noop,\n    \n            destroy: function() {\n                this._container && this._container.remove();\n                this._parent && this._parent.removeClass('webuploader-container');\n                this.off();\n            }\n        });\n    \n        Runtime.orders = 'html5,flash';\n    \n    \n        /**\n         * 添加Runtime实现。\n         * @param {String} type    类型\n         * @param {Runtime} factory 具体Runtime实现。\n         */\n        Runtime.addRuntime = function( type, factory ) {\n            factories[ type ] = factory;\n        };\n    \n        Runtime.hasRuntime = function( type ) {\n            return !!(type ? factories[ type ] : getFirstKey( factories ));\n        };\n    \n        Runtime.create = function( opts, orders ) {\n            var type, runtime;\n    \n            orders = orders || Runtime.orders;\n            $.each( orders.split( /\\s*,\\s*/g ), function() {\n                if ( factories[ this ] ) {\n                    type = this;\n                    return false;\n                }\n            });\n    \n            type = type || getFirstKey( factories );\n    \n            if ( !type ) {\n                throw new Error('Runtime Error');\n            }\n    \n            runtime = new factories[ type ]( opts );\n            return runtime;\n        };\n    \n        Mediator.installTo( Runtime.prototype );\n        return Runtime;\n    });\n    \n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/client',[\n        'base',\n        'mediator',\n        'runtime/runtime'\n    ], function( Base, Mediator, Runtime ) {\n    \n        var cache;\n    \n        cache = (function() {\n            var obj = {};\n    \n            return {\n                add: function( runtime ) {\n                    obj[ runtime.uid ] = runtime;\n                },\n    \n                get: function( ruid, standalone ) {\n                    var i;\n    \n                    if ( ruid ) {\n                        return obj[ ruid ];\n                    }\n    \n                    for ( i in obj ) {\n                        // 有些类型不能重用，比如filepicker.\n                        if ( standalone && obj[ i ].__standalone ) {\n                            continue;\n                        }\n    \n                        return obj[ i ];\n                    }\n    \n                    return null;\n                },\n    \n                remove: function( runtime ) {\n                    delete obj[ runtime.uid ];\n                }\n            };\n        })();\n    \n        function RuntimeClient( component, standalone ) {\n            var deferred = Base.Deferred(),\n                runtime;\n    \n            this.uid = Base.guid('client_');\n    \n            // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n            this.runtimeReady = function( cb ) {\n                return deferred.done( cb );\n            };\n    \n            this.connectRuntime = function( opts, cb ) {\n    \n                // already connected.\n                if ( runtime ) {\n                    throw new Error('already connected!');\n                }\n    \n                deferred.done( cb );\n    \n                if ( typeof opts === 'string' && cache.get( opts ) ) {\n                    runtime = cache.get( opts );\n                }\n    \n                // 像filePicker只能独立存在，不能公用。\n                runtime = runtime || cache.get( null, standalone );\n    \n                // 需要创建\n                if ( !runtime ) {\n                    runtime = Runtime.create( opts, opts.runtimeOrder );\n                    runtime.__promise = deferred.promise();\n                    runtime.once( 'ready', deferred.resolve );\n                    runtime.init();\n                    cache.add( runtime );\n                    runtime.__client = 1;\n                } else {\n                    // 来自cache\n                    Base.$.extend( runtime.options, opts );\n                    runtime.__promise.then( deferred.resolve );\n                    runtime.__client++;\n                }\n    \n                standalone && (runtime.__standalone = standalone);\n                return runtime;\n            };\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.disconnectRuntime = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                runtime.__client--;\n    \n                if ( runtime.__client <= 0 ) {\n                    cache.remove( runtime );\n                    delete runtime.__promise;\n                    runtime.destroy();\n                }\n    \n                runtime = null;\n            };\n    \n            this.exec = function() {\n                if ( !runtime ) {\n                    return;\n                }\n    \n                var args = Base.slice( arguments );\n                component && args.unshift( component );\n    \n                return runtime.exec.apply( this, args );\n            };\n    \n            this.getRuid = function() {\n                return runtime && runtime.uid;\n            };\n    \n            this.destroy = (function( destroy ) {\n                return function() {\n                    destroy && destroy.apply( this, arguments );\n                    this.trigger('destroy');\n                    this.off();\n                    this.exec('destroy');\n                    this.disconnectRuntime();\n                };\n            })( this.destroy );\n        }\n    \n        Mediator.installTo( RuntimeClient.prototype );\n        return RuntimeClient;\n    });\n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/dnd',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function DragAndDrop( opts ) {\n            opts = this.options = $.extend({}, DragAndDrop.options, opts );\n    \n            opts.container = $( opts.container );\n    \n            if ( !opts.container.length ) {\n                return;\n            }\n    \n            RuntimeClent.call( this, 'DragAndDrop' );\n        }\n    \n        DragAndDrop.options = {\n            accept: null,\n            disableGlobalDnd: false\n        };\n    \n        Base.inherits( RuntimeClent, {\n            constructor: DragAndDrop,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( DragAndDrop.prototype );\n    \n        return DragAndDrop;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/widget',[\n        'base',\n        'uploader'\n    ], function( Base, Uploader ) {\n    \n        var $ = Base.$,\n            _init = Uploader.prototype._init,\n            _destroy = Uploader.prototype.destroy,\n            IGNORE = {},\n            widgetClass = [];\n    \n        function isArrayLike( obj ) {\n            if ( !obj ) {\n                return false;\n            }\n    \n            var length = obj.length,\n                type = $.type( obj );\n    \n            if ( obj.nodeType === 1 && length ) {\n                return true;\n            }\n    \n            return type === 'array' || type !== 'function' && type !== 'string' &&\n                    (length === 0 || typeof length === 'number' && length > 0 &&\n                    (length - 1) in obj);\n        }\n    \n        function Widget( uploader ) {\n            this.owner = uploader;\n            this.options = uploader.options;\n        }\n    \n        $.extend( Widget.prototype, {\n    \n            init: Base.noop,\n    \n            // 类Backbone的事件监听声明，监听uploader实例上的事件\n            // widget直接无法监听事件，事件只能通过uploader来传递\n            invoke: function( apiName, args ) {\n    \n                /*\n                    {\n                        'make-thumb': 'makeThumb'\n                    }\n                 */\n                var map = this.responseMap;\n    \n                // 如果无API响应声明则忽略\n                if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                        !$.isFunction( this[ map[ apiName ] ] ) ) {\n    \n                    return IGNORE;\n                }\n    \n                return this[ map[ apiName ] ].apply( this, args );\n    \n            },\n    \n            /**\n             * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n             * @method request\n             * @grammar request( command, args ) => * | Promise\n             * @grammar request( command, args, callback ) => Promise\n             * @for  Uploader\n             */\n            request: function() {\n                return this.owner.request.apply( this.owner, arguments );\n            }\n        });\n    \n        // 扩展Uploader.\n        $.extend( Uploader.prototype, {\n    \n            /**\n             * @property {String | Array} [disableWidgets=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n             */\n    \n            // 覆写_init用来初始化widgets\n            _init: function() {\n                var me = this,\n                    widgets = me._widgets = [],\n                    deactives = me.options.disableWidgets || '';\n    \n                $.each( widgetClass, function( _, klass ) {\n                    (!deactives || !~deactives.indexOf( klass._name )) &&\n                        widgets.push( new klass( me ) );\n                });\n    \n                return _init.apply( me, arguments );\n            },\n    \n            request: function( apiName, args, callback ) {\n                var i = 0,\n                    widgets = this._widgets,\n                    len = widgets && widgets.length,\n                    rlts = [],\n                    dfds = [],\n                    widget, rlt, promise, key;\n    \n                args = isArrayLike( args ) ? args : [ args ];\n    \n                for ( ; i < len; i++ ) {\n                    widget = widgets[ i ];\n                    rlt = widget.invoke( apiName, args );\n    \n                    if ( rlt !== IGNORE ) {\n    \n                        // Deferred对象\n                        if ( Base.isPromise( rlt ) ) {\n                            dfds.push( rlt );\n                        } else {\n                            rlts.push( rlt );\n                        }\n                    }\n                }\n    \n                // 如果有callback，则用异步方式。\n                if ( callback || dfds.length ) {\n                    promise = Base.when.apply( Base, dfds );\n                    key = promise.pipe ? 'pipe' : 'then';\n    \n                    // 很重要不能删除。删除了会死循环。\n                    // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                    return promise[ key ](function() {\n                                var deferred = Base.Deferred(),\n                                    args = arguments;\n    \n                                if ( args.length === 1 ) {\n                                    args = args[ 0 ];\n                                }\n    \n                                setTimeout(function() {\n                                    deferred.resolve( args );\n                                }, 1 );\n    \n                                return deferred.promise();\n                            })[ callback ? key : 'done' ]( callback || Base.noop );\n                } else {\n                    return rlts[ 0 ];\n                }\n            },\n    \n            destroy: function() {\n                _destroy.apply( this, arguments );\n                this._widgets = null;\n            }\n        });\n    \n        /**\n         * 添加组件\n         * @grammar Uploader.register(proto);\n         * @grammar Uploader.register(map, proto);\n         * @param  {object} responseMap API 名称与函数实现的映射\n         * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n         * @method Uploader.register\n         * @for Uploader\n         * @example\n         * Uploader.register({\n         *     'make-thumb': 'makeThumb'\n         * }, {\n         *     init: function( options ) {},\n         *     makeThumb: function() {}\n         * });\n         *\n         * Uploader.register({\n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         */\n        Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n            var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n                klass;\n    \n            if ( arguments.length === 1 ) {\n                widgetProto = responseMap;\n    \n                // 自动生成 map 表。\n                $.each(widgetProto, function(key) {\n                    if ( key[0] === '_' || key === 'name' ) {\n                        key === 'name' && (map.name = widgetProto.name);\n                        return;\n                    }\n    \n                    map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n                });\n    \n            } else {\n                map = $.extend( map, responseMap );\n            }\n    \n            widgetProto.responseMap = map;\n            klass = Base.inherits( Widget, widgetProto );\n            klass._name = map.name;\n            widgetClass.push( klass );\n    \n            return klass;\n        };\n    \n        /**\n         * 删除插件，只有在注册时指定了名字的才能被删除。\n         * @grammar Uploader.unRegister(name);\n         * @param  {string} name 组件名字\n         * @method Uploader.unRegister\n         * @for Uploader\n         * @example\n         *\n         * Uploader.register({\n         *     name: 'custom',\n         *     \n         *     'make-thumb': function() {\n         *         \n         *     }\n         * });\n         *\n         * Uploader.unRegister('custom');\n         */\n        Uploader.unRegister = Widget.unRegister = function( name ) {\n            if ( !name || name === 'anonymous' ) {\n                return;\n            }\n            \n            // 删除指定的插件。\n            for ( var i = widgetClass.length; i--; ) {\n                if ( widgetClass[i]._name === name ) {\n                    widgetClass.splice(i, 1)\n                }\n            }\n        };\n    \n        return Widget;\n    });\n    /**\n     * @fileOverview DragAndDrop Widget。\n     */\n    define('widgets/filednd',[\n        'base',\n        'uploader',\n        'lib/dnd',\n        'widgets/widget'\n    ], function( Base, Uploader, Dnd ) {\n        var $ = Base.$;\n    \n        Uploader.options.dnd = '';\n    \n        /**\n         * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n         * @namespace options\n         * @for Uploader\n         */\n        \n        /**\n         * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n         * @namespace options\n         * @for Uploader\n         */\n    \n        /**\n         * @event dndAccept\n         * @param {DataTransferItemList} items DataTransferItem\n         * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n         * @for  Uploader\n         */\n        return Uploader.register({\n            name: 'dnd',\n            \n            init: function( opts ) {\n    \n                if ( !opts.dnd ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        disableGlobalDnd: opts.disableGlobalDnd,\n                        container: opts.dnd,\n                        accept: opts.accept\n                    }),\n                    dnd;\n    \n                this.dnd = dnd = new Dnd( options );\n    \n                dnd.once( 'ready', deferred.resolve );\n                dnd.on( 'drop', function( files ) {\n                    me.request( 'add-file', [ files ]);\n                });\n    \n                // 检测文件是否全部允许添加。\n                dnd.on( 'accept', function( items ) {\n                    return me.owner.trigger( 'dndAccept', items );\n                });\n    \n                dnd.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.dnd && this.dnd.destroy();\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepaste',[\n        'base',\n        'mediator',\n        'runtime/client'\n    ], function( Base, Mediator, RuntimeClent ) {\n    \n        var $ = Base.$;\n    \n        function FilePaste( opts ) {\n            opts = this.options = $.extend({}, opts );\n            opts.container = $( opts.container || document.body );\n            RuntimeClent.call( this, 'FilePaste' );\n        }\n    \n        Base.inherits( RuntimeClent, {\n            constructor: FilePaste,\n    \n            init: function() {\n                var me = this;\n    \n                me.connectRuntime( me.options, function() {\n                    me.exec('init');\n                    me.trigger('ready');\n                });\n            }\n        });\n    \n        Mediator.installTo( FilePaste.prototype );\n    \n        return FilePaste;\n    });\n    /**\n     * @fileOverview 组件基类。\n     */\n    define('widgets/filepaste',[\n        'base',\n        'uploader',\n        'lib/filepaste',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePaste ) {\n        var $ = Base.$;\n    \n        /**\n         * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n         * @namespace options\n         * @for Uploader\n         */\n        return Uploader.register({\n            name: 'paste',\n            \n            init: function( opts ) {\n    \n                if ( !opts.paste ||\n                        this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                var me = this,\n                    deferred = Base.Deferred(),\n                    options = $.extend({}, {\n                        container: opts.paste,\n                        accept: opts.accept\n                    }),\n                    paste;\n    \n                this.paste = paste = new FilePaste( options );\n    \n                paste.once( 'ready', deferred.resolve );\n                paste.on( 'paste', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                paste.init();\n    \n                return deferred.promise();\n            },\n    \n            destroy: function() {\n                this.paste && this.paste.destroy();\n            }\n        });\n    });\n    /**\n     * @fileOverview Blob\n     */\n    define('lib/blob',[\n        'base',\n        'runtime/client'\n    ], function( Base, RuntimeClient ) {\n    \n        function Blob( ruid, source ) {\n            var me = this;\n    \n            me.source = source;\n            me.ruid = ruid;\n            this.size = source.size || 0;\n    \n            // 如果没有指定 mimetype, 但是知道文件后缀。\n            if ( !source.type && this.ext &&\n                    ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n                this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n            } else {\n                this.type = source.type || 'application/octet-stream';\n            }\n    \n            RuntimeClient.call( me, 'Blob' );\n            this.uid = source.uid || this.uid;\n    \n            if ( ruid ) {\n                me.connectRuntime( ruid );\n            }\n        }\n    \n        Base.inherits( RuntimeClient, {\n            constructor: Blob,\n    \n            slice: function( start, end ) {\n                return this.exec( 'slice', start, end );\n            },\n    \n            getSource: function() {\n                return this.source;\n            }\n        });\n    \n        return Blob;\n    });\n    /**\n     * 为了统一化Flash的File和HTML5的File而存在。\n     * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n     * @fileOverview File\n     */\n    define('lib/file',[\n        'base',\n        'lib/blob'\n    ], function( Base, Blob ) {\n    \n        var uid = 1,\n            rExt = /\\.([^.]+)$/;\n    \n        function File( ruid, file ) {\n            var ext;\n    \n            this.name = file.name || ('untitled' + uid++);\n            ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n    \n            // todo 支持其他类型文件的转换。\n            // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n            if ( !ext && file.type ) {\n                ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                        RegExp.$1.toLowerCase() : '';\n                this.name += '.' + ext;\n            }\n    \n            this.ext = ext;\n            this.lastModifiedDate = file.lastModifiedDate || \n                    file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                    (new Date()).toLocaleString();\n    \n            Blob.apply( this, arguments );\n        }\n    \n        return Base.inherits( Blob, File );\n    });\n    \n    /**\n     * @fileOverview 错误信息\n     */\n    define('lib/filepicker',[\n        'base',\n        'runtime/client',\n        'lib/file'\n    ], function( Base, RuntimeClient, File ) {\n    \n        var $ = Base.$;\n    \n        function FilePicker( opts ) {\n            opts = this.options = $.extend({}, FilePicker.options, opts );\n    \n            opts.container = $( opts.id );\n    \n            if ( !opts.container.length ) {\n                throw new Error('按钮指定错误');\n            }\n    \n            opts.innerHTML = opts.innerHTML || opts.label ||\n                    opts.container.html() || '';\n    \n            opts.button = $( opts.button || document.createElement('div') );\n            opts.button.html( opts.innerHTML );\n            opts.container.html( opts.button );\n    \n            RuntimeClient.call( this, 'FilePicker', true );\n        }\n    \n        FilePicker.options = {\n            button: null,\n            container: null,\n            label: null,\n            innerHTML: null,\n            multiple: true,\n            accept: null,\n            name: 'file',\n            style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n        };\n    \n        Base.inherits( RuntimeClient, {\n            constructor: FilePicker,\n    \n            init: function() {\n                var me = this,\n                    opts = me.options,\n                    button = opts.button,\n                    style = opts.style;\n    \n                if (style)\n                    button.addClass('webuploader-pick');\n    \n                me.on( 'all', function( type ) {\n                    var files;\n    \n                    switch ( type ) {\n                        case 'mouseenter':\n                            if (style)\n                                button.addClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'mouseleave':\n                            if (style)\n                                button.removeClass('webuploader-pick-hover');\n                            break;\n    \n                        case 'change':\n                            files = me.exec('getFiles');\n                            me.trigger( 'select', $.map( files, function( file ) {\n                                file = new File( me.getRuid(), file );\n    \n                                // 记录来源。\n                                file._refer = opts.container;\n                                return file;\n                            }), opts.container );\n                            break;\n                    }\n                });\n    \n                me.connectRuntime( opts, function() {\n                    me.refresh();\n                    me.exec( 'init', opts );\n                    me.trigger('ready');\n                });\n    \n                this._resizeHandler = Base.bindFn( this.refresh, this );\n                $( window ).on( 'resize', this._resizeHandler );\n            },\n    \n            refresh: function() {\n                var shimContainer = this.getRuntime().getContainer(),\n                    button = this.options.button,\n                    /*\n                    width = button.outerWidth ?\n                            button.outerWidth() : button.width(),\n    \n                    height = button.outerHeight ?\n                            button.outerHeight() : button.height(),\n                    */\n                    width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                    height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                    pos = button.offset();\n    \n                width && height && shimContainer.css({\n                    bottom: 'auto',\n                    right: 'auto',\n                    width: width + 'px',\n                    height: height + 'px'\n                }).offset( pos );\n            },\n    \n            enable: function() {\n                var btn = this.options.button;\n    \n                btn.removeClass('webuploader-pick-disable');\n                this.refresh();\n            },\n    \n            disable: function() {\n                var btn = this.options.button;\n    \n                this.getRuntime().getContainer().css({\n                    top: '-99999px'\n                });\n    \n                btn.addClass('webuploader-pick-disable');\n            },\n    \n            destroy: function() {\n                var btn = this.options.button;\n                $( window ).off( 'resize', this._resizeHandler );\n                btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                    'webuploader-pick');\n            }\n        });\n    \n        return FilePicker;\n    });\n    \n    /**\n     * @fileOverview 文件选择相关\n     */\n    define('widgets/filepicker',[\n        'base',\n        'uploader',\n        'lib/filepicker',\n        'widgets/widget'\n    ], function( Base, Uploader, FilePicker ) {\n        var $ = Base.$;\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Selector | Object} [pick=undefined]\n             * @namespace options\n             * @for Uploader\n             * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n             *\n             * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n             * * `label` {String} 请采用 `innerHTML` 代替\n             * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n             * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n             */\n            pick: null,\n    \n            /**\n             * @property {Array} [accept=null]\n             * @namespace options\n             * @for Uploader\n             * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n             *\n             * * `title` {String} 文字描述\n             * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n             * * `mimeTypes` {String} 多个用逗号分割。\n             *\n             * 如：\n             *\n             * ```\n             * {\n             *     title: 'Images',\n             *     extensions: 'gif,jpg,jpeg,bmp,png',\n             *     mimeTypes: 'image/*'\n             * }\n             * ```\n             */\n            accept: null/*{\n                title: 'Images',\n                extensions: 'gif,jpg,jpeg,bmp,png',\n                mimeTypes: 'image/*'\n            }*/\n        });\n    \n        return Uploader.register({\n            name: 'picker',\n    \n            init: function( opts ) {\n                this.pickers = [];\n                return opts.pick && this.addBtn( opts.pick );\n            },\n    \n            refresh: function() {\n                $.each( this.pickers, function() {\n                    this.refresh();\n                });\n            },\n    \n            /**\n             * @method addButton\n             * @for Uploader\n             * @grammar addButton( pick ) => Promise\n             * @description\n             * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n             * @example\n             * uploader.addButton({\n             *     id: '#btnContainer',\n             *     innerHTML: '选择文件'\n             * });\n             */\n            addBtn: function( pick ) {\n                var me = this,\n                    opts = me.options,\n                    accept = opts.accept,\n                    promises = [];\n    \n                if ( !pick ) {\n                    return;\n                }\n    \n                $.isPlainObject( pick ) || (pick = {\n                    id: pick\n                });\n    \n                $( pick.id ).each(function() {\n                    var options, picker, deferred;\n    \n                    deferred = Base.Deferred();\n    \n                    options = $.extend({}, pick, {\n                        accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                        swf: opts.swf,\n                        runtimeOrder: opts.runtimeOrder,\n                        id: this\n                    });\n    \n                    picker = new FilePicker( options );\n    \n                    picker.once( 'ready', deferred.resolve );\n                    picker.on( 'select', function( files ) {\n                        me.owner.request( 'add-file', [ files ]);\n                    });\n                    picker.on('dialogopen', function() {\n                        me.owner.trigger('dialogOpen', picker.button);\n                    });\n                    picker.init();\n    \n                    me.pickers.push( picker );\n    \n                    promises.push( deferred.promise() );\n                });\n    \n                return Base.when.apply( Base, promises );\n            },\n    \n            disable: function() {\n                $.each( this.pickers, function() {\n                    this.disable();\n                });\n            },\n    \n            enable: function() {\n                $.each( this.pickers, function() {\n                    this.enable();\n                });\n            },\n    \n            destroy: function() {\n                $.each( this.pickers, function() {\n                    this.destroy();\n                });\n                this.pickers = null;\n            }\n        });\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('lib/image',[\n        'base',\n        'runtime/client',\n        'lib/blob'\n    ], function( Base, RuntimeClient, Blob ) {\n        var $ = Base.$;\n    \n        // 构造器。\n        function Image( opts ) {\n            this.options = $.extend({}, Image.options, opts );\n            RuntimeClient.call( this, 'Image' );\n    \n            this.on( 'load', function() {\n                this._info = this.exec('info');\n                this._meta = this.exec('meta');\n            });\n        }\n    \n        // 默认选项。\n        Image.options = {\n    \n            // 默认的图片处理质量\n            quality: 90,\n    \n            // 是否裁剪\n            crop: false,\n    \n            // 是否保留头部信息\n            preserveHeaders: false,\n    \n            // 是否允许放大。\n            allowMagnify: false\n        };\n    \n        // 继承RuntimeClient.\n        Base.inherits( RuntimeClient, {\n            constructor: Image,\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._meta = val;\n                    return this;\n                }\n    \n                // getter\n                return this._meta;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    ruid = blob.getRuid();\n    \n                this.connectRuntime( ruid, function() {\n                    me.exec( 'init', me.options );\n                    me.exec( 'loadFromBlob', blob );\n                });\n            },\n    \n            resize: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'resize' ].concat( args ) );\n            },\n    \n            crop: function() {\n                var args = Base.slice( arguments );\n                return this.exec.apply( this, [ 'crop' ].concat( args ) );\n            },\n    \n            getAsDataUrl: function( type ) {\n                return this.exec( 'getAsDataUrl', type );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this.exec( 'getAsBlob', type );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    \n        return Image;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/image',[\n        'base',\n        'uploader',\n        'lib/image',\n        'widgets/widget'\n    ], function( Base, Uploader, Image ) {\n    \n        var $ = Base.$,\n            throttle;\n    \n        // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n        throttle = (function( max ) {\n            var occupied = 0,\n                waiting = [],\n                tick = function() {\n                    var item;\n    \n                    while ( waiting.length && occupied < max ) {\n                        item = waiting.shift();\n                        occupied += item[ 0 ];\n                        item[ 1 ]();\n                    }\n                };\n    \n            return function( emiter, size, cb ) {\n                waiting.push([ size, cb ]);\n                emiter.once( 'destroy', function() {\n                    occupied -= size;\n                    setTimeout( tick, 1 );\n                });\n                setTimeout( tick, 1 );\n            };\n        })( 5 * 1024 * 1024 );\n    \n        $.extend( Uploader.options, {\n    \n            /**\n             * @property {Object} [thumb]\n             * @namespace options\n             * @for Uploader\n             * @description 配置生成缩略图的选项。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 110,\n             *     height: 110,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 70,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: true,\n             *\n             *     // 是否允许裁剪。\n             *     crop: true,\n             *\n             *     // 为空的话则保留原有图片格式。\n             *     // 否则强制转换成指定的类型。\n             *     type: 'image/jpeg'\n             * }\n             * ```\n             */\n            thumb: {\n                width: 110,\n                height: 110,\n                quality: 70,\n                allowMagnify: true,\n                crop: true,\n                preserveHeaders: false,\n    \n                // 为空的话则保留原有图片格式。\n                // 否则强制转换成指定的类型。\n                // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n                // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n                type: 'image/jpeg'\n            },\n    \n            /**\n             * @property {Object} [compress]\n             * @namespace options\n             * @for Uploader\n             * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n             *\n             * 默认为：\n             *\n             * ```javascript\n             * {\n             *     width: 1600,\n             *     height: 1600,\n             *\n             *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n             *     quality: 90,\n             *\n             *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n             *     allowMagnify: false,\n             *\n             *     // 是否允许裁剪。\n             *     crop: false,\n             *\n             *     // 是否保留头部meta信息。\n             *     preserveHeaders: true,\n             *\n             *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n             *     // 此属性可能会影响图片自动纠正功能\n             *     noCompressIfLarger: false,\n             *\n             *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n             *     compressSize: 0\n             * }\n             * ```\n             */\n            compress: {\n                width: 1600,\n                height: 1600,\n                quality: 90,\n                allowMagnify: false,\n                crop: false,\n                preserveHeaders: true\n            }\n        });\n    \n        return Uploader.register({\n    \n            name: 'image',\n    \n    \n            /**\n             * 生成缩略图，此过程为异步，所以需要传入`callback`。\n             * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n             *\n             * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n             *\n             * `callback`中可以接收到两个参数。\n             * * 第一个为error，如果生成缩略图有错误，此error将为真。\n             * * 第二个为ret, 缩略图的Data URL值。\n             *\n             * **注意**\n             * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n             * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n             *\n             * @method makeThumb\n             * @grammar makeThumb( file, callback ) => undefined\n             * @grammar makeThumb( file, callback, width, height ) => undefined\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.makeThumb( file, function( error, ret ) {\n             *         if ( error ) {\n             *             $li.text('预览错误');\n             *         } else {\n             *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n             *         }\n             *     });\n             *\n             * });\n             */\n            makeThumb: function( file, cb, width, height ) {\n                var opts, image;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只预览图片格式。\n                if ( !file.type.match( /^image/ ) ) {\n                    cb( true );\n                    return;\n                }\n    \n                opts = $.extend({}, this.options.thumb );\n    \n                // 如果传入的是object.\n                if ( $.isPlainObject( width ) ) {\n                    opts = $.extend( opts, width );\n                    width = null;\n                }\n    \n                width = width || opts.width;\n                height = height || opts.height;\n    \n                image = new Image( opts );\n    \n                image.once( 'load', function() {\n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                // 当 resize 完后\n                image.once( 'complete', function() {\n                    cb( false, image.getAsDataUrl( opts.type ) );\n                    image.destroy();\n                });\n    \n                image.once( 'error', function( reason ) {\n                    cb( reason || true );\n                    image.destroy();\n                });\n    \n                throttle( image, file.source.size, function() {\n                    file._info && image.info( file._info );\n                    file._meta && image.meta( file._meta );\n                    image.loadFromBlob( file.source );\n                });\n            },\n    \n            beforeSendFile: function( file ) {\n                var opts = this.options.compress || this.options.resize,\n                    compressSize = opts && opts.compressSize || 0,\n                    noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                    image, deferred;\n    \n                file = this.request( 'get-file', file );\n    \n                // 只压缩 jpeg 图片格式。\n                // gif 可能会丢失针\n                // bmp png 基本上尺寸都不大，且压缩比比较小。\n                if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                        file.size < compressSize ||\n                        file._compressed ) {\n                    return;\n                }\n    \n                opts = $.extend({}, opts );\n                deferred = Base.Deferred();\n    \n                image = new Image( opts );\n    \n                deferred.always(function() {\n                    image.destroy();\n                    image = null;\n                });\n                image.once( 'error', deferred.reject );\n                image.once( 'load', function() {\n                    var width = opts.width,\n                        height = opts.height;\n    \n                    file._info = file._info || image.info();\n                    file._meta = file._meta || image.meta();\n    \n                    // 如果 width 的值介于 0 - 1\n                    // 说明设置的是百分比。\n                    if ( width <= 1 && width > 0 ) {\n                        width = file._info.width * width;\n                    }\n    \n                    // 同样的规则应用于 height\n                    if ( height <= 1 && height > 0 ) {\n                        height = file._info.height * height;\n                    }\n    \n                    image.resize( width, height );\n                });\n    \n                image.once( 'complete', function() {\n                    var blob, size;\n    \n                    // 移动端 UC / qq 浏览器的无图模式下\n                    // ctx.getImageData 处理大图的时候会报 Exception\n                    // INDEX_SIZE_ERR: DOM Exception 1\n                    try {\n                        blob = image.getAsBlob( opts.type );\n    \n                        size = file.size;\n    \n                        // 如果压缩后，比原来还大则不用压缩后的。\n                        if ( !noCompressIfLarger || blob.size < size ) {\n                            // file.source.destroy && file.source.destroy();\n                            file.source = blob;\n                            file.size = blob.size;\n    \n                            file.trigger( 'resize', blob.size, size );\n                        }\n    \n                        // 标记，避免重复压缩。\n                        file._compressed = true;\n                        deferred.resolve();\n                    } catch ( e ) {\n                        // 出错了直接继续，让其上传原始图片\n                        deferred.resolve();\n                    }\n                });\n    \n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n    \n                image.loadFromBlob( file.source );\n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview 文件属性封装\n     */\n    define('file',[\n        'base',\n        'mediator'\n    ], function( Base, Mediator ) {\n    \n        var $ = Base.$,\n            idPrefix = 'WU_FILE_',\n            idSuffix = 0,\n            rExt = /\\.([^.]+)$/,\n            statusMap = {};\n    \n        function gid() {\n            return idPrefix + idSuffix++;\n        }\n    \n        /**\n         * 文件类\n         * @class File\n         * @constructor 构造函数\n         * @grammar new File( source ) => File\n         * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n         */\n        function WUFile( source ) {\n    \n            /**\n             * 文件名，包括扩展名（后缀）\n             * @property name\n             * @type {string}\n             */\n            this.name = source.name || 'Untitled';\n    \n            /**\n             * 文件体积（字节）\n             * @property size\n             * @type {uint}\n             * @default 0\n             */\n            this.size = source.size || 0;\n    \n            /**\n             * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n             * @property type\n             * @type {string}\n             * @default 'application/octet-stream'\n             */\n            this.type = source.type || 'application/octet-stream';\n    \n            /**\n             * 文件最后修改日期\n             * @property lastModifiedDate\n             * @type {int}\n             * @default 当前时间戳\n             */\n            this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n    \n            /**\n             * 文件ID，每个对象具有唯一ID，与文件名无关\n             * @property id\n             * @type {string}\n             */\n            this.id = gid();\n    \n            /**\n             * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n             * @property ext\n             * @type {string}\n             */\n            this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n    \n    \n            /**\n             * 状态文字说明。在不同的status语境下有不同的用途。\n             * @property statusText\n             * @type {string}\n             */\n            this.statusText = '';\n    \n            // 存储文件状态，防止通过属性直接修改\n            statusMap[ this.id ] = WUFile.Status.INITED;\n    \n            this.source = source;\n            this.loaded = 0;\n    \n            this.on( 'error', function( msg ) {\n                this.setStatus( WUFile.Status.ERROR, msg );\n            });\n        }\n    \n        $.extend( WUFile.prototype, {\n    \n            /**\n             * 设置状态，状态变化时会触发`change`事件。\n             * @method setStatus\n             * @grammar setStatus( status[, statusText] );\n             * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n             * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n             */\n            setStatus: function( status, text ) {\n    \n                var prevStatus = statusMap[ this.id ];\n    \n                typeof text !== 'undefined' && (this.statusText = text);\n    \n                if ( status !== prevStatus ) {\n                    statusMap[ this.id ] = status;\n                    /**\n                     * 文件状态变化\n                     * @event statuschange\n                     */\n                    this.trigger( 'statuschange', status, prevStatus );\n                }\n    \n            },\n    \n            /**\n             * 获取文件状态\n             * @return {File.Status}\n             * @example\n                     文件状态具体包括以下几种类型：\n                     {\n                         // 初始化\n                        INITED:     0,\n                        // 已入队列\n                        QUEUED:     1,\n                        // 正在上传\n                        PROGRESS:     2,\n                        // 上传出错\n                        ERROR:         3,\n                        // 上传成功\n                        COMPLETE:     4,\n                        // 上传取消\n                        CANCELLED:     5\n                    }\n             */\n            getStatus: function() {\n                return statusMap[ this.id ];\n            },\n    \n            /**\n             * 获取文件原始信息。\n             * @return {*}\n             */\n            getSource: function() {\n                return this.source;\n            },\n    \n            destroy: function() {\n                this.off();\n                delete statusMap[ this.id ];\n            }\n        });\n    \n        Mediator.installTo( WUFile.prototype );\n    \n        /**\n         * 文件状态值，具体包括以下几种类型：\n         * * `inited` 初始状态\n         * * `queued` 已经进入队列, 等待上传\n         * * `progress` 上传中\n         * * `complete` 上传完成。\n         * * `error` 上传出错，可重试\n         * * `interrupt` 上传中断，可续传。\n         * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n         * * `cancelled` 文件被移除。\n         * @property {Object} Status\n         * @namespace File\n         * @class File\n         * @static\n         */\n        WUFile.Status = {\n            INITED:     'inited',    // 初始状态\n            QUEUED:     'queued',    // 已经进入队列, 等待上传\n            PROGRESS:   'progress',    // 上传中\n            ERROR:      'error',    // 上传出错，可重试\n            COMPLETE:   'complete',    // 上传完成。\n            CANCELLED:  'cancelled',    // 上传取消。\n            INTERRUPT:  'interrupt',    // 上传中断，可续传。\n            INVALID:    'invalid'    // 文件不合格，不能重试上传。\n        };\n    \n        return WUFile;\n    });\n    \n    /**\n     * @fileOverview 文件队列\n     */\n    define('queue',[\n        'base',\n        'mediator',\n        'file'\n    ], function( Base, Mediator, WUFile ) {\n    \n        var $ = Base.$,\n            STATUS = WUFile.Status;\n    \n        /**\n         * 文件队列, 用来存储各个状态中的文件。\n         * @class Queue\n         * @extends Mediator\n         */\n        function Queue() {\n    \n            /**\n             * 统计文件数。\n             * * `numOfQueue` 队列中的文件数。\n             * * `numOfSuccess` 上传成功的文件数\n             * * `numOfCancel` 被取消的文件数\n             * * `numOfProgress` 正在上传中的文件数\n             * * `numOfUploadFailed` 上传错误的文件数。\n             * * `numOfInvalid` 无效的文件数。\n             * * `numOfDeleted` 被移除的文件数。\n             * * `numOfInterrupt` 被中断的文件数。\n             * @property {Object} stats\n             */\n            this.stats = {\n                numOfQueue: 0,\n                numOfSuccess: 0,\n                numOfCancel: 0,\n                numOfProgress: 0,\n                numOfUploadFailed: 0,\n                numOfInvalid: 0,\n                numOfDeleted: 0,\n                numOfInterrupt: 0\n            };\n    \n            // 上传队列，仅包括等待上传的文件\n            this._queue = [];\n    \n            // 存储所有文件\n            this._map = {};\n        }\n    \n        $.extend( Queue.prototype, {\n    \n            /**\n             * 将新文件加入对队列尾部\n             *\n             * @method append\n             * @param  {File} file   文件对象\n             */\n            append: function( file ) {\n                this._queue.push( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 将新文件加入对队列头部\n             *\n             * @method prepend\n             * @param  {File} file   文件对象\n             */\n            prepend: function( file ) {\n                this._queue.unshift( file );\n                this._fileAdded( file );\n                return this;\n            },\n    \n            /**\n             * 获取文件对象\n             *\n             * @method getFile\n             * @param  {String} fileId   文件ID\n             * @return {File}\n             */\n            getFile: function( fileId ) {\n                if ( typeof fileId !== 'string' ) {\n                    return fileId;\n                }\n                return this._map[ fileId ];\n            },\n    \n            /**\n             * 从队列中取出一个指定状态的文件。\n             * @grammar fetch( status ) => File\n             * @method fetch\n             * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n             * @return {File} [File](#WebUploader:File)\n             */\n            fetch: function( status ) {\n                var len = this._queue.length,\n                    i, file;\n    \n                status = status || STATUS.QUEUED;\n    \n                for ( i = 0; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( status === file.getStatus() ) {\n                        return file;\n                    }\n                }\n    \n                return null;\n            },\n    \n            /**\n             * 对队列进行排序，能够控制文件上传顺序。\n             * @grammar sort( fn ) => undefined\n             * @method sort\n             * @param {Function} fn 排序方法\n             */\n            sort: function( fn ) {\n                if ( typeof fn === 'function' ) {\n                    this._queue.sort( fn );\n                }\n            },\n    \n            /**\n             * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n             * @grammar getFiles( [status1[, status2 ...]] ) => Array\n             * @method getFiles\n             * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n             */\n            getFiles: function() {\n                var sts = [].slice.call( arguments, 0 ),\n                    ret = [],\n                    i = 0,\n                    len = this._queue.length,\n                    file;\n    \n                for ( ; i < len; i++ ) {\n                    file = this._queue[ i ];\n    \n                    if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                        continue;\n                    }\n    \n                    ret.push( file );\n                }\n    \n                return ret;\n            },\n    \n            /**\n             * 在队列中删除文件。\n             * @grammar removeFile( file ) => Array\n             * @method removeFile\n             * @param {File} 文件对象。\n             */\n            removeFile: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( existing ) {\n                    delete this._map[ file.id ];\n                    this._delFile(file);\n                    file.destroy();\n                    this.stats.numOfDeleted++;\n                    \n                }\n            },\n    \t\t\n            _fileAdded: function( file ) {\n                var me = this,\n                    existing = this._map[ file.id ];\n    \n                if ( !existing ) {\n                    this._map[ file.id ] = file;\n    \n                    file.on( 'statuschange', function( cur, pre ) {\n                        me._onFileStatusChange( cur, pre );\n                    });\n                }\n            },\n    \n            _delFile : function(file){\n                for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                    if(this._queue[i] == file){\n                        this._queue.splice(i,1); \n                        break;\n                    }\n                }\n            },\n    \n            _onFileStatusChange: function( curStatus, preStatus ) {\n                var stats = this.stats;\n    \n                switch ( preStatus ) {\n                    case STATUS.PROGRESS:\n                        stats.numOfProgress--;\n                        break;\n    \n                    case STATUS.QUEUED:\n                        stats.numOfQueue --;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed--;\n                        break;\n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid--;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt--;\n                        break;\n                }\n    \n                switch ( curStatus ) {\n                    case STATUS.QUEUED:\n                        stats.numOfQueue++;\n                        break;\n    \n                    case STATUS.PROGRESS:\n                        stats.numOfProgress++;\n                        break;\n    \n                    case STATUS.ERROR:\n                        stats.numOfUploadFailed++;\n                        break;\n    \n                    case STATUS.COMPLETE:\n                        stats.numOfSuccess++;\n                        break;\n    \n                    case STATUS.CANCELLED:\n                        stats.numOfCancel++;\n                        break;\n    \n    \n                    case STATUS.INVALID:\n                        stats.numOfInvalid++;\n                        break;\n    \n                    case STATUS.INTERRUPT:\n                        stats.numOfInterrupt++;\n                        break;\n                }\n            }\n    \n        });\n    \n        Mediator.installTo( Queue.prototype );\n    \n        return Queue;\n    });\n    \n    /**\n     * @fileOverview 队列\n     */\n    define('widgets/queue',[\n        'base',\n        'uploader',\n        'queue',\n        'file',\n        'lib/file',\n        'runtime/client',\n        'widgets/widget'\n    ], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n    \n        var $ = Base.$,\n            rExt = /\\.\\w+$/,\n            Status = WUFile.Status;\n    \n        return Uploader.register({\n            name: 'queue',\n    \n            init: function( opts ) {\n                var me = this,\n                    deferred, len, i, item, arr, accept, runtime;\n    \n                if ( $.isPlainObject( opts.accept ) ) {\n                    opts.accept = [ opts.accept ];\n                }\n    \n                // accept中的中生成匹配正则。\n                if ( opts.accept ) {\n                    arr = [];\n    \n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                        item = opts.accept[ i ].extensions;\n                        item && arr.push( item );\n                    }\n    \n                    if ( arr.length ) {\n                        accept = '\\\\.' + arr.join(',')\n                                .replace( /,/g, '$|\\\\.' )\n                                .replace( /\\*/g, '.*' ) + '$';\n                    }\n    \n                    me.accept = new RegExp( accept, 'i' );\n                }\n    \n                me.queue = new Queue();\n                me.stats = me.queue.stats;\n    \n                // 如果当前不是html5运行时，那就算了。\n                // 不执行后续操作\n                if ( this.request('predict-runtime-type') !== 'html5' ) {\n                    return;\n                }\n    \n                // 创建一个 html5 运行时的 placeholder\n                // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n                deferred = Base.Deferred();\n                this.placeholder = runtime = new RuntimeClient('Placeholder');\n                runtime.connectRuntime({\n                    runtimeOrder: 'html5'\n                }, function() {\n                    me._ruid = runtime.getRuid();\n                    deferred.resolve();\n                });\n                return deferred.promise();\n            },\n    \n    \n            // 为了支持外部直接添加一个原生File对象。\n            _wrapFile: function( file ) {\n                if ( !(file instanceof WUFile) ) {\n    \n                    if ( !(file instanceof File) ) {\n                        if ( !this._ruid ) {\n                            throw new Error('Can\\'t add external files.');\n                        }\n                        file = new File( this._ruid, file );\n                    }\n    \n                    file = new WUFile( file );\n                }\n    \n                return file;\n            },\n    \n            // 判断文件是否可以被加入队列\n            acceptFile: function( file ) {\n                var invalid = !file || !file.size || this.accept &&\n    \n                        // 如果名字中有后缀，才做后缀白名单处理。\n                        rExt.exec( file.name ) && !this.accept.test( file.name );\n    \n                return !invalid;\n            },\n    \n    \n            /**\n             * @event beforeFileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event fileQueued\n             * @param {File} file File对象\n             * @description 当文件被加入队列以后触发。\n             * @for  Uploader\n             */\n    \n            _addFile: function( file ) {\n                var me = this;\n    \n                file = me._wrapFile( file );\n    \n                // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n                if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                    return;\n                }\n    \n                // 类型不匹配，则派送错误事件，并返回。\n                if ( !me.acceptFile( file ) ) {\n                    me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                    return;\n                }\n    \n                me.queue.append( file );\n                me.owner.trigger( 'fileQueued', file );\n                return file;\n            },\n    \n            getFile: function( fileId ) {\n                return this.queue.getFile( fileId );\n            },\n    \n            /**\n             * @event filesQueued\n             * @param {File} files 数组，内容为原始File(lib/File）对象。\n             * @description 当一批文件添加进队列以后触发。\n             * @for  Uploader\n             */\n            \n            /**\n             * @property {Boolean} [auto=false]\n             * @namespace options\n             * @for Uploader\n             * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n             * \n             */\n    \n            /**\n             * @method addFiles\n             * @grammar addFiles( file ) => undefined\n             * @grammar addFiles( [file1, file2 ...] ) => undefined\n             * @param {Array of File or File} [files] Files 对象 数组\n             * @description 添加文件到队列\n             * @for  Uploader\n             */\n            addFile: function( files ) {\n                var me = this;\n    \n                if ( !files.length ) {\n                    files = [ files ];\n                }\n    \n                files = $.map( files, function( file ) {\n                    return me._addFile( file );\n                });\n    \t\t\t\n    \t\t\tif ( files.length ) {\n    \n                    me.owner.trigger( 'filesQueued', files );\n    \n    \t\t\t\tif ( me.options.auto ) {\n    \t\t\t\t\tsetTimeout(function() {\n    \t\t\t\t\t\tme.request('start-upload');\n    \t\t\t\t\t}, 20 );\n    \t\t\t\t}\n                }\n            },\n    \n            getStats: function() {\n                return this.stats;\n            },\n    \n            /**\n             * @event fileDequeued\n             * @param {File} file File对象\n             * @description 当文件被移除队列后触发。\n             * @for  Uploader\n             */\n    \n             /**\n             * @method removeFile\n             * @grammar removeFile( file ) => undefined\n             * @grammar removeFile( id ) => undefined\n             * @grammar removeFile( file, true ) => undefined\n             * @grammar removeFile( id, true ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.removeFile( file );\n             * })\n             */\n            removeFile: function( file, remove ) {\n                var me = this;\n    \n                file = file.id ? file : me.queue.getFile( file );\n    \n                this.request( 'cancel-file', file );\n    \n                if ( remove ) {\n                    this.queue.removeFile( file );\n                }\n            },\n    \n            /**\n             * @method getFiles\n             * @grammar getFiles() => Array\n             * @grammar getFiles( status1, status2, status... ) => Array\n             * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n             * @for  Uploader\n             * @example\n             * console.log( uploader.getFiles() );    // => all files\n             * console.log( uploader.getFiles('error') )    // => all error files.\n             */\n            getFiles: function() {\n                return this.queue.getFiles.apply( this.queue, arguments );\n            },\n    \n            fetchFile: function() {\n                return this.queue.fetch.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @method retry\n             * @grammar retry() => undefined\n             * @grammar retry( file ) => undefined\n             * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n             * @for  Uploader\n             * @example\n             * function retry() {\n             *     uploader.retry();\n             * }\n             */\n            retry: function( file, noForceStart ) {\n                var me = this,\n                    files, i, len;\n    \n                if ( file ) {\n                    file = file.id ? file : me.queue.getFile( file );\n                    file.setStatus( Status.QUEUED );\n                    noForceStart || me.request('start-upload');\n                    return;\n                }\n    \n                files = me.queue.getFiles( Status.ERROR );\n                i = 0;\n                len = files.length;\n    \n                for ( ; i < len; i++ ) {\n                    file = files[ i ];\n                    file.setStatus( Status.QUEUED );\n                }\n    \n                me.request('start-upload');\n            },\n    \n            /**\n             * @method sort\n             * @grammar sort( fn ) => undefined\n             * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n             * @for  Uploader\n             */\n            sortFiles: function() {\n                return this.queue.sort.apply( this.queue, arguments );\n            },\n    \n            /**\n             * @event reset\n             * @description 当 uploader 被重置的时候触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @method reset\n             * @grammar reset() => undefined\n             * @description 重置uploader。目前只重置了队列。\n             * @for  Uploader\n             * @example\n             * uploader.reset();\n             */\n            reset: function() {\n                this.owner.trigger('reset');\n                this.queue = new Queue();\n                this.stats = this.queue.stats;\n            },\n    \n            destroy: function() {\n                this.reset();\n                this.placeholder && this.placeholder.destroy();\n            }\n        });\n    \n    });\n    /**\n     * @fileOverview 添加获取Runtime相关信息的方法。\n     */\n    define('widgets/runtime',[\n        'uploader',\n        'runtime/runtime',\n        'widgets/widget'\n    ], function( Uploader, Runtime ) {\n    \n        Uploader.support = function() {\n            return Runtime.hasRuntime.apply( Runtime, arguments );\n        };\n    \n        /**\n         * @property {Object} [runtimeOrder=html5,flash]\n         * @namespace options\n         * @for Uploader\n         * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n         *\n         * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n         */\n    \n        return Uploader.register({\n            name: 'runtime',\n    \n            init: function() {\n                if ( !this.predictRuntimeType() ) {\n                    throw Error('Runtime Error');\n                }\n            },\n    \n            /**\n             * 预测Uploader将采用哪个`Runtime`\n             * @grammar predictRuntimeType() => String\n             * @method predictRuntimeType\n             * @for  Uploader\n             */\n            predictRuntimeType: function() {\n                var orders = this.options.runtimeOrder || Runtime.orders,\n                    type = this.type,\n                    i, len;\n    \n                if ( !type ) {\n                    orders = orders.split( /\\s*,\\s*/g );\n    \n                    for ( i = 0, len = orders.length; i < len; i++ ) {\n                        if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                            this.type = type = orders[ i ];\n                            break;\n                        }\n                    }\n                }\n    \n                return type;\n            }\n        });\n    });\n    /**\n     * @fileOverview Transport\n     */\n    define('lib/transport',[\n        'base',\n        'runtime/client',\n        'mediator'\n    ], function( Base, RuntimeClient, Mediator ) {\n    \n        var $ = Base.$;\n    \n        function Transport( opts ) {\n            var me = this;\n    \n            opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n            RuntimeClient.call( this, 'Transport' );\n    \n            this._blob = null;\n            this._formData = opts.formData || {};\n            this._headers = opts.headers || {};\n    \n            this.on( 'progress', this._timeout );\n            this.on( 'load error', function() {\n                me.trigger( 'progress', 1 );\n                clearTimeout( me._timer );\n            });\n        }\n    \n        Transport.options = {\n            server: '',\n            method: 'POST',\n    \n            // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n            withCredentials: false,\n            fileVal: 'file',\n            timeout: 2 * 60 * 1000,    // 2分钟\n            formData: {},\n            headers: {},\n            sendAsBinary: false\n        };\n    \n        $.extend( Transport.prototype, {\n    \n            // 添加Blob, 只能添加一次，最后一次有效。\n            appendBlob: function( key, blob, filename ) {\n                var me = this,\n                    opts = me.options;\n    \n                if ( me.getRuid() ) {\n                    me.disconnectRuntime();\n                }\n    \n                // 连接到blob归属的同一个runtime.\n                me.connectRuntime( blob.ruid, function() {\n                    me.exec('init');\n                });\n    \n                me._blob = blob;\n                opts.fileVal = key || opts.fileVal;\n                opts.filename = filename || opts.filename;\n            },\n    \n            // 添加其他字段\n            append: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._formData, key );\n                } else {\n                    this._formData[ key ] = value;\n                }\n            },\n    \n            setRequestHeader: function( key, value ) {\n                if ( typeof key === 'object' ) {\n                    $.extend( this._headers, key );\n                } else {\n                    this._headers[ key ] = value;\n                }\n            },\n    \n            send: function( method ) {\n                this.exec( 'send', method );\n                this._timeout();\n            },\n    \n            abort: function() {\n                clearTimeout( this._timer );\n                return this.exec('abort');\n            },\n    \n            destroy: function() {\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            },\n    \n            getResponseHeaders: function() {\n                return this.exec('getResponseHeaders');\n            },\n    \n            getResponse: function() {\n                return this.exec('getResponse');\n            },\n    \n            getResponseAsJson: function() {\n                return this.exec('getResponseAsJson');\n            },\n    \n            getStatus: function() {\n                return this.exec('getStatus');\n            },\n    \n            _timeout: function() {\n                var me = this,\n                    duration = me.options.timeout;\n    \n                if ( !duration ) {\n                    return;\n                }\n    \n                clearTimeout( me._timer );\n                me._timer = setTimeout(function() {\n                    me.abort();\n                    me.trigger( 'error', 'timeout' );\n                }, duration );\n            }\n    \n        });\n    \n        // 让Transport具备事件功能。\n        Mediator.installTo( Transport.prototype );\n    \n        return Transport;\n    });\n    \n    /**\n     * @fileOverview 负责文件上传相关。\n     */\n    define('widgets/upload',[\n        'base',\n        'uploader',\n        'file',\n        'lib/transport',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile, Transport ) {\n    \n        var $ = Base.$,\n            isPromise = Base.isPromise,\n            Status = WUFile.Status;\n    \n        // 添加默认配置项\n        $.extend( Uploader.options, {\n    \n    \n            /**\n             * @property {Boolean} [prepareNextFile=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否允许在文件传输时提前把下一个文件准备好。\n             * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n             * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n             */\n            prepareNextFile: false,\n    \n            /**\n             * @property {Boolean} [chunked=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否要分片处理大文件上传。\n             */\n            chunked: false,\n    \n            /**\n             * @property {Boolean} [chunkSize=5242880]\n             * @namespace options\n             * @for Uploader\n             * @description 如果要分片，分多大一片？ 默认大小为5M.\n             */\n            chunkSize: 5 * 1024 * 1024,\n    \n            /**\n             * @property {Boolean} [chunkRetry=2]\n             * @namespace options\n             * @for Uploader\n             * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n             */\n            chunkRetry: 2,\n    \n            /**\n             * @property {Number} [chunkRetryDelay=1000]\n             * @namespace options\n             * @for Uploader\n             * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n             */\n            chunkRetryDelay: 1000,\n    \n            /**\n             * @property {Boolean} [threads=3]\n             * @namespace options\n             * @for Uploader\n             * @description 上传并发数。允许同时最大上传进程数。\n             */\n            threads: 3,\n    \n    \n            /**\n             * @property {Object} [formData={}]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n             */\n            formData: {}\n    \n            /**\n             * @property {Object} [fileVal='file']\n             * @namespace options\n             * @for Uploader\n             * @description 设置文件上传域的name。\n             */\n    \n             /**\n             * @property {Object} [method=POST]\n             * @namespace options\n             * @for Uploader\n             * @description 文件上传方式，`POST` 或者 `GET`。\n             */\n    \n            /**\n             * @property {Object} [sendAsBinary=false]\n             * @namespace options\n             * @for Uploader\n             * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n             * 其他参数在$_GET数组中。\n             */\n        });\n    \n        // 负责将文件切片。\n        function CuteFile( file, chunkSize ) {\n            var pending = [],\n                blob = file.source,\n                total = blob.size,\n                chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n                start = 0,\n                index = 0,\n                len, api;\n    \n            api = {\n                file: file,\n    \n                has: function() {\n                    return !!pending.length;\n                },\n    \n                shift: function() {\n                    return pending.shift();\n                },\n    \n                unshift: function( block ) {\n                    pending.unshift( block );\n                }\n            };\n    \n            while ( index < chunks ) {\n                len = Math.min( chunkSize, total - start );\n    \n                pending.push({\n                    file: file,\n                    start: start,\n                    end: chunkSize ? (start + len) : total,\n                    total: total,\n                    chunks: chunks,\n                    chunk: index++,\n                    cuted: api\n                });\n                start += len;\n            }\n    \n            file.blocks = pending.concat();\n            file.remaning = pending.length;\n    \n            return api;\n        }\n    \n        Uploader.register({\n            name: 'upload',\n    \n            init: function() {\n                var owner = this.owner,\n                    me = this;\n    \n                this.runing = false;\n                this.progress = false;\n    \n                owner\n                    .on( 'startUpload', function() {\n                        me.progress = true;\n                    })\n                    .on( 'uploadFinished', function() {\n                        me.progress = false;\n                    });\n    \n                // 记录当前正在传的数据，跟threads相关\n                this.pool = [];\n    \n                // 缓存分好片的文件。\n                this.stack = [];\n    \n                // 缓存即将上传的文件。\n                this.pending = [];\n    \n                // 跟踪还有多少分片在上传中但是没有完成上传。\n                this.remaning = 0;\n                this.__tick = Base.bindFn( this._tick, this );\n    \n                // 销毁上传相关的属性。\n                owner.on( 'uploadComplete', function( file ) {\n    \n                    // 把其他块取消了。\n                    file.blocks && $.each( file.blocks, function( _, v ) {\n                        v.transport && (v.transport.abort(), v.transport.destroy());\n                        delete v.transport;\n                    });\n    \n                    delete file.blocks;\n                    delete file.remaning;\n                });\n            },\n    \n            reset: function() {\n                this.request( 'stop-upload', true );\n                this.runing = false;\n                this.pool = [];\n                this.stack = [];\n                this.pending = [];\n                this.remaning = 0;\n                this._trigged = false;\n                this._promise = null;\n            },\n    \n            /**\n             * @event startUpload\n             * @description 当开始上传流程时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n             *\n             * 可以指定开始某一个文件。\n             * @grammar upload() => undefined\n             * @grammar upload( file | fileId) => undefined\n             * @method upload\n             * @for  Uploader\n             */\n            startUpload: function(file) {\n                var me = this;\n    \n                // 移出invalid的文件\n                $.each( me.request( 'get-files', Status.INVALID ), function() {\n                    me.request( 'remove-file', this );\n                });\n    \n                // 如果指定了开始某个文件，则只开始指定的文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if (file.getStatus() === Status.INTERRUPT) {\n                        file.setStatus( Status.QUEUED );\n    \n                        $.each( me.pool, function( _, v ) {\n    \n                            // 之前暂停过。\n                            if (v.file !== file) {\n                                return;\n                            }\n    \n                            v.transport && v.transport.send();\n                            file.setStatus( Status.PROGRESS );\n                        });\n    \n                        \n                    } else if (file.getStatus() !== Status.PROGRESS) {\n                        file.setStatus( Status.QUEUED );\n                    }\n                } else {\n                    $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                        this.setStatus( Status.QUEUED );\n                    });\n                }\n    \n                if ( me.runing ) {\n                    me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = true;\n                var files = [];\n    \n                // 如果有暂停的，则续传\n                file || $.each( me.pool, function( _, v ) {\n                    var file = v.file;\n    \n                    if ( file.getStatus() === Status.INTERRUPT ) {\n                        me._trigged = false;\n                        files.push(file);\n    \n                        if (v.waiting) {\n                            return;\n                        }\n                        \n                        // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                        v.transport ? v.transport.send() : me._doSend(v);\n                    }\n                });\n    \n                $.each(files, function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                file || $.each( me.request( 'get-files',\n                        Status.INTERRUPT ), function() {\n                    this.setStatus( Status.PROGRESS );\n                });\n    \n                me._trigged = false;\n                Base.nextTick( me.__tick );\n                me.owner.trigger('startUpload');\n            },\n    \n            /**\n             * @event stopUpload\n             * @description 当开始上传流程暂停时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n             *\n             * 如果第一个参数是文件，则只暂停指定文件。\n             * @grammar stop() => undefined\n             * @grammar stop( true ) => undefined\n             * @grammar stop( file ) => undefined\n             * @method stop\n             * @for  Uploader\n             */\n            stopUpload: function( file, interrupt ) {\n                var me = this;\n    \n                if (file === true) {\n                    interrupt = file;\n                    file = null;\n                }\n    \n                if ( me.runing === false ) {\n                    return;\n                }\n    \n                // 如果只是暂停某个文件。\n                if ( file ) {\n                    file = file.id ? file : me.request( 'get-file', file );\n    \n                    if ( file.getStatus() !== Status.PROGRESS &&\n                            file.getStatus() !== Status.QUEUED ) {\n                        return;\n                    }\n    \n                    file.setStatus( Status.INTERRUPT );\n    \n    \n                    $.each( me.pool, function( _, v ) {\n    \n                        // 只 abort 指定的文件，每一个分片。\n                        if (v.file === file) {\n                            v.transport && v.transport.abort();\n    \n                            if (interrupt) {\n                                me._putback(v);\n                                me._popBlock(v);\n                            }\n                        }\n                    });\n    \n                    me.owner.trigger('stopUpload', file);// 暂停，trigger event\n    \n                    return Base.nextTick( me.__tick );\n                }\n    \n                me.runing = false;\n    \n                // 正在准备中的文件。\n                if (this._promise && this._promise.file) {\n                    this._promise.file.setStatus( Status.INTERRUPT );\n                }\n    \n                interrupt && $.each( me.pool, function( _, v ) {\n                    v.transport && v.transport.abort();\n                    v.file.setStatus( Status.INTERRUPT );\n                });\n    \n                me.owner.trigger('stopUpload');\n            },\n    \n            /**\n             * @method cancelFile\n             * @grammar cancelFile( file ) => undefined\n             * @grammar cancelFile( id ) => undefined\n             * @param {File|id} file File对象或这File对象的id\n             * @description 标记文件状态为已取消, 同时将中断文件传输。\n             * @for  Uploader\n             * @example\n             *\n             * $li.on('click', '.remove-this', function() {\n             *     uploader.cancelFile( file );\n             * })\n             */\n            cancelFile: function( file ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                file.setStatus( Status.CANCELLED );\n                this.owner.trigger( 'fileDequeued', file );\n            },\n    \n            /**\n             * 判断`Uploader`是否正在上传中。\n             * @grammar isInProgress() => Boolean\n             * @method isInProgress\n             * @for  Uploader\n             */\n            isInProgress: function() {\n                return !!this.progress;\n            },\n    \n            _getStats: function() {\n                return this.request('get-stats');\n            },\n    \n            /**\n             * 跳过一个文件上传，直接标记指定文件为已上传状态。\n             * @grammar skipFile( file ) => undefined\n             * @method skipFile\n             * @for  Uploader\n             */\n            skipFile: function( file, status ) {\n                file = file.id ? file : this.request( 'get-file', file );\n    \n                file.setStatus( status || Status.COMPLETE );\n                file.skipped = true;\n    \n                // 如果正在上传。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    var _tr = v.transport;\n    \n                    if ( _tr ) {\n                        _tr.abort();\n                        _tr.destroy();\n                        delete v.transport;\n                    }\n                });\n    \n                this.owner.trigger( 'uploadSkip', file );\n            },\n    \n            /**\n             * @event uploadFinished\n             * @description 当所有文件上传结束时触发。\n             * @for  Uploader\n             */\n            _tick: function() {\n                var me = this,\n                    opts = me.options,\n                    fn, val;\n    \n                // 上一个promise还没有结束，则等待完成后再执行。\n                if ( me._promise ) {\n                    return me._promise.always( me.__tick );\n                }\n    \n                // 还有位置，且还有文件要处理的话。\n                if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                    me._trigged = false;\n    \n                    fn = function( val ) {\n                        me._promise = null;\n    \n                        // 有可能是reject过来的，所以要检测val的类型。\n                        val && val.file && me._startSend( val );\n                        Base.nextTick( me.__tick );\n                    };\n    \n                    me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n    \n                // 没有要上传的了，且没有正在传输的了。\n                } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                    !me._getStats().numOfInterrupt ) {\n                    me.runing = false;\n    \n                    me._trigged || Base.nextTick(function() {\n                        me.owner.trigger('uploadFinished');\n                    });\n                    me._trigged = true;\n                }\n            },\n    \n            _putback: function(block) {\n                var idx;\n    \n                block.cuted.unshift(block);\n                idx = this.stack.indexOf(block.cuted);\n    \n                if (!~idx) {\n                    // 如果不在里面，说明移除过，需要把计数还原回去。\n                    this.remaning++;\n                    block.file.remaning++;\n                    this.stack.unshift(block.cuted);\n                }\n            },\n    \n            _getStack: function() {\n                var i = 0,\n                    act;\n    \n                while ( (act = this.stack[ i++ ]) ) {\n                    if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                        return act;\n                    } else if (!act.has() ||\n                            act.file.getStatus() !== Status.PROGRESS &&\n                            act.file.getStatus() !== Status.INTERRUPT ) {\n    \n                        // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                        // interupt（暂停中） 的移除。\n                        this.stack.splice( --i, 1 );\n                    }\n                }\n    \n                return null;\n            },\n    \n            _nextBlock: function() {\n                var me = this,\n                    opts = me.options,\n                    act, next, done, preparing;\n    \n                // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n                if ( (act = this._getStack()) ) {\n    \n                    // 是否提前准备下一个文件\n                    if ( opts.prepareNextFile && !me.pending.length ) {\n                        me._prepareNextFile();\n                    }\n    \n                    return act.shift();\n    \n                // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n                } else if ( me.runing ) {\n    \n                    // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                    if ( !me.pending.length && me._getStats().numOfQueue ) {\n                        me._prepareNextFile();\n                    }\n    \n                    next = me.pending.shift();\n                    done = function( file ) {\n                        if ( !file ) {\n                            return null;\n                        }\n    \n                        act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                        me.stack.push(act);\n                        return act.shift();\n                    };\n    \n                    // 文件可能还在prepare中，也有可能已经完全准备好了。\n                    if ( isPromise( next) ) {\n                        preparing = next.file;\n                        next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                        next.file = preparing;\n                        return next;\n                    }\n    \n                    return done( next );\n                }\n            },\n    \n    \n            /**\n             * @event uploadStart\n             * @param {File} file File对象\n             * @description 某个文件开始上传前触发，一个文件只会触发一次。\n             * @for  Uploader\n             */\n            _prepareNextFile: function() {\n                var me = this,\n                    file = me.request('fetch-file'),\n                    pending = me.pending,\n                    promise;\n    \n                if ( file ) {\n                    promise = me.request( 'before-send-file', file, function() {\n    \n                        // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                        if ( file.getStatus() === Status.PROGRESS ||\n                            file.getStatus() === Status.INTERRUPT ) {\n                            return file;\n                        }\n    \n                        return me._finishFile( file );\n                    });\n    \n                    me.owner.trigger( 'uploadStart', file );\n                    file.setStatus( Status.PROGRESS );\n    \n                    promise.file = file;\n    \n                    // 如果还在pending中，则替换成文件本身。\n                    promise.done(function() {\n                        var idx = $.inArray( promise, pending );\n    \n                        ~idx && pending.splice( idx, 1, file );\n                    });\n    \n                    // befeore-send-file的钩子就有错误发生。\n                    promise.fail(function( reason ) {\n                        file.setStatus( Status.ERROR, reason );\n                        me.owner.trigger( 'uploadError', file, reason );\n                        me.owner.trigger( 'uploadComplete', file );\n                    });\n    \n                    pending.push( promise );\n                }\n            },\n    \n            // 让出位置了，可以让其他分片开始上传\n            _popBlock: function( block ) {\n                var idx = $.inArray( block, this.pool );\n    \n                this.pool.splice( idx, 1 );\n                block.file.remaning--;\n                this.remaning--;\n            },\n    \n            // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n            _startSend: function( block ) {\n                var me = this,\n                    file = block.file,\n                    promise;\n    \n                // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n                // 如：暂停，取消\n                // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n                if ( file.getStatus() !== Status.PROGRESS ) {\n    \n                    // 如果是中断，则还需要放回去。\n                    if (file.getStatus() === Status.INTERRUPT) {\n                        me._putback(block);\n                    }\n    \n                    return;\n                }\n    \n                me.pool.push( block );\n                me.remaning++;\n    \n                // 如果没有分片，则直接使用原始的。\n                // 不会丢失content-type信息。\n                block.blob = block.chunks === 1 ? file.source :\n                        file.source.slice( block.start, block.end );\n    \n                // hook, 每个分片发送之前可能要做些异步的事情。\n                block.waiting = promise = me.request( 'before-send', block, function() {\n                    delete block.waiting;\n    \n                    // 有可能文件已经上传出错了，所以不需要再传输了。\n                    if ( file.getStatus() === Status.PROGRESS ) {\n                        me._doSend( block );\n                    } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                        me._popBlock(block);\n                    }\n    \n                    Base.nextTick(me.__tick);\n                });\n    \n                // 如果为fail了，则跳过此分片。\n                promise.fail(function() {\n                    delete block.waiting;\n    \n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file ).always(function() {\n                            block.percentage = 1;\n                            me._popBlock( block );\n                            me.owner.trigger( 'uploadComplete', file );\n                            Base.nextTick( me.__tick );\n                        });\n                    } else {\n                        block.percentage = 1;\n                        me.updateFileProgress( file );\n                        me._popBlock( block );\n                        Base.nextTick( me.__tick );\n                    }\n                });\n            },\n    \n    \n            /**\n             * @event uploadBeforeSend\n             * @param {Object} object\n             * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n             * @param {Object} headers 可以扩展此对象来控制上传头部。\n             * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadAccept\n             * @param {Object} object\n             * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n             * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadProgress\n             * @param {File} file File对象\n             * @param {Number} percentage 上传进度\n             * @description 上传过程中触发，携带上传进度。\n             * @for  Uploader\n             */\n    \n    \n            /**\n             * @event uploadError\n             * @param {File} file File对象\n             * @param {String} reason 出错的code\n             * @description 当文件上传出错时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadSuccess\n             * @param {File} file File对象\n             * @param {Object} response 服务端返回的数据\n             * @description 当文件上传成功时触发。\n             * @for  Uploader\n             */\n    \n            /**\n             * @event uploadComplete\n             * @param {File} [file] File对象\n             * @description 不管成功或者失败，文件上传完成时触发。\n             * @for  Uploader\n             */\n    \n            // 做上传操作。\n            _doSend: function( block ) {\n                var me = this,\n                    owner = me.owner,\n                    opts = $.extend({}, me.options, block.options),\n                    file = block.file,\n                    tr = new Transport( opts ),\n                    data = $.extend({}, opts.formData ),\n                    headers = $.extend({}, opts.headers ),\n                    requestAccept, ret;\n    \n                block.transport = tr;\n    \n                tr.on( 'destroy', function() {\n                    delete block.transport;\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                });\n    \n                // 广播上传进度。以文件为单位。\n                tr.on( 'progress', function( percentage ) {\n                    block.percentage = percentage;\n                    me.updateFileProgress( file );\n                });\n    \n                // 用来询问，是否返回的结果是有错误的。\n                requestAccept = function( reject ) {\n                    var fn;\n    \n                    ret = tr.getResponseAsJson() || {};\n                    ret._raw = tr.getResponse();\n                    ret._headers = tr.getResponseHeaders();\n                    block.response = ret;\n                    fn = function( value ) {\n                        reject = value;\n                    };\n    \n                    // 服务端响应了，不代表成功了，询问是否响应正确。\n                    if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                        reject = reject || 'server';\n                    }\n    \n                    return reject;\n                };\n    \n                // 尝试重试，然后广播文件上传出错。\n                tr.on( 'error', function( type, flag ) {\n                    // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                    // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                    var typeArr = type.split( '|' ), status, statusText;  \n                    type = typeArr[0];\n                    status = parseFloat( typeArr[1] ),\n                    statusText = typeArr[2];\n    \n                    block.retried = block.retried || 0;\n    \n                    // 自动重试\n                    if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                            block.retried < opts.chunkRetry ) {\n    \n                        block.retried++;\n    \n                        me.retryTimer = setTimeout(function() {\n                            tr.send();\n                        }, opts.chunkRetryDelay || 1000);\n    \n                    } else {\n    \n                        // http status 500 ~ 600\n                        if ( !flag && type === 'server' ) {\n                            type = requestAccept( type );\n                        }\n    \n                        file.setStatus( Status.ERROR, type );\n                        owner.trigger( 'uploadError', file, type, status, statusText );\n                        owner.trigger( 'uploadComplete', file );\n                    }\n                });\n    \n                // 上传成功\n                tr.on( 'load', function() {\n                    var reason;\n    \n                    // 如果非预期，转向上传出错。\n                    if ( (reason = requestAccept()) ) {\n                        tr.trigger( 'error', reason, true );\n                        return;\n                    }\n    \n                    // 全部上传完成。\n                    if ( file.remaning === 1 ) {\n                        me._finishFile( file, ret );\n                    } else {\n                        tr.destroy();\n                    }\n                });\n    \n                // 配置默认的上传字段。\n                data = $.extend( data, {\n                    id: file.id,\n                    name: file.name,\n                    type: file.type,\n                    lastModifiedDate: file.lastModifiedDate,\n                    size: file.size\n                });\n    \n                block.chunks > 1 && $.extend( data, {\n                    chunks: block.chunks,\n                    chunk: block.chunk\n                });\n    \n                // 在发送之间可以添加字段什么的。。。\n                // 如果默认的字段不够使用，可以通过监听此事件来扩展\n                owner.trigger( 'uploadBeforeSend', block, data, headers );\n    \n                // 开始发送。\n                tr.appendBlob( opts.fileVal, block.blob, file.name );\n                tr.append( data );\n                tr.setRequestHeader( headers );\n                tr.send();\n            },\n    \n            // 完成上传。\n            _finishFile: function( file, ret, hds ) {\n                var owner = this.owner;\n    \n                return owner\n                        .request( 'after-send-file', arguments, function() {\n                            file.setStatus( Status.COMPLETE );\n                            owner.trigger( 'uploadSuccess', file, ret, hds );\n                        })\n                        .fail(function( reason ) {\n    \n                            // 如果外部已经标记为invalid什么的，不再改状态。\n                            if ( file.getStatus() === Status.PROGRESS ) {\n                                file.setStatus( Status.ERROR, reason );\n                            }\n    \n                            owner.trigger( 'uploadError', file, reason );\n                        })\n                        .always(function() {\n                            owner.trigger( 'uploadComplete', file );\n                        });\n            },\n    \n            updateFileProgress: function(file) {\n                var totalPercent = 0,\n                    uploaded = 0;\n    \n                if (!file.blocks) {\n                    return;\n                }\n    \n                $.each( file.blocks, function( _, v ) {\n                    uploaded += (v.percentage || 0) * (v.end - v.start);\n                });\n    \n                totalPercent = uploaded / file.size;\n                this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n            },\n    \n            destroy: function() {\n                clearTimeout(this.retryTimer);\n            }\n    \n        });\n    });\n    \n    /**\n     * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n     */\n    \n    define('widgets/validator',[\n        'base',\n        'uploader',\n        'file',\n        'widgets/widget'\n    ], function( Base, Uploader, WUFile ) {\n    \n        var $ = Base.$,\n            validators = {},\n            api;\n    \n        /**\n         * @event error\n         * @param {String} type 错误类型。\n         * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n         *\n         * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n         * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n         * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n         * @for  Uploader\n         */\n    \n        // 暴露给外面的api\n        api = {\n    \n            // 添加验证器\n            addValidator: function( type, cb ) {\n                validators[ type ] = cb;\n            },\n    \n            // 移除验证器\n            removeValidator: function( type ) {\n                delete validators[ type ];\n            }\n        };\n    \n        // 在Uploader初始化的时候启动Validators的初始化\n        Uploader.register({\n            name: 'validator',\n    \n            init: function() {\n                var me = this;\n                Base.nextTick(function() {\n                    $.each( validators, function() {\n                        this.call( me.owner );\n                    });\n                });\n            }\n        });\n    \n        /**\n         * @property {int} [fileNumLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总数量, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileNumLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileNumLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                    // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n                if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                    return false;\n                }\n                if ( count >= max && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return count >= max ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function() {\n                count++;\n            });\n    \n            uploader.on( 'fileDequeued', function() {\n                count--;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n    \n        /**\n         * @property {int} [fileSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                count = 0,\n                max = parseInt( opts.fileSizeLimit, 10 ),\n                flag = true;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var invalid = count + file.size > max;\n    \n                if ( invalid && flag ) {\n                    flag = false;\n                    this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                    setTimeout(function() {\n                        flag = true;\n                    }, 1 );\n                }\n    \n                return invalid ? false : true;\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                count += file.size;\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                count -= file.size;\n            });\n    \n            uploader.on( 'reset', function() {\n                count = 0;\n            });\n        });\n    \n        /**\n         * @property {int} [fileSingleSizeLimit=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n         */\n        api.addValidator( 'fileSingleSizeLimit', function() {\n            var uploader = this,\n                opts = uploader.options,\n                max = opts.fileSingleSizeLimit;\n    \n            if ( !max ) {\n                return;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n    \n                if ( file.size > max ) {\n                    file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                    this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                    return false;\n                }\n    \n            });\n    \n        });\n    \n        /**\n         * @property {Boolean} [duplicate=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n         */\n        api.addValidator( 'duplicate', function() {\n            var uploader = this,\n                opts = uploader.options,\n                mapping = {};\n    \n            if ( opts.duplicate ) {\n                return;\n            }\n    \n            function hashString( str ) {\n                var hash = 0,\n                    i = 0,\n                    len = str.length,\n                    _char;\n    \n                for ( ; i < len; i++ ) {\n                    _char = str.charCodeAt( i );\n                    hash = _char + (hash << 6) + (hash << 16) - hash;\n                }\n    \n                return hash;\n            }\n    \n            uploader.on( 'beforeFileQueued', function( file ) {\n                var hash = file.__hash || (file.__hash = hashString( file.name +\n                        file.size + file.lastModifiedDate ));\n    \n                // 已经重复了\n                if ( mapping[ hash ] ) {\n                    this.trigger( 'error', 'F_DUPLICATE', file );\n                    return false;\n                }\n            });\n    \n            uploader.on( 'fileQueued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (mapping[ hash ] = true);\n            });\n    \n            uploader.on( 'fileDequeued', function( file ) {\n                var hash = file.__hash;\n    \n                hash && (delete mapping[ hash ]);\n            });\n    \n            uploader.on( 'reset', function() {\n                mapping = {};\n            });\n        });\n    \n        return api;\n    });\n    \n    /**\n     * @fileOverview Md5\n     */\n    define('lib/md5',[\n        'runtime/client',\n        'mediator'\n    ], function( RuntimeClient, Mediator ) {\n    \n        function Md5() {\n            RuntimeClient.call( this, 'Md5' );\n        }\n    \n        // 让 Md5 具备事件功能。\n        Mediator.installTo( Md5.prototype );\n    \n        Md5.prototype.loadFromBlob = function( blob ) {\n            var me = this;\n    \n            if ( me.getRuid() ) {\n                me.disconnectRuntime();\n            }\n    \n            // 连接到blob归属的同一个runtime.\n            me.connectRuntime( blob.ruid, function() {\n                me.exec('init');\n                me.exec( 'loadFromBlob', blob );\n            });\n        };\n    \n        Md5.prototype.getResult = function() {\n            return this.exec('getResult');\n        };\n    \n        return Md5;\n    });\n    /**\n     * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n     */\n    define('widgets/md5',[\n        'base',\n        'uploader',\n        'lib/md5',\n        'lib/blob',\n        'widgets/widget'\n    ], function( Base, Uploader, Md5, Blob ) {\n    \n        return Uploader.register({\n            name: 'md5',\n    \n    \n            /**\n             * 计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。\n             *\n             *\n             * @method md5File\n             * @grammar md5File( file[, start[, end]] ) => promise\n             * @for Uploader\n             * @example\n             *\n             * uploader.on( 'fileQueued', function( file ) {\n             *     var $li = ...;\n             *\n             *     uploader.md5File( file )\n             *\n             *         // 及时显示进度\n             *         .progress(function(percentage) {\n             *             console.log('Percentage:', percentage);\n             *         })\n             *\n             *         // 完成\n             *         .then(function(val) {\n             *             console.log('md5 result:', val);\n             *         });\n             *\n             * });\n             */\n            md5File: function( file, start, end ) {\n                var md5 = new Md5(),\n                    deferred = Base.Deferred(),\n                    blob = (file instanceof Blob) ? file :\n                        this.request( 'get-file', file ).source;\n    \n                md5.on( 'progress load', function( e ) {\n                    e = e || {};\n                    deferred.notify( e.total ? e.loaded / e.total : 1 );\n                });\n    \n                md5.on( 'complete', function() {\n                    deferred.resolve( md5.getResult() );\n                });\n    \n                md5.on( 'error', function( reason ) {\n                    deferred.reject( reason );\n                });\n    \n                if ( arguments.length > 1 ) {\n                    start = start || 0;\n                    end = end || 0;\n                    start < 0 && (start = blob.size + start);\n                    end < 0 && (end = blob.size + end);\n                    end = Math.min( end, blob.size );\n                    blob = blob.slice( start, end );\n                }\n    \n                md5.loadFromBlob( blob );\n    \n                return deferred.promise();\n            }\n        });\n    });\n    /**\n     * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n     */\n    define('runtime/compbase',[],function() {\n    \n        function CompBase( owner, runtime ) {\n    \n            this.owner = owner;\n            this.options = owner.options;\n    \n            this.getRuntime = function() {\n                return runtime;\n            };\n    \n            this.getRuid = function() {\n                return runtime.uid;\n            };\n    \n            this.trigger = function() {\n                return owner.trigger.apply( owner, arguments );\n            };\n        }\n    \n        return CompBase;\n    });\n    /**\n     * @fileOverview Html5Runtime\n     */\n    define('runtime/html5/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var type = 'html5',\n            components = {};\n    \n        function Html5Runtime() {\n            var pool = {},\n                me = this,\n                destroy = this.destroy;\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                if ( components[ comp ] ) {\n                    instance = pool[ uid ] = pool[ uid ] ||\n                            new components[ comp ]( client, me );\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n            };\n    \n            me.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: Html5Runtime,\n    \n            // 不需要连接其他程序，直接执行callback\n            init: function() {\n                var me = this;\n                setTimeout(function() {\n                    me.trigger('ready');\n                }, 1 );\n            }\n    \n        });\n    \n        // 注册Components\n        Html5Runtime.register = function( name, component ) {\n            var klass = components[ name ] = Base.inherits( CompBase, component );\n            return klass;\n        };\n    \n        // 注册html5运行时。\n        // 只有在支持的前提下注册。\n        if ( window.Blob && window.FileReader && window.DataView ) {\n            Runtime.addRuntime( type, Html5Runtime );\n        }\n    \n        return Html5Runtime;\n    });\n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/html5/blob',[\n        'runtime/html5/runtime',\n        'lib/blob'\n    ], function( Html5Runtime, Blob ) {\n    \n        return Html5Runtime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.owner.source,\n                    slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n    \n                blob = slice.call( blob, start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/dnd',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        var $ = Base.$,\r\n            prefix = 'webuploader-dnd-';\r\n    \r\n        return Html5Runtime.register( 'DragAndDrop', {\r\n            init: function() {\r\n                var elem = this.elem = this.options.container;\r\n    \r\n                this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n                this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n                this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n                this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n                this.dndOver = false;\r\n    \r\n                elem.on( 'dragenter', this.dragEnterHandler );\r\n                elem.on( 'dragover', this.dragOverHandler );\r\n                elem.on( 'dragleave', this.dragLeaveHandler );\r\n                elem.on( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).on( 'dragover', this.dragOverHandler );\r\n                    $( document ).on( 'drop', this.dropHandler );\r\n                }\r\n            },\r\n    \r\n            _dragEnterHandler: function( e ) {\r\n                var me = this,\r\n                    denied = me._denied || false,\r\n                    items;\r\n    \r\n                e = e.originalEvent || e;\r\n    \r\n                if ( !me.dndOver ) {\r\n                    me.dndOver = true;\r\n    \r\n                    // 注意只有 chrome 支持。\r\n                    items = e.dataTransfer.items;\r\n    \r\n                    if ( items && items.length ) {\r\n                        me._denied = denied = !me.trigger( 'accept', items );\r\n                    }\r\n    \r\n                    me.elem.addClass( prefix + 'over' );\r\n                    me.elem[ denied ? 'addClass' :\r\n                            'removeClass' ]( prefix + 'denied' );\r\n                }\r\n    \r\n                e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragOverHandler: function( e ) {\r\n                // 只处理框内的。\r\n                var parentElem = this.elem.parent().get( 0 );\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                clearTimeout( this._leaveTimer );\r\n                this._dragEnterHandler.call( this, e );\r\n    \r\n                return false;\r\n            },\r\n    \r\n            _dragLeaveHandler: function() {\r\n                var me = this,\r\n                    handler;\r\n    \r\n                handler = function() {\r\n                    me.dndOver = false;\r\n                    me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n                };\r\n    \r\n                clearTimeout( me._leaveTimer );\r\n                me._leaveTimer = setTimeout( handler, 100 );\r\n                return false;\r\n            },\r\n    \r\n            _dropHandler: function( e ) {\r\n                var me = this,\r\n                    ruid = me.getRuid(),\r\n                    parentElem = me.elem.parent().get( 0 ),\r\n                    dataTransfer, data;\r\n    \r\n                // 只处理框内的。\r\n                if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                    return false;\r\n                }\r\n    \r\n                e = e.originalEvent || e;\r\n                dataTransfer = e.dataTransfer;\r\n    \r\n                // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n                // 此处 ie11 下会报参数错误，\r\n                try {\r\n                    data = dataTransfer.getData('text/html');\r\n                } catch( err ) {\r\n                }\r\n    \r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over' );\r\n    \r\n                if ( !dataTransfer || data ) {\r\n                    return;\r\n                }\r\n    \r\n                me._getTansferFiles( dataTransfer, function( results ) {\r\n                    me.trigger( 'drop', $.map( results, function( file ) {\r\n                        return new File( ruid, file );\r\n                    }) );\r\n                });\r\n    \r\n                return false;\r\n            },\r\n    \r\n            // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n            _getTansferFiles: function( dataTransfer, callback ) {\r\n                var results  = [],\r\n                    promises = [],\r\n                    items, files, file, item, i, len, canAccessFolder;\r\n    \r\n                items = dataTransfer.items;\r\n                files = dataTransfer.files;\r\n    \r\n                canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n    \r\n                for ( i = 0, len = files.length; i < len; i++ ) {\r\n                    file = files[ i ];\r\n                    item = items && items[ i ];\r\n    \r\n                    if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n    \r\n                        promises.push( this._traverseDirectoryTree(\r\n                                item.webkitGetAsEntry(), results ) );\r\n                    } else {\r\n                        results.push( file );\r\n                    }\r\n                }\r\n    \r\n                Base.when.apply( Base, promises ).done(function() {\r\n    \r\n                    if ( !results.length ) {\r\n                        return;\r\n                    }\r\n    \r\n                    callback( results );\r\n                });\r\n            },\r\n    \r\n            _traverseDirectoryTree: function( entry, results ) {\r\n                var deferred = Base.Deferred(),\r\n                    me = this;\r\n    \r\n                if ( entry.isFile ) {\r\n                    entry.file(function( file ) {\r\n                        results.push( file );\r\n                        deferred.resolve();\r\n                    });\r\n                } else if ( entry.isDirectory ) {\r\n                    entry.createReader().readEntries(function( entries ) {\r\n                        var len = entries.length,\r\n                            promises = [],\r\n                            arr = [],    // 为了保证顺序。\r\n                            i;\r\n    \r\n                        for ( i = 0; i < len; i++ ) {\r\n                            promises.push( me._traverseDirectoryTree(\r\n                                    entries[ i ], arr ) );\r\n                        }\r\n    \r\n                        Base.when.apply( Base, promises ).then(function() {\r\n                            results.push.apply( results, arr );\r\n                            deferred.resolve();\r\n                        }, deferred.reject );\r\n                    });\r\n                }\r\n    \r\n                return deferred.promise();\r\n            },\r\n    \r\n            destroy: function() {\r\n                var elem = this.elem;\r\n    \r\n                // 还没 init 就调用 destroy\r\n                if (!elem) {\r\n                    return;\r\n                }\r\n    \r\n                elem.off( 'dragenter', this.dragEnterHandler );\r\n                elem.off( 'dragover', this.dragOverHandler );\r\n                elem.off( 'dragleave', this.dragLeaveHandler );\r\n                elem.off( 'drop', this.dropHandler );\r\n    \r\n                if ( this.options.disableGlobalDnd ) {\r\n                    $( document ).off( 'dragover', this.dragOverHandler );\r\n                    $( document ).off( 'drop', this.dropHandler );\r\n                }\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePaste\r\n     */\r\n    define('runtime/html5/filepaste',[\r\n        'base',\r\n        'runtime/html5/runtime',\r\n        'lib/file'\r\n    ], function( Base, Html5Runtime, File ) {\r\n    \r\n        return Html5Runtime.register( 'FilePaste', {\r\n            init: function() {\r\n                var opts = this.options,\r\n                    elem = this.elem = opts.container,\r\n                    accept = '.*',\r\n                    arr, i, len, item;\r\n    \r\n                // accetp的mimeTypes中生成匹配正则。\r\n                if ( opts.accept ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        item = opts.accept[ i ].mimeTypes;\r\n                        item && arr.push( item );\r\n                    }\r\n    \r\n                    if ( arr.length ) {\r\n                        accept = arr.join(',');\r\n                        accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                    }\r\n                }\r\n                this.accept = accept = new RegExp( accept, 'i' );\r\n                this.hander = Base.bindFn( this._pasteHander, this );\r\n                elem.on( 'paste', this.hander );\r\n            },\r\n    \r\n            _pasteHander: function( e ) {\r\n                var allowed = [],\r\n                    ruid = this.getRuid(),\r\n                    items, item, blob, i, len;\r\n    \r\n                e = e.originalEvent || e;\r\n                items = e.clipboardData.items;\r\n    \r\n                for ( i = 0, len = items.length; i < len; i++ ) {\r\n                    item = items[ i ];\r\n    \r\n                    if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                        continue;\r\n                    }\r\n    \r\n                    allowed.push( new File( ruid, blob ) );\r\n                }\r\n    \r\n                if ( allowed.length ) {\r\n                    // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n                    this.trigger( 'paste', allowed );\r\n                }\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.elem.off( 'paste', this.hander );\r\n            }\r\n        });\r\n    });\r\n    \n    /**\r\n     * @fileOverview FilePicker\r\n     */\r\n    define('runtime/html5/filepicker',[\r\n        'base',\r\n        'runtime/html5/runtime'\r\n    ], function( Base, Html5Runtime ) {\r\n    \r\n        var $ = Base.$;\r\n    \r\n        return Html5Runtime.register( 'FilePicker', {\r\n            init: function() {\r\n                var container = this.getRuntime().getContainer(),\r\n                    me = this,\r\n                    owner = me.owner,\r\n                    opts = me.options,\r\n                    label = this.label = $( document.createElement('label') ),\r\n                    input =  this.input = $( document.createElement('input') ),\r\n                    arr, i, len, mouseHandler, changeHandler;\r\n    \r\n                input.attr( 'type', 'file' );\r\n                input.attr( 'capture', 'camera');\r\n                input.attr( 'name', opts.name );\r\n                input.addClass('webuploader-element-invisible');\r\n    \r\n                label.on( 'click', function(e) {\r\n                    input.trigger('click');\r\n                    e.stopPropagation();\r\n                    owner.trigger('dialogopen');\r\n                });\r\n    \r\n                label.css({\r\n                    opacity: 0,\r\n                    width: '100%',\r\n                    height: '100%',\r\n                    display: 'block',\r\n                    cursor: 'pointer',\r\n                    background: '#ffffff'\r\n                });\r\n    \r\n                if ( opts.multiple ) {\r\n                    input.attr( 'multiple', 'multiple' );\r\n                }\r\n    \r\n                // @todo Firefox不支持单独指定后缀\r\n                if ( opts.accept && opts.accept.length > 0 ) {\r\n                    arr = [];\r\n    \r\n                    for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                        arr.push( opts.accept[ i ].mimeTypes );\r\n                    }\r\n    \r\n                    input.attr( 'accept', arr.join(',') );\r\n                }\r\n    \r\n                container.append( input );\r\n                container.append( label );\r\n    \r\n                mouseHandler = function( e ) {\r\n                    owner.trigger( e.type );\r\n                };\r\n    \r\n                changeHandler = function( e ) {\r\n                    var clone;\r\n    \r\n                    // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                    if (e.target.files.length === 0){\r\n                        return false;\r\n                    }\r\n    \r\n                    // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                    me.files = e.target.files;\r\n    \r\n    \r\n                    // reset input\r\n                    clone = this.cloneNode( true );\r\n                    clone.value = null;\r\n                    this.parentNode.replaceChild( clone, this );\r\n    \r\n                    input.off();\r\n                    input = $( clone ).on( 'change', changeHandler )\r\n                            .on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n                    owner.trigger('change');\r\n                }\r\n                input.on( 'change', changeHandler);\r\n                label.on( 'mouseenter mouseleave', mouseHandler );\r\n    \r\n            },\r\n    \r\n    \r\n            getFiles: function() {\r\n                return this.files;\r\n            },\r\n    \r\n            destroy: function() {\r\n                this.input.off();\r\n                this.label.off();\r\n            }\r\n        });\r\n    });\r\n    \n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/util',[\n        'base'\n    ], function( Base ) {\n    \n        var urlAPI = window.createObjectURL && window ||\n                window.URL && URL.revokeObjectURL && URL ||\n                window.webkitURL,\n            createObjectURL = Base.noop,\n            revokeObjectURL = createObjectURL;\n    \n        if ( urlAPI ) {\n    \n            // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n            createObjectURL = function() {\n                return urlAPI.createObjectURL.apply( urlAPI, arguments );\n            };\n    \n            revokeObjectURL = function() {\n                return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n            };\n        }\n    \n        return {\n            createObjectURL: createObjectURL,\n            revokeObjectURL: revokeObjectURL,\n    \n            dataURL2Blob: function( dataURI ) {\n                var byteStr, intArray, ab, i, mimetype, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                ab = new ArrayBuffer( byteStr.length );\n                intArray = new Uint8Array( ab );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n    \n                return this.arrayBufferToBlob( ab, mimetype );\n            },\n    \n            dataURL2ArrayBuffer: function( dataURI ) {\n                var byteStr, intArray, i, parts;\n    \n                parts = dataURI.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    byteStr = atob( parts[ 1 ] );\n                } else {\n                    byteStr = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                intArray = new Uint8Array( byteStr.length );\n    \n                for ( i = 0; i < byteStr.length; i++ ) {\n                    intArray[ i ] = byteStr.charCodeAt( i );\n                }\n    \n                return intArray.buffer;\n            },\n    \n            arrayBufferToBlob: function( buffer, type ) {\n                var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                    bb;\n    \n                // android不支持直接new Blob, 只能借助blobbuilder.\n                if ( builder ) {\n                    bb = new builder();\n                    bb.append( buffer );\n                    return bb.getBlob( type );\n                }\n    \n                return new Blob([ buffer ], type ? { type: type } : {} );\n            },\n    \n            // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n            // 你得到的结果是png.\n            canvasToDataUrl: function( canvas, type, quality ) {\n                return canvas.toDataURL( type, quality / 100 );\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            parseMeta: function( blob, callback ) {\n                callback( false, {});\n            },\n    \n            // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n            updateImageHead: function( data ) {\n                return data;\n            }\n        };\n    });\n    /**\n     * Terms:\n     *\n     * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n     * @fileOverview Image控件\n     */\n    define('runtime/html5/imagemeta',[\n        'runtime/html5/util'\n    ], function( Util ) {\n    \n        var api;\n    \n        api = {\n            parsers: {\n                0xffe1: []\n            },\n    \n            maxMetaDataSize: 262144,\n    \n            parse: function( blob, cb ) {\n                var me = this,\n                    fr = new FileReader();\n    \n                fr.onload = function() {\n                    cb( false, me._parse( this.result ) );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                fr.onerror = function( e ) {\n                    cb( e.message );\n                    fr = fr.onload = fr.onerror = null;\n                };\n    \n                blob = blob.slice( 0, me.maxMetaDataSize );\n                fr.readAsArrayBuffer( blob.getSource() );\n            },\n    \n            _parse: function( buffer, noParse ) {\n                if ( buffer.byteLength < 6 ) {\n                    return;\n                }\n    \n                var dataview = new DataView( buffer ),\n                    offset = 2,\n                    maxOffset = dataview.byteLength - 4,\n                    headLength = offset,\n                    ret = {},\n                    markerBytes, markerLength, parsers, i;\n    \n                if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n    \n                    while ( offset < maxOffset ) {\n                        markerBytes = dataview.getUint16( offset );\n    \n                        if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                                markerBytes === 0xfffe ) {\n    \n                            markerLength = dataview.getUint16( offset + 2 ) + 2;\n    \n                            if ( offset + markerLength > dataview.byteLength ) {\n                                break;\n                            }\n    \n                            parsers = api.parsers[ markerBytes ];\n    \n                            if ( !noParse && parsers ) {\n                                for ( i = 0; i < parsers.length; i += 1 ) {\n                                    parsers[ i ].call( api, dataview, offset,\n                                            markerLength, ret );\n                                }\n                            }\n    \n                            offset += markerLength;\n                            headLength = offset;\n                        } else {\n                            break;\n                        }\n                    }\n    \n                    if ( headLength > 6 ) {\n                        if ( buffer.slice ) {\n                            ret.imageHead = buffer.slice( 2, headLength );\n                        } else {\n                            // Workaround for IE10, which does not yet\n                            // support ArrayBuffer.slice:\n                            ret.imageHead = new Uint8Array( buffer )\n                                    .subarray( 2, headLength );\n                        }\n                    }\n                }\n    \n                return ret;\n            },\n    \n            updateImageHead: function( buffer, head ) {\n                var data = this._parse( buffer, true ),\n                    buf1, buf2, bodyoffset;\n    \n    \n                bodyoffset = 2;\n                if ( data.imageHead ) {\n                    bodyoffset = 2 + data.imageHead.byteLength;\n                }\n    \n                if ( buffer.slice ) {\n                    buf2 = buffer.slice( bodyoffset );\n                } else {\n                    buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n                }\n    \n                buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n    \n                buf1[ 0 ] = 0xFF;\n                buf1[ 1 ] = 0xD8;\n                buf1.set( new Uint8Array( head ), 2 );\n                buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n    \n                return buf1.buffer;\n            }\n        };\n    \n        Util.parseMeta = function() {\n            return api.parse.apply( api, arguments );\n        };\n    \n        Util.updateImageHead = function() {\n            return api.updateImageHead.apply( api, arguments );\n        };\n    \n        return api;\n    });\n    /**\n     * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n     * 暂时项目中只用了orientation.\n     *\n     * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n     * @fileOverview EXIF解析\n     */\n    \n    // Sample\n    // ====================================\n    // Make : Apple\n    // Model : iPhone 4S\n    // Orientation : 1\n    // XResolution : 72 [72/1]\n    // YResolution : 72 [72/1]\n    // ResolutionUnit : 2\n    // Software : QuickTime 7.7.1\n    // DateTime : 2013:09:01 22:53:55\n    // ExifIFDPointer : 190\n    // ExposureTime : 0.058823529411764705 [1/17]\n    // FNumber : 2.4 [12/5]\n    // ExposureProgram : Normal program\n    // ISOSpeedRatings : 800\n    // ExifVersion : 0220\n    // DateTimeOriginal : 2013:09:01 22:52:51\n    // DateTimeDigitized : 2013:09:01 22:52:51\n    // ComponentsConfiguration : YCbCr\n    // ShutterSpeedValue : 4.058893515764426\n    // ApertureValue : 2.5260688216892597 [4845/1918]\n    // BrightnessValue : -0.3126686601998395\n    // MeteringMode : Pattern\n    // Flash : Flash did not fire, compulsory flash mode\n    // FocalLength : 4.28 [107/25]\n    // SubjectArea : [4 values]\n    // FlashpixVersion : 0100\n    // ColorSpace : 1\n    // PixelXDimension : 2448\n    // PixelYDimension : 3264\n    // SensingMethod : One-chip color area sensor\n    // ExposureMode : 0\n    // WhiteBalance : Auto white balance\n    // FocalLengthIn35mmFilm : 35\n    // SceneCaptureType : Standard\n    define('runtime/html5/imagemeta/exif',[\n        'base',\n        'runtime/html5/imagemeta'\n    ], function( Base, ImageMeta ) {\n    \n        var EXIF = {};\n    \n        EXIF.ExifMap = function() {\n            return this;\n        };\n    \n        EXIF.ExifMap.prototype.map = {\n            'Orientation': 0x0112\n        };\n    \n        EXIF.ExifMap.prototype.get = function( id ) {\n            return this[ id ] || this[ this.map[ id ] ];\n        };\n    \n        EXIF.exifTagTypes = {\n            // byte, 8-bit unsigned int:\n            1: {\n                getValue: function( dataView, dataOffset ) {\n                    return dataView.getUint8( dataOffset );\n                },\n                size: 1\n            },\n    \n            // ascii, 8-bit byte:\n            2: {\n                getValue: function( dataView, dataOffset ) {\n                    return String.fromCharCode( dataView.getUint8( dataOffset ) );\n                },\n                size: 1,\n                ascii: true\n            },\n    \n            // short, 16 bit int:\n            3: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint16( dataOffset, littleEndian );\n                },\n                size: 2\n            },\n    \n            // long, 32 bit int:\n            4: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // rational = two long values,\n            // first is numerator, second is denominator:\n            5: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getUint32( dataOffset, littleEndian ) /\n                        dataView.getUint32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            },\n    \n            // slong, 32 bit signed int:\n            9: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian );\n                },\n                size: 4\n            },\n    \n            // srational, two slongs, first is numerator, second is denominator:\n            10: {\n                getValue: function( dataView, dataOffset, littleEndian ) {\n                    return dataView.getInt32( dataOffset, littleEndian ) /\n                        dataView.getInt32( dataOffset + 4, littleEndian );\n                },\n                size: 8\n            }\n        };\n    \n        // undefined, 8-bit byte, value depending on field:\n        EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n    \n        EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n                littleEndian ) {\n    \n            var tagType = EXIF.exifTagTypes[ type ],\n                tagSize, dataOffset, values, i, str, c;\n    \n            if ( !tagType ) {\n                Base.log('Invalid Exif data: Invalid tag type.');\n                return;\n            }\n    \n            tagSize = tagType.size * length;\n    \n            // Determine if the value is contained in the dataOffset bytes,\n            // or if the value at the dataOffset is a pointer to the actual data:\n            dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                    littleEndian ) : (offset + 8);\n    \n            if ( dataOffset + tagSize > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid data offset.');\n                return;\n            }\n    \n            if ( length === 1 ) {\n                return tagType.getValue( dataView, dataOffset, littleEndian );\n            }\n    \n            values = [];\n    \n            for ( i = 0; i < length; i += 1 ) {\n                values[ i ] = tagType.getValue( dataView,\n                        dataOffset + i * tagType.size, littleEndian );\n            }\n    \n            if ( tagType.ascii ) {\n                str = '';\n    \n                // Concatenate the chars:\n                for ( i = 0; i < values.length; i += 1 ) {\n                    c = values[ i ];\n    \n                    // Ignore the terminating NULL byte(s):\n                    if ( c === '\\u0000' ) {\n                        break;\n                    }\n                    str += c;\n                }\n    \n                return str;\n            }\n            return values;\n        };\n    \n        EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n                data ) {\n    \n            var tag = dataView.getUint16( offset, littleEndian );\n            data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                    dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                    dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                    littleEndian );\n        };\n    \n        EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n                littleEndian, data ) {\n    \n            var tagsNumber, dirEndOffset, i;\n    \n            if ( dirOffset + 6 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory offset.');\n                return;\n            }\n    \n            tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n            dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n    \n            if ( dirEndOffset + 4 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid directory size.');\n                return;\n            }\n    \n            for ( i = 0; i < tagsNumber; i += 1 ) {\n                this.parseExifTag( dataView, tiffOffset,\n                        dirOffset + 2 + 12 * i,    // tag offset\n                        littleEndian, data );\n            }\n    \n            // Return the offset to the next directory:\n            return dataView.getUint32( dirEndOffset, littleEndian );\n        };\n    \n        // EXIF.getExifThumbnail = function(dataView, offset, length) {\n        //     var hexData,\n        //         i,\n        //         b;\n        //     if (!length || offset + length > dataView.byteLength) {\n        //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n        //         return;\n        //     }\n        //     hexData = [];\n        //     for (i = 0; i < length; i += 1) {\n        //         b = dataView.getUint8(offset + i);\n        //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n        //     }\n        //     return 'data:image/jpeg,%' + hexData.join('%');\n        // };\n    \n        EXIF.parseExifData = function( dataView, offset, length, data ) {\n    \n            var tiffOffset = offset + 10,\n                littleEndian, dirOffset;\n    \n            // Check for the ASCII code for \"Exif\" (0x45786966):\n            if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n                // No Exif data, might be XMP data instead\n                return;\n            }\n            if ( tiffOffset + 8 > dataView.byteLength ) {\n                Base.log('Invalid Exif data: Invalid segment size.');\n                return;\n            }\n    \n            // Check for the two null bytes:\n            if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n                Base.log('Invalid Exif data: Missing byte alignment offset.');\n                return;\n            }\n    \n            // Check the byte alignment:\n            switch ( dataView.getUint16( tiffOffset ) ) {\n                case 0x4949:\n                    littleEndian = true;\n                    break;\n    \n                case 0x4D4D:\n                    littleEndian = false;\n                    break;\n    \n                default:\n                    Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                    return;\n            }\n    \n            // Check for the TIFF tag marker (0x002A):\n            if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n                Base.log('Invalid Exif data: Missing TIFF marker.');\n                return;\n            }\n    \n            // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n            dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n            // Create the exif object to store the tags:\n            data.exif = new EXIF.ExifMap();\n            // Parse the tags of the main image directory and retrieve the\n            // offset to the next directory, usually the thumbnail directory:\n            dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                    tiffOffset + dirOffset, littleEndian, data );\n    \n            // 尝试读取缩略图\n            // if ( dirOffset ) {\n            //     thumbnailData = {exif: {}};\n            //     dirOffset = EXIF.parseExifTags(\n            //         dataView,\n            //         tiffOffset,\n            //         tiffOffset + dirOffset,\n            //         littleEndian,\n            //         thumbnailData\n            //     );\n    \n            //     // Check for JPEG Thumbnail offset:\n            //     if (thumbnailData.exif[0x0201]) {\n            //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n            //             dataView,\n            //             tiffOffset + thumbnailData.exif[0x0201],\n            //             thumbnailData.exif[0x0202] // Thumbnail data length\n            //         );\n            //     }\n            // }\n        };\n    \n        ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n        return EXIF;\n    });\n    /**\n     * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n     * android里面toDataUrl('image/jpege')得到的结果却是png.\n     *\n     * 所以这里没辙，只能借助这个工具\n     * @fileOverview jpeg encoder\n     */\n    define('runtime/html5/jpegencoder',[], function( require, exports, module ) {\n    \n        /*\n          Copyright (c) 2008, Adobe Systems Incorporated\n          All rights reserved.\n    \n          Redistribution and use in source and binary forms, with or without\n          modification, are permitted provided that the following conditions are\n          met:\n    \n          * Redistributions of source code must retain the above copyright notice,\n            this list of conditions and the following disclaimer.\n    \n          * Redistributions in binary form must reproduce the above copyright\n            notice, this list of conditions and the following disclaimer in the\n            documentation and/or other materials provided with the distribution.\n    \n          * Neither the name of Adobe Systems Incorporated nor the names of its\n            contributors may be used to endorse or promote products derived from\n            this software without specific prior written permission.\n    \n          THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n          IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n          THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n          PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n          CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n          EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n          PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n          PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n          LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n          NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n          SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n        */\n        /*\n        JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n    \n        Basic GUI blocking jpeg encoder\n        */\n    \n        function JPEGEncoder(quality) {\n          var self = this;\n            var fround = Math.round;\n            var ffloor = Math.floor;\n            var YTable = new Array(64);\n            var UVTable = new Array(64);\n            var fdtbl_Y = new Array(64);\n            var fdtbl_UV = new Array(64);\n            var YDC_HT;\n            var UVDC_HT;\n            var YAC_HT;\n            var UVAC_HT;\n    \n            var bitcode = new Array(65535);\n            var category = new Array(65535);\n            var outputfDCTQuant = new Array(64);\n            var DU = new Array(64);\n            var byteout = [];\n            var bytenew = 0;\n            var bytepos = 7;\n    \n            var YDU = new Array(64);\n            var UDU = new Array(64);\n            var VDU = new Array(64);\n            var clt = new Array(256);\n            var RGB_YUV_TABLE = new Array(2048);\n            var currentQuality;\n    \n            var ZigZag = [\n                     0, 1, 5, 6,14,15,27,28,\n                     2, 4, 7,13,16,26,29,42,\n                     3, 8,12,17,25,30,41,43,\n                     9,11,18,24,31,40,44,53,\n                    10,19,23,32,39,45,52,54,\n                    20,22,33,38,46,51,55,60,\n                    21,34,37,47,50,56,59,61,\n                    35,36,48,49,57,58,62,63\n                ];\n    \n            var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n            var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n            var std_ac_luminance_values = [\n                    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                    0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                    0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                    0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                    0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                    0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                    0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                    0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                    0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                    0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                    0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                    0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                    0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                    0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                    0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                    0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                    0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                    0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                    0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                    0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n            var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n            var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n            var std_ac_chrominance_values = [\n                    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                    0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                    0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                    0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                    0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                    0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                    0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                    0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                    0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                    0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                    0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                    0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                    0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                    0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                    0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                    0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                    0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                    0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                    0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                    0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                    0xf9,0xfa\n                ];\n    \n            function initQuantTables(sf){\n                    var YQT = [\n                        16, 11, 10, 16, 24, 40, 51, 61,\n                        12, 12, 14, 19, 26, 58, 60, 55,\n                        14, 13, 16, 24, 40, 57, 69, 56,\n                        14, 17, 22, 29, 51, 87, 80, 62,\n                        18, 22, 37, 56, 68,109,103, 77,\n                        24, 35, 55, 64, 81,104,113, 92,\n                        49, 64, 78, 87,103,121,120,101,\n                        72, 92, 95, 98,112,100,103, 99\n                    ];\n    \n                    for (var i = 0; i < 64; i++) {\n                        var t = ffloor((YQT[i]*sf+50)/100);\n                        if (t < 1) {\n                            t = 1;\n                        } else if (t > 255) {\n                            t = 255;\n                        }\n                        YTable[ZigZag[i]] = t;\n                    }\n                    var UVQT = [\n                        17, 18, 24, 47, 99, 99, 99, 99,\n                        18, 21, 26, 66, 99, 99, 99, 99,\n                        24, 26, 56, 99, 99, 99, 99, 99,\n                        47, 66, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99,\n                        99, 99, 99, 99, 99, 99, 99, 99\n                    ];\n                    for (var j = 0; j < 64; j++) {\n                        var u = ffloor((UVQT[j]*sf+50)/100);\n                        if (u < 1) {\n                            u = 1;\n                        } else if (u > 255) {\n                            u = 255;\n                        }\n                        UVTable[ZigZag[j]] = u;\n                    }\n                    var aasf = [\n                        1.0, 1.387039845, 1.306562965, 1.175875602,\n                        1.0, 0.785694958, 0.541196100, 0.275899379\n                    ];\n                    var k = 0;\n                    for (var row = 0; row < 8; row++)\n                    {\n                        for (var col = 0; col < 8; col++)\n                        {\n                            fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                            k++;\n                        }\n                    }\n                }\n    \n                function computeHuffmanTbl(nrcodes, std_table){\n                    var codevalue = 0;\n                    var pos_in_table = 0;\n                    var HT = new Array();\n                    for (var k = 1; k <= 16; k++) {\n                        for (var j = 1; j <= nrcodes[k]; j++) {\n                            HT[std_table[pos_in_table]] = [];\n                            HT[std_table[pos_in_table]][0] = codevalue;\n                            HT[std_table[pos_in_table]][1] = k;\n                            pos_in_table++;\n                            codevalue++;\n                        }\n                        codevalue*=2;\n                    }\n                    return HT;\n                }\n    \n                function initHuffmanTbl()\n                {\n                    YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                    UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                    YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                    UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n                }\n    \n                function initCategoryNumber()\n                {\n                    var nrlower = 1;\n                    var nrupper = 2;\n                    for (var cat = 1; cat <= 15; cat++) {\n                        //Positive numbers\n                        for (var nr = nrlower; nr<nrupper; nr++) {\n                            category[32767+nr] = cat;\n                            bitcode[32767+nr] = [];\n                            bitcode[32767+nr][1] = cat;\n                            bitcode[32767+nr][0] = nr;\n                        }\n                        //Negative numbers\n                        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                            category[32767+nrneg] = cat;\n                            bitcode[32767+nrneg] = [];\n                            bitcode[32767+nrneg][1] = cat;\n                            bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                        }\n                        nrlower <<= 1;\n                        nrupper <<= 1;\n                    }\n                }\n    \n                function initRGBYUVTable() {\n                    for(var i = 0; i < 256;i++) {\n                        RGB_YUV_TABLE[i]            =  19595 * i;\n                        RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                        RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                        RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                        RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                        RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                        RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                        RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                    }\n                }\n    \n                // IO functions\n                function writeBits(bs)\n                {\n                    var value = bs[0];\n                    var posval = bs[1]-1;\n                    while ( posval >= 0 ) {\n                        if (value & (1 << posval) ) {\n                            bytenew |= (1 << bytepos);\n                        }\n                        posval--;\n                        bytepos--;\n                        if (bytepos < 0) {\n                            if (bytenew == 0xFF) {\n                                writeByte(0xFF);\n                                writeByte(0);\n                            }\n                            else {\n                                writeByte(bytenew);\n                            }\n                            bytepos=7;\n                            bytenew=0;\n                        }\n                    }\n                }\n    \n                function writeByte(value)\n                {\n                    byteout.push(clt[value]); // write char directly instead of converting later\n                }\n    \n                function writeWord(value)\n                {\n                    writeByte((value>>8)&0xFF);\n                    writeByte((value   )&0xFF);\n                }\n    \n                // DCT & quantization core\n                function fDCTQuant(data, fdtbl)\n                {\n                    var d0, d1, d2, d3, d4, d5, d6, d7;\n                    /* Pass 1: process rows. */\n                    var dataOff=0;\n                    var i;\n                    var I8 = 8;\n                    var I64 = 64;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff+1];\n                        d2 = data[dataOff+2];\n                        d3 = data[dataOff+3];\n                        d4 = data[dataOff+4];\n                        d5 = data[dataOff+5];\n                        d6 = data[dataOff+6];\n                        d7 = data[dataOff+7];\n    \n                        var tmp0 = d0 + d7;\n                        var tmp7 = d0 - d7;\n                        var tmp1 = d1 + d6;\n                        var tmp6 = d1 - d6;\n                        var tmp2 = d2 + d5;\n                        var tmp5 = d2 - d5;\n                        var tmp3 = d3 + d4;\n                        var tmp4 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                        var tmp13 = tmp0 - tmp3;\n                        var tmp11 = tmp1 + tmp2;\n                        var tmp12 = tmp1 - tmp2;\n    \n                        data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                        data[dataOff+4] = tmp10 - tmp11;\n    \n                        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                        data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                        data[dataOff+6] = tmp13 - z1;\n    \n                        /* Odd part */\n                        tmp10 = tmp4 + tmp5; /* phase 2 */\n                        tmp11 = tmp5 + tmp6;\n                        tmp12 = tmp6 + tmp7;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                        var z3 = tmp11 * 0.707106781; /* c4 */\n    \n                        var z11 = tmp7 + z3;    /* phase 5 */\n                        var z13 = tmp7 - z3;\n    \n                        data[dataOff+5] = z13 + z2; /* phase 6 */\n                        data[dataOff+3] = z13 - z2;\n                        data[dataOff+1] = z11 + z4;\n                        data[dataOff+7] = z11 - z4;\n    \n                        dataOff += 8; /* advance pointer to next row */\n                    }\n    \n                    /* Pass 2: process columns. */\n                    dataOff = 0;\n                    for (i=0; i<I8; ++i)\n                    {\n                        d0 = data[dataOff];\n                        d1 = data[dataOff + 8];\n                        d2 = data[dataOff + 16];\n                        d3 = data[dataOff + 24];\n                        d4 = data[dataOff + 32];\n                        d5 = data[dataOff + 40];\n                        d6 = data[dataOff + 48];\n                        d7 = data[dataOff + 56];\n    \n                        var tmp0p2 = d0 + d7;\n                        var tmp7p2 = d0 - d7;\n                        var tmp1p2 = d1 + d6;\n                        var tmp6p2 = d1 - d6;\n                        var tmp2p2 = d2 + d5;\n                        var tmp5p2 = d2 - d5;\n                        var tmp3p2 = d3 + d4;\n                        var tmp4p2 = d3 - d4;\n    \n                        /* Even part */\n                        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                        var tmp13p2 = tmp0p2 - tmp3p2;\n                        var tmp11p2 = tmp1p2 + tmp2p2;\n                        var tmp12p2 = tmp1p2 - tmp2p2;\n    \n                        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                        data[dataOff+32] = tmp10p2 - tmp11p2;\n    \n                        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                        data[dataOff+48] = tmp13p2 - z1p2;\n    \n                        /* Odd part */\n                        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                        tmp11p2 = tmp5p2 + tmp6p2;\n                        tmp12p2 = tmp6p2 + tmp7p2;\n    \n                        /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                        var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n    \n                        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                        var z13p2 = tmp7p2 - z3p2;\n    \n                        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                        data[dataOff+24] = z13p2 - z2p2;\n                        data[dataOff+ 8] = z11p2 + z4p2;\n                        data[dataOff+56] = z11p2 - z4p2;\n    \n                        dataOff++; /* advance pointer to next column */\n                    }\n    \n                    // Quantize/descale the coefficients\n                    var fDCTQuant;\n                    for (i=0; i<I64; ++i)\n                    {\n                        // Apply the quantization and scaling factor & Round to nearest integer\n                        fDCTQuant = data[i]*fdtbl[i];\n                        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                        //outputfDCTQuant[i] = fround(fDCTQuant);\n    \n                    }\n                    return outputfDCTQuant;\n                }\n    \n                function writeAPP0()\n                {\n                    writeWord(0xFFE0); // marker\n                    writeWord(16); // length\n                    writeByte(0x4A); // J\n                    writeByte(0x46); // F\n                    writeByte(0x49); // I\n                    writeByte(0x46); // F\n                    writeByte(0); // = \"JFIF\",'\\0'\n                    writeByte(1); // versionhi\n                    writeByte(1); // versionlo\n                    writeByte(0); // xyunits\n                    writeWord(1); // xdensity\n                    writeWord(1); // ydensity\n                    writeByte(0); // thumbnwidth\n                    writeByte(0); // thumbnheight\n                }\n    \n                function writeSOF0(width, height)\n                {\n                    writeWord(0xFFC0); // marker\n                    writeWord(17);   // length, truecolor YUV JPG\n                    writeByte(8);    // precision\n                    writeWord(height);\n                    writeWord(width);\n                    writeByte(3);    // nrofcomponents\n                    writeByte(1);    // IdY\n                    writeByte(0x11); // HVY\n                    writeByte(0);    // QTY\n                    writeByte(2);    // IdU\n                    writeByte(0x11); // HVU\n                    writeByte(1);    // QTU\n                    writeByte(3);    // IdV\n                    writeByte(0x11); // HVV\n                    writeByte(1);    // QTV\n                }\n    \n                function writeDQT()\n                {\n                    writeWord(0xFFDB); // marker\n                    writeWord(132);    // length\n                    writeByte(0);\n                    for (var i=0; i<64; i++) {\n                        writeByte(YTable[i]);\n                    }\n                    writeByte(1);\n                    for (var j=0; j<64; j++) {\n                        writeByte(UVTable[j]);\n                    }\n                }\n    \n                function writeDHT()\n                {\n                    writeWord(0xFFC4); // marker\n                    writeWord(0x01A2); // length\n    \n                    writeByte(0); // HTYDCinfo\n                    for (var i=0; i<16; i++) {\n                        writeByte(std_dc_luminance_nrcodes[i+1]);\n                    }\n                    for (var j=0; j<=11; j++) {\n                        writeByte(std_dc_luminance_values[j]);\n                    }\n    \n                    writeByte(0x10); // HTYACinfo\n                    for (var k=0; k<16; k++) {\n                        writeByte(std_ac_luminance_nrcodes[k+1]);\n                    }\n                    for (var l=0; l<=161; l++) {\n                        writeByte(std_ac_luminance_values[l]);\n                    }\n    \n                    writeByte(1); // HTUDCinfo\n                    for (var m=0; m<16; m++) {\n                        writeByte(std_dc_chrominance_nrcodes[m+1]);\n                    }\n                    for (var n=0; n<=11; n++) {\n                        writeByte(std_dc_chrominance_values[n]);\n                    }\n    \n                    writeByte(0x11); // HTUACinfo\n                    for (var o=0; o<16; o++) {\n                        writeByte(std_ac_chrominance_nrcodes[o+1]);\n                    }\n                    for (var p=0; p<=161; p++) {\n                        writeByte(std_ac_chrominance_values[p]);\n                    }\n                }\n    \n                function writeSOS()\n                {\n                    writeWord(0xFFDA); // marker\n                    writeWord(12); // length\n                    writeByte(3); // nrofcomponents\n                    writeByte(1); // IdY\n                    writeByte(0); // HTY\n                    writeByte(2); // IdU\n                    writeByte(0x11); // HTU\n                    writeByte(3); // IdV\n                    writeByte(0x11); // HTV\n                    writeByte(0); // Ss\n                    writeByte(0x3f); // Se\n                    writeByte(0); // Bf\n                }\n    \n                function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                    var EOB = HTAC[0x00];\n                    var M16zeroes = HTAC[0xF0];\n                    var pos;\n                    var I16 = 16;\n                    var I63 = 63;\n                    var I64 = 64;\n                    var DU_DCT = fDCTQuant(CDU, fdtbl);\n                    //ZigZag reorder\n                    for (var j=0;j<I64;++j) {\n                        DU[ZigZag[j]]=DU_DCT[j];\n                    }\n                    var Diff = DU[0] - DC; DC = DU[0];\n                    //Encode DC\n                    if (Diff==0) {\n                        writeBits(HTDC[0]); // Diff might be 0\n                    } else {\n                        pos = 32767+Diff;\n                        writeBits(HTDC[category[pos]]);\n                        writeBits(bitcode[pos]);\n                    }\n                    //Encode ACs\n                    var end0pos = 63; // was const... which is crazy\n                    for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                    //end0pos = first element in reverse order !=0\n                    if ( end0pos == 0) {\n                        writeBits(EOB);\n                        return DC;\n                    }\n                    var i = 1;\n                    var lng;\n                    while ( i <= end0pos ) {\n                        var startpos = i;\n                        for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                        var nrzeroes = i-startpos;\n                        if ( nrzeroes >= I16 ) {\n                            lng = nrzeroes>>4;\n                            for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                                writeBits(M16zeroes);\n                            nrzeroes = nrzeroes&0xF;\n                        }\n                        pos = 32767+DU[i];\n                        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                        writeBits(bitcode[pos]);\n                        i++;\n                    }\n                    if ( end0pos != I63 ) {\n                        writeBits(EOB);\n                    }\n                    return DC;\n                }\n    \n                function initCharLookupTable(){\n                    var sfcc = String.fromCharCode;\n                    for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                        clt[i] = sfcc(i);\n                    }\n                }\n    \n                this.encode = function(image,quality) // image data object\n                {\n                    // var time_start = new Date().getTime();\n    \n                    if(quality) setQuality(quality);\n    \n                    // Initialize bit writer\n                    byteout = new Array();\n                    bytenew=0;\n                    bytepos=7;\n    \n                    // Add JPEG headers\n                    writeWord(0xFFD8); // SOI\n                    writeAPP0();\n                    writeDQT();\n                    writeSOF0(image.width,image.height);\n                    writeDHT();\n                    writeSOS();\n    \n    \n                    // Encode 8x8 macroblocks\n                    var DCY=0;\n                    var DCU=0;\n                    var DCV=0;\n    \n                    bytenew=0;\n                    bytepos=7;\n    \n    \n                    this.encode.displayName = \"_encode_\";\n    \n                    var imageData = image.data;\n                    var width = image.width;\n                    var height = image.height;\n    \n                    var quadWidth = width*4;\n                    var tripleWidth = width*3;\n    \n                    var x, y = 0;\n                    var r, g, b;\n                    var start,p, col,row,pos;\n                    while(y < height){\n                        x = 0;\n                        while(x < quadWidth){\n                        start = quadWidth * y + x;\n                        p = start;\n                        col = -1;\n                        row = 0;\n    \n                        for(pos=0; pos < 64; pos++){\n                            row = pos >> 3;// /8\n                            col = ( pos & 7 ) * 4; // %8\n                            p = start + ( row * quadWidth ) + col;\n    \n                            if(y+row >= height){ // padding bottom\n                                p-= (quadWidth*(y+1+row-height));\n                            }\n    \n                            if(x+col >= quadWidth){ // padding right\n                                p-= ((x+col) - quadWidth +4)\n                            }\n    \n                            r = imageData[ p++ ];\n                            g = imageData[ p++ ];\n                            b = imageData[ p++ ];\n    \n    \n                            /* // calculate YUV values dynamically\n                            YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                            UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                            VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                            */\n    \n                            // use lookup table (slightly faster)\n                            YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                            UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                            VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n    \n                        }\n    \n                        DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                        DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                        DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                        x+=32;\n                        }\n                        y+=8;\n                    }\n    \n    \n                    ////////////////////////////////////////////////////////////////\n    \n                    // Do the bit alignment of the EOI marker\n                    if ( bytepos >= 0 ) {\n                        var fillbits = [];\n                        fillbits[1] = bytepos+1;\n                        fillbits[0] = (1<<(bytepos+1))-1;\n                        writeBits(fillbits);\n                    }\n    \n                    writeWord(0xFFD9); //EOI\n    \n                    var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n    \n                    byteout = [];\n    \n                    // benchmarking\n                    // var duration = new Date().getTime() - time_start;\n                    // console.log('Encoding time: '+ currentQuality + 'ms');\n                    //\n    \n                    return jpegDataUri\n            }\n    \n            function setQuality(quality){\n                if (quality <= 0) {\n                    quality = 1;\n                }\n                if (quality > 100) {\n                    quality = 100;\n                }\n    \n                if(currentQuality == quality) return // don't recalc if unchanged\n    \n                var sf = 0;\n                if (quality < 50) {\n                    sf = Math.floor(5000 / quality);\n                } else {\n                    sf = Math.floor(200 - quality*2);\n                }\n    \n                initQuantTables(sf);\n                currentQuality = quality;\n                // console.log('Quality set to: '+quality +'%');\n            }\n    \n            function init(){\n                // var time_start = new Date().getTime();\n                if(!quality) quality = 50;\n                // Create tables\n                initCharLookupTable()\n                initHuffmanTbl();\n                initCategoryNumber();\n                initRGBYUVTable();\n    \n                setQuality(quality);\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Initialization '+ duration + 'ms');\n            }\n    \n            init();\n    \n        };\n    \n        JPEGEncoder.encode = function( data, quality ) {\n            var encoder = new JPEGEncoder( quality );\n    \n            return encoder.encode( data );\n        }\n    \n        return JPEGEncoder;\n    });\n    /**\n     * @fileOverview Fix android canvas.toDataUrl bug.\n     */\n    define('runtime/html5/androidpatch',[\n        'runtime/html5/util',\n        'runtime/html5/jpegencoder',\n        'base'\n    ], function( Util, encoder, Base ) {\n        var origin = Util.canvasToDataUrl,\n            supportJpeg;\n    \n        Util.canvasToDataUrl = function( canvas, type, quality ) {\n            var ctx, w, h, fragement, parts;\n    \n            // 非android手机直接跳过。\n            if ( !Base.os.android ) {\n                return origin.apply( null, arguments );\n            }\n    \n            // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n            // JPEG 前两位分别是：255, 216\n            if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n                fragement = origin.apply( null, arguments );\n    \n                parts = fragement.split(',');\n    \n                if ( ~parts[ 0 ].indexOf('base64') ) {\n                    fragement = atob( parts[ 1 ] );\n                } else {\n                    fragement = decodeURIComponent( parts[ 1 ] );\n                }\n    \n                fragement = fragement.substring( 0, 2 );\n    \n                supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                        fragement.charCodeAt( 1 ) === 216;\n            }\n    \n            // 只有在android环境下才修复\n            if ( type === 'image/jpeg' && !supportJpeg ) {\n                w = canvas.width;\n                h = canvas.height;\n                ctx = canvas.getContext('2d');\n    \n                return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n            }\n    \n            return origin.apply( null, arguments );\n        };\n    });\n    /**\n     * @fileOverview Image\n     */\n    define('runtime/html5/image',[\n        'base',\n        'runtime/html5/runtime',\n        'runtime/html5/util'\n    ], function( Base, Html5Runtime, Util ) {\n    \n        var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n    \n        return Html5Runtime.register( 'Image', {\n    \n            // flag: 标记是否被修改过。\n            modified: false,\n    \n            init: function() {\n                var me = this,\n                    img = new Image();\n    \n                img.onload = function() {\n    \n                    me._info = {\n                        type: me.type,\n                        width: this.width,\n                        height: this.height\n                    };\n    \n                    //debugger;\n    \n                    // 读取meta信息。\n                    if ( !me._metas && 'image/jpeg' === me.type ) {\n                        Util.parseMeta( me._blob, function( error, ret ) {\n                            me._metas = ret;\n                            me.owner.trigger('load');\n                        });\n                    } else {\n                        me.owner.trigger('load');\n                    }\n                };\n    \n                img.onerror = function() {\n                    me.owner.trigger('error');\n                };\n    \n                me._img = img;\n            },\n    \n            loadFromBlob: function( blob ) {\n                var me = this,\n                    img = me._img;\n    \n                me._blob = blob;\n                me.type = blob.type;\n                img.src = Util.createObjectURL( blob.getSource() );\n                me.owner.once( 'load', function() {\n                    Util.revokeObjectURL( img.src );\n                });\n            },\n    \n            resize: function( width, height ) {\n                var canvas = this._canvas ||\n                        (this._canvas = document.createElement('canvas'));\n    \n                this._resize( this._img, canvas, width, height );\n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'resize' );\n            },\n    \n            crop: function( x, y, w, h, s ) {\n                var cvs = this._canvas ||\n                        (this._canvas = document.createElement('canvas')),\n                    opts = this.options,\n                    img = this._img,\n                    iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    orientation = this.getOrientation();\n    \n                s = s || 1;\n    \n                // todo 解决 orientation 的问题。\n                // values that require 90 degree rotation\n                // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                //     switch ( orientation ) {\n                //         case 6:\n                //             tmp = x;\n                //             x = y;\n                //             y = iw * s - tmp - w;\n                //             console.log(ih * s, tmp, w)\n                //             break;\n                //     }\n    \n                //     (w ^= h, h ^= w, w ^= h);\n                // }\n    \n                cvs.width = w;\n                cvs.height = h;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n                this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n    \n                this._blob = null;    // 没用了，可以删掉了。\n                this.modified = true;\n                this.owner.trigger( 'complete', 'crop' );\n            },\n    \n            getAsBlob: function( type ) {\n                var blob = this._blob,\n                    opts = this.options,\n                    canvas;\n    \n                type = type || this.type;\n    \n                // blob需要重新生成。\n                if ( this.modified || this.type !== type ) {\n                    canvas = this._canvas;\n    \n                    if ( type === 'image/jpeg' ) {\n    \n                        blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n    \n                        if ( opts.preserveHeaders && this._metas &&\n                                this._metas.imageHead ) {\n    \n                            blob = Util.dataURL2ArrayBuffer( blob );\n                            blob = Util.updateImageHead( blob,\n                                    this._metas.imageHead );\n                            blob = Util.arrayBufferToBlob( blob, type );\n                            return blob;\n                        }\n                    } else {\n                        blob = Util.canvasToDataUrl( canvas, type );\n                    }\n    \n                    blob = Util.dataURL2Blob( blob );\n                }\n    \n                return blob;\n            },\n    \n            getAsDataUrl: function( type ) {\n                var opts = this.options;\n    \n                type = type || this.type;\n    \n                if ( type === 'image/jpeg' ) {\n                    return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n                } else {\n                    return this._canvas.toDataURL( type );\n                }\n            },\n    \n            getOrientation: function() {\n                return this._metas && this._metas.exif &&\n                        this._metas.exif.get('Orientation') || 1;\n            },\n    \n            info: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._info = val;\n                    return this;\n                }\n    \n                // getter\n                return this._info;\n            },\n    \n            meta: function( val ) {\n    \n                // setter\n                if ( val ) {\n                    this._metas = val;\n                    return this;\n                }\n    \n                // getter\n                return this._metas;\n            },\n    \n            destroy: function() {\n                var canvas = this._canvas;\n                this._img.onload = null;\n    \n                if ( canvas ) {\n                    canvas.getContext('2d')\n                            .clearRect( 0, 0, canvas.width, canvas.height );\n                    canvas.width = canvas.height = 0;\n                    this._canvas = null;\n                }\n    \n                // 释放内存。非常重要，否则释放不了image的内存。\n                this._img.src = BLANK;\n                this._img = this._blob = null;\n            },\n    \n            _resize: function( img, cvs, width, height ) {\n                var opts = this.options,\n                    naturalWidth = img.width,\n                    naturalHeight = img.height,\n                    orientation = this.getOrientation(),\n                    scale, w, h, x, y;\n    \n                // values that require 90 degree rotation\n                if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n    \n                    // 交换width, height的值。\n                    width ^= height;\n                    height ^= width;\n                    width ^= height;\n                }\n    \n                scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                        height / naturalHeight );\n    \n                // 不允许放大。\n                opts.allowMagnify || (scale = Math.min( 1, scale ));\n    \n                w = naturalWidth * scale;\n                h = naturalHeight * scale;\n    \n                if ( opts.crop ) {\n                    cvs.width = width;\n                    cvs.height = height;\n                } else {\n                    cvs.width = w;\n                    cvs.height = h;\n                }\n    \n                x = (cvs.width - w) / 2;\n                y = (cvs.height - h) / 2;\n    \n                opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n    \n                this._renderImageToCanvas( cvs, img, x, y, w, h );\n            },\n    \n            _rotate2Orientaion: function( canvas, orientation ) {\n                var width = canvas.width,\n                    height = canvas.height,\n                    ctx = canvas.getContext('2d');\n    \n                switch ( orientation ) {\n                    case 5:\n                    case 6:\n                    case 7:\n                    case 8:\n                        canvas.width = height;\n                        canvas.height = width;\n                        break;\n                }\n    \n                switch ( orientation ) {\n                    case 2:    // horizontal flip\n                        ctx.translate( width, 0 );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 3:    // 180 rotate left\n                        ctx.translate( width, height );\n                        ctx.rotate( Math.PI );\n                        break;\n    \n                    case 4:    // vertical flip\n                        ctx.translate( 0, height );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 5:    // vertical flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.scale( 1, -1 );\n                        break;\n    \n                    case 6:    // 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( 0, -height );\n                        break;\n    \n                    case 7:    // horizontal flip + 90 rotate right\n                        ctx.rotate( 0.5 * Math.PI );\n                        ctx.translate( width, -height );\n                        ctx.scale( -1, 1 );\n                        break;\n    \n                    case 8:    // 90 rotate left\n                        ctx.rotate( -0.5 * Math.PI );\n                        ctx.translate( -width, 0 );\n                        break;\n                }\n            },\n    \n            // https://github.com/stomita/ios-imagefile-megapixel/\n            // blob/master/src/megapix-image.js\n            _renderImageToCanvas: (function() {\n    \n                // 如果不是ios, 不需要这么复杂！\n                if ( !Base.os.ios ) {\n                    return function( canvas ) {\n                        var args = Base.slice( arguments, 1 ),\n                            ctx = canvas.getContext('2d');\n    \n                        ctx.drawImage.apply( ctx, args );\n                    };\n                }\n    \n                /**\n                 * Detecting vertical squash in loaded image.\n                 * Fixes a bug which squash image vertically while drawing into\n                 * canvas for some images.\n                 */\n                function detectVerticalSquash( img, iw, ih ) {\n                    var canvas = document.createElement('canvas'),\n                        ctx = canvas.getContext('2d'),\n                        sy = 0,\n                        ey = ih,\n                        py = ih,\n                        data, alpha, ratio;\n    \n    \n                    canvas.width = 1;\n                    canvas.height = ih;\n                    ctx.drawImage( img, 0, 0 );\n                    data = ctx.getImageData( 0, 0, 1, ih ).data;\n    \n                    // search image edge pixel position in case\n                    // it is squashed vertically.\n                    while ( py > sy ) {\n                        alpha = data[ (py - 1) * 4 + 3 ];\n    \n                        if ( alpha === 0 ) {\n                            ey = py;\n                        } else {\n                            sy = py;\n                        }\n    \n                        py = (ey + sy) >> 1;\n                    }\n    \n                    ratio = (py / ih);\n                    return (ratio === 0) ? 1 : ratio;\n                }\n    \n                // fix ie7 bug\n                // http://stackoverflow.com/questions/11929099/\n                // html5-canvas-drawimage-ratio-bug-ios\n                if ( Base.os.ios >= 7 ) {\n                    return function( canvas, img, x, y, w, h ) {\n                        var iw = img.naturalWidth,\n                            ih = img.naturalHeight,\n                            vertSquashRatio = detectVerticalSquash( img, iw, ih );\n    \n                        return canvas.getContext('2d').drawImage( img, 0, 0,\n                                iw * vertSquashRatio, ih * vertSquashRatio,\n                                x, y, w, h );\n                    };\n                }\n    \n                /**\n                 * Detect subsampling in loaded image.\n                 * In iOS, larger images than 2M pixels may be\n                 * subsampled in rendering.\n                 */\n                function detectSubsampling( img ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        canvas, ctx;\n    \n                    // subsampling may happen overmegapixel image\n                    if ( iw * ih > 1024 * 1024 ) {\n                        canvas = document.createElement('canvas');\n                        canvas.width = canvas.height = 1;\n                        ctx = canvas.getContext('2d');\n                        ctx.drawImage( img, -iw + 1, 0 );\n    \n                        // subsampled image becomes half smaller in rendering size.\n                        // check alpha channel value to confirm image is covering\n                        // edge pixel or not. if alpha value is 0\n                        // image is not covering, hence subsampled.\n                        return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                    } else {\n                        return false;\n                    }\n                }\n    \n    \n                return function( canvas, img, x, y, width, height ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        ctx = canvas.getContext('2d'),\n                        subsampled = detectSubsampling( img ),\n                        doSquash = this.type === 'image/jpeg',\n                        d = 1024,\n                        sy = 0,\n                        dy = 0,\n                        tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n    \n                    if ( subsampled ) {\n                        iw /= 2;\n                        ih /= 2;\n                    }\n    \n                    ctx.save();\n                    tmpCanvas = document.createElement('canvas');\n                    tmpCanvas.width = tmpCanvas.height = d;\n    \n                    tmpCtx = tmpCanvas.getContext('2d');\n                    vertSquashRatio = doSquash ?\n                            detectVerticalSquash( img, iw, ih ) : 1;\n    \n                    dw = Math.ceil( d * width / iw );\n                    dh = Math.ceil( d * height / ih / vertSquashRatio );\n    \n                    while ( sy < ih ) {\n                        sx = 0;\n                        dx = 0;\n                        while ( sx < iw ) {\n                            tmpCtx.clearRect( 0, 0, d, d );\n                            tmpCtx.drawImage( img, -sx, -sy );\n                            ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                    x + dx, y + dy, dw, dh );\n                            sx += d;\n                            dx += dw;\n                        }\n                        sy += d;\n                        dy += dh;\n                    }\n                    ctx.restore();\n                    tmpCanvas = tmpCtx = null;\n                };\n            })()\n        });\n    });\n    \n    /**\n     * @fileOverview Transport\n     * @todo 支持chunked传输，优势：\n     * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n     * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n     */\n    define('runtime/html5/transport',[\n        'base',\n        'runtime/html5/runtime'\n    ], function( Base, Html5Runtime ) {\n    \n        var noop = Base.noop,\n            $ = Base.$;\n    \n        return Html5Runtime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    formData, binary, fr;\n    \n                if ( opts.sendAsBinary ) {\n                    server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData )) : '';\n    \n                    binary = blob.getSource();\n                } else {\n                    formData = new FormData();\n                    $.each( owner._formData, function( k, v ) {\n                        formData.append( k, v );\n                    });\n    \n                    formData.append( opts.fileVal, blob.getSource(),\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                    xhr.open( opts.method, server, true );\n                    xhr.withCredentials = true;\n                } else {\n                    xhr.open( opts.method, server );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n    \n                if ( binary ) {\n                    // 强制设置成 content-type 为文件流。\n                    xhr.overrideMimeType &&\n                            xhr.overrideMimeType('application/octet-stream');\n    \n                    // android直接发送blob会导致服务端接收到的是空文件。\n                    // bug详情。\n                    // https://code.google.com/p/android/issues/detail?id=39882\n                    // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                    if ( Base.os.android ) {\n                        fr = new FileReader();\n    \n                        fr.onload = function() {\n                            xhr.send( this.result );\n                            fr = fr.onload = null;\n                        };\n    \n                        fr.readAsArrayBuffer( binary );\n                    } else {\n                        xhr.send( binary );\n                    }\n                } else {\n                    xhr.send( formData );\n                }\n            },\n    \n            getResponse: function() {\n                return this._response;\n            },\n    \n            getResponseAsJson: function() {\n                return this._parseJson( this._response );\n            },\n    \n            getResponseHeaders: function() {\n                return this._headers;\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    xhr.abort();\n    \n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _parseHeader: function(raw) {\n                var ret = {};\n    \n                raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                    ret[key.trim()] = value.trim();\n                });\n    \n                return ret;\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new XMLHttpRequest(),\n                    opts = this.options;\n    \n                if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                        typeof XDomainRequest !== 'undefined' ) {\n                    xhr = new XDomainRequest();\n                }\n    \n                xhr.upload.onprogress = function( e ) {\n                    var percentage = 0;\n    \n                    if ( e.lengthComputable ) {\n                        percentage = e.loaded / e.total;\n                    }\n    \n                    return me.trigger( 'progress', percentage );\n                };\n    \n                xhr.onreadystatechange = function() {\n    \n                    if ( xhr.readyState !== 4 ) {\n                        return;\n                    }\n    \n                    xhr.upload.onprogress = noop;\n                    xhr.onreadystatechange = noop;\n                    me._xhr = null;\n                    me._status = xhr.status;\n    \n                    var separator = '|', // 分隔符\n                         // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                        status = separator + xhr.status +\n                                 separator + xhr.statusText;\n    \n                    if ( xhr.status >= 200 && xhr.status < 300 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger('load');\n                    } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                        me._response = xhr.responseText;\n                        me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                        return me.trigger( 'error', 'server' + status );\n                    }\n    \n    \n                    return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n                };\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.setRequestHeader( key, val );\n                });\n            },\n    \n            _parseJson: function( str ) {\n                var json;\n    \n                try {\n                    json = JSON.parse( str );\n                } catch ( ex ) {\n                    json = {};\n                }\n    \n                return json;\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/html5/md5',[\n        'runtime/html5/runtime'\n    ], function( FlashRuntime ) {\n    \n        /*\n         * Fastest md5 implementation around (JKM md5)\n         * Credits: Joseph Myers\n         *\n         * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n         * @see http://jsperf.com/md5-shootout/7\n         */\n    \n        /* this function is much faster,\n          so if possible we use it. Some IEs\n          are the only ones I know of that\n          need the idiotic second function,\n          generated by an if clause.  */\n        var add32 = function (a, b) {\n            return (a + b) & 0xFFFFFFFF;\n        },\n    \n        cmn = function (q, a, b, x, s, t) {\n            a = add32(add32(a, q), add32(x, t));\n            return add32((a << s) | (a >>> (32 - s)), b);\n        },\n    \n        ff = function (a, b, c, d, x, s, t) {\n            return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n        },\n    \n        gg = function (a, b, c, d, x, s, t) {\n            return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n        },\n    \n        hh = function (a, b, c, d, x, s, t) {\n            return cmn(b ^ c ^ d, a, b, x, s, t);\n        },\n    \n        ii = function (a, b, c, d, x, s, t) {\n            return cmn(c ^ (b | (~d)), a, b, x, s, t);\n        },\n    \n        md5cycle = function (x, k) {\n            var a = x[0],\n                b = x[1],\n                c = x[2],\n                d = x[3];\n    \n            a = ff(a, b, c, d, k[0], 7, -680876936);\n            d = ff(d, a, b, c, k[1], 12, -389564586);\n            c = ff(c, d, a, b, k[2], 17, 606105819);\n            b = ff(b, c, d, a, k[3], 22, -1044525330);\n            a = ff(a, b, c, d, k[4], 7, -176418897);\n            d = ff(d, a, b, c, k[5], 12, 1200080426);\n            c = ff(c, d, a, b, k[6], 17, -1473231341);\n            b = ff(b, c, d, a, k[7], 22, -45705983);\n            a = ff(a, b, c, d, k[8], 7, 1770035416);\n            d = ff(d, a, b, c, k[9], 12, -1958414417);\n            c = ff(c, d, a, b, k[10], 17, -42063);\n            b = ff(b, c, d, a, k[11], 22, -1990404162);\n            a = ff(a, b, c, d, k[12], 7, 1804603682);\n            d = ff(d, a, b, c, k[13], 12, -40341101);\n            c = ff(c, d, a, b, k[14], 17, -1502002290);\n            b = ff(b, c, d, a, k[15], 22, 1236535329);\n    \n            a = gg(a, b, c, d, k[1], 5, -165796510);\n            d = gg(d, a, b, c, k[6], 9, -1069501632);\n            c = gg(c, d, a, b, k[11], 14, 643717713);\n            b = gg(b, c, d, a, k[0], 20, -373897302);\n            a = gg(a, b, c, d, k[5], 5, -701558691);\n            d = gg(d, a, b, c, k[10], 9, 38016083);\n            c = gg(c, d, a, b, k[15], 14, -660478335);\n            b = gg(b, c, d, a, k[4], 20, -405537848);\n            a = gg(a, b, c, d, k[9], 5, 568446438);\n            d = gg(d, a, b, c, k[14], 9, -1019803690);\n            c = gg(c, d, a, b, k[3], 14, -187363961);\n            b = gg(b, c, d, a, k[8], 20, 1163531501);\n            a = gg(a, b, c, d, k[13], 5, -1444681467);\n            d = gg(d, a, b, c, k[2], 9, -51403784);\n            c = gg(c, d, a, b, k[7], 14, 1735328473);\n            b = gg(b, c, d, a, k[12], 20, -1926607734);\n    \n            a = hh(a, b, c, d, k[5], 4, -378558);\n            d = hh(d, a, b, c, k[8], 11, -2022574463);\n            c = hh(c, d, a, b, k[11], 16, 1839030562);\n            b = hh(b, c, d, a, k[14], 23, -35309556);\n            a = hh(a, b, c, d, k[1], 4, -1530992060);\n            d = hh(d, a, b, c, k[4], 11, 1272893353);\n            c = hh(c, d, a, b, k[7], 16, -155497632);\n            b = hh(b, c, d, a, k[10], 23, -1094730640);\n            a = hh(a, b, c, d, k[13], 4, 681279174);\n            d = hh(d, a, b, c, k[0], 11, -358537222);\n            c = hh(c, d, a, b, k[3], 16, -722521979);\n            b = hh(b, c, d, a, k[6], 23, 76029189);\n            a = hh(a, b, c, d, k[9], 4, -640364487);\n            d = hh(d, a, b, c, k[12], 11, -421815835);\n            c = hh(c, d, a, b, k[15], 16, 530742520);\n            b = hh(b, c, d, a, k[2], 23, -995338651);\n    \n            a = ii(a, b, c, d, k[0], 6, -198630844);\n            d = ii(d, a, b, c, k[7], 10, 1126891415);\n            c = ii(c, d, a, b, k[14], 15, -1416354905);\n            b = ii(b, c, d, a, k[5], 21, -57434055);\n            a = ii(a, b, c, d, k[12], 6, 1700485571);\n            d = ii(d, a, b, c, k[3], 10, -1894986606);\n            c = ii(c, d, a, b, k[10], 15, -1051523);\n            b = ii(b, c, d, a, k[1], 21, -2054922799);\n            a = ii(a, b, c, d, k[8], 6, 1873313359);\n            d = ii(d, a, b, c, k[15], 10, -30611744);\n            c = ii(c, d, a, b, k[6], 15, -1560198380);\n            b = ii(b, c, d, a, k[13], 21, 1309151649);\n            a = ii(a, b, c, d, k[4], 6, -145523070);\n            d = ii(d, a, b, c, k[11], 10, -1120210379);\n            c = ii(c, d, a, b, k[2], 15, 718787259);\n            b = ii(b, c, d, a, k[9], 21, -343485551);\n    \n            x[0] = add32(a, x[0]);\n            x[1] = add32(b, x[1]);\n            x[2] = add32(c, x[2]);\n            x[3] = add32(d, x[3]);\n        },\n    \n        /* there needs to be support for Unicode here,\n           * unless we pretend that we can redefine the MD-5\n           * algorithm for multi-byte characters (perhaps\n           * by adding every four 16-bit characters and\n           * shortening the sum to 32 bits). Otherwise\n           * I suggest performing MD-5 as if every character\n           * was two bytes--e.g., 0040 0025 = @%--but then\n           * how will an ordinary MD-5 sum be matched?\n           * There is no way to standardize text to something\n           * like UTF-8 before transformation; speed cost is\n           * utterly prohibitive. The JavaScript standard\n           * itself needs to look at this: it should start\n           * providing access to strings as preformed UTF-8\n           * 8-bit unsigned value arrays.\n           */\n        md5blk = function (s) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n            }\n            return md5blks;\n        },\n    \n        md5blk_array = function (a) {\n            var md5blks = [],\n                i; /* Andy King said do it this way. */\n    \n            for (i = 0; i < 64; i += 4) {\n                md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n            }\n            return md5blks;\n        },\n    \n        md51 = function (s) {\n            var n = s.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk(s.substring(i - 64, i)));\n            }\n            s = s.substring(i - 64);\n            length = s.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n            }\n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n            return state;\n        },\n    \n        md51_array = function (a) {\n            var n = a.length,\n                state = [1732584193, -271733879, -1732584194, 271733878],\n                i,\n                length,\n                tail,\n                tmp,\n                lo,\n                hi;\n    \n            for (i = 64; i <= n; i += 64) {\n                md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n            }\n    \n            // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n            // containing the last element of the parent array if the sub array specified starts\n            // beyond the length of the parent array - weird.\n            // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n            a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n    \n            length = a.length;\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= a[i] << ((i % 4) << 3);\n            }\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Beware that the final length might not fit in 32 bits so we take care of that\n            tmp = n * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n    \n            md5cycle(state, tail);\n    \n            return state;\n        },\n    \n        hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],\n    \n        rhex = function (n) {\n            var s = '',\n                j;\n            for (j = 0; j < 4; j += 1) {\n                s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n            }\n            return s;\n        },\n    \n        hex = function (x) {\n            var i;\n            for (i = 0; i < x.length; i += 1) {\n                x[i] = rhex(x[i]);\n            }\n            return x.join('');\n        },\n    \n        md5 = function (s) {\n            return hex(md51(s));\n        },\n    \n    \n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * SparkMD5 OOP implementation.\n         *\n         * Use this class to perform an incremental md5, otherwise use the\n         * static methods instead.\n         */\n        SparkMD5 = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n    \n        // In some cases the fast add32 function cannot be used..\n        if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {\n            add32 = function (x, y) {\n                var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                    msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n                return (msw << 16) | (lsw & 0xFFFF);\n            };\n        }\n    \n    \n        /**\n         * Appends a string.\n         * A conversion will be applied if an utf8 string is detected.\n         *\n         * @param {String} str The string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.append = function (str) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            // then append as binary\n            this.appendBinary(str);\n    \n            return this;\n        };\n    \n        /**\n         * Appends a binary string.\n         *\n         * @param {String} contents The binary string to be appended\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.appendBinary = function (contents) {\n            this._buff += contents;\n            this._length += contents.length;\n    \n            var length = this._buff.length,\n                i;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));\n            }\n    \n            this._buff = this._buff.substr(i - 64);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                i,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        /**\n         * Finish the final calculation based on the tail.\n         *\n         * @param {Array}  tail   The tail (will be modified)\n         * @param {Number} length The length of the remaining buffer\n         */\n        SparkMD5.prototype._finish = function (tail, length) {\n            var i = length,\n                tmp,\n                lo,\n                hi;\n    \n            tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n            if (i > 55) {\n                md5cycle(this._state, tail);\n                for (i = 0; i < 16; i += 1) {\n                    tail[i] = 0;\n                }\n            }\n    \n            // Do the final computation based on the tail and length\n            // Beware that the final length may not fit in 32 bits so we take care of that\n            tmp = this._length * 8;\n            tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n            lo = parseInt(tmp[2], 16);\n            hi = parseInt(tmp[1], 16) || 0;\n    \n            tail[14] = lo;\n            tail[15] = hi;\n            md5cycle(this._state, tail);\n        };\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5} The instance itself\n         */\n        SparkMD5.prototype.reset = function () {\n            this._buff = \"\";\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.prototype.destroy = function () {\n            delete this._state;\n            delete this._buff;\n            delete this._length;\n        };\n    \n    \n        /**\n         * Performs the md5 hash on a string.\n         * A conversion will be applied if utf8 string is detected.\n         *\n         * @param {String}  str The string\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hash = function (str, raw) {\n            // converts the string to utf8 bytes if necessary\n            if (/[\\u0080-\\uFFFF]/.test(str)) {\n                str = unescape(encodeURIComponent(str));\n            }\n    \n            var hash = md51(str);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * Performs the md5 hash on a binary string.\n         *\n         * @param {String}  content The binary string\n         * @param {Boolean} raw     True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.hashBinary = function (content, raw) {\n            var hash = md51(content);\n    \n            return !!raw ? hash : hex(hash);\n        };\n    \n        /**\n         * SparkMD5 OOP implementation for array buffers.\n         *\n         * Use this class to perform an incremental md5 ONLY for array buffers.\n         */\n        SparkMD5.ArrayBuffer = function () {\n            // call reset to init the instance\n            this.reset();\n        };\n    \n        ////////////////////////////////////////////////////////////////////////////\n    \n        /**\n         * Appends an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array to be appended\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n            // TODO: we could avoid the concatenation here but the algorithm would be more complex\n            //       if you find yourself needing extra performance, please make a PR.\n            var buff = this._concatArrayBuffer(this._buff, arr),\n                length = buff.length,\n                i;\n    \n            this._length += arr.byteLength;\n    \n            for (i = 64; i <= length; i += 64) {\n                md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));\n            }\n    \n            // Avoids IE10 weirdness (documented above)\n            this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);\n    \n            return this;\n        };\n    \n        /**\n         * Finishes the incremental computation, reseting the internal state and\n         * returning the result.\n         * Use the raw parameter to obtain the raw result instead of the hex one.\n         *\n         * @param {Boolean} raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n            var buff = this._buff,\n                length = buff.length,\n                tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n                i,\n                ret;\n    \n            for (i = 0; i < length; i += 1) {\n                tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n            }\n    \n            this._finish(tail, length);\n            ret = !!raw ? this._state : hex(this._state);\n    \n            this.reset();\n    \n            return ret;\n        };\n    \n        SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n    \n        /**\n         * Resets the internal state of the computation.\n         *\n         * @return {SparkMD5.ArrayBuffer} The instance itself\n         */\n        SparkMD5.ArrayBuffer.prototype.reset = function () {\n            this._buff = new Uint8Array(0);\n            this._length = 0;\n            this._state = [1732584193, -271733879, -1732584194, 271733878];\n    \n            return this;\n        };\n    \n        /**\n         * Releases memory used by the incremental buffer and other aditional\n         * resources. If you plan to use the instance again, use reset instead.\n         */\n        SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n    \n        /**\n         * Concats two array buffers, returning a new one.\n         *\n         * @param  {ArrayBuffer} first  The first array buffer\n         * @param  {ArrayBuffer} second The second array buffer\n         *\n         * @return {ArrayBuffer} The new array buffer\n         */\n        SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {\n            var firstLength = first.length,\n                result = new Uint8Array(firstLength + second.byteLength);\n    \n            result.set(first);\n            result.set(new Uint8Array(second), firstLength);\n    \n            return result;\n        };\n    \n        /**\n         * Performs the md5 hash on an array buffer.\n         *\n         * @param {ArrayBuffer} arr The array buffer\n         * @param {Boolean}     raw True to get the raw result, false to get the hex result\n         *\n         * @return {String|Array} The result\n         */\n        SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n            var hash = md51_array(new Uint8Array(arr));\n    \n            return !!raw ? hash : hex(hash);\n        };\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( file ) {\n                var blob = file.getSource(),\n                    chunkSize = 2 * 1024 * 1024,\n                    chunks = Math.ceil( blob.size / chunkSize ),\n                    chunk = 0,\n                    owner = this.owner,\n                    spark = new SparkMD5.ArrayBuffer(),\n                    me = this,\n                    blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice,\n                    loadNext, fr;\n    \n                fr = new FileReader();\n    \n                loadNext = function() {\n                    var start, end;\n    \n                    start = chunk * chunkSize;\n                    end = Math.min( start + chunkSize, blob.size );\n    \n                    fr.onload = function( e ) {\n                        spark.append( e.target.result );\n                        owner.trigger( 'progress', {\n                            total: file.size,\n                            loaded: end\n                        });\n                    };\n    \n                    fr.onloadend = function() {\n                        fr.onloadend = fr.onload = null;\n    \n                        if ( ++chunk < chunks ) {\n                            setTimeout( loadNext, 1 );\n                        } else {\n                            setTimeout(function(){\n                                owner.trigger('load');\n                                me.result = spark.end();\n                                loadNext = file = blob = spark = null;\n                                owner.trigger('complete');\n                            }, 50 );\n                        }\n                    };\n    \n                    fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) );\n                };\n    \n                loadNext();\n            },\n    \n            getResult: function() {\n                return this.result;\n            }\n        });\n    });\n    /**\n     * @fileOverview FlashRuntime\n     */\n    define('runtime/flash/runtime',[\n        'base',\n        'runtime/runtime',\n        'runtime/compbase'\n    ], function( Base, Runtime, CompBase ) {\n    \n        var $ = Base.$,\n            type = 'flash',\n            components = {};\n    \n    \n        function getFlashVersion() {\n            var version;\n    \n            try {\n                version = navigator.plugins[ 'Shockwave Flash' ];\n                version = version.description;\n            } catch ( ex ) {\n                try {\n                    version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                            .GetVariable('$version');\n                } catch ( ex2 ) {\n                    version = '0.0';\n                }\n            }\n            version = version.match( /\\d+/g );\n            return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n        }\n    \n        function FlashRuntime() {\n            var pool = {},\n                clients = {},\n                destroy = this.destroy,\n                me = this,\n                jsreciver = Base.guid('webuploader_');\n    \n            Runtime.apply( me, arguments );\n            me.type = type;\n    \n    \n            // 这个方法的调用者，实际上是RuntimeClient\n            me.exec = function( comp, fn/*, args...*/ ) {\n                var client = this,\n                    uid = client.uid,\n                    args = Base.slice( arguments, 2 ),\n                    instance;\n    \n                clients[ uid ] = client;\n    \n                if ( components[ comp ] ) {\n                    if ( !pool[ uid ] ) {\n                        pool[ uid ] = new components[ comp ]( client, me );\n                    }\n    \n                    instance = pool[ uid ];\n    \n                    if ( instance[ fn ] ) {\n                        return instance[ fn ].apply( instance, args );\n                    }\n                }\n    \n                return me.flashExec.apply( client, arguments );\n            };\n    \n            function handler( evt, obj ) {\n                var type = evt.type || evt,\n                    parts, uid;\n    \n                parts = type.split('::');\n                uid = parts[ 0 ];\n                type = parts[ 1 ];\n    \n                // console.log.apply( console, arguments );\n    \n                if ( type === 'Ready' && uid === me.uid ) {\n                    me.trigger('ready');\n                } else if ( clients[ uid ] ) {\n                    clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n                }\n    \n                // Base.log( evt, obj );\n            }\n    \n            // flash的接受器。\n            window[ jsreciver ] = function() {\n                var args = arguments;\n    \n                // 为了能捕获得到。\n                setTimeout(function() {\n                    handler.apply( null, args );\n                }, 1 );\n            };\n    \n            this.jsreciver = jsreciver;\n    \n            this.destroy = function() {\n                // @todo 删除池子中的所有实例\n                return destroy && destroy.apply( this, arguments );\n            };\n    \n            this.flashExec = function( comp, fn ) {\n                var flash = me.getFlash(),\n                    args = Base.slice( arguments, 2 );\n    \n                return flash.exec( this.uid, comp, fn, args );\n            };\n    \n            // @todo\n        }\n    \n        Base.inherits( Runtime, {\n            constructor: FlashRuntime,\n    \n            init: function() {\n                var container = this.getContainer(),\n                    opts = this.options,\n                    html;\n    \n                // if not the minimal height, shims are not initialized\n                // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n                container.css({\n                    position: 'absolute',\n                    top: '-8px',\n                    left: '-8px',\n                    width: '9px',\n                    height: '9px',\n                    overflow: 'hidden'\n                });\n    \n                // insert flash object\n                html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                        'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n    \n                if ( Base.browser.ie ) {\n                    html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n                }\n    \n                html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                    '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                    '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                    '&jsreciver=' + this.jsreciver + '\" />' +\n                    '<param name=\"wmode\" value=\"transparent\" />' +\n                    '<param name=\"allowscriptaccess\" value=\"always\" />' +\n                '</object>';\n    \n                container.html( html );\n            },\n    \n            getFlash: function() {\n                if ( this._flash ) {\n                    return this._flash;\n                }\n    \n                this._flash = $( '#' + this.uid ).get( 0 );\n                return this._flash;\n            }\n    \n        });\n    \n        FlashRuntime.register = function( name, component ) {\n            component = components[ name ] = Base.inherits( CompBase, $.extend({\n    \n                // @todo fix this later\n                flashExec: function() {\n                    var owner = this.owner,\n                        runtime = this.getRuntime();\n    \n                    return runtime.flashExec.apply( owner, arguments );\n                }\n            }, component ) );\n    \n            return component;\n        };\n    \n        if ( getFlashVersion() >= 11.4 ) {\n            Runtime.addRuntime( type, FlashRuntime );\n        }\n    \n        return FlashRuntime;\n    });\n    /**\n     * @fileOverview FilePicker\n     */\n    define('runtime/flash/filepicker',[\n        'base',\n        'runtime/flash/runtime'\n    ], function( Base, FlashRuntime ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'FilePicker', {\n            init: function( opts ) {\n                var copy = $.extend({}, opts ),\n                    len, i;\n    \n                // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n                len = copy.accept && copy.accept.length;\n                for (  i = 0; i < len; i++ ) {\n                    if ( !copy.accept[ i ].title ) {\n                        copy.accept[ i ].title = 'Files';\n                    }\n                }\n    \n                delete copy.button;\n                delete copy.id;\n                delete copy.container;\n    \n                this.flashExec( 'FilePicker', 'init', copy );\n            },\n    \n            destroy: function() {\n                this.flashExec( 'FilePicker', 'destroy' );\n            }\n        });\n    });\n    /**\n     * @fileOverview 图片压缩\n     */\n    define('runtime/flash/image',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n    \n        return FlashRuntime.register( 'Image', {\n            // init: function( options ) {\n            //     var owner = this.owner;\n    \n            //     this.flashExec( 'Image', 'init', options );\n            //     owner.on( 'load', function() {\n            //         debugger;\n            //     });\n            // },\n    \n            loadFromBlob: function( blob ) {\n                var owner = this.owner;\n    \n                owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n                owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n    \n                this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Transport flash实现\n     */\n    define('runtime/flash/transport',[\n        'base',\n        'runtime/flash/runtime',\n        'runtime/client'\n    ], function( Base, FlashRuntime, RuntimeClient ) {\n        var $ = Base.$;\n    \n        return FlashRuntime.register( 'Transport', {\n            init: function() {\n                this._status = 0;\n                this._response = null;\n                this._responseJson = null;\n            },\n    \n            send: function() {\n                var owner = this.owner,\n                    opts = this.options,\n                    xhr = this._initAjax(),\n                    blob = owner._blob,\n                    server = opts.server,\n                    binary;\n    \n                xhr.connectRuntime( blob.ruid );\n    \n                if ( opts.sendAsBinary ) {\n                    server += (/\\?/.test( server ) ? '&' : '?') +\n                            $.param( owner._formData );\n    \n                    binary = blob.uid;\n                } else {\n                    $.each( owner._formData, function( k, v ) {\n                        xhr.exec( 'append', k, v );\n                    });\n    \n                    xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                            opts.filename || owner._formData.name || '' );\n                }\n    \n                this._setRequestHeader( xhr, opts.headers );\n                xhr.exec( 'send', {\n                    method: opts.method,\n                    url: server,\n                    forceURLStream: opts.forceURLStream,\n                    mimeType: 'application/octet-stream'\n                }, binary );\n            },\n    \n            getStatus: function() {\n                return this._status;\n            },\n    \n            getResponse: function() {\n                return this._response || '';\n            },\n    \n            getResponseAsJson: function() {\n                return this._responseJson;\n            },\n    \n            getResponseHeaders: function() {\n                // flash 暂不支持\n                return {};\n            },\n    \n            abort: function() {\n                var xhr = this._xhr;\n    \n                if ( xhr ) {\n                    xhr.exec('abort');\n                    xhr.destroy();\n                    this._xhr = xhr = null;\n                }\n            },\n    \n            destroy: function() {\n                this.abort();\n            },\n    \n            _initAjax: function() {\n                var me = this,\n                    xhr = new RuntimeClient('XMLHttpRequest');\n    \n                xhr.on( 'uploadprogress progress', function( e ) {\n                    var percent = e.loaded / e.total;\n                    percent = Math.min( 1, Math.max( 0, percent ) );\n                    return me.trigger( 'progress', percent );\n                });\n    \n                xhr.on( 'load', function() {\n                    var status = xhr.exec('getStatus'),\n                        readBody = false,\n                        err = '',\n                        p;\n    \n                    xhr.off();\n                    me._xhr = null;\n    \n                    if ( status >= 200 && status < 300 ) {\n                        readBody = true;\n                    } else if ( status >= 500 && status < 600 ) {\n                        readBody = true;\n                        err = 'server-'+status;\n                    } else {\n                        err = 'http-'+status;\n                    }\n    \n                    if ( readBody ) {\n                        me._response = xhr.exec('getResponse');\n                        me._response = decodeURIComponent( me._response );\n    \n                        // flash 处理可能存在 bug, 没辙只能靠 js 了\n                        // try {\n                        //     me._responseJson = xhr.exec('getResponseAsJson');\n                        // } catch ( error ) {\n    \n                        p = function( s ) {\n                            try {\n                                if (window.JSON && window.JSON.parse) {\n                                    return JSON.parse(s);\n                                }\n    \n                                return new Function('return ' + s).call();\n                            } catch ( err ) {\n                                return {};\n                            }\n                        };\n                        me._responseJson  = me._response ? p(me._response) : {};\n    \n                        // }\n                    }\n    \n                    xhr.destroy();\n                    xhr = null;\n    \n                    return err ? me.trigger( 'error', err ) : me.trigger('load');\n                });\n    \n                xhr.on( 'error', function() {\n                    var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                    xhr.off();\n                    me._xhr = null;\n                    me.trigger( 'error', err );\n                });\n    \n                me._xhr = xhr;\n                return xhr;\n            },\n    \n            _setRequestHeader: function( xhr, headers ) {\n                $.each( headers, function( key, val ) {\n                    xhr.exec( 'setRequestHeader', key, val );\n                });\n            }\n        });\n    });\n    \n    /**\n     * @fileOverview Blob Html实现\n     */\n    define('runtime/flash/blob',[\n        'runtime/flash/runtime',\n        'lib/blob'\n    ], function( FlashRuntime, Blob ) {\n    \n        return FlashRuntime.register( 'Blob', {\n            slice: function( start, end ) {\n                var blob = this.flashExec( 'Blob', 'slice', start, end );\n    \n                return new Blob( this.getRuid(), blob );\n            }\n        });\n    });\n    /**\n     * @fileOverview  Md5 flash实现\n     */\n    define('runtime/flash/md5',[\n        'runtime/flash/runtime'\n    ], function( FlashRuntime ) {\n        \n        return FlashRuntime.register( 'Md5', {\n            init: function() {\n                // do nothing.\n            },\n    \n            loadFromBlob: function( blob ) {\n                return this.flashExec( 'Md5', 'loadFromBlob', blob.uid );\n            }\n        });\n    });\n    /**\n     * @fileOverview 完全版本。\n     */\n    define('preset/all',[\n        'base',\n    \n        // widgets\n        'widgets/filednd',\n        'widgets/filepaste',\n        'widgets/filepicker',\n        'widgets/image',\n        'widgets/queue',\n        'widgets/runtime',\n        'widgets/upload',\n        'widgets/validator',\n        'widgets/md5',\n    \n        // runtimes\n        // html5\n        'runtime/html5/blob',\n        'runtime/html5/dnd',\n        'runtime/html5/filepaste',\n        'runtime/html5/filepicker',\n        'runtime/html5/imagemeta/exif',\n        'runtime/html5/androidpatch',\n        'runtime/html5/image',\n        'runtime/html5/transport',\n        'runtime/html5/md5',\n    \n        // flash\n        'runtime/flash/filepicker',\n        'runtime/flash/image',\n        'runtime/flash/transport',\n        'runtime/flash/blob',\n        'runtime/flash/md5'\n    ], function( Base ) {\n        return Base;\n    });\n    /**\n     * @fileOverview 日志组件，主要用来收集错误信息，可以帮助 webuploader 更好的定位问题和发展。\n     *\n     * 如果您不想要启用此功能，请在打包的时候去掉 log 模块。\n     *\n     * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。\n     *\n     * 如：\n     * WebUploader.create({\n     *     ...\n     *\n     *     disableWidgets: 'log',\n     *\n     *     ...\n     * })\n     */\n    define('widgets/log',[\n        'base',\n        'uploader',\n        'widgets/widget'\n    ], function( Base, Uploader ) {\n        var $ = Base.$,\n            logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??',\n            product = (location.hostname || location.host || 'protected').toLowerCase(),\n    \n            // 只针对 baidu 内部产品用户做统计功能。\n            enable = product && /baidu/i.exec(product),\n            base;\n    \n        if (!enable) {\n            return;\n        }\n    \n        base = {\n            dv: 3,\n            master: 'webuploader',\n            online: /test/.exec(product) ? 0 : 1,\n            module: '',\n            product: product,\n            type: 0\n        };\n    \n        function send(data) {\n            var obj = $.extend({}, base, data),\n                url = logUrl.replace(/^(.*)\\?/, '$1' + $.param( obj )),\n                image = new Image();\n    \n            image.src = url;\n        }\n    \n        return Uploader.register({\n            name: 'log',\n    \n            init: function() {\n                var owner = this.owner,\n                    count = 0,\n                    size = 0;\n    \n                owner\n                    .on('error', function(code) {\n                        send({\n                            type: 2,\n                            c_error_code: code\n                        });\n                    })\n                    .on('uploadError', function(file, reason) {\n                        send({\n                            type: 2,\n                            c_error_code: 'UPLOAD_ERROR',\n                            c_reason: '' + reason\n                        });\n                    })\n                    .on('uploadComplete', function(file) {\n                        count++;\n                        size += file.size;\n                    }).\n                    on('uploadFinished', function() {\n                        send({\n                            c_count: count,\n                            c_size: size\n                        });\n                        count = size = 0;\n                    });\n    \n                send({\n                    c_usage: 1\n                });\n            }\n        });\n    });\n    /**\n     * @fileOverview Uploader上传类\n     */\n    define('webuploader',[\n        'preset/all',\n        'widgets/log'\n    ], function( preset ) {\n        return preset;\n    });\n    return require('webuploader');\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"WebUploader\",\n  \"version\": \"0.1.8-alpha\",\n  \"description\": \"WebUploader是一个简单的以Html5为主，Flash为辅的现代文件上传组件。在现代的浏览器里面能充分发挥html5的优势，同时又不摒弃主流IE浏览器，延用原来的Flash运行时。两套运行时，同样的调用方式，可供用户任意选用。WebUploader采用大文件分片并发上传，极大的提高了文件上传效率。\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/fex-team/webuploader.git\"\n  },\n  \"keywords\": [\n    \"chunk\",\n    \"uploader\",\n    \"html5\",\n    \"flash\"\n  ],\n  \"author\": \"fex-team\",\n  \"license\": \"BSD\",\n  \"gitHead\": \"7f92712020f66940ead10f73c16a03305b03259f\",\n  \"readmeFilename\": \"README.md\",\n  \"devDependencies\": {\n    \"grunt\": \"0.4.1\",\n    \"grunt-size\": \"0.1.0\",\n    \"grunt-jsbint\": \"0.0.4\",\n    \"grunt-contrib-uglify\": \"0.2.1\",\n    \"grunt-contrib-watch\": \"0.4.4\",\n    \"gmudoc\": \"0.0.3\",\n    \"grunt-jekyll\": \"0.4.1\",\n    \"grunt-gh-pages\": \"0.9.0\",\n    \"grunt-contrib-copy\": \"0.5.0\",\n    \"requirejs\": \"2.1.9\",\n    \"grunt-contrib-qunit\": \"0.4.0\",\n    \"grunt-contrib-connect\": \"0.7.1\",\n    \"load-grunt-tasks\": \"0.4.0\"\n  }\n}\n"
  },
  {
    "path": "server/crossdomain.xml",
    "content": "<?xml version=\"1.0\" ?>\n<cross-domain-policy>\n<allow-access-from domain=\"*\" />\n</cross-domain-policy>"
  },
  {
    "path": "server/fileupload.php",
    "content": "<?php\n/**\n * upload.php\n *\n * Copyright 2013, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\n#!! 注意\n#!! 此文件只是个示例，不要用于真正的产品之中。\n#!! 不保证代码安全性。\n\n#!! IMPORTANT:\n#!! this file is just an example, it doesn't incorporate any security checks and\n#!! is not recommended to be used in production environment as it is. Be sure to\n#!! revise it and customize to your needs.\n\n\n// Make sure file is not cached (as it happens for example on iOS devices)\nheader(\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\");\nheader(\"Last-Modified: \" . gmdate(\"D, d M Y H:i:s\") . \" GMT\");\nheader(\"Cache-Control: no-store, no-cache, must-revalidate\");\nheader(\"Cache-Control: post-check=0, pre-check=0\", false);\nheader(\"Pragma: no-cache\");\n\n\n// Support CORS\n// header(\"Access-Control-Allow-Origin: *\");\n// other CORS headers if any...\nif ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {\n    exit; // finish preflight CORS requests here\n}\n\n\nif ( !empty($_REQUEST[ 'debug' ]) ) {\n    $random = rand(0, intval($_REQUEST[ 'debug' ]) );\n    if ( $random === 0 ) {\n        header(\"HTTP/1.0 500 Internal Server Error\");\n        exit;\n    }\n}\n\n// header(\"HTTP/1.0 500 Internal Server Error\");\n// exit;\n\n\n// 5 minutes execution time\n@set_time_limit(5 * 60);\n\n// Uncomment this one to fake upload time\n// usleep(5000);\n\n// Settings\n// $targetDir = ini_get(\"upload_tmp_dir\") . DIRECTORY_SEPARATOR . \"plupload\";\n$targetDir = 'upload_tmp';\n$uploadDir = 'upload';\n\n$cleanupTargetDir = true; // Remove old files\n$maxFileAge = 5 * 3600; // Temp file age in seconds\n\n\n// Create target dir\nif (!file_exists($targetDir)) {\n    @mkdir($targetDir);\n}\n\n// Create target dir\nif (!file_exists($uploadDir)) {\n    @mkdir($uploadDir);\n}\n\n// Get a file name\nif (isset($_REQUEST[\"name\"])) {\n    $fileName = $_REQUEST[\"name\"];\n} elseif (!empty($_FILES)) {\n    $fileName = $_FILES[\"file\"][\"name\"];\n} else {\n    $fileName = uniqid(\"file_\");\n}\n\n$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;\n$uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;\n\n// Chunking might be enabled\n$chunk = isset($_REQUEST[\"chunk\"]) ? intval($_REQUEST[\"chunk\"]) : 0;\n$chunks = isset($_REQUEST[\"chunks\"]) ? intval($_REQUEST[\"chunks\"]) : 1;\n\n\n// Remove old temp files\nif ($cleanupTargetDir) {\n    if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 100, \"message\": \"Failed to open temp directory.\"}, \"id\" : \"id\"}');\n    }\n\n    while (($file = readdir($dir)) !== false) {\n        $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;\n\n        // If temp file is current file proceed to the next\n        if ($tmpfilePath == \"{$filePath}_{$chunk}.part\" || $tmpfilePath == \"{$filePath}_{$chunk}.parttmp\") {\n            continue;\n        }\n\n        // Remove temp file if it is older than the max age and is not the current file\n        if (preg_match('/\\.(part|parttmp)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {\n            @unlink($tmpfilePath);\n        }\n    }\n    closedir($dir);\n}\n\n\n// Open temp file\nif (!$out = @fopen(\"{$filePath}_{$chunk}.parttmp\", \"wb\")) {\n    die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 102, \"message\": \"Failed to open output stream.\"}, \"id\" : \"id\"}');\n}\n\nif (!empty($_FILES)) {\n    if ($_FILES[\"file\"][\"error\"] || !is_uploaded_file($_FILES[\"file\"][\"tmp_name\"])) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 103, \"message\": \"Failed to move uploaded file.\"}, \"id\" : \"id\"}');\n    }\n\n    // Read binary input stream and append it to temp file\n    if (!$in = @fopen($_FILES[\"file\"][\"tmp_name\"], \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n} else {\n    if (!$in = @fopen(\"php://input\", \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n}\n\nwhile ($buff = fread($in, 4096)) {\n    fwrite($out, $buff);\n}\n\n@fclose($out);\n@fclose($in);\n\nrename(\"{$filePath}_{$chunk}.parttmp\", \"{$filePath}_{$chunk}.part\");\n\n$index = 0;\n$done = true;\nfor( $index = 0; $index < $chunks; $index++ ) {\n    if ( !file_exists(\"{$filePath}_{$index}.part\") ) {\n        $done = false;\n        break;\n    }\n}\nif ( $done ) {\n    if (!$out = @fopen($uploadPath, \"wb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 102, \"message\": \"Failed to open output stream.\"}, \"id\" : \"id\"}');\n    }\n\n    if ( flock($out, LOCK_EX) ) {\n        for( $index = 0; $index < $chunks; $index++ ) {\n            if (!$in = @fopen(\"{$filePath}_{$index}.part\", \"rb\")) {\n                break;\n            }\n\n            while ($buff = fread($in, 4096)) {\n                fwrite($out, $buff);\n            }\n\n            @fclose($in);\n            @unlink(\"{$filePath}_{$index}.part\");\n        }\n\n        flock($out, LOCK_UN);\n    }\n    @fclose($out);\n}\n\n// Return Success JSON-RPC response\ndie('{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\"}');"
  },
  {
    "path": "server/fileupload2.php",
    "content": "<?php\n/**\n * upload.php\n *\n * Copyright 2013, Moxiecode Systems AB\n * Released under GPL License.\n *\n * License: http://www.plupload.com/license\n * Contributing: http://www.plupload.com/contributing\n */\n\n#!! IMPORTANT:\n#!! this file is just an example, it doesn't incorporate any security checks and\n#!! is not recommended to be used in production environment as it is. Be sure to\n#!! revise it and customize to your needs.\n\n\n// Make sure file is not cached (as it happens for example on iOS devices)\nheader(\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\");\nheader(\"Last-Modified: \" . gmdate(\"D, d M Y H:i:s\") . \" GMT\");\nheader(\"Cache-Control: no-store, no-cache, must-revalidate\");\nheader(\"Cache-Control: post-check=0, pre-check=0\", false);\nheader(\"Pragma: no-cache\");\n\n// header(\"HTTP/1.0 500 Internal Server Error\");\n\n// echo mymd5('upload/C程序设计语言.pdf'); die;\n// Support CORS\n// header(\"Access-Control-Allow-Origin: *\");\n// other CORS headers if any...\nif ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {\n    exit; // finish preflight CORS requests here\n}\n\n\nif ( !empty($_REQUEST[ 'debug' ]) ) {\n    $random = rand(0, intval($_REQUEST[ 'debug' ]) );\n    if ( $random === 0 ) {\n        header(\"HTTP/1.0 500 Internal Server Error\");\n        exit;\n    }\n}\n\n\n// 5 minutes execution time\n@set_time_limit(5 * 60);\n\n// Uncomment this one to fake upload time\n// usleep(5000);\n\n// Settings\n// $targetDir = ini_get(\"upload_tmp_dir\") . DIRECTORY_SEPARATOR . \"plupload\";\n$targetDir = 'upload_tmp';\n$uploadDir = 'upload';\n\n$cleanupTargetDir = true; // Remove old files\n$maxFileAge = 5 * 3600; // Temp file age in seconds\n\n\n// Create target dir\nif (!file_exists($targetDir)) {\n    @mkdir($targetDir);\n}\n\n// Create target dir\nif (!file_exists($uploadDir)) {\n    @mkdir($uploadDir);\n}\n\n// Get a file name\nif (isset($_REQUEST[\"name\"])) {\n    $fileName = $_REQUEST[\"name\"];\n} elseif (!empty($_FILES)) {\n    $fileName = $_FILES[\"file\"][\"name\"];\n} else {\n    $fileName = uniqid(\"file_\");\n}\n\n$md5File = @file('md5list2.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);\n$md5File = $md5File ? $md5File : array();\n\nif (isset($_REQUEST[\"md5\"]) && array_search($_REQUEST[\"md5\"], $md5File ) !== FALSE ) {\n    die('{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\", \"exist\": 1}');\n}\n\n$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;\n$uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;\n\n// Chunking might be enabled\n$chunk = isset($_REQUEST[\"chunk\"]) ? intval($_REQUEST[\"chunk\"]) : 0;\n$chunks = isset($_REQUEST[\"chunks\"]) ? intval($_REQUEST[\"chunks\"]) : 0;\n\n\n// Remove old temp files\nif ($cleanupTargetDir) {\n    if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 100, \"message\": \"Failed to open temp directory.\"}, \"id\" : \"id\"}');\n    }\n\n    while (($file = readdir($dir)) !== false) {\n        $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;\n\n        // If temp file is current file proceed to the next\n        if ($tmpfilePath == \"{$filePath}.part\") {\n            continue;\n        }\n\n        // Remove temp file if it is older than the max age and is not the current file\n        if (preg_match('/\\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {\n            @unlink($tmpfilePath);\n        }\n    }\n    closedir($dir);\n}\n\n\n// Open temp file\nif (!$out = @fopen(\"{$filePath}.part\", $chunks ? \"ab\" : \"wb\")) {\n    die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 102, \"message\": \"Failed to open output stream.\"}, \"id\" : \"id\"}');\n}\n\nif (!empty($_FILES)) {\n    if ($_FILES[\"file\"][\"error\"] || !is_uploaded_file($_FILES[\"file\"][\"tmp_name\"])) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 103, \"message\": \"Failed to move uploaded file.\"}, \"id\" : \"id\"}');\n    }\n\n    // Read binary input stream and append it to temp file\n    if (!$in = @fopen($_FILES[\"file\"][\"tmp_name\"], \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n} else {\n    if (!$in = @fopen(\"php://input\", \"rb\")) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 101, \"message\": \"Failed to open input stream.\"}, \"id\" : \"id\"}');\n    }\n}\n\nwhile ($buff = fread($in, 4096)) {\n    fwrite($out, $buff);\n}\n\n@fclose($out);\n@fclose($in);\n\n// Check if file has been uploaded\nif (!$chunks || $chunk == $chunks - 1) {\n    // Strip the temp .part suffix off\n    rename(\"{$filePath}.part\", $filePath);\n\n    rename($filePath, $uploadPath);\n    array_push($md5File, mymd5($uploadPath));\n    $md5File = array_unique($md5File);\n    file_put_contents('md5list2.txt', join($md5File, \"\\n\"));\n}\n\nfunction mymd5( $file ) {\n    $fragment = 65536;\n\n    $rh = fopen($file, 'rb');\n    $size = filesize($file);\n\n    $part1 = fread( $rh, $fragment );\n    fseek($rh, $size-$fragment);\n    $part2 = fread( $rh, $fragment);\n    fclose($rh);\n\n    return md5( $part1.$part2 );\n}\n\n\n\n// Return Success JSON-RPC response\ndie('{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\"}');"
  },
  {
    "path": "server/preview.php",
    "content": "<?php\n/**\n * 此页面用来协助 IE6/7 预览图片，因为 IE 6/7 不支持 base64\n */\n\n#!! 注意\n#!! 此文件只是个示例，不要用于真正的产品之中。\n#!! 不保证代码安全性。\n#!! IMPORTANT:\n#!! this file is just an example, it doesn't incorporate any security checks and\n#!! is not recommended to be used in production environment as it is. Be sure to\n#!! revise it and customize to your needs.\n\n$DIR = 'preview';\n// Create target dir\nif (!file_exists($DIR)) {\n    @mkdir($DIR);\n}\n\n$cleanupTargetDir = true; // Remove old files\n$maxFileAge = 5 * 3600; // Temp file age in seconds\n\nif ($cleanupTargetDir) {\n    if (!is_dir($DIR) || !$dir = opendir($DIR)) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 100, \"message\": \"Failed to open temp directory.\"}, \"id\" : \"id\"}');\n    }\n\n    while (($file = readdir($dir)) !== false) {\n        $tmpfilePath = $DIR . DIRECTORY_SEPARATOR . $file;\n\n        // Remove temp file if it is older than the max age and is not the current file\n        if (@filemtime($tmpfilePath) < time() - $maxFileAge) {\n            @unlink($tmpfilePath);\n        }\n    }\n    closedir($dir);\n}\n\n$src = file_get_contents('php://input');\n\nif (preg_match(\"#^data:image/(\\w+);base64,(.*)$#\", $src, $matches)) {\n\n    $previewUrl = sprintf(\n        \"%s://%s%s\",\n        isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http',\n        $_SERVER['HTTP_HOST'],\n        $_SERVER['REQUEST_URI']\n    );\n    $previewUrl = str_replace(\"preview.php\", \"\", $previewUrl);\n\n\n    $base64 = $matches[2];\n    $type = $matches[1];\n    if ($type === 'jpeg') {\n        $type = 'jpg';\n    }\n\n    if (!in_array($type, array(\"jpg\", \"png\", \"gif\", \"bmp\"))) {\n        die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 200, \"message\": \"un recoginized image source\"}, \"id\" : \"id\"}');\n    }\n\n    $filename = md5($base64).\".$type\";\n    $filePath = $DIR.DIRECTORY_SEPARATOR.$filename;\n\n    if (file_exists($filePath)) {\n        die('{\"jsonrpc\" : \"2.0\", \"result\" : \"'.$previewUrl.'preview/'.$filename.'\", \"id\" : \"id\"}');\n    } else {\n        $data = base64_decode($base64);\n        file_put_contents($filePath, $data);\n        die('{\"jsonrpc\" : \"2.0\", \"result\" : \"'.$previewUrl.'preview/'.$filename.'\", \"id\" : \"id\"}');\n    }\n\n} else {\n    die('{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": 100, \"message\": \"un recoginized source\"}}');\n}\n"
  },
  {
    "path": "src/base.js",
    "content": "/**\n * @fileOverview 基础类方法。\n */\n\n/**\n * Web Uploader内部类的详细说明，以下提及的功能类，都可以在`WebUploader`这个变量中访问到。\n *\n * As you know, Web Uploader的每个文件都是用过[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)规范中的`define`组织起来的, 每个Module都会有个module id.\n * 默认module id为该文件的路径，而此路径将会转化成名字空间存放在WebUploader中。如：\n *\n * * module `base`：WebUploader.Base\n * * module `file`: WebUploader.File\n * * module `lib/dnd`: WebUploader.Lib.Dnd\n * * module `runtime/html5/dnd`: WebUploader.Runtime.Html5.Dnd\n *\n *\n * 以下文档中对类的使用可能省略掉了`WebUploader`前缀。\n * @module WebUploader\n * @title WebUploader API文档\n */\ndefine([\n    './dollar',\n    './promise'\n], function( $, promise ) {\n\n    var noop = function() {},\n        call = Function.call;\n\n    // http://jsperf.com/uncurrythis\n    // 反科里化\n    function uncurryThis( fn ) {\n        return function() {\n            return call.apply( fn, arguments );\n        };\n    }\n\n    function bindFn( fn, context ) {\n        return function() {\n            return fn.apply( context, arguments );\n        };\n    }\n\n    function createObject( proto ) {\n        var f;\n\n        if ( Object.create ) {\n            return Object.create( proto );\n        } else {\n            f = function() {};\n            f.prototype = proto;\n            return new f();\n        }\n    }\n\n\n    /**\n     * 基础类，提供一些简单常用的方法。\n     * @class Base\n     */\n    return {\n\n        /**\n         * @property {String} version 当前版本号。\n         */\n        version: '@version@',\n\n        /**\n         * @property {jQuery|Zepto} $ 引用依赖的jQuery或者Zepto对象。\n         */\n        $: $,\n\n        Deferred: promise.Deferred,\n\n        isPromise: promise.isPromise,\n\n        when: promise.when,\n\n        /**\n         * @description  简单的浏览器检查结果。\n         *\n         * * `webkit`  webkit版本号，如果浏览器为非webkit内核，此属性为`undefined`。\n         * * `chrome`  chrome浏览器版本号，如果浏览器为chrome，此属性为`undefined`。\n         * * `ie`  ie浏览器版本号，如果浏览器为非ie，此属性为`undefined`。**暂不支持ie10+**\n         * * `firefox`  firefox浏览器版本号，如果浏览器为非firefox，此属性为`undefined`。\n         * * `safari`  safari浏览器版本号，如果浏览器为非safari，此属性为`undefined`。\n         * * `opera`  opera浏览器版本号，如果浏览器为非opera，此属性为`undefined`。\n         *\n         * @property {Object} [browser]\n         */\n        browser: (function( ua ) {\n            var ret = {},\n                webkit = ua.match( /WebKit\\/([\\d.]+)/ ),\n                chrome = ua.match( /Chrome\\/([\\d.]+)/ ) ||\n                    ua.match( /CriOS\\/([\\d.]+)/ ),\n\n                ie = ua.match( /MSIE\\s([\\d\\.]+)/ ) ||\n                    ua.match( /(?:trident)(?:.*rv:([\\w.]+))?/i ),\n                firefox = ua.match( /Firefox\\/([\\d.]+)/ ),\n                safari = ua.match( /Safari\\/([\\d.]+)/ ),\n                opera = ua.match( /OPR\\/([\\d.]+)/ );\n\n            webkit && (ret.webkit = parseFloat( webkit[ 1 ] ));\n            chrome && (ret.chrome = parseFloat( chrome[ 1 ] ));\n            ie && (ret.ie = parseFloat( ie[ 1 ] ));\n            firefox && (ret.firefox = parseFloat( firefox[ 1 ] ));\n            safari && (ret.safari = parseFloat( safari[ 1 ] ));\n            opera && (ret.opera = parseFloat( opera[ 1 ] ));\n\n            return ret;\n        })( navigator.userAgent ),\n\n        /**\n         * @description  操作系统检查结果。\n         *\n         * * `android`  如果在android浏览器环境下，此值为对应的android版本号，否则为`undefined`。\n         * * `ios` 如果在ios浏览器环境下，此值为对应的ios版本号，否则为`undefined`。\n         * @property {Object} [os]\n         */\n        os: (function( ua ) {\n            var ret = {},\n\n                // osx = !!ua.match( /\\(Macintosh\\; Intel / ),\n                android = ua.match( /(?:Android);?[\\s\\/]+([\\d.]+)?/ ),\n                ios = ua.match( /(?:iPad|iPod|iPhone).*OS\\s([\\d_]+)/ );\n\n            // osx && (ret.osx = true);\n            android && (ret.android = parseFloat( android[ 1 ] ));\n            ios && (ret.ios = parseFloat( ios[ 1 ].replace( /_/g, '.' ) ));\n\n            return ret;\n        })( navigator.userAgent ),\n\n        /**\n         * 实现类与类之间的继承。\n         * @method inherits\n         * @grammar Base.inherits( super ) => child\n         * @grammar Base.inherits( super, protos ) => child\n         * @grammar Base.inherits( super, protos, statics ) => child\n         * @param  {Class} super 父类\n         * @param  {Object | Function} [protos] 子类或者对象。如果对象中包含constructor，子类将是用此属性值。\n         * @param  {Function} [protos.constructor] 子类构造器，不指定的话将创建个临时的直接执行父类构造器的方法。\n         * @param  {Object} [statics] 静态属性或方法。\n         * @return {Class} 返回子类。\n         * @example\n         * function Person() {\n         *     console.log( 'Super' );\n         * }\n         * Person.prototype.hello = function() {\n         *     console.log( 'hello' );\n         * };\n         *\n         * var Manager = Base.inherits( Person, {\n         *     world: function() {\n         *         console.log( 'World' );\n         *     }\n         * });\n         *\n         * // 因为没有指定构造器，父类的构造器将会执行。\n         * var instance = new Manager();    // => Super\n         *\n         * // 继承子父类的方法\n         * instance.hello();    // => hello\n         * instance.world();    // => World\n         *\n         * // 子类的__super__属性指向父类\n         * console.log( Manager.__super__ === Person );    // => true\n         */\n        inherits: function( Super, protos, staticProtos ) {\n            var child;\n\n            if ( typeof protos === 'function' ) {\n                child = protos;\n                protos = null;\n            } else if ( protos && protos.hasOwnProperty('constructor') ) {\n                child = protos.constructor;\n            } else {\n                child = function() {\n                    return Super.apply( this, arguments );\n                };\n            }\n\n            // 复制静态方法\n            $.extend( true, child, Super, staticProtos || {} );\n\n            /* jshint camelcase: false */\n\n            // 让子类的__super__属性指向父类。\n            child.__super__ = Super.prototype;\n\n            // 构建原型，添加原型方法或属性。\n            // 暂时用Object.create实现。\n            child.prototype = createObject( Super.prototype );\n            protos && $.extend( true, child.prototype, protos );\n\n            return child;\n        },\n\n        /**\n         * 一个不做任何事情的方法。可以用来赋值给默认的callback.\n         * @method noop\n         */\n        noop: noop,\n\n        /**\n         * 返回一个新的方法，此方法将已指定的`context`来执行。\n         * @grammar Base.bindFn( fn, context ) => Function\n         * @method bindFn\n         * @example\n         * var doSomething = function() {\n         *         console.log( this.name );\n         *     },\n         *     obj = {\n         *         name: 'Object Name'\n         *     },\n         *     aliasFn = Base.bind( doSomething, obj );\n         *\n         *  aliasFn();    // => Object Name\n         *\n         */\n        bindFn: bindFn,\n\n        /**\n         * 引用Console.log如果存在的话，否则引用一个[空函数noop](#WebUploader:Base.noop)。\n         * @grammar Base.log( args... ) => undefined\n         * @method log\n         */\n        log: (function() {\n            if ( window.console ) {\n                return bindFn( console.log, console );\n            }\n            return noop;\n        })(),\n\n        nextTick: (function() {\n\n            return function( cb ) {\n                setTimeout( cb, 1 );\n            };\n\n            // @bug 当浏览器不在当前窗口时就停了。\n            // var next = window.requestAnimationFrame ||\n            //     window.webkitRequestAnimationFrame ||\n            //     window.mozRequestAnimationFrame ||\n            //     function( cb ) {\n            //         window.setTimeout( cb, 1000 / 60 );\n            //     };\n\n            // // fix: Uncaught TypeError: Illegal invocation\n            // return bindFn( next, window );\n        })(),\n\n        /**\n         * 被[uncurrythis](http://www.2ality.com/2011/11/uncurrying-this.html)的数组slice方法。\n         * 将用来将非数组对象转化成数组对象。\n         * @grammar Base.slice( target, start[, end] ) => Array\n         * @method slice\n         * @example\n         * function doSomthing() {\n         *     var args = Base.slice( arguments, 1 );\n         *     console.log( args );\n         * }\n         *\n         * doSomthing( 'ignored', 'arg2', 'arg3' );    // => Array [\"arg2\", \"arg3\"]\n         */\n        slice: uncurryThis( [].slice ),\n\n        /**\n         * 生成唯一的ID\n         * @method guid\n         * @grammar Base.guid() => String\n         * @grammar Base.guid( prefx ) => String\n         */\n        guid: (function() {\n            var counter = 0;\n\n            return function( prefix ) {\n                var guid = (+new Date()).toString( 32 ),\n                    i = 0;\n\n                for ( ; i < 5; i++ ) {\n                    guid += Math.floor( Math.random() * 65535 ).toString( 32 );\n                }\n\n                return (prefix || 'wu_') + guid + (counter++).toString( 32 );\n            };\n        })(),\n\n        /**\n         * 格式化文件大小, 输出成带单位的字符串\n         * @method formatSize\n         * @grammar Base.formatSize( size ) => String\n         * @grammar Base.formatSize( size, pointLength ) => String\n         * @grammar Base.formatSize( size, pointLength, units ) => String\n         * @param {Number} size 文件大小\n         * @param {Number} [pointLength=2] 精确到的小数点数。\n         * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.\n         * @example\n         * console.log( Base.formatSize( 100 ) );    // => 100B\n         * console.log( Base.formatSize( 1024 ) );    // => 1.00K\n         * console.log( Base.formatSize( 1024, 0 ) );    // => 1K\n         * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M\n         * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G\n         * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB\n         */\n        formatSize: function( size, pointLength, units ) {\n            var unit;\n\n            units = units || [ 'B', 'K', 'M', 'G', 'TB' ];\n\n            while ( (unit = units.shift()) && size > 1024 ) {\n                size = size / 1024;\n            }\n\n            return (unit === 'B' ? size : size.toFixed( pointLength || 2 )) +\n                    unit;\n        }\n    };\n});"
  },
  {
    "path": "src/dollar-builtin.js",
    "content": "/**\n * @fileOverview  jq-bridge 主要实现像jQuery一样的功能方法，可以替换成jQuery，\n * 这里只实现了此组件所需的部分。\n *\n * **此文件的代码还不可用，还是直接用jquery吧**\n * @beta\n */\ndefine(function() {\n    var doc = window.document,\n        emptyArray = [],\n        slice = emptyArray.slice,\n        class2type = {},\n        hasOwn = class2type.hasOwnProperty,\n        toString = class2type.toString,\n        rId = /^#(.*)$/;\n\n    function each( obj, iterator ) {\n        var i;\n\n        //fix error, add guard here\n        if(!obj) {\n            return;\n        }\n\n        // like array\n        if ( typeof obj !== 'function' && typeof obj.length === 'number' ) {\n            for ( i = 0; i < obj.length; i++ ) {\n                if ( iterator.call( obj[ i ], i, obj[ i ] ) === false ) {\n                    return obj;\n                }\n            }\n        } else {\n            for ( i in obj ) {\n                if ( hasOwn.call( obj, i ) && iterator.call( obj[ i ], i,\n                        obj[ i ] ) === false ) {\n                    return obj;\n                }\n            }\n        }\n\n        return obj;\n    }\n\n    function extend( target, source, deep ) {\n        each( source, function( key, val ) {\n            if ( deep && typeof val === 'object' ) {\n                if ( typeof target[ key ] !== 'object' ) {\n                    target[ key ] = type( val ) === 'array' ? [] : {};\n                }\n                extend( target[ key ], val, deep );\n            } else {\n                target[ key ] = val;\n            }\n        });\n    }\n\n    each( ('Boolean Number String Function Array Date RegExp Object' +\n            ' Error').split(' '), function( i, name ) {\n        class2type[ '[object ' + name + ']' ] = name.toLowerCase();\n    });\n\n    function setAttribute( node, name, value ) {\n        value == null ? node.removeAttribute( name ) :\n                node.setAttribute( name, value );\n    }\n\n    /**\n     * 只支持ID选择。\n     */\n    function $( elem ) {\n        var api = {};\n\n        elem = typeof elem === 'string' && rId.test( elem ) ?\n                doc.getElementById( RegExp.$1 ) : elem;\n\n        if ( elem ) {\n            api[ 0 ] = elem;\n            api.length = 1;\n        }\n\n        return $.extend( api, {\n            _wrap: true,\n\n            get: function() {\n                return elem;\n            },\n\n            /**\n             * 添加className\n             */\n            addClass: function( classname ) {\n                elem.classList.add( classname );\n                return this;\n            },\n\n            removeClass: function( classname ) {\n                elem.classList.remove( classname );\n                return this;\n            },\n\n            //fix error, $(...).each is used in the source\n            each: function(callback){\n              [].every.call(this, function(el, idx){\n                return callback.call(el, idx, el) !== false\n              })\n              return this\n            },\n\n            html: function( html ) {\n                if ( html ) {\n                    elem.innerHTML = html;\n                }\n                return elem.innerHTML;\n            },\n\n            attr: function( key, val ) {\n                if ( $.isObject( key ) ) {\n                    $.each( key, function( k, v ) {\n                        setAttribute( elem, k, v );\n                    });\n                } else {\n                    setAttribute( elem, key, val );\n                }\n            },\n\n            empty: function() {\n                elem.innerHTML = '';\n                return this;\n            },\n\n            before: function( el ) {\n                elem.parentNode.insertBefore( el, elem );\n            },\n\n            append: function( el ) {\n                el = el._wrap ? el.get() : el;\n                elem.appendChild( el );\n            },\n\n            text: function() {\n                return elem.textContent;\n            },\n\n            // on\n            on: function( type, fn ) {\n                if ( elem.addEventListener ) {\n                    elem.addEventListener( type, fn, false );\n                } else if ( elem.attachEvent ) {\n                    elem.attachEvent( 'on' + type, fn );\n                }\n\n                return this;\n            },\n\n            // off\n            off: function( type, fn ) {\n                if ( elem.removeEventListener ) {\n                    elem.removeEventListener( type, fn, false );\n                } else if ( elem.attachEvent ) {\n                    elem.detachEvent( 'on' + type, fn );\n                }\n                return this;\n            }\n\n        });\n    }\n\n    $.each = each;\n    $.extend = function( /*[deep, ]*/target/*, source...*/ ) {\n        var args = slice.call( arguments, 1 ),\n            deep;\n\n        if ( typeof target === 'boolean' ) {\n            deep = target;\n            target = args.shift();\n        }\n\n        args.forEach(function( arg ) {\n            arg && extend( target, arg, deep );\n        });\n\n        return target;\n    };\n\n    function type( obj ) {\n\n        /*jshint eqnull:true*/\n        return obj == null ? String( obj ) :\n                class2type[ toString.call( obj ) ] || 'object';\n    }\n    $.type = type;\n\n    //fix error, $.grep is used in the source\n    $.grep = function( elems, callback, invert ) {\n        var callbackInverse,\n            matches = [],\n            i = 0,\n            length = elems.length,\n            callbackExpect = !invert;\n\n        // Go through the array, only saving the items\n        // that pass the validator function\n        for ( ; i < length; i++ ) {\n            callbackInverse = !callback( elems[ i ], i );\n            if ( callbackInverse !== callbackExpect ) {\n                matches.push( elems[ i ] );\n            }\n        }\n\n        return matches;\n    }\n\n    $.isWindow = function( obj ) {\n        return obj && obj.window === obj;\n    };\n\n    $.isPlainObject = function( obj ) {\n        if ( type( obj ) !== 'object' || obj.nodeType || $.isWindow( obj ) ) {\n            return false;\n        }\n\n        try {\n            if ( obj.constructor && !hasOwn.call( obj.constructor.prototype,\n                    'isPrototypeOf' ) ) {\n                return false;\n            }\n        } catch ( ex ) {\n            return false;\n        }\n\n        return true;\n    };\n\n    $.isObject = function( anything ) {\n        return type( anything ) === 'object';\n    };\n\n    $.trim = function( str ) {\n        return str ? str.trim() : '';\n    };\n\n    $.isFunction = function( obj ) {\n        return type( obj ) === 'function';\n    };\n\n    emptyArray = null;\n\n    return $;\n});\n"
  },
  {
    "path": "src/dollar-third.js",
    "content": "/**\n * @fileOverview jQuery or Zepto\n * @require \"jquery\"\n * @require \"zepto\"\n */\ndefine(function() {\n    var req = window.require;\n    var $ = window.__dollar || \n        window.jQuery || \n        window.Zepto || \n        req('jquery') || \n        req('zepto');\n\n    if ( !$ ) {\n        throw new Error('jQuery or Zepto not found!');\n    }\n\n    return $;\n});\n"
  },
  {
    "path": "src/dollar.js",
    "content": "/**\n * @fileOverview Dom 操作相关\n */\ndefine([\n    './dollar-third'\n], function( _ ) {\n    return _;\n});"
  },
  {
    "path": "src/file.js",
    "content": "/**\n * @fileOverview 文件属性封装\n */\ndefine([\n    './base',\n    './mediator'\n], function( Base, Mediator ) {\n\n    var $ = Base.$,\n        idPrefix = 'WU_FILE_',\n        idSuffix = 0,\n        rExt = /\\.([^.]+)$/,\n        statusMap = {};\n\n    function gid() {\n        return idPrefix + idSuffix++;\n    }\n\n    /**\n     * 文件类\n     * @class File\n     * @constructor 构造函数\n     * @grammar new File( source ) => File\n     * @param {Lib.File} source [lib.File](#Lib.File)实例, 此source对象是带有Runtime信息的。\n     */\n    function WUFile( source ) {\n\n        /**\n         * 文件名，包括扩展名（后缀）\n         * @property name\n         * @type {string}\n         */\n        this.name = source.name || 'Untitled';\n\n        /**\n         * 文件体积（字节）\n         * @property size\n         * @type {uint}\n         * @default 0\n         */\n        this.size = source.size || 0;\n\n        /**\n         * 文件MIMETYPE类型，与文件类型的对应关系请参考[http://t.cn/z8ZnFny](http://t.cn/z8ZnFny)\n         * @property type\n         * @type {string}\n         * @default 'application/octet-stream'\n         */\n        this.type = source.type || 'application/octet-stream';\n\n        /**\n         * 文件最后修改日期\n         * @property lastModifiedDate\n         * @type {int}\n         * @default 当前时间戳\n         */\n        this.lastModifiedDate = source.lastModifiedDate || (new Date() * 1);\n\n        /**\n         * 文件ID，每个对象具有唯一ID，与文件名无关\n         * @property id\n         * @type {string}\n         */\n        this.id = gid();\n\n        /**\n         * 文件扩展名，通过文件名获取，例如test.png的扩展名为png\n         * @property ext\n         * @type {string}\n         */\n        this.ext = rExt.exec( this.name ) ? RegExp.$1 : '';\n\n\n        /**\n         * 状态文字说明。在不同的status语境下有不同的用途。\n         * @property statusText\n         * @type {string}\n         */\n        this.statusText = '';\n\n        // 存储文件状态，防止通过属性直接修改\n        statusMap[ this.id ] = WUFile.Status.INITED;\n\n        this.source = source;\n        this.loaded = 0;\n\n        this.on( 'error', function( msg ) {\n            this.setStatus( WUFile.Status.ERROR, msg );\n        });\n    }\n\n    $.extend( WUFile.prototype, {\n\n        /**\n         * 设置状态，状态变化时会触发`change`事件。\n         * @method setStatus\n         * @grammar setStatus( status[, statusText] );\n         * @param {File.Status|String} status [文件状态值](#WebUploader:File:File.Status)\n         * @param {String} [statusText=''] 状态说明，常在error时使用，用http, abort,server等来标记是由于什么原因导致文件错误。\n         */\n        setStatus: function( status, text ) {\n\n            var prevStatus = statusMap[ this.id ];\n\n            typeof text !== 'undefined' && (this.statusText = text);\n\n            if ( status !== prevStatus ) {\n                statusMap[ this.id ] = status;\n                /**\n                 * 文件状态变化\n                 * @event statuschange\n                 */\n                this.trigger( 'statuschange', status, prevStatus );\n            }\n\n        },\n\n        /**\n         * 获取文件状态\n         * @return {File.Status}\n         * @example\n                 文件状态具体包括以下几种类型：\n                 {\n                     // 初始化\n                    INITED:     0,\n                    // 已入队列\n                    QUEUED:     1,\n                    // 正在上传\n                    PROGRESS:     2,\n                    // 上传出错\n                    ERROR:         3,\n                    // 上传成功\n                    COMPLETE:     4,\n                    // 上传取消\n                    CANCELLED:     5\n                }\n         */\n        getStatus: function() {\n            return statusMap[ this.id ];\n        },\n\n        /**\n         * 获取文件原始信息。\n         * @return {*}\n         */\n        getSource: function() {\n            return this.source;\n        },\n\n        destroy: function() {\n            this.off();\n            delete statusMap[ this.id ];\n        }\n    });\n\n    Mediator.installTo( WUFile.prototype );\n\n    /**\n     * 文件状态值，具体包括以下几种类型：\n     * * `inited` 初始状态\n     * * `queued` 已经进入队列, 等待上传\n     * * `progress` 上传中\n     * * `complete` 上传完成。\n     * * `error` 上传出错，可重试\n     * * `interrupt` 上传中断，可续传。\n     * * `invalid` 文件不合格，不能重试上传。会自动从队列中移除。\n     * * `cancelled` 文件被移除。\n     * @property {Object} Status\n     * @namespace File\n     * @class File\n     * @static\n     */\n    WUFile.Status = {\n        INITED:     'inited',    // 初始状态\n        QUEUED:     'queued',    // 已经进入队列, 等待上传\n        PROGRESS:   'progress',    // 上传中\n        ERROR:      'error',    // 上传出错，可重试\n        COMPLETE:   'complete',    // 上传完成。\n        CANCELLED:  'cancelled',    // 上传取消。\n        INTERRUPT:  'interrupt',    // 上传中断，可续传。\n        INVALID:    'invalid'    // 文件不合格，不能重试上传。\n    };\n\n    return WUFile;\n});\n"
  },
  {
    "path": "src/lib/blob.js",
    "content": "/**\n * @fileOverview Blob\n */\ndefine([\n    '../base',\n    '../runtime/client'\n], function( Base, RuntimeClient ) {\n\n    function Blob( ruid, source ) {\n        var me = this;\n\n        me.source = source;\n        me.ruid = ruid;\n        this.size = source.size || 0;\n\n        // 如果没有指定 mimetype, 但是知道文件后缀。\n        if ( !source.type && this.ext &&\n                ~'jpg,jpeg,png,gif,bmp'.indexOf( this.ext ) ) {\n            this.type = 'image/' + (this.ext === 'jpg' ? 'jpeg' : this.ext);\n        } else {\n            this.type = source.type || 'application/octet-stream';\n        }\n\n        RuntimeClient.call( me, 'Blob' );\n        this.uid = source.uid || this.uid;\n\n        if ( ruid ) {\n            me.connectRuntime( ruid );\n        }\n    }\n\n    Base.inherits( RuntimeClient, {\n        constructor: Blob,\n\n        slice: function( start, end ) {\n            return this.exec( 'slice', start, end );\n        },\n\n        getSource: function() {\n            return this.source;\n        }\n    });\n\n    return Blob;\n});"
  },
  {
    "path": "src/lib/dnd.js",
    "content": "/**\n * @fileOverview 错误信息\n */\ndefine([\n    '../base',\n    '../mediator',\n    '../runtime/client'\n], function( Base, Mediator, RuntimeClent ) {\n\n    var $ = Base.$;\n\n    function DragAndDrop( opts ) {\n        opts = this.options = $.extend({}, DragAndDrop.options, opts );\n\n        opts.container = $( opts.container );\n\n        if ( !opts.container.length ) {\n            return;\n        }\n\n        RuntimeClent.call( this, 'DragAndDrop' );\n    }\n\n    DragAndDrop.options = {\n        accept: null,\n        disableGlobalDnd: false\n    };\n\n    Base.inherits( RuntimeClent, {\n        constructor: DragAndDrop,\n\n        init: function() {\n            var me = this;\n\n            me.connectRuntime( me.options, function() {\n                me.exec('init');\n                me.trigger('ready');\n            });\n        }\n    });\n\n    Mediator.installTo( DragAndDrop.prototype );\n\n    return DragAndDrop;\n});"
  },
  {
    "path": "src/lib/file.js",
    "content": "/**\n * 为了统一化Flash的File和HTML5的File而存在。\n * 以至于要调用Flash里面的File，也可以像调用HTML5版本的File一下。\n * @fileOverview File\n */\ndefine([\n    '../base',\n    './blob'\n], function( Base, Blob ) {\n\n    var uid = 1,\n        rExt = /\\.([^.]+)$/;\n\n    function File( ruid, file ) {\n        var ext;\n\n        this.name = file.name || ('untitled' + uid++);\n        ext = rExt.exec( file.name ) ? RegExp.$1.toLowerCase() : '';\n\n        // todo 支持其他类型文件的转换。\n        // 如果有 mimetype, 但是文件名里面没有找出后缀规律\n        if ( !ext && file.type ) {\n            ext = /\\/(jpg|jpeg|png|gif|bmp)$/i.exec( file.type ) ?\n                    RegExp.$1.toLowerCase() : '';\n            this.name += '.' + ext;\n        }\n\n        this.ext = ext;\n        this.lastModifiedDate = file.lastModifiedDate || \n                file.lastModified && new Date(file.lastModified).toLocaleString() ||\n                (new Date()).toLocaleString();\n\n        Blob.apply( this, arguments );\n    }\n\n    return Base.inherits( Blob, File );\n});\n"
  },
  {
    "path": "src/lib/filepaste.js",
    "content": "/**\n * @fileOverview 错误信息\n */\ndefine([\n    '../base',\n    '../mediator',\n    '../runtime/client'\n], function( Base, Mediator, RuntimeClent ) {\n\n    var $ = Base.$;\n\n    function FilePaste( opts ) {\n        opts = this.options = $.extend({}, opts );\n        opts.container = $( opts.container || document.body );\n        RuntimeClent.call( this, 'FilePaste' );\n    }\n\n    Base.inherits( RuntimeClent, {\n        constructor: FilePaste,\n\n        init: function() {\n            var me = this;\n\n            me.connectRuntime( me.options, function() {\n                me.exec('init');\n                me.trigger('ready');\n            });\n        }\n    });\n\n    Mediator.installTo( FilePaste.prototype );\n\n    return FilePaste;\n});"
  },
  {
    "path": "src/lib/filepicker.js",
    "content": "/**\n * @fileOverview 错误信息\n */\ndefine([\n    '../base',\n    '../runtime/client',\n    './file'\n], function( Base, RuntimeClient, File ) {\n\n    var $ = Base.$;\n\n    function FilePicker( opts ) {\n        opts = this.options = $.extend({}, FilePicker.options, opts );\n\n        opts.container = $( opts.id );\n\n        if ( !opts.container.length ) {\n            throw new Error('按钮指定错误');\n        }\n\n        opts.innerHTML = opts.innerHTML || opts.label ||\n                opts.container.html() || '';\n\n        opts.button = $( opts.button || document.createElement('div') );\n        opts.button.html( opts.innerHTML );\n        opts.container.html( opts.button );\n\n        RuntimeClient.call( this, 'FilePicker', true );\n    }\n\n    FilePicker.options = {\n        button: null,\n        container: null,\n        label: null,\n        innerHTML: null,\n        multiple: true,\n        accept: null,\n        name: 'file',\n        style: 'webuploader-pick'   //pick element class attribute, default is \"webuploader-pick\"\n    };\n\n    Base.inherits( RuntimeClient, {\n        constructor: FilePicker,\n\n        init: function() {\n            var me = this,\n                opts = me.options,\n                button = opts.button,\n                style = opts.style;\n\n            if (style)\n                button.addClass('webuploader-pick');\n\n            me.on( 'all', function( type ) {\n                var files;\n\n                switch ( type ) {\n                    case 'mouseenter':\n                        if (style)\n                            button.addClass('webuploader-pick-hover');\n                        break;\n\n                    case 'mouseleave':\n                        if (style)\n                            button.removeClass('webuploader-pick-hover');\n                        break;\n\n                    case 'change':\n                        files = me.exec('getFiles');\n                        me.trigger( 'select', $.map( files, function( file ) {\n                            file = new File( me.getRuid(), file );\n\n                            // 记录来源。\n                            file._refer = opts.container;\n                            return file;\n                        }), opts.container );\n                        break;\n                }\n            });\n\n            me.connectRuntime( opts, function() {\n                me.refresh();\n                me.exec( 'init', opts );\n                me.trigger('ready');\n            });\n\n            this._resizeHandler = Base.bindFn( this.refresh, this );\n            $( window ).on( 'resize', this._resizeHandler );\n        },\n\n        refresh: function() {\n            var shimContainer = this.getRuntime().getContainer(),\n                button = this.options.button,\n                /*\n                width = button.outerWidth ?\n                        button.outerWidth() : button.width(),\n\n                height = button.outerHeight ?\n                        button.outerHeight() : button.height(),\n                */\n                width = button[0] && button[0].offsetWidth || button.outerWidth() || button.width(),\n                height = button[0] && button[0].offsetHeight || button.outerHeight() || button.height(),\n                pos = button.offset();\n\n            width && height && shimContainer.css({\n                bottom: 'auto',\n                right: 'auto',\n                width: width + 'px',\n                height: height + 'px'\n            }).offset( pos );\n        },\n\n        enable: function() {\n            var btn = this.options.button;\n\n            btn.removeClass('webuploader-pick-disable');\n            this.refresh();\n        },\n\n        disable: function() {\n            var btn = this.options.button;\n\n            this.getRuntime().getContainer().css({\n                top: '-99999px'\n            });\n\n            btn.addClass('webuploader-pick-disable');\n        },\n\n        destroy: function() {\n            var btn = this.options.button;\n            $( window ).off( 'resize', this._resizeHandler );\n            btn.removeClass('webuploader-pick-disable webuploader-pick-hover ' +\n                'webuploader-pick');\n        }\n    });\n\n    return FilePicker;\n});\n"
  },
  {
    "path": "src/lib/image.js",
    "content": "/**\n * @fileOverview Image\n */\ndefine([\n    '../base',\n    '../runtime/client',\n    './blob'\n], function( Base, RuntimeClient, Blob ) {\n    var $ = Base.$;\n\n    // 构造器。\n    function Image( opts ) {\n        this.options = $.extend({}, Image.options, opts );\n        RuntimeClient.call( this, 'Image' );\n\n        this.on( 'load', function() {\n            this._info = this.exec('info');\n            this._meta = this.exec('meta');\n        });\n    }\n\n    // 默认选项。\n    Image.options = {\n\n        // 默认的图片处理质量\n        quality: 90,\n\n        // 是否裁剪\n        crop: false,\n\n        // 是否保留头部信息\n        preserveHeaders: false,\n\n        // 是否允许放大。\n        allowMagnify: false\n    };\n\n    // 继承RuntimeClient.\n    Base.inherits( RuntimeClient, {\n        constructor: Image,\n\n        info: function( val ) {\n\n            // setter\n            if ( val ) {\n                this._info = val;\n                return this;\n            }\n\n            // getter\n            return this._info;\n        },\n\n        meta: function( val ) {\n\n            // setter\n            if ( val ) {\n                this._meta = val;\n                return this;\n            }\n\n            // getter\n            return this._meta;\n        },\n\n        loadFromBlob: function( blob ) {\n            var me = this,\n                ruid = blob.getRuid();\n\n            this.connectRuntime( ruid, function() {\n                me.exec( 'init', me.options );\n                me.exec( 'loadFromBlob', blob );\n            });\n        },\n\n        resize: function() {\n            var args = Base.slice( arguments );\n            return this.exec.apply( this, [ 'resize' ].concat( args ) );\n        },\n\n        crop: function() {\n            var args = Base.slice( arguments );\n            return this.exec.apply( this, [ 'crop' ].concat( args ) );\n        },\n\n        getAsDataUrl: function( type ) {\n            return this.exec( 'getAsDataUrl', type );\n        },\n\n        getAsBlob: function( type ) {\n            var blob = this.exec( 'getAsBlob', type );\n\n            return new Blob( this.getRuid(), blob );\n        }\n    });\n\n    return Image;\n});"
  },
  {
    "path": "src/lib/md5.js",
    "content": "/**\n * @fileOverview Md5\n */\ndefine([\n    '../runtime/client',\n    '../mediator'\n], function( RuntimeClient, Mediator ) {\n\n    function Md5() {\n        RuntimeClient.call( this, 'Md5' );\n    }\n\n    // 让 Md5 具备事件功能。\n    Mediator.installTo( Md5.prototype );\n\n    Md5.prototype.loadFromBlob = function( blob ) {\n        var me = this;\n\n        if ( me.getRuid() ) {\n            me.disconnectRuntime();\n        }\n\n        // 连接到blob归属的同一个runtime.\n        me.connectRuntime( blob.ruid, function() {\n            me.exec('init');\n            me.exec( 'loadFromBlob', blob );\n        });\n    };\n\n    Md5.prototype.getResult = function() {\n        return this.exec('getResult');\n    };\n\n    return Md5;\n});"
  },
  {
    "path": "src/lib/transport.js",
    "content": "/**\n * @fileOverview Transport\n */\ndefine([\n    '../base',\n    '../runtime/client',\n    '../mediator'\n], function( Base, RuntimeClient, Mediator ) {\n\n    var $ = Base.$;\n\n    function Transport( opts ) {\n        var me = this;\n\n        opts = me.options = $.extend( true, {}, Transport.options, opts || {} );\n        RuntimeClient.call( this, 'Transport' );\n\n        this._blob = null;\n        this._formData = opts.formData || {};\n        this._headers = opts.headers || {};\n\n        this.on( 'progress', this._timeout );\n        this.on( 'load error', function() {\n            me.trigger( 'progress', 1 );\n            clearTimeout( me._timer );\n        });\n    }\n\n    Transport.options = {\n        server: '',\n        method: 'POST',\n\n        // 跨域时，是否允许携带cookie, 只有html5 runtime才有效\n        withCredentials: false,\n        fileVal: 'file',\n        timeout: 2 * 60 * 1000,    // 2分钟\n        formData: {},\n        headers: {},\n        sendAsBinary: false\n    };\n\n    $.extend( Transport.prototype, {\n\n        // 添加Blob, 只能添加一次，最后一次有效。\n        appendBlob: function( key, blob, filename ) {\n            var me = this,\n                opts = me.options;\n\n            if ( me.getRuid() ) {\n                me.disconnectRuntime();\n            }\n\n            // 连接到blob归属的同一个runtime.\n            me.connectRuntime( blob.ruid, function() {\n                me.exec('init');\n            });\n\n            me._blob = blob;\n            opts.fileVal = key || opts.fileVal;\n            opts.filename = filename || opts.filename;\n        },\n\n        // 添加其他字段\n        append: function( key, value ) {\n            if ( typeof key === 'object' ) {\n                $.extend( this._formData, key );\n            } else {\n                this._formData[ key ] = value;\n            }\n        },\n\n        setRequestHeader: function( key, value ) {\n            if ( typeof key === 'object' ) {\n                $.extend( this._headers, key );\n            } else {\n                this._headers[ key ] = value;\n            }\n        },\n\n        send: function( method ) {\n            this.exec( 'send', method );\n            this._timeout();\n        },\n\n        abort: function() {\n            clearTimeout( this._timer );\n            return this.exec('abort');\n        },\n\n        destroy: function() {\n            this.trigger('destroy');\n            this.off();\n            this.exec('destroy');\n            this.disconnectRuntime();\n        },\n\n        getResponseHeaders: function() {\n            return this.exec('getResponseHeaders');\n        },\n\n        getResponse: function() {\n            return this.exec('getResponse');\n        },\n\n        getResponseAsJson: function() {\n            return this.exec('getResponseAsJson');\n        },\n\n        getStatus: function() {\n            return this.exec('getStatus');\n        },\n\n        _timeout: function() {\n            var me = this,\n                duration = me.options.timeout;\n\n            if ( !duration ) {\n                return;\n            }\n\n            clearTimeout( me._timer );\n            me._timer = setTimeout(function() {\n                me.abort();\n                me.trigger( 'error', 'timeout' );\n            }, duration );\n        }\n\n    });\n\n    // 让Transport具备事件功能。\n    Mediator.installTo( Transport.prototype );\n\n    return Transport;\n});\n"
  },
  {
    "path": "src/mediator.js",
    "content": "/**\n * 事件处理类，可以独立使用，也可以扩展给对象使用。\n * @fileOverview Mediator\n */\ndefine([\n    './base'\n], function( Base ) {\n    var $ = Base.$,\n        slice = [].slice,\n        separator = /\\s+/,\n        protos;\n\n    // 根据条件过滤出事件handlers.\n    function findHandlers( arr, name, callback, context ) {\n        return $.grep( arr, function( handler ) {\n            return handler &&\n                    (!name || handler.e === name) &&\n                    (!callback || handler.cb === callback ||\n                    handler.cb._cb === callback) &&\n                    (!context || handler.ctx === context);\n        });\n    }\n\n    function eachEvent( events, callback, iterator ) {\n        // 不支持对象，只支持多个event用空格隔开\n        $.each( (events || '').split( separator ), function( _, key ) {\n            iterator( key, callback );\n        });\n    }\n\n    function triggerHanders( events, args ) {\n        var stoped = false,\n            i = -1,\n            len = events.length,\n            handler;\n\n        while ( ++i < len ) {\n            handler = events[ i ];\n\n            if ( handler.cb.apply( handler.ctx2, args ) === false ) {\n                stoped = true;\n                break;\n            }\n        }\n\n        return !stoped;\n    }\n\n    protos = {\n\n        /**\n         * 绑定事件。\n         *\n         * `callback`方法在执行时，arguments将会来源于trigger的时候携带的参数。如\n         * ```javascript\n         * var obj = {};\n         *\n         * // 使得obj有事件行为\n         * Mediator.installTo( obj );\n         *\n         * obj.on( 'testa', function( arg1, arg2 ) {\n         *     console.log( arg1, arg2 ); // => 'arg1', 'arg2'\n         * });\n         *\n         * obj.trigger( 'testa', 'arg1', 'arg2' );\n         * ```\n         *\n         * 如果`callback`中，某一个方法`return false`了，则后续的其他`callback`都不会被执行到。\n         * 切会影响到`trigger`方法的返回值，为`false`。\n         *\n         * `on`还可以用来添加一个特殊事件`all`, 这样所有的事件触发都会响应到。同时此类`callback`中的arguments有一个不同处，\n         * 就是第一个参数为`type`，记录当前是什么事件在触发。此类`callback`的优先级比脚低，会再正常`callback`执行完后触发。\n         * ```javascript\n         * obj.on( 'all', function( type, arg1, arg2 ) {\n         *     console.log( type, arg1, arg2 ); // => 'testa', 'arg1', 'arg2'\n         * });\n         * ```\n         *\n         * @method on\n         * @grammar on( name, callback[, context] ) => self\n         * @param  {String}   name     事件名，支持多个事件用空格隔开\n         * @param  {Function} callback 事件处理器\n         * @param  {Object}   [context]  事件处理器的上下文。\n         * @return {self} 返回自身，方便链式\n         * @chainable\n         * @class Mediator\n         */\n        on: function( name, callback, context ) {\n            var me = this,\n                set;\n\n            if ( !callback ) {\n                return this;\n            }\n\n            set = this._events || (this._events = []);\n\n            eachEvent( name, callback, function( name, callback ) {\n                var handler = { e: name };\n\n                handler.cb = callback;\n                handler.ctx = context;\n                handler.ctx2 = context || me;\n                handler.id = set.length;\n\n                set.push( handler );\n            });\n\n            return this;\n        },\n\n        /**\n         * 绑定事件，且当handler执行完后，自动解除绑定。\n         * @method once\n         * @grammar once( name, callback[, context] ) => self\n         * @param  {String}   name     事件名\n         * @param  {Function} callback 事件处理器\n         * @param  {Object}   [context]  事件处理器的上下文。\n         * @return {self} 返回自身，方便链式\n         * @chainable\n         */\n        once: function( name, callback, context ) {\n            var me = this;\n\n            if ( !callback ) {\n                return me;\n            }\n\n            eachEvent( name, callback, function( name, callback ) {\n                var once = function() {\n                        me.off( name, once );\n                        return callback.apply( context || me, arguments );\n                    };\n\n                once._cb = callback;\n                me.on( name, once, context );\n            });\n\n            return me;\n        },\n\n        /**\n         * 解除事件绑定\n         * @method off\n         * @grammar off( [name[, callback[, context] ] ] ) => self\n         * @param  {String}   [name]     事件名\n         * @param  {Function} [callback] 事件处理器\n         * @param  {Object}   [context]  事件处理器的上下文。\n         * @return {self} 返回自身，方便链式\n         * @chainable\n         */\n        off: function( name, cb, ctx ) {\n            var events = this._events;\n\n            if ( !events ) {\n                return this;\n            }\n\n            if ( !name && !cb && !ctx ) {\n                this._events = [];\n                return this;\n            }\n\n            eachEvent( name, cb, function( name, cb ) {\n                $.each( findHandlers( events, name, cb, ctx ), function() {\n                    delete events[ this.id ];\n                });\n            });\n\n            return this;\n        },\n\n        /**\n         * 触发事件\n         * @method trigger\n         * @grammar trigger( name[, args...] ) => self\n         * @param  {String}   type     事件名\n         * @param  {*} [...] 任意参数\n         * @return {Boolean} 如果handler中return false了，则返回false, 否则返回true\n         */\n        trigger: function( type ) {\n            var args, events, allEvents;\n\n            if ( !this._events || !type ) {\n                return this;\n            }\n\n            args = slice.call( arguments, 1 );\n            events = findHandlers( this._events, type );\n            allEvents = findHandlers( this._events, 'all' );\n\n            return triggerHanders( events, args ) &&\n                    triggerHanders( allEvents, arguments );\n        }\n    };\n\n    /**\n     * 中介者，它本身是个单例，但可以通过[installTo](#WebUploader:Mediator:installTo)方法，使任何对象具备事件行为。\n     * 主要目的是负责模块与模块之间的合作，降低耦合度。\n     *\n     * @class Mediator\n     */\n    return $.extend({\n\n        /**\n         * 可以通过这个接口，使任何对象具备事件功能。\n         * @method installTo\n         * @param  {Object} obj 需要具备事件行为的对象。\n         * @return {Object} 返回obj.\n         */\n        installTo: function( obj ) {\n            return $.extend( obj, protos );\n        }\n\n    }, protos );\n});"
  },
  {
    "path": "src/preset/all.js",
    "content": "/**\n * @fileOverview 完全版本。\n */\ndefine([\n    '../base',\n\n    // widgets\n    '../widgets/filednd',\n    '../widgets/filepaste',\n    '../widgets/filepicker',\n    '../widgets/image',\n    '../widgets/queue',\n    '../widgets/runtime',\n    '../widgets/upload',\n    '../widgets/validator',\n    '../widgets/md5',\n\n    // runtimes\n    // html5\n    '../runtime/html5/blob',\n    '../runtime/html5/dnd',\n    '../runtime/html5/filepaste',\n    '../runtime/html5/filepicker',\n    '../runtime/html5/imagemeta/exif',\n    '../runtime/html5/androidpatch',\n    '../runtime/html5/image',\n    '../runtime/html5/transport',\n    '../runtime/html5/md5',\n\n    // flash\n    '../runtime/flash/filepicker',\n    '../runtime/flash/image',\n    '../runtime/flash/transport',\n    '../runtime/flash/blob',\n    '../runtime/flash/md5'\n], function( Base ) {\n    return Base;\n});"
  },
  {
    "path": "src/preset/flashonly.js",
    "content": "/**\n * @fileOverview 只有flash实现的文件版本。\n */\ndefine([\n    '../base',\n\n    // widgets\n    '../widgets/filepicker',\n    '../widgets/image',\n    '../widgets/queue',\n    '../widgets/runtime',\n    '../widgets/upload',\n    '../widgets/validator',\n\n    // runtimes\n\n    // flash\n    '../runtime/flash/filepicker',\n    '../runtime/flash/image',\n    '../runtime/flash/blob',\n    '../runtime/flash/transport'\n], function( Base ) {\n    return Base;\n});"
  },
  {
    "path": "src/preset/html5only.js",
    "content": "/**\n * @fileOverview 只有html5实现的文件版本。\n */\ndefine([\n    '../base',\n\n    // widgets\n    '../widgets/filednd',\n    '../widgets/filepaste',\n    '../widgets/filepicker',\n    '../widgets/image',\n    '../widgets/queue',\n    '../widgets/runtime',\n    '../widgets/upload',\n    '../widgets/validator',\n\n    // runtimes\n    // html5\n    '../runtime/html5/blob',\n    '../runtime/html5/dnd',\n    '../runtime/html5/filepaste',\n    '../runtime/html5/filepicker',\n    '../runtime/html5/imagemeta/exif',\n    '../runtime/html5/image',\n    '../runtime/html5/transport'\n], function( Base ) {\n    return Base;\n});"
  },
  {
    "path": "src/preset/withoutimage.js",
    "content": "/**\n * @fileOverview 没有图像处理的版本。\n */\ndefine([\n    '../base',\n\n    // widgets\n    '../widgets/filednd',\n    '../widgets/filepaste',\n    '../widgets/filepicker',\n    '../widgets/queue',\n    '../widgets/runtime',\n    '../widgets/upload',\n    '../widgets/validator',\n\n    // runtimes\n    // html5\n    '../runtime/html5/blob',\n    '../runtime/html5/dnd',\n    '../runtime/html5/filepaste',\n    '../runtime/html5/filepicker',\n    '../runtime/html5/transport',\n\n    // flash\n    '../runtime/flash/filepicker',\n    '../runtime/flash/transport',\n    '../runtime/flash/blob'\n], function( Base ) {\n    return Base;\n});"
  },
  {
    "path": "src/promise-builtin.js",
    "content": "/**\n * 直接来源于jquery的代码。\n * @fileOverview Promise/A+\n * @beta\n */\ndefine([\n    './dollar'\n], function( $ ) {\n\n    var api;\n\n    // 简单版Callbacks, 默认memory，可选once.\n    function Callbacks( once ) {\n        var list = [],\n            stack = !once && [],\n            fire = function( data ) {\n                memory = data;\n                fired = true;\n                firingIndex = firingStart || 0;\n                firingStart = 0;\n                firingLength = list.length;\n                firing = true;\n\n                for ( ; list && firingIndex < firingLength; firingIndex++ ) {\n                    list[ firingIndex ].apply( data[ 0 ], data[ 1 ] );\n                }\n                firing = false;\n\n                if ( list ) {\n                    if ( stack ) {\n                        stack.length && fire( stack.shift() );\n                    }  else {\n                        list = [];\n                    }\n                }\n            },\n            self = {\n                add: function() {\n                    if ( list ) {\n                        var start = list.length;\n                        (function add ( args ) {\n                            $.each( args, function( _, arg ) {\n                                var type = $.type( arg );\n                                if ( type === 'function' ) {\n                                    list.push( arg );\n                                } else if ( arg && arg.length &&\n                                        type !== 'string' ) {\n\n                                    add( arg );\n                                }\n                            });\n                        })( arguments );\n\n                        if ( firing ) {\n                            firingLength = list.length;\n                        } else if ( memory ) {\n                            firingStart = start;\n                            fire( memory );\n                        }\n                    }\n                    return this;\n                },\n\n                disable: function() {\n                    list = stack = memory = undefined;\n                    return this;\n                },\n\n                // Lock the list in its current state\n                lock: function() {\n                    stack = undefined;\n                    if ( !memory ) {\n                        self.disable();\n                    }\n                    return this;\n                },\n\n                fireWith: function( context, args ) {\n                    if ( list && (!fired || stack) ) {\n                        args = args || [];\n                        args = [ context, args.slice ? args.slice() : args ];\n                        if ( firing ) {\n                            stack.push( args );\n                        } else {\n                            fire( args );\n                        }\n                    }\n                    return this;\n                },\n\n                fire: function() {\n                    self.fireWith( this, arguments );\n                    return this;\n                }\n            },\n\n            fired, firing, firingStart, firingLength, firingIndex, memory;\n\n        return self;\n    }\n\n    function Deferred( func ) {\n        var tuples = [\n                // action, add listener, listener list, final state\n                [ 'resolve', 'done', Callbacks( true ), 'resolved' ],\n                [ 'reject', 'fail', Callbacks( true ), 'rejected' ],\n                [ 'notify', 'progress', Callbacks() ]\n            ],\n            state = 'pending',\n            promise = {\n                state: function() {\n                    return state;\n                },\n                always: function() {\n                    deferred.done( arguments ).fail( arguments );\n                    return this;\n                },\n                then: function( /* fnDone, fnFail, fnProgress */ ) {\n                    var fns = arguments;\n                    return Deferred(function( newDefer ) {\n                        $.each( tuples, function( i, tuple ) {\n                            var action = tuple[ 0 ],\n                                fn = $.isFunction( fns[ i ] ) && fns[ i ];\n\n                            // deferred[ done | fail | progress ] for\n                            // forwarding actions to newDefer\n                            deferred[ tuple[ 1 ] ](function() {\n                                var returned;\n\n                                returned = fn && fn.apply( this, arguments );\n\n                                if ( returned &&\n                                        $.isFunction( returned.promise ) ) {\n\n                                    returned.promise()\n                                            .done( newDefer.resolve )\n                                            .fail( newDefer.reject )\n                                            .progress( newDefer.notify );\n                                } else {\n                                    newDefer[ action + 'With' ](\n                                            this === promise ?\n                                            newDefer.promise() :\n                                            this,\n                                            fn ? [ returned ] : arguments );\n                                }\n                            });\n                        });\n                        fns = null;\n                    }).promise();\n                },\n\n                // Get a promise for this deferred\n                // If obj is provided, the promise aspect is added to the object\n                promise: function( obj ) {\n\n                    return obj != null ? $.extend( obj, promise ) : promise;\n                }\n            },\n            deferred = {};\n\n        // Keep pipe for back-compat\n        promise.pipe = promise.then;\n\n        // Add list-specific methods\n        $.each( tuples, function( i, tuple ) {\n            var list = tuple[ 2 ],\n                stateString = tuple[ 3 ];\n\n            // promise[ done | fail | progress ] = list.add\n            promise[ tuple[ 1 ] ] = list.add;\n\n            // Handle state\n            if ( stateString ) {\n                list.add(function() {\n                    // state = [ resolved | rejected ]\n                    state = stateString;\n\n                // [ reject_list | resolve_list ].disable; progress_list.lock\n                }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );\n            }\n\n            // deferred[ resolve | reject | notify ]\n            deferred[ tuple[ 0 ] ] = function() {\n                deferred[ tuple[ 0 ] + 'With' ]( this === deferred ? promise :\n                        this, arguments );\n                return this;\n            };\n            deferred[ tuple[ 0 ] + 'With' ] = list.fireWith;\n        });\n\n        // Make the deferred a promise\n        promise.promise( deferred );\n\n        // Call given func if any\n        if ( func ) {\n            func.call( deferred, deferred );\n        }\n\n        // All done!\n        return deferred;\n    }\n\n    api = {\n        /**\n         * 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。\n         * 详细的Deferred用法说明，请参照jQuery的API文档。\n         *\n         * Deferred对象在钩子回掉函数中经常要用到，用来处理需要等待的异步操作。\n         *\n         * @for  Base\n         * @method Deferred\n         * @grammar Base.Deferred() => Deferred\n         * @example\n         * // 在文件开始发送前做些异步操作。\n         * // WebUploader会等待此异步操作完成后，开始发送文件。\n         * Uploader.register({\n         *     'before-send-file': 'doSomthingAsync'\n         * }, {\n         *\n         *     doSomthingAsync: function() {\n         *         var deferred = Base.Deferred();\n         *\n         *         // 模拟一次异步操作。\n         *         setTimeout(deferred.resolve, 2000);\n         *\n         *         return deferred.promise();\n         *     }\n         * });\n         */\n        Deferred: Deferred,\n\n        /**\n         * 判断传入的参数是否为一个promise对象。\n         * @method isPromise\n         * @grammar Base.isPromise( anything ) => Boolean\n         * @param  {*}  anything 检测对象。\n         * @return {Boolean}\n         * @for  Base\n         * @example\n         * console.log( Base.isPromise() );    // => false\n         * console.log( Base.isPromise({ key: '123' }) );    // => false\n         * console.log( Base.isPromise( Base.Deferred().promise() ) );    // => true\n         *\n         * // Deferred也是一个Promise\n         * console.log( Base.isPromise( Base.Deferred() ) );    // => true\n         */\n        isPromise: function( anything ) {\n            return anything && typeof anything.then === 'function';\n        },\n\n        /**\n         * 返回一个promise，此promise在所有传入的promise都完成了后完成。\n         * 详细请查看[这里](http://api.jquery.com/jQuery.when/)。\n         *\n         * @method when\n         * @for  Base\n         * @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise\n         */\n        when: function( subordinate /* , ..., subordinateN */ ) {\n            var i = 0,\n                slice = [].slice,\n                resolveValues = slice.call( arguments ),\n                length = resolveValues.length,\n\n                // the count of uncompleted subordinates\n                remaining = length !== 1 || (subordinate &&\n                    $.isFunction( subordinate.promise )) ? length : 0,\n\n                // the master Deferred. If resolveValues consist of\n                // only a single Deferred, just use that.\n                deferred = remaining === 1 ? subordinate : Deferred(),\n\n                // Update function for both resolve and progress values\n                updateFunc = function( i, contexts, values ) {\n                    return function( value ) {\n                        contexts[ i ] = this;\n                        values[ i ] = arguments.length > 1 ?\n                                slice.call( arguments ) : value;\n\n                        if ( values === progressValues ) {\n                            deferred.notifyWith( contexts, values );\n                        } else if ( !(--remaining) ) {\n                            deferred.resolveWith( contexts, values );\n                        }\n                    };\n                },\n\n                progressValues, progressContexts, resolveContexts;\n\n            // add listeners to Deferred subordinates; treat others as resolved\n            if ( length > 1 ) {\n                progressValues = new Array( length );\n                progressContexts = new Array( length );\n                resolveContexts = new Array( length );\n                for ( ; i < length; i++ ) {\n                    if ( resolveValues[ i ] &&\n                            $.isFunction( resolveValues[ i ].promise ) ) {\n\n                        resolveValues[ i ].promise()\n                                .done( updateFunc( i, resolveContexts,\n                                        resolveValues ) )\n                                .fail( deferred.reject )\n                                .progress( updateFunc( i, progressContexts,\n                                        progressValues ) );\n                    } else {\n                        --remaining;\n                    }\n                }\n            }\n\n            // if we're not waiting on anything, resolve the master\n            if ( !remaining ) {\n                deferred.resolveWith( resolveContexts, resolveValues );\n            }\n\n            return deferred.promise();\n        }\n    };\n\n    return api;\n});"
  },
  {
    "path": "src/promise-third.js",
    "content": "/**\n * @fileOverview 使用jQuery的Promise\n */\ndefine([\n    './dollar'\n], function( $ ) {\n    return {\n        Deferred: $.Deferred,\n        when: $.when,\n\n        isPromise: function( anything ) {\n            return anything && typeof anything.then === 'function';\n        }\n    };\n});"
  },
  {
    "path": "src/promise.js",
    "content": "/**\n * @fileOverview Promise/A+\n */\ndefine([\n    './promise-third'\n], function( _ ) {\n    return _;\n});"
  },
  {
    "path": "src/queue.js",
    "content": "/**\n * @fileOverview 文件队列\n */\ndefine([\n    './base',\n    './mediator',\n    './file'\n], function( Base, Mediator, WUFile ) {\n\n    var $ = Base.$,\n        STATUS = WUFile.Status;\n\n    /**\n     * 文件队列, 用来存储各个状态中的文件。\n     * @class Queue\n     * @extends Mediator\n     */\n    function Queue() {\n\n        /**\n         * 统计文件数。\n         * * `numOfQueue` 队列中的文件数。\n         * * `numOfSuccess` 上传成功的文件数\n         * * `numOfCancel` 被取消的文件数\n         * * `numOfProgress` 正在上传中的文件数\n         * * `numOfUploadFailed` 上传错误的文件数。\n         * * `numOfInvalid` 无效的文件数。\n         * * `numOfDeleted` 被移除的文件数。\n         * * `numOfInterrupt` 被中断的文件数。\n         * @property {Object} stats\n         */\n        this.stats = {\n            numOfQueue: 0,\n            numOfSuccess: 0,\n            numOfCancel: 0,\n            numOfProgress: 0,\n            numOfUploadFailed: 0,\n            numOfInvalid: 0,\n            numOfDeleted: 0,\n            numOfInterrupt: 0\n        };\n\n        // 上传队列，仅包括等待上传的文件\n        this._queue = [];\n\n        // 存储所有文件\n        this._map = {};\n    }\n\n    $.extend( Queue.prototype, {\n\n        /**\n         * 将新文件加入对队列尾部\n         *\n         * @method append\n         * @param  {File} file   文件对象\n         */\n        append: function( file ) {\n            this._queue.push( file );\n            this._fileAdded( file );\n            return this;\n        },\n\n        /**\n         * 将新文件加入对队列头部\n         *\n         * @method prepend\n         * @param  {File} file   文件对象\n         */\n        prepend: function( file ) {\n            this._queue.unshift( file );\n            this._fileAdded( file );\n            return this;\n        },\n\n        /**\n         * 获取文件对象\n         *\n         * @method getFile\n         * @param  {String} fileId   文件ID\n         * @return {File}\n         */\n        getFile: function( fileId ) {\n            if ( typeof fileId !== 'string' ) {\n                return fileId;\n            }\n            return this._map[ fileId ];\n        },\n\n        /**\n         * 从队列中取出一个指定状态的文件。\n         * @grammar fetch( status ) => File\n         * @method fetch\n         * @param {String} status [文件状态值](#WebUploader:File:File.Status)\n         * @return {File} [File](#WebUploader:File)\n         */\n        fetch: function( status ) {\n            var len = this._queue.length,\n                i, file;\n\n            status = status || STATUS.QUEUED;\n\n            for ( i = 0; i < len; i++ ) {\n                file = this._queue[ i ];\n\n                if ( status === file.getStatus() ) {\n                    return file;\n                }\n            }\n\n            return null;\n        },\n\n        /**\n         * 对队列进行排序，能够控制文件上传顺序。\n         * @grammar sort( fn ) => undefined\n         * @method sort\n         * @param {Function} fn 排序方法\n         */\n        sort: function( fn ) {\n            if ( typeof fn === 'function' ) {\n                this._queue.sort( fn );\n            }\n        },\n\n        /**\n         * 获取指定类型的文件列表, 列表中每一个成员为[File](#WebUploader:File)对象。\n         * @grammar getFiles( [status1[, status2 ...]] ) => Array\n         * @method getFiles\n         * @param {String} [status] [文件状态值](#WebUploader:File:File.Status)\n         */\n        getFiles: function() {\n            var sts = [].slice.call( arguments, 0 ),\n                ret = [],\n                i = 0,\n                len = this._queue.length,\n                file;\n\n            for ( ; i < len; i++ ) {\n                file = this._queue[ i ];\n\n                if ( sts.length && !~$.inArray( file.getStatus(), sts ) ) {\n                    continue;\n                }\n\n                ret.push( file );\n            }\n\n            return ret;\n        },\n\n        /**\n         * 在队列中删除文件。\n         * @grammar removeFile( file ) => Array\n         * @method removeFile\n         * @param {File} 文件对象。\n         */\n        removeFile: function( file ) {\n            var me = this,\n                existing = this._map[ file.id ];\n\n            if ( existing ) {\n                delete this._map[ file.id ];\n                this._delFile(file);\n                file.destroy();\n                this.stats.numOfDeleted++;\n                \n            }\n        },\n\t\t\n        _fileAdded: function( file ) {\n            var me = this,\n                existing = this._map[ file.id ];\n\n            if ( !existing ) {\n                this._map[ file.id ] = file;\n\n                file.on( 'statuschange', function( cur, pre ) {\n                    me._onFileStatusChange( cur, pre );\n                });\n            }\n        },\n\n        _delFile : function(file){\n            for(var i = this._queue.length - 1 ; i >= 0 ; i-- ){\n                if(this._queue[i] == file){\n                    this._queue.splice(i,1); \n                    break;\n                }\n            }\n        },\n\n        _onFileStatusChange: function( curStatus, preStatus ) {\n            var stats = this.stats;\n\n            switch ( preStatus ) {\n                case STATUS.PROGRESS:\n                    stats.numOfProgress--;\n                    break;\n\n                case STATUS.QUEUED:\n                    stats.numOfQueue --;\n                    break;\n\n                case STATUS.ERROR:\n                    stats.numOfUploadFailed--;\n                    break;\n\n                case STATUS.INVALID:\n                    stats.numOfInvalid--;\n                    break;\n\n                case STATUS.INTERRUPT:\n                    stats.numOfInterrupt--;\n                    break;\n            }\n\n            switch ( curStatus ) {\n                case STATUS.QUEUED:\n                    stats.numOfQueue++;\n                    break;\n\n                case STATUS.PROGRESS:\n                    stats.numOfProgress++;\n                    break;\n\n                case STATUS.ERROR:\n                    stats.numOfUploadFailed++;\n                    break;\n\n                case STATUS.COMPLETE:\n                    stats.numOfSuccess++;\n                    break;\n\n                case STATUS.CANCELLED:\n                    stats.numOfCancel++;\n                    break;\n\n\n                case STATUS.INVALID:\n                    stats.numOfInvalid++;\n                    break;\n\n                case STATUS.INTERRUPT:\n                    stats.numOfInterrupt++;\n                    break;\n            }\n        }\n\n    });\n\n    Mediator.installTo( Queue.prototype );\n\n    return Queue;\n});\n"
  },
  {
    "path": "src/runtime/client.js",
    "content": "/**\n * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n */\ndefine([\n    '../base',\n    '../mediator',\n    './runtime'\n], function( Base, Mediator, Runtime ) {\n\n    var cache;\n\n    cache = (function() {\n        var obj = {};\n\n        return {\n            add: function( runtime ) {\n                obj[ runtime.uid ] = runtime;\n            },\n\n            get: function( ruid, standalone ) {\n                var i;\n\n                if ( ruid ) {\n                    return obj[ ruid ];\n                }\n\n                for ( i in obj ) {\n                    // 有些类型不能重用，比如filepicker.\n                    if ( standalone && obj[ i ].__standalone ) {\n                        continue;\n                    }\n\n                    return obj[ i ];\n                }\n\n                return null;\n            },\n\n            remove: function( runtime ) {\n                delete obj[ runtime.uid ];\n            }\n        };\n    })();\n\n    function RuntimeClient( component, standalone ) {\n        var deferred = Base.Deferred(),\n            runtime;\n\n        this.uid = Base.guid('client_');\n\n        // 允许runtime没有初始化之前，注册一些方法在初始化后执行。\n        this.runtimeReady = function( cb ) {\n            return deferred.done( cb );\n        };\n\n        this.connectRuntime = function( opts, cb ) {\n\n            // already connected.\n            if ( runtime ) {\n                throw new Error('already connected!');\n            }\n\n            deferred.done( cb );\n\n            if ( typeof opts === 'string' && cache.get( opts ) ) {\n                runtime = cache.get( opts );\n            }\n\n            // 像filePicker只能独立存在，不能公用。\n            runtime = runtime || cache.get( null, standalone );\n\n            // 需要创建\n            if ( !runtime ) {\n                runtime = Runtime.create( opts, opts.runtimeOrder );\n                runtime.__promise = deferred.promise();\n                runtime.once( 'ready', deferred.resolve );\n                runtime.init();\n                cache.add( runtime );\n                runtime.__client = 1;\n            } else {\n                // 来自cache\n                Base.$.extend( runtime.options, opts );\n                runtime.__promise.then( deferred.resolve );\n                runtime.__client++;\n            }\n\n            standalone && (runtime.__standalone = standalone);\n            return runtime;\n        };\n\n        this.getRuntime = function() {\n            return runtime;\n        };\n\n        this.disconnectRuntime = function() {\n            if ( !runtime ) {\n                return;\n            }\n\n            runtime.__client--;\n\n            if ( runtime.__client <= 0 ) {\n                cache.remove( runtime );\n                delete runtime.__promise;\n                runtime.destroy();\n            }\n\n            runtime = null;\n        };\n\n        this.exec = function() {\n            if ( !runtime ) {\n                return;\n            }\n\n            var args = Base.slice( arguments );\n            component && args.unshift( component );\n\n            return runtime.exec.apply( this, args );\n        };\n\n        this.getRuid = function() {\n            return runtime && runtime.uid;\n        };\n\n        this.destroy = (function( destroy ) {\n            return function() {\n                destroy && destroy.apply( this, arguments );\n                this.trigger('destroy');\n                this.off();\n                this.exec('destroy');\n                this.disconnectRuntime();\n            };\n        })( this.destroy );\n    }\n\n    Mediator.installTo( RuntimeClient.prototype );\n    return RuntimeClient;\n});"
  },
  {
    "path": "src/runtime/compbase.js",
    "content": "/**\n * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n */\ndefine(function() {\n\n    function CompBase( owner, runtime ) {\n\n        this.owner = owner;\n        this.options = owner.options;\n\n        this.getRuntime = function() {\n            return runtime;\n        };\n\n        this.getRuid = function() {\n            return runtime.uid;\n        };\n\n        this.trigger = function() {\n            return owner.trigger.apply( owner, arguments );\n        };\n    }\n\n    return CompBase;\n});"
  },
  {
    "path": "src/runtime/flash/blob.js",
    "content": "/**\n * @fileOverview Blob Html实现\n */\ndefine([\n    './runtime',\n    '../../lib/blob'\n], function( FlashRuntime, Blob ) {\n\n    return FlashRuntime.register( 'Blob', {\n        slice: function( start, end ) {\n            var blob = this.flashExec( 'Blob', 'slice', start, end );\n\n            return new Blob( this.getRuid(), blob );\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/flash/filepicker.js",
    "content": "/**\n * @fileOverview FilePicker\n */\ndefine([\n    '../../base',\n    './runtime'\n], function( Base, FlashRuntime ) {\n    var $ = Base.$;\n\n    return FlashRuntime.register( 'FilePicker', {\n        init: function( opts ) {\n            var copy = $.extend({}, opts ),\n                len, i;\n\n            // 修复Flash再没有设置title的情况下无法弹出flash文件选择框的bug.\n            len = copy.accept && copy.accept.length;\n            for (  i = 0; i < len; i++ ) {\n                if ( !copy.accept[ i ].title ) {\n                    copy.accept[ i ].title = 'Files';\n                }\n            }\n\n            delete copy.button;\n            delete copy.id;\n            delete copy.container;\n\n            this.flashExec( 'FilePicker', 'init', copy );\n        },\n\n        destroy: function() {\n            this.flashExec( 'FilePicker', 'destroy' );\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/flash/image.js",
    "content": "/**\n * @fileOverview 图片压缩\n */\ndefine([\n    './runtime'\n], function( FlashRuntime ) {\n\n    return FlashRuntime.register( 'Image', {\n        // init: function( options ) {\n        //     var owner = this.owner;\n\n        //     this.flashExec( 'Image', 'init', options );\n        //     owner.on( 'load', function() {\n        //         debugger;\n        //     });\n        // },\n\n        loadFromBlob: function( blob ) {\n            var owner = this.owner;\n\n            owner.info() && this.flashExec( 'Image', 'info', owner.info() );\n            owner.meta() && this.flashExec( 'Image', 'meta', owner.meta() );\n\n            this.flashExec( 'Image', 'loadFromBlob', blob.uid );\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/flash/md5.js",
    "content": "/**\n * @fileOverview  Md5 flash实现\n */\ndefine([\n    './runtime'\n], function( FlashRuntime ) {\n    \n    return FlashRuntime.register( 'Md5', {\n        init: function() {\n            // do nothing.\n        },\n\n        loadFromBlob: function( blob ) {\n            return this.flashExec( 'Md5', 'loadFromBlob', blob.uid );\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/flash/runtime.js",
    "content": "/**\n * @fileOverview FlashRuntime\n */\ndefine([\n    '../../base',\n    '../runtime',\n    '../compbase'\n], function( Base, Runtime, CompBase ) {\n\n    var $ = Base.$,\n        type = 'flash',\n        components = {};\n\n\n    function getFlashVersion() {\n        var version;\n\n        try {\n            version = navigator.plugins[ 'Shockwave Flash' ];\n            version = version.description;\n        } catch ( ex ) {\n            try {\n                version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')\n                        .GetVariable('$version');\n            } catch ( ex2 ) {\n                version = '0.0';\n            }\n        }\n        version = version.match( /\\d+/g );\n        return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );\n    }\n\n    function FlashRuntime() {\n        var pool = {},\n            clients = {},\n            destroy = this.destroy,\n            me = this,\n            jsreciver = Base.guid('webuploader_');\n\n        Runtime.apply( me, arguments );\n        me.type = type;\n\n\n        // 这个方法的调用者，实际上是RuntimeClient\n        me.exec = function( comp, fn/*, args...*/ ) {\n            var client = this,\n                uid = client.uid,\n                args = Base.slice( arguments, 2 ),\n                instance;\n\n            clients[ uid ] = client;\n\n            if ( components[ comp ] ) {\n                if ( !pool[ uid ] ) {\n                    pool[ uid ] = new components[ comp ]( client, me );\n                }\n\n                instance = pool[ uid ];\n\n                if ( instance[ fn ] ) {\n                    return instance[ fn ].apply( instance, args );\n                }\n            }\n\n            return me.flashExec.apply( client, arguments );\n        };\n\n        function handler( evt, obj ) {\n            var type = evt.type || evt,\n                parts, uid;\n\n            parts = type.split('::');\n            uid = parts[ 0 ];\n            type = parts[ 1 ];\n\n            // console.log.apply( console, arguments );\n\n            if ( type === 'Ready' && uid === me.uid ) {\n                me.trigger('ready');\n            } else if ( clients[ uid ] ) {\n                clients[ uid ].trigger( type.toLowerCase(), evt, obj );\n            }\n\n            // Base.log( evt, obj );\n        }\n\n        // flash的接受器。\n        window[ jsreciver ] = function() {\n            var args = arguments;\n\n            // 为了能捕获得到。\n            setTimeout(function() {\n                handler.apply( null, args );\n            }, 1 );\n        };\n\n        this.jsreciver = jsreciver;\n\n        this.destroy = function() {\n            // @todo 删除池子中的所有实例\n            return destroy && destroy.apply( this, arguments );\n        };\n\n        this.flashExec = function( comp, fn ) {\n            var flash = me.getFlash(),\n                args = Base.slice( arguments, 2 );\n\n            return flash.exec( this.uid, comp, fn, args );\n        };\n\n        // @todo\n    }\n\n    Base.inherits( Runtime, {\n        constructor: FlashRuntime,\n\n        init: function() {\n            var container = this.getContainer(),\n                opts = this.options,\n                html;\n\n            // if not the minimal height, shims are not initialized\n            // in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)\n            container.css({\n                position: 'absolute',\n                top: '-8px',\n                left: '-8px',\n                width: '9px',\n                height: '9px',\n                overflow: 'hidden'\n            });\n\n            // insert flash object\n            html = '<object id=\"' + this.uid + '\" type=\"application/' +\n                    'x-shockwave-flash\" data=\"' +  opts.swf + '\" ';\n\n            if ( Base.browser.ie ) {\n                html += 'classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" ';\n            }\n\n            html += 'width=\"100%\" height=\"100%\" style=\"outline:0\">'  +\n                '<param name=\"movie\" value=\"' + opts.swf + '\" />' +\n                '<param name=\"flashvars\" value=\"uid=' + this.uid +\n                '&jsreciver=' + this.jsreciver + '\" />' +\n                '<param name=\"wmode\" value=\"transparent\" />' +\n                '<param name=\"allowscriptaccess\" value=\"always\" />' +\n            '</object>';\n\n            container.html( html );\n        },\n\n        getFlash: function() {\n            if ( this._flash ) {\n                return this._flash;\n            }\n\n            this._flash = $( '#' + this.uid ).get( 0 );\n            return this._flash;\n        }\n\n    });\n\n    FlashRuntime.register = function( name, component ) {\n        component = components[ name ] = Base.inherits( CompBase, $.extend({\n\n            // @todo fix this later\n            flashExec: function() {\n                var owner = this.owner,\n                    runtime = this.getRuntime();\n\n                return runtime.flashExec.apply( owner, arguments );\n            }\n        }, component ) );\n\n        return component;\n    };\n\n    if ( getFlashVersion() >= 11.4 ) {\n        Runtime.addRuntime( type, FlashRuntime );\n    }\n\n    return FlashRuntime;\n});"
  },
  {
    "path": "src/runtime/flash/transport.js",
    "content": "/**\n * @fileOverview  Transport flash实现\n */\ndefine([\n    '../../base',\n    './runtime',\n    '../client'\n], function( Base, FlashRuntime, RuntimeClient ) {\n    var $ = Base.$;\n\n    return FlashRuntime.register( 'Transport', {\n        init: function() {\n            this._status = 0;\n            this._response = null;\n            this._responseJson = null;\n        },\n\n        send: function() {\n            var owner = this.owner,\n                opts = this.options,\n                xhr = this._initAjax(),\n                blob = owner._blob,\n                server = opts.server,\n                binary;\n\n            xhr.connectRuntime( blob.ruid );\n\n            if ( opts.sendAsBinary ) {\n                server += (/\\?/.test( server ) ? '&' : '?') +\n                        $.param( owner._formData );\n\n                binary = blob.uid;\n            } else {\n                $.each( owner._formData, function( k, v ) {\n                    xhr.exec( 'append', k, v );\n                });\n\n                xhr.exec( 'appendBlob', opts.fileVal, blob.uid,\n                        opts.filename || owner._formData.name || '' );\n            }\n\n            this._setRequestHeader( xhr, opts.headers );\n            xhr.exec( 'send', {\n                method: opts.method,\n                url: server,\n                forceURLStream: opts.forceURLStream,\n                mimeType: 'application/octet-stream'\n            }, binary );\n        },\n\n        getStatus: function() {\n            return this._status;\n        },\n\n        getResponse: function() {\n            return this._response || '';\n        },\n\n        getResponseAsJson: function() {\n            return this._responseJson;\n        },\n\n        getResponseHeaders: function() {\n            // flash 暂不支持\n            return {};\n        },\n\n        abort: function() {\n            var xhr = this._xhr;\n\n            if ( xhr ) {\n                xhr.exec('abort');\n                xhr.destroy();\n                this._xhr = xhr = null;\n            }\n        },\n\n        destroy: function() {\n            this.abort();\n        },\n\n        _initAjax: function() {\n            var me = this,\n                xhr = new RuntimeClient('XMLHttpRequest');\n\n            xhr.on( 'uploadprogress progress', function( e ) {\n                var percent = e.loaded / e.total;\n                percent = Math.min( 1, Math.max( 0, percent ) );\n                return me.trigger( 'progress', percent );\n            });\n\n            xhr.on( 'load', function() {\n                var status = xhr.exec('getStatus'),\n                    readBody = false,\n                    err = '',\n                    p;\n\n                xhr.off();\n                me._xhr = null;\n\n                if ( status >= 200 && status < 300 ) {\n                    readBody = true;\n                } else if ( status >= 500 && status < 600 ) {\n                    readBody = true;\n                    err = 'server-'+status;\n                } else {\n                    err = 'http-'+status;\n                }\n\n                if ( readBody ) {\n                    me._response = xhr.exec('getResponse');\n                    me._response = decodeURIComponent( me._response );\n\n                    // flash 处理可能存在 bug, 没辙只能靠 js 了\n                    // try {\n                    //     me._responseJson = xhr.exec('getResponseAsJson');\n                    // } catch ( error ) {\n\n                    p = function( s ) {\n                        try {\n                            if (window.JSON && window.JSON.parse) {\n                                return JSON.parse(s);\n                            }\n\n                            return new Function('return ' + s).call();\n                        } catch ( err ) {\n                            return {};\n                        }\n                    };\n                    me._responseJson  = me._response ? p(me._response) : {};\n\n                    // }\n                }\n\n                xhr.destroy();\n                xhr = null;\n\n                return err ? me.trigger( 'error', err ) : me.trigger('load');\n            });\n\n            xhr.on( 'error', function() {\n                var status = xhr.exec('getStatus'),err = status?'http-'+status:'http';\n                xhr.off();\n                me._xhr = null;\n                me.trigger( 'error', err );\n            });\n\n            me._xhr = xhr;\n            return xhr;\n        },\n\n        _setRequestHeader: function( xhr, headers ) {\n            $.each( headers, function( key, val ) {\n                xhr.exec( 'setRequestHeader', key, val );\n            });\n        }\n    });\n});\n"
  },
  {
    "path": "src/runtime/html5/androidpatch.js",
    "content": "/**\n * @fileOverview Fix android canvas.toDataUrl bug.\n */\ndefine([\n    './util',\n    './jpegencoder',\n    '../../base'\n], function( Util, encoder, Base ) {\n    var origin = Util.canvasToDataUrl,\n        supportJpeg;\n\n    Util.canvasToDataUrl = function( canvas, type, quality ) {\n        var ctx, w, h, fragement, parts;\n\n        // 非android手机直接跳过。\n        if ( !Base.os.android ) {\n            return origin.apply( null, arguments );\n        }\n\n        // 检测是否canvas支持jpeg导出，根据数据格式来判断。\n        // JPEG 前两位分别是：255, 216\n        if ( type === 'image/jpeg' && typeof supportJpeg === 'undefined' ) {\n            fragement = origin.apply( null, arguments );\n\n            parts = fragement.split(',');\n\n            if ( ~parts[ 0 ].indexOf('base64') ) {\n                fragement = atob( parts[ 1 ] );\n            } else {\n                fragement = decodeURIComponent( parts[ 1 ] );\n            }\n\n            fragement = fragement.substring( 0, 2 );\n\n            supportJpeg = fragement.charCodeAt( 0 ) === 255 &&\n                    fragement.charCodeAt( 1 ) === 216;\n        }\n\n        // 只有在android环境下才修复\n        if ( type === 'image/jpeg' && !supportJpeg ) {\n            w = canvas.width;\n            h = canvas.height;\n            ctx = canvas.getContext('2d');\n\n            return encoder.encode( ctx.getImageData( 0, 0, w, h ), quality );\n        }\n\n        return origin.apply( null, arguments );\n    };\n});"
  },
  {
    "path": "src/runtime/html5/blob.js",
    "content": "/**\n * @fileOverview Blob Html实现\n */\ndefine([\n    './runtime',\n    '../../lib/blob'\n], function( Html5Runtime, Blob ) {\n\n    return Html5Runtime.register( 'Blob', {\n        slice: function( start, end ) {\n            var blob = this.owner.source,\n                slice = blob.slice || blob.webkitSlice || blob.mozSlice;\n\n            blob = slice.call( blob, start, end );\n\n            return new Blob( this.getRuid(), blob );\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/html5/dnd.js",
    "content": "/**\r\n * @fileOverview FilePaste\r\n */\r\ndefine([\r\n    '../../base',\r\n    './runtime',\r\n    '../../lib/file'\r\n], function( Base, Html5Runtime, File ) {\r\n\r\n    var $ = Base.$,\r\n        prefix = 'webuploader-dnd-';\r\n\r\n    return Html5Runtime.register( 'DragAndDrop', {\r\n        init: function() {\r\n            var elem = this.elem = this.options.container;\r\n\r\n            this.dragEnterHandler = Base.bindFn( this._dragEnterHandler, this );\r\n            this.dragOverHandler = Base.bindFn( this._dragOverHandler, this );\r\n            this.dragLeaveHandler = Base.bindFn( this._dragLeaveHandler, this );\r\n            this.dropHandler = Base.bindFn( this._dropHandler, this );\r\n            this.dndOver = false;\r\n\r\n            elem.on( 'dragenter', this.dragEnterHandler );\r\n            elem.on( 'dragover', this.dragOverHandler );\r\n            elem.on( 'dragleave', this.dragLeaveHandler );\r\n            elem.on( 'drop', this.dropHandler );\r\n\r\n            if ( this.options.disableGlobalDnd ) {\r\n                $( document ).on( 'dragover', this.dragOverHandler );\r\n                $( document ).on( 'drop', this.dropHandler );\r\n            }\r\n        },\r\n\r\n        _dragEnterHandler: function( e ) {\r\n            var me = this,\r\n                denied = me._denied || false,\r\n                items;\r\n\r\n            e = e.originalEvent || e;\r\n\r\n            if ( !me.dndOver ) {\r\n                me.dndOver = true;\r\n\r\n                // 注意只有 chrome 支持。\r\n                items = e.dataTransfer.items;\r\n\r\n                if ( items && items.length ) {\r\n                    me._denied = denied = !me.trigger( 'accept', items );\r\n                }\r\n\r\n                me.elem.addClass( prefix + 'over' );\r\n                me.elem[ denied ? 'addClass' :\r\n                        'removeClass' ]( prefix + 'denied' );\r\n            }\r\n\r\n            e.dataTransfer.dropEffect = denied ? 'none' : 'copy';\r\n\r\n            return false;\r\n        },\r\n\r\n        _dragOverHandler: function( e ) {\r\n            // 只处理框内的。\r\n            var parentElem = this.elem.parent().get( 0 );\r\n            if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                return false;\r\n            }\r\n\r\n            clearTimeout( this._leaveTimer );\r\n            this._dragEnterHandler.call( this, e );\r\n\r\n            return false;\r\n        },\r\n\r\n        _dragLeaveHandler: function() {\r\n            var me = this,\r\n                handler;\r\n\r\n            handler = function() {\r\n                me.dndOver = false;\r\n                me.elem.removeClass( prefix + 'over ' + prefix + 'denied' );\r\n            };\r\n\r\n            clearTimeout( me._leaveTimer );\r\n            me._leaveTimer = setTimeout( handler, 100 );\r\n            return false;\r\n        },\r\n\r\n        _dropHandler: function( e ) {\r\n            var me = this,\r\n                ruid = me.getRuid(),\r\n                parentElem = me.elem.parent().get( 0 ),\r\n                dataTransfer, data;\r\n\r\n            // 只处理框内的。\r\n            if ( parentElem && !$.contains( parentElem, e.currentTarget ) ) {\r\n                return false;\r\n            }\r\n\r\n            e = e.originalEvent || e;\r\n            dataTransfer = e.dataTransfer;\r\n\r\n            // 如果是页面内拖拽，还不能处理，不阻止事件。\r\n            // 此处 ie11 下会报参数错误，\r\n            try {\r\n                data = dataTransfer.getData('text/html');\r\n            } catch( err ) {\r\n            }\r\n\r\n            me.dndOver = false;\r\n            me.elem.removeClass( prefix + 'over' );\r\n\r\n            if ( !dataTransfer || data ) {\r\n                return;\r\n            }\r\n\r\n            me._getTansferFiles( dataTransfer, function( results ) {\r\n                me.trigger( 'drop', $.map( results, function( file ) {\r\n                    return new File( ruid, file );\r\n                }) );\r\n            });\r\n\r\n            return false;\r\n        },\r\n\r\n        // 如果传入 callback 则去查看文件夹，否则只管当前文件夹。\r\n        _getTansferFiles: function( dataTransfer, callback ) {\r\n            var results  = [],\r\n                promises = [],\r\n                items, files, file, item, i, len, canAccessFolder;\r\n\r\n            items = dataTransfer.items;\r\n            files = dataTransfer.files;\r\n\r\n            canAccessFolder = !!(items && items[ 0 ].webkitGetAsEntry);\r\n\r\n            for ( i = 0, len = files.length; i < len; i++ ) {\r\n                file = files[ i ];\r\n                item = items && items[ i ];\r\n\r\n                if ( canAccessFolder && item.webkitGetAsEntry().isDirectory ) {\r\n\r\n                    promises.push( this._traverseDirectoryTree(\r\n                            item.webkitGetAsEntry(), results ) );\r\n                } else {\r\n                    results.push( file );\r\n                }\r\n            }\r\n\r\n            Base.when.apply( Base, promises ).done(function() {\r\n\r\n                if ( !results.length ) {\r\n                    return;\r\n                }\r\n\r\n                callback( results );\r\n            });\r\n        },\r\n\r\n        _traverseDirectoryTree: function( entry, results ) {\r\n            var deferred = Base.Deferred(),\r\n                me = this;\r\n\r\n            if ( entry.isFile ) {\r\n                entry.file(function( file ) {\r\n                    results.push( file );\r\n                    deferred.resolve();\r\n                });\r\n            } else if ( entry.isDirectory ) {\r\n                entry.createReader().readEntries(function( entries ) {\r\n                    var len = entries.length,\r\n                        promises = [],\r\n                        arr = [],    // 为了保证顺序。\r\n                        i;\r\n\r\n                    for ( i = 0; i < len; i++ ) {\r\n                        promises.push( me._traverseDirectoryTree(\r\n                                entries[ i ], arr ) );\r\n                    }\r\n\r\n                    Base.when.apply( Base, promises ).then(function() {\r\n                        results.push.apply( results, arr );\r\n                        deferred.resolve();\r\n                    }, deferred.reject );\r\n                });\r\n            }\r\n\r\n            return deferred.promise();\r\n        },\r\n\r\n        destroy: function() {\r\n            var elem = this.elem;\r\n\r\n            // 还没 init 就调用 destroy\r\n            if (!elem) {\r\n                return;\r\n            }\r\n\r\n            elem.off( 'dragenter', this.dragEnterHandler );\r\n            elem.off( 'dragover', this.dragOverHandler );\r\n            elem.off( 'dragleave', this.dragLeaveHandler );\r\n            elem.off( 'drop', this.dropHandler );\r\n\r\n            if ( this.options.disableGlobalDnd ) {\r\n                $( document ).off( 'dragover', this.dragOverHandler );\r\n                $( document ).off( 'drop', this.dropHandler );\r\n            }\r\n        }\r\n    });\r\n});\r\n"
  },
  {
    "path": "src/runtime/html5/filepaste.js",
    "content": "/**\r\n * @fileOverview FilePaste\r\n */\r\ndefine([\r\n    '../../base',\r\n    './runtime',\r\n    '../../lib/file'\r\n], function( Base, Html5Runtime, File ) {\r\n\r\n    return Html5Runtime.register( 'FilePaste', {\r\n        init: function() {\r\n            var opts = this.options,\r\n                elem = this.elem = opts.container,\r\n                accept = '.*',\r\n                arr, i, len, item;\r\n\r\n            // accetp的mimeTypes中生成匹配正则。\r\n            if ( opts.accept ) {\r\n                arr = [];\r\n\r\n                for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                    item = opts.accept[ i ].mimeTypes;\r\n                    item && arr.push( item );\r\n                }\r\n\r\n                if ( arr.length ) {\r\n                    accept = arr.join(',');\r\n                    accept = accept.replace( /,/g, '|' ).replace( /\\*/g, '.*' );\r\n                }\r\n            }\r\n            this.accept = accept = new RegExp( accept, 'i' );\r\n            this.hander = Base.bindFn( this._pasteHander, this );\r\n            elem.on( 'paste', this.hander );\r\n        },\r\n\r\n        _pasteHander: function( e ) {\r\n            var allowed = [],\r\n                ruid = this.getRuid(),\r\n                items, item, blob, i, len;\r\n\r\n            e = e.originalEvent || e;\r\n            items = e.clipboardData.items;\r\n\r\n            for ( i = 0, len = items.length; i < len; i++ ) {\r\n                item = items[ i ];\r\n\r\n                if ( item.kind !== 'file' || !(blob = item.getAsFile()) ) {\r\n                    continue;\r\n                }\r\n\r\n                allowed.push( new File( ruid, blob ) );\r\n            }\r\n\r\n            if ( allowed.length ) {\r\n                // 不阻止非文件粘贴（文字粘贴）的事件冒泡\r\n                e.preventDefault();\r\n                e.stopPropagation();\r\n                this.trigger( 'paste', allowed );\r\n            }\r\n        },\r\n\r\n        destroy: function() {\r\n            this.elem.off( 'paste', this.hander );\r\n        }\r\n    });\r\n});\r\n"
  },
  {
    "path": "src/runtime/html5/filepicker.js",
    "content": "/**\r\n * @fileOverview FilePicker\r\n */\r\ndefine([\r\n    '../../base',\r\n    './runtime'\r\n], function( Base, Html5Runtime ) {\r\n\r\n    var $ = Base.$;\r\n\r\n    return Html5Runtime.register( 'FilePicker', {\r\n        init: function() {\r\n            var container = this.getRuntime().getContainer(),\r\n                me = this,\r\n                owner = me.owner,\r\n                opts = me.options,\r\n                label = this.label = $( document.createElement('label') ),\r\n                input =  this.input = $( document.createElement('input') ),\r\n                arr, i, len, mouseHandler, changeHandler;\r\n\r\n            input.attr( 'type', 'file' );\r\n            input.attr( 'capture', 'camera');\r\n            input.attr( 'name', opts.name );\r\n            input.addClass('webuploader-element-invisible');\r\n\r\n            label.on( 'click', function(e) {\r\n                input.trigger('click');\r\n                e.stopPropagation();\r\n                owner.trigger('dialogopen');\r\n            });\r\n\r\n            label.css({\r\n                opacity: 0,\r\n                width: '100%',\r\n                height: '100%',\r\n                display: 'block',\r\n                cursor: 'pointer',\r\n                background: '#ffffff'\r\n            });\r\n\r\n            if ( opts.multiple ) {\r\n                input.attr( 'multiple', 'multiple' );\r\n            }\r\n\r\n            // @todo Firefox不支持单独指定后缀\r\n            if ( opts.accept && opts.accept.length > 0 ) {\r\n                arr = [];\r\n\r\n                for ( i = 0, len = opts.accept.length; i < len; i++ ) {\r\n                    arr.push( opts.accept[ i ].mimeTypes );\r\n                }\r\n\r\n                input.attr( 'accept', arr.join(',') );\r\n            }\r\n\r\n            container.append( input );\r\n            container.append( label );\r\n\r\n            mouseHandler = function( e ) {\r\n                owner.trigger( e.type );\r\n            };\r\n\r\n            changeHandler = function( e ) {\r\n                var clone;\r\n\r\n                // 解决chrome 56 第二次打开文件选择器，然后点击取消，依然会触发change事件的问题\r\n                if (e.target.files.length === 0){\r\n                    return false;\r\n                }\r\n\r\n                // 第一次上传图片后，第二次再点击弹出文件选择器窗，等待\r\n                me.files = e.target.files;\r\n\r\n\r\n                // reset input\r\n                clone = this.cloneNode( true );\r\n                clone.value = null;\r\n                this.parentNode.replaceChild( clone, this );\r\n\r\n                input.off();\r\n                input = $( clone ).on( 'change', changeHandler )\r\n                        .on( 'mouseenter mouseleave', mouseHandler );\r\n\r\n                owner.trigger('change');\r\n            }\r\n            input.on( 'change', changeHandler);\r\n            label.on( 'mouseenter mouseleave', mouseHandler );\r\n\r\n        },\r\n\r\n\r\n        getFiles: function() {\r\n            return this.files;\r\n        },\r\n\r\n        destroy: function() {\r\n            this.input.off();\r\n            this.label.off();\r\n        }\r\n    });\r\n});\r\n"
  },
  {
    "path": "src/runtime/html5/image.js",
    "content": "/**\n * @fileOverview Image\n */\ndefine([\n    '../../base',\n    './runtime',\n    './util'\n], function( Base, Html5Runtime, Util ) {\n\n    var BLANK = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D';\n\n    return Html5Runtime.register( 'Image', {\n\n        // flag: 标记是否被修改过。\n        modified: false,\n\n        init: function() {\n            var me = this,\n                img = new Image();\n\n            img.onload = function() {\n\n                me._info = {\n                    type: me.type,\n                    width: this.width,\n                    height: this.height\n                };\n\n                //debugger;\n\n                // 读取meta信息。\n                if ( !me._metas && 'image/jpeg' === me.type ) {\n                    Util.parseMeta( me._blob, function( error, ret ) {\n                        me._metas = ret;\n                        me.owner.trigger('load');\n                    });\n                } else {\n                    me.owner.trigger('load');\n                }\n            };\n\n            img.onerror = function() {\n                me.owner.trigger('error');\n            };\n\n            me._img = img;\n        },\n\n        loadFromBlob: function( blob ) {\n            var me = this,\n                img = me._img;\n\n            me._blob = blob;\n            me.type = blob.type;\n            img.src = Util.createObjectURL( blob.getSource() );\n            me.owner.once( 'load', function() {\n                Util.revokeObjectURL( img.src );\n            });\n        },\n\n        resize: function( width, height ) {\n            var canvas = this._canvas ||\n                    (this._canvas = document.createElement('canvas'));\n\n            this._resize( this._img, canvas, width, height );\n            this._blob = null;    // 没用了，可以删掉了。\n            this.modified = true;\n            this.owner.trigger( 'complete', 'resize' );\n        },\n\n        crop: function( x, y, w, h, s ) {\n            var cvs = this._canvas ||\n                    (this._canvas = document.createElement('canvas')),\n                opts = this.options,\n                img = this._img,\n                iw = img.naturalWidth,\n                ih = img.naturalHeight,\n                orientation = this.getOrientation();\n\n            s = s || 1;\n\n            // todo 解决 orientation 的问题。\n            // values that require 90 degree rotation\n            // if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n\n            //     switch ( orientation ) {\n            //         case 6:\n            //             tmp = x;\n            //             x = y;\n            //             y = iw * s - tmp - w;\n            //             console.log(ih * s, tmp, w)\n            //             break;\n            //     }\n\n            //     (w ^= h, h ^= w, w ^= h);\n            // }\n\n            cvs.width = w;\n            cvs.height = h;\n\n            opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n            this._renderImageToCanvas( cvs, img, -x, -y, iw * s, ih * s );\n\n            this._blob = null;    // 没用了，可以删掉了。\n            this.modified = true;\n            this.owner.trigger( 'complete', 'crop' );\n        },\n\n        getAsBlob: function( type ) {\n            var blob = this._blob,\n                opts = this.options,\n                canvas;\n\n            type = type || this.type;\n\n            // blob需要重新生成。\n            if ( this.modified || this.type !== type ) {\n                canvas = this._canvas;\n\n                if ( type === 'image/jpeg' ) {\n\n                    blob = Util.canvasToDataUrl( canvas, type, opts.quality );\n\n                    if ( opts.preserveHeaders && this._metas &&\n                            this._metas.imageHead ) {\n\n                        blob = Util.dataURL2ArrayBuffer( blob );\n                        blob = Util.updateImageHead( blob,\n                                this._metas.imageHead );\n                        blob = Util.arrayBufferToBlob( blob, type );\n                        return blob;\n                    }\n                } else {\n                    blob = Util.canvasToDataUrl( canvas, type );\n                }\n\n                blob = Util.dataURL2Blob( blob );\n            }\n\n            return blob;\n        },\n\n        getAsDataUrl: function( type ) {\n            var opts = this.options;\n\n            type = type || this.type;\n\n            if ( type === 'image/jpeg' ) {\n                return Util.canvasToDataUrl( this._canvas, type, opts.quality );\n            } else {\n                return this._canvas.toDataURL( type );\n            }\n        },\n\n        getOrientation: function() {\n            return this._metas && this._metas.exif &&\n                    this._metas.exif.get('Orientation') || 1;\n        },\n\n        info: function( val ) {\n\n            // setter\n            if ( val ) {\n                this._info = val;\n                return this;\n            }\n\n            // getter\n            return this._info;\n        },\n\n        meta: function( val ) {\n\n            // setter\n            if ( val ) {\n                this._metas = val;\n                return this;\n            }\n\n            // getter\n            return this._metas;\n        },\n\n        destroy: function() {\n            var canvas = this._canvas;\n            this._img.onload = null;\n\n            if ( canvas ) {\n                canvas.getContext('2d')\n                        .clearRect( 0, 0, canvas.width, canvas.height );\n                canvas.width = canvas.height = 0;\n                this._canvas = null;\n            }\n\n            // 释放内存。非常重要，否则释放不了image的内存。\n            this._img.src = BLANK;\n            this._img = this._blob = null;\n        },\n\n        _resize: function( img, cvs, width, height ) {\n            var opts = this.options,\n                naturalWidth = img.width,\n                naturalHeight = img.height,\n                orientation = this.getOrientation(),\n                scale, w, h, x, y;\n\n            // values that require 90 degree rotation\n            if ( ~[ 5, 6, 7, 8 ].indexOf( orientation ) ) {\n\n                // 交换width, height的值。\n                width ^= height;\n                height ^= width;\n                width ^= height;\n            }\n\n            scale = Math[ opts.crop ? 'max' : 'min' ]( width / naturalWidth,\n                    height / naturalHeight );\n\n            // 不允许放大。\n            opts.allowMagnify || (scale = Math.min( 1, scale ));\n\n            w = naturalWidth * scale;\n            h = naturalHeight * scale;\n\n            if ( opts.crop ) {\n                cvs.width = width;\n                cvs.height = height;\n            } else {\n                cvs.width = w;\n                cvs.height = h;\n            }\n\n            x = (cvs.width - w) / 2;\n            y = (cvs.height - h) / 2;\n\n            opts.preserveHeaders || this._rotate2Orientaion( cvs, orientation );\n\n            this._renderImageToCanvas( cvs, img, x, y, w, h );\n        },\n\n        _rotate2Orientaion: function( canvas, orientation ) {\n            var width = canvas.width,\n                height = canvas.height,\n                ctx = canvas.getContext('2d');\n\n            switch ( orientation ) {\n                case 5:\n                case 6:\n                case 7:\n                case 8:\n                    canvas.width = height;\n                    canvas.height = width;\n                    break;\n            }\n\n            switch ( orientation ) {\n                case 2:    // horizontal flip\n                    ctx.translate( width, 0 );\n                    ctx.scale( -1, 1 );\n                    break;\n\n                case 3:    // 180 rotate left\n                    ctx.translate( width, height );\n                    ctx.rotate( Math.PI );\n                    break;\n\n                case 4:    // vertical flip\n                    ctx.translate( 0, height );\n                    ctx.scale( 1, -1 );\n                    break;\n\n                case 5:    // vertical flip + 90 rotate right\n                    ctx.rotate( 0.5 * Math.PI );\n                    ctx.scale( 1, -1 );\n                    break;\n\n                case 6:    // 90 rotate right\n                    ctx.rotate( 0.5 * Math.PI );\n                    ctx.translate( 0, -height );\n                    break;\n\n                case 7:    // horizontal flip + 90 rotate right\n                    ctx.rotate( 0.5 * Math.PI );\n                    ctx.translate( width, -height );\n                    ctx.scale( -1, 1 );\n                    break;\n\n                case 8:    // 90 rotate left\n                    ctx.rotate( -0.5 * Math.PI );\n                    ctx.translate( -width, 0 );\n                    break;\n            }\n        },\n\n        // https://github.com/stomita/ios-imagefile-megapixel/\n        // blob/master/src/megapix-image.js\n        _renderImageToCanvas: (function() {\n\n            // 如果不是ios, 不需要这么复杂！\n            if ( !Base.os.ios ) {\n                return function( canvas ) {\n                    var args = Base.slice( arguments, 1 ),\n                        ctx = canvas.getContext('2d');\n\n                    ctx.drawImage.apply( ctx, args );\n                };\n            }\n\n            /**\n             * Detecting vertical squash in loaded image.\n             * Fixes a bug which squash image vertically while drawing into\n             * canvas for some images.\n             */\n            function detectVerticalSquash( img, iw, ih ) {\n                var canvas = document.createElement('canvas'),\n                    ctx = canvas.getContext('2d'),\n                    sy = 0,\n                    ey = ih,\n                    py = ih,\n                    data, alpha, ratio;\n\n\n                canvas.width = 1;\n                canvas.height = ih;\n                ctx.drawImage( img, 0, 0 );\n                data = ctx.getImageData( 0, 0, 1, ih ).data;\n\n                // search image edge pixel position in case\n                // it is squashed vertically.\n                while ( py > sy ) {\n                    alpha = data[ (py - 1) * 4 + 3 ];\n\n                    if ( alpha === 0 ) {\n                        ey = py;\n                    } else {\n                        sy = py;\n                    }\n\n                    py = (ey + sy) >> 1;\n                }\n\n                ratio = (py / ih);\n                return (ratio === 0) ? 1 : ratio;\n            }\n\n            // fix ie7 bug\n            // http://stackoverflow.com/questions/11929099/\n            // html5-canvas-drawimage-ratio-bug-ios\n            if ( Base.os.ios >= 7 ) {\n                return function( canvas, img, x, y, w, h ) {\n                    var iw = img.naturalWidth,\n                        ih = img.naturalHeight,\n                        vertSquashRatio = detectVerticalSquash( img, iw, ih );\n\n                    return canvas.getContext('2d').drawImage( img, 0, 0,\n                            iw * vertSquashRatio, ih * vertSquashRatio,\n                            x, y, w, h );\n                };\n            }\n\n            /**\n             * Detect subsampling in loaded image.\n             * In iOS, larger images than 2M pixels may be\n             * subsampled in rendering.\n             */\n            function detectSubsampling( img ) {\n                var iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    canvas, ctx;\n\n                // subsampling may happen overmegapixel image\n                if ( iw * ih > 1024 * 1024 ) {\n                    canvas = document.createElement('canvas');\n                    canvas.width = canvas.height = 1;\n                    ctx = canvas.getContext('2d');\n                    ctx.drawImage( img, -iw + 1, 0 );\n\n                    // subsampled image becomes half smaller in rendering size.\n                    // check alpha channel value to confirm image is covering\n                    // edge pixel or not. if alpha value is 0\n                    // image is not covering, hence subsampled.\n                    return ctx.getImageData( 0, 0, 1, 1 ).data[ 3 ] === 0;\n                } else {\n                    return false;\n                }\n            }\n\n\n            return function( canvas, img, x, y, width, height ) {\n                var iw = img.naturalWidth,\n                    ih = img.naturalHeight,\n                    ctx = canvas.getContext('2d'),\n                    subsampled = detectSubsampling( img ),\n                    doSquash = this.type === 'image/jpeg',\n                    d = 1024,\n                    sy = 0,\n                    dy = 0,\n                    tmpCanvas, tmpCtx, vertSquashRatio, dw, dh, sx, dx;\n\n                if ( subsampled ) {\n                    iw /= 2;\n                    ih /= 2;\n                }\n\n                ctx.save();\n                tmpCanvas = document.createElement('canvas');\n                tmpCanvas.width = tmpCanvas.height = d;\n\n                tmpCtx = tmpCanvas.getContext('2d');\n                vertSquashRatio = doSquash ?\n                        detectVerticalSquash( img, iw, ih ) : 1;\n\n                dw = Math.ceil( d * width / iw );\n                dh = Math.ceil( d * height / ih / vertSquashRatio );\n\n                while ( sy < ih ) {\n                    sx = 0;\n                    dx = 0;\n                    while ( sx < iw ) {\n                        tmpCtx.clearRect( 0, 0, d, d );\n                        tmpCtx.drawImage( img, -sx, -sy );\n                        ctx.drawImage( tmpCanvas, 0, 0, d, d,\n                                x + dx, y + dy, dw, dh );\n                        sx += d;\n                        dx += dw;\n                    }\n                    sy += d;\n                    dy += dh;\n                }\n                ctx.restore();\n                tmpCanvas = tmpCtx = null;\n            };\n        })()\n    });\n});\n"
  },
  {
    "path": "src/runtime/html5/imagemeta/exif.js",
    "content": "/**\n * 代码来自于：https://github.com/blueimp/JavaScript-Load-Image\n * 暂时项目中只用了orientation.\n *\n * 去除了 Exif Sub IFD Pointer, GPS Info IFD Pointer, Exif Thumbnail.\n * @fileOverview EXIF解析\n */\n\n// Sample\n// ====================================\n// Make : Apple\n// Model : iPhone 4S\n// Orientation : 1\n// XResolution : 72 [72/1]\n// YResolution : 72 [72/1]\n// ResolutionUnit : 2\n// Software : QuickTime 7.7.1\n// DateTime : 2013:09:01 22:53:55\n// ExifIFDPointer : 190\n// ExposureTime : 0.058823529411764705 [1/17]\n// FNumber : 2.4 [12/5]\n// ExposureProgram : Normal program\n// ISOSpeedRatings : 800\n// ExifVersion : 0220\n// DateTimeOriginal : 2013:09:01 22:52:51\n// DateTimeDigitized : 2013:09:01 22:52:51\n// ComponentsConfiguration : YCbCr\n// ShutterSpeedValue : 4.058893515764426\n// ApertureValue : 2.5260688216892597 [4845/1918]\n// BrightnessValue : -0.3126686601998395\n// MeteringMode : Pattern\n// Flash : Flash did not fire, compulsory flash mode\n// FocalLength : 4.28 [107/25]\n// SubjectArea : [4 values]\n// FlashpixVersion : 0100\n// ColorSpace : 1\n// PixelXDimension : 2448\n// PixelYDimension : 3264\n// SensingMethod : One-chip color area sensor\n// ExposureMode : 0\n// WhiteBalance : Auto white balance\n// FocalLengthIn35mmFilm : 35\n// SceneCaptureType : Standard\ndefine([\n    '../../../base',\n    '../imagemeta'\n], function( Base, ImageMeta ) {\n\n    var EXIF = {};\n\n    EXIF.ExifMap = function() {\n        return this;\n    };\n\n    EXIF.ExifMap.prototype.map = {\n        'Orientation': 0x0112\n    };\n\n    EXIF.ExifMap.prototype.get = function( id ) {\n        return this[ id ] || this[ this.map[ id ] ];\n    };\n\n    EXIF.exifTagTypes = {\n        // byte, 8-bit unsigned int:\n        1: {\n            getValue: function( dataView, dataOffset ) {\n                return dataView.getUint8( dataOffset );\n            },\n            size: 1\n        },\n\n        // ascii, 8-bit byte:\n        2: {\n            getValue: function( dataView, dataOffset ) {\n                return String.fromCharCode( dataView.getUint8( dataOffset ) );\n            },\n            size: 1,\n            ascii: true\n        },\n\n        // short, 16 bit int:\n        3: {\n            getValue: function( dataView, dataOffset, littleEndian ) {\n                return dataView.getUint16( dataOffset, littleEndian );\n            },\n            size: 2\n        },\n\n        // long, 32 bit int:\n        4: {\n            getValue: function( dataView, dataOffset, littleEndian ) {\n                return dataView.getUint32( dataOffset, littleEndian );\n            },\n            size: 4\n        },\n\n        // rational = two long values,\n        // first is numerator, second is denominator:\n        5: {\n            getValue: function( dataView, dataOffset, littleEndian ) {\n                return dataView.getUint32( dataOffset, littleEndian ) /\n                    dataView.getUint32( dataOffset + 4, littleEndian );\n            },\n            size: 8\n        },\n\n        // slong, 32 bit signed int:\n        9: {\n            getValue: function( dataView, dataOffset, littleEndian ) {\n                return dataView.getInt32( dataOffset, littleEndian );\n            },\n            size: 4\n        },\n\n        // srational, two slongs, first is numerator, second is denominator:\n        10: {\n            getValue: function( dataView, dataOffset, littleEndian ) {\n                return dataView.getInt32( dataOffset, littleEndian ) /\n                    dataView.getInt32( dataOffset + 4, littleEndian );\n            },\n            size: 8\n        }\n    };\n\n    // undefined, 8-bit byte, value depending on field:\n    EXIF.exifTagTypes[ 7 ] = EXIF.exifTagTypes[ 1 ];\n\n    EXIF.getExifValue = function( dataView, tiffOffset, offset, type, length,\n            littleEndian ) {\n\n        var tagType = EXIF.exifTagTypes[ type ],\n            tagSize, dataOffset, values, i, str, c;\n\n        if ( !tagType ) {\n            Base.log('Invalid Exif data: Invalid tag type.');\n            return;\n        }\n\n        tagSize = tagType.size * length;\n\n        // Determine if the value is contained in the dataOffset bytes,\n        // or if the value at the dataOffset is a pointer to the actual data:\n        dataOffset = tagSize > 4 ? tiffOffset + dataView.getUint32( offset + 8,\n                littleEndian ) : (offset + 8);\n\n        if ( dataOffset + tagSize > dataView.byteLength ) {\n            Base.log('Invalid Exif data: Invalid data offset.');\n            return;\n        }\n\n        if ( length === 1 ) {\n            return tagType.getValue( dataView, dataOffset, littleEndian );\n        }\n\n        values = [];\n\n        for ( i = 0; i < length; i += 1 ) {\n            values[ i ] = tagType.getValue( dataView,\n                    dataOffset + i * tagType.size, littleEndian );\n        }\n\n        if ( tagType.ascii ) {\n            str = '';\n\n            // Concatenate the chars:\n            for ( i = 0; i < values.length; i += 1 ) {\n                c = values[ i ];\n\n                // Ignore the terminating NULL byte(s):\n                if ( c === '\\u0000' ) {\n                    break;\n                }\n                str += c;\n            }\n\n            return str;\n        }\n        return values;\n    };\n\n    EXIF.parseExifTag = function( dataView, tiffOffset, offset, littleEndian,\n            data ) {\n\n        var tag = dataView.getUint16( offset, littleEndian );\n        data.exif[ tag ] = EXIF.getExifValue( dataView, tiffOffset, offset,\n                dataView.getUint16( offset + 2, littleEndian ),    // tag type\n                dataView.getUint32( offset + 4, littleEndian ),    // tag length\n                littleEndian );\n    };\n\n    EXIF.parseExifTags = function( dataView, tiffOffset, dirOffset,\n            littleEndian, data ) {\n\n        var tagsNumber, dirEndOffset, i;\n\n        if ( dirOffset + 6 > dataView.byteLength ) {\n            Base.log('Invalid Exif data: Invalid directory offset.');\n            return;\n        }\n\n        tagsNumber = dataView.getUint16( dirOffset, littleEndian );\n        dirEndOffset = dirOffset + 2 + 12 * tagsNumber;\n\n        if ( dirEndOffset + 4 > dataView.byteLength ) {\n            Base.log('Invalid Exif data: Invalid directory size.');\n            return;\n        }\n\n        for ( i = 0; i < tagsNumber; i += 1 ) {\n            this.parseExifTag( dataView, tiffOffset,\n                    dirOffset + 2 + 12 * i,    // tag offset\n                    littleEndian, data );\n        }\n\n        // Return the offset to the next directory:\n        return dataView.getUint32( dirEndOffset, littleEndian );\n    };\n\n    // EXIF.getExifThumbnail = function(dataView, offset, length) {\n    //     var hexData,\n    //         i,\n    //         b;\n    //     if (!length || offset + length > dataView.byteLength) {\n    //         Base.log('Invalid Exif data: Invalid thumbnail data.');\n    //         return;\n    //     }\n    //     hexData = [];\n    //     for (i = 0; i < length; i += 1) {\n    //         b = dataView.getUint8(offset + i);\n    //         hexData.push((b < 16 ? '0' : '') + b.toString(16));\n    //     }\n    //     return 'data:image/jpeg,%' + hexData.join('%');\n    // };\n\n    EXIF.parseExifData = function( dataView, offset, length, data ) {\n\n        var tiffOffset = offset + 10,\n            littleEndian, dirOffset;\n\n        // Check for the ASCII code for \"Exif\" (0x45786966):\n        if ( dataView.getUint32( offset + 4 ) !== 0x45786966 ) {\n            // No Exif data, might be XMP data instead\n            return;\n        }\n        if ( tiffOffset + 8 > dataView.byteLength ) {\n            Base.log('Invalid Exif data: Invalid segment size.');\n            return;\n        }\n\n        // Check for the two null bytes:\n        if ( dataView.getUint16( offset + 8 ) !== 0x0000 ) {\n            Base.log('Invalid Exif data: Missing byte alignment offset.');\n            return;\n        }\n\n        // Check the byte alignment:\n        switch ( dataView.getUint16( tiffOffset ) ) {\n            case 0x4949:\n                littleEndian = true;\n                break;\n\n            case 0x4D4D:\n                littleEndian = false;\n                break;\n\n            default:\n                Base.log('Invalid Exif data: Invalid byte alignment marker.');\n                return;\n        }\n\n        // Check for the TIFF tag marker (0x002A):\n        if ( dataView.getUint16( tiffOffset + 2, littleEndian ) !== 0x002A ) {\n            Base.log('Invalid Exif data: Missing TIFF marker.');\n            return;\n        }\n\n        // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:\n        dirOffset = dataView.getUint32( tiffOffset + 4, littleEndian );\n        // Create the exif object to store the tags:\n        data.exif = new EXIF.ExifMap();\n        // Parse the tags of the main image directory and retrieve the\n        // offset to the next directory, usually the thumbnail directory:\n        dirOffset = EXIF.parseExifTags( dataView, tiffOffset,\n                tiffOffset + dirOffset, littleEndian, data );\n\n        // 尝试读取缩略图\n        // if ( dirOffset ) {\n        //     thumbnailData = {exif: {}};\n        //     dirOffset = EXIF.parseExifTags(\n        //         dataView,\n        //         tiffOffset,\n        //         tiffOffset + dirOffset,\n        //         littleEndian,\n        //         thumbnailData\n        //     );\n\n        //     // Check for JPEG Thumbnail offset:\n        //     if (thumbnailData.exif[0x0201]) {\n        //         data.exif.Thumbnail = EXIF.getExifThumbnail(\n        //             dataView,\n        //             tiffOffset + thumbnailData.exif[0x0201],\n        //             thumbnailData.exif[0x0202] // Thumbnail data length\n        //         );\n        //     }\n        // }\n    };\n\n    ImageMeta.parsers[ 0xffe1 ].push( EXIF.parseExifData );\n    return EXIF;\n});"
  },
  {
    "path": "src/runtime/html5/imagemeta.js",
    "content": "/**\n * Terms:\n *\n * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n * @fileOverview Image控件\n */\ndefine([\n    './util'\n], function( Util ) {\n\n    var api;\n\n    api = {\n        parsers: {\n            0xffe1: []\n        },\n\n        maxMetaDataSize: 262144,\n\n        parse: function( blob, cb ) {\n            var me = this,\n                fr = new FileReader();\n\n            fr.onload = function() {\n                cb( false, me._parse( this.result ) );\n                fr = fr.onload = fr.onerror = null;\n            };\n\n            fr.onerror = function( e ) {\n                cb( e.message );\n                fr = fr.onload = fr.onerror = null;\n            };\n\n            blob = blob.slice( 0, me.maxMetaDataSize );\n            fr.readAsArrayBuffer( blob.getSource() );\n        },\n\n        _parse: function( buffer, noParse ) {\n            if ( buffer.byteLength < 6 ) {\n                return;\n            }\n\n            var dataview = new DataView( buffer ),\n                offset = 2,\n                maxOffset = dataview.byteLength - 4,\n                headLength = offset,\n                ret = {},\n                markerBytes, markerLength, parsers, i;\n\n            if ( dataview.getUint16( 0 ) === 0xffd8 ) {\n\n                while ( offset < maxOffset ) {\n                    markerBytes = dataview.getUint16( offset );\n\n                    if ( markerBytes >= 0xffe0 && markerBytes <= 0xffef ||\n                            markerBytes === 0xfffe ) {\n\n                        markerLength = dataview.getUint16( offset + 2 ) + 2;\n\n                        if ( offset + markerLength > dataview.byteLength ) {\n                            break;\n                        }\n\n                        parsers = api.parsers[ markerBytes ];\n\n                        if ( !noParse && parsers ) {\n                            for ( i = 0; i < parsers.length; i += 1 ) {\n                                parsers[ i ].call( api, dataview, offset,\n                                        markerLength, ret );\n                            }\n                        }\n\n                        offset += markerLength;\n                        headLength = offset;\n                    } else {\n                        break;\n                    }\n                }\n\n                if ( headLength > 6 ) {\n                    if ( buffer.slice ) {\n                        ret.imageHead = buffer.slice( 2, headLength );\n                    } else {\n                        // Workaround for IE10, which does not yet\n                        // support ArrayBuffer.slice:\n                        ret.imageHead = new Uint8Array( buffer )\n                                .subarray( 2, headLength );\n                    }\n                }\n            }\n\n            return ret;\n        },\n\n        updateImageHead: function( buffer, head ) {\n            var data = this._parse( buffer, true ),\n                buf1, buf2, bodyoffset;\n\n\n            bodyoffset = 2;\n            if ( data.imageHead ) {\n                bodyoffset = 2 + data.imageHead.byteLength;\n            }\n\n            if ( buffer.slice ) {\n                buf2 = buffer.slice( bodyoffset );\n            } else {\n                buf2 = new Uint8Array( buffer ).subarray( bodyoffset );\n            }\n\n            buf1 = new Uint8Array( head.byteLength + 2 + buf2.byteLength );\n\n            buf1[ 0 ] = 0xFF;\n            buf1[ 1 ] = 0xD8;\n            buf1.set( new Uint8Array( head ), 2 );\n            buf1.set( new Uint8Array( buf2 ), head.byteLength + 2 );\n\n            return buf1.buffer;\n        }\n    };\n\n    Util.parseMeta = function() {\n        return api.parse.apply( api, arguments );\n    };\n\n    Util.updateImageHead = function() {\n        return api.updateImageHead.apply( api, arguments );\n    };\n\n    return api;\n});"
  },
  {
    "path": "src/runtime/html5/jpegencoder.js",
    "content": "/**\n * 这个方式性能不行，但是可以解决android里面的toDataUrl的bug\n * android里面toDataUrl('image/jpege')得到的结果却是png.\n *\n * 所以这里没辙，只能借助这个工具\n * @fileOverview jpeg encoder\n */\ndefine([], function( require, exports, module ) {\n\n    /*\n      Copyright (c) 2008, Adobe Systems Incorporated\n      All rights reserved.\n\n      Redistribution and use in source and binary forms, with or without\n      modification, are permitted provided that the following conditions are\n      met:\n\n      * Redistributions of source code must retain the above copyright notice,\n        this list of conditions and the following disclaimer.\n\n      * Redistributions in binary form must reproduce the above copyright\n        notice, this list of conditions and the following disclaimer in the\n        documentation and/or other materials provided with the distribution.\n\n      * Neither the name of Adobe Systems Incorporated nor the names of its\n        contributors may be used to endorse or promote products derived from\n        this software without specific prior written permission.\n\n      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n      IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n      THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n      PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n      EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n      PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n      PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n      LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n      NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n      SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n    */\n    /*\n    JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009\n\n    Basic GUI blocking jpeg encoder\n    */\n\n    function JPEGEncoder(quality) {\n      var self = this;\n        var fround = Math.round;\n        var ffloor = Math.floor;\n        var YTable = new Array(64);\n        var UVTable = new Array(64);\n        var fdtbl_Y = new Array(64);\n        var fdtbl_UV = new Array(64);\n        var YDC_HT;\n        var UVDC_HT;\n        var YAC_HT;\n        var UVAC_HT;\n\n        var bitcode = new Array(65535);\n        var category = new Array(65535);\n        var outputfDCTQuant = new Array(64);\n        var DU = new Array(64);\n        var byteout = [];\n        var bytenew = 0;\n        var bytepos = 7;\n\n        var YDU = new Array(64);\n        var UDU = new Array(64);\n        var VDU = new Array(64);\n        var clt = new Array(256);\n        var RGB_YUV_TABLE = new Array(2048);\n        var currentQuality;\n\n        var ZigZag = [\n                 0, 1, 5, 6,14,15,27,28,\n                 2, 4, 7,13,16,26,29,42,\n                 3, 8,12,17,25,30,41,43,\n                 9,11,18,24,31,40,44,53,\n                10,19,23,32,39,45,52,54,\n                20,22,33,38,46,51,55,60,\n                21,34,37,47,50,56,59,61,\n                35,36,48,49,57,58,62,63\n            ];\n\n        var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];\n        var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n        var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];\n        var std_ac_luminance_values = [\n                0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,\n                0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,\n                0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n                0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,\n                0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,\n                0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n                0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,\n                0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,\n                0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n                0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,\n                0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,\n                0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n                0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,\n                0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,\n                0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n                0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,\n                0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,\n                0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n                0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,\n                0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                0xf9,0xfa\n            ];\n\n        var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];\n        var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];\n        var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];\n        var std_ac_chrominance_values = [\n                0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,\n                0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,\n                0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n                0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,\n                0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,\n                0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n                0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,\n                0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,\n                0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n                0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,\n                0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,\n                0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n                0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,\n                0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,\n                0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n                0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,\n                0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,\n                0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n                0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,\n                0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,\n                0xf9,0xfa\n            ];\n\n        function initQuantTables(sf){\n                var YQT = [\n                    16, 11, 10, 16, 24, 40, 51, 61,\n                    12, 12, 14, 19, 26, 58, 60, 55,\n                    14, 13, 16, 24, 40, 57, 69, 56,\n                    14, 17, 22, 29, 51, 87, 80, 62,\n                    18, 22, 37, 56, 68,109,103, 77,\n                    24, 35, 55, 64, 81,104,113, 92,\n                    49, 64, 78, 87,103,121,120,101,\n                    72, 92, 95, 98,112,100,103, 99\n                ];\n\n                for (var i = 0; i < 64; i++) {\n                    var t = ffloor((YQT[i]*sf+50)/100);\n                    if (t < 1) {\n                        t = 1;\n                    } else if (t > 255) {\n                        t = 255;\n                    }\n                    YTable[ZigZag[i]] = t;\n                }\n                var UVQT = [\n                    17, 18, 24, 47, 99, 99, 99, 99,\n                    18, 21, 26, 66, 99, 99, 99, 99,\n                    24, 26, 56, 99, 99, 99, 99, 99,\n                    47, 66, 99, 99, 99, 99, 99, 99,\n                    99, 99, 99, 99, 99, 99, 99, 99,\n                    99, 99, 99, 99, 99, 99, 99, 99,\n                    99, 99, 99, 99, 99, 99, 99, 99,\n                    99, 99, 99, 99, 99, 99, 99, 99\n                ];\n                for (var j = 0; j < 64; j++) {\n                    var u = ffloor((UVQT[j]*sf+50)/100);\n                    if (u < 1) {\n                        u = 1;\n                    } else if (u > 255) {\n                        u = 255;\n                    }\n                    UVTable[ZigZag[j]] = u;\n                }\n                var aasf = [\n                    1.0, 1.387039845, 1.306562965, 1.175875602,\n                    1.0, 0.785694958, 0.541196100, 0.275899379\n                ];\n                var k = 0;\n                for (var row = 0; row < 8; row++)\n                {\n                    for (var col = 0; col < 8; col++)\n                    {\n                        fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                        fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));\n                        k++;\n                    }\n                }\n            }\n\n            function computeHuffmanTbl(nrcodes, std_table){\n                var codevalue = 0;\n                var pos_in_table = 0;\n                var HT = new Array();\n                for (var k = 1; k <= 16; k++) {\n                    for (var j = 1; j <= nrcodes[k]; j++) {\n                        HT[std_table[pos_in_table]] = [];\n                        HT[std_table[pos_in_table]][0] = codevalue;\n                        HT[std_table[pos_in_table]][1] = k;\n                        pos_in_table++;\n                        codevalue++;\n                    }\n                    codevalue*=2;\n                }\n                return HT;\n            }\n\n            function initHuffmanTbl()\n            {\n                YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);\n                UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);\n                YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);\n                UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);\n            }\n\n            function initCategoryNumber()\n            {\n                var nrlower = 1;\n                var nrupper = 2;\n                for (var cat = 1; cat <= 15; cat++) {\n                    //Positive numbers\n                    for (var nr = nrlower; nr<nrupper; nr++) {\n                        category[32767+nr] = cat;\n                        bitcode[32767+nr] = [];\n                        bitcode[32767+nr][1] = cat;\n                        bitcode[32767+nr][0] = nr;\n                    }\n                    //Negative numbers\n                    for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {\n                        category[32767+nrneg] = cat;\n                        bitcode[32767+nrneg] = [];\n                        bitcode[32767+nrneg][1] = cat;\n                        bitcode[32767+nrneg][0] = nrupper-1+nrneg;\n                    }\n                    nrlower <<= 1;\n                    nrupper <<= 1;\n                }\n            }\n\n            function initRGBYUVTable() {\n                for(var i = 0; i < 256;i++) {\n                    RGB_YUV_TABLE[i]            =  19595 * i;\n                    RGB_YUV_TABLE[(i+ 256)>>0]  =  38470 * i;\n                    RGB_YUV_TABLE[(i+ 512)>>0]  =   7471 * i + 0x8000;\n                    RGB_YUV_TABLE[(i+ 768)>>0]  = -11059 * i;\n                    RGB_YUV_TABLE[(i+1024)>>0]  = -21709 * i;\n                    RGB_YUV_TABLE[(i+1280)>>0]  =  32768 * i + 0x807FFF;\n                    RGB_YUV_TABLE[(i+1536)>>0]  = -27439 * i;\n                    RGB_YUV_TABLE[(i+1792)>>0]  = - 5329 * i;\n                }\n            }\n\n            // IO functions\n            function writeBits(bs)\n            {\n                var value = bs[0];\n                var posval = bs[1]-1;\n                while ( posval >= 0 ) {\n                    if (value & (1 << posval) ) {\n                        bytenew |= (1 << bytepos);\n                    }\n                    posval--;\n                    bytepos--;\n                    if (bytepos < 0) {\n                        if (bytenew == 0xFF) {\n                            writeByte(0xFF);\n                            writeByte(0);\n                        }\n                        else {\n                            writeByte(bytenew);\n                        }\n                        bytepos=7;\n                        bytenew=0;\n                    }\n                }\n            }\n\n            function writeByte(value)\n            {\n                byteout.push(clt[value]); // write char directly instead of converting later\n            }\n\n            function writeWord(value)\n            {\n                writeByte((value>>8)&0xFF);\n                writeByte((value   )&0xFF);\n            }\n\n            // DCT & quantization core\n            function fDCTQuant(data, fdtbl)\n            {\n                var d0, d1, d2, d3, d4, d5, d6, d7;\n                /* Pass 1: process rows. */\n                var dataOff=0;\n                var i;\n                var I8 = 8;\n                var I64 = 64;\n                for (i=0; i<I8; ++i)\n                {\n                    d0 = data[dataOff];\n                    d1 = data[dataOff+1];\n                    d2 = data[dataOff+2];\n                    d3 = data[dataOff+3];\n                    d4 = data[dataOff+4];\n                    d5 = data[dataOff+5];\n                    d6 = data[dataOff+6];\n                    d7 = data[dataOff+7];\n\n                    var tmp0 = d0 + d7;\n                    var tmp7 = d0 - d7;\n                    var tmp1 = d1 + d6;\n                    var tmp6 = d1 - d6;\n                    var tmp2 = d2 + d5;\n                    var tmp5 = d2 - d5;\n                    var tmp3 = d3 + d4;\n                    var tmp4 = d3 - d4;\n\n                    /* Even part */\n                    var tmp10 = tmp0 + tmp3;    /* phase 2 */\n                    var tmp13 = tmp0 - tmp3;\n                    var tmp11 = tmp1 + tmp2;\n                    var tmp12 = tmp1 - tmp2;\n\n                    data[dataOff] = tmp10 + tmp11; /* phase 3 */\n                    data[dataOff+4] = tmp10 - tmp11;\n\n                    var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */\n                    data[dataOff+2] = tmp13 + z1; /* phase 5 */\n                    data[dataOff+6] = tmp13 - z1;\n\n                    /* Odd part */\n                    tmp10 = tmp4 + tmp5; /* phase 2 */\n                    tmp11 = tmp5 + tmp6;\n                    tmp12 = tmp6 + tmp7;\n\n                    /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                    var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */\n                    var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */\n                    var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */\n                    var z3 = tmp11 * 0.707106781; /* c4 */\n\n                    var z11 = tmp7 + z3;    /* phase 5 */\n                    var z13 = tmp7 - z3;\n\n                    data[dataOff+5] = z13 + z2; /* phase 6 */\n                    data[dataOff+3] = z13 - z2;\n                    data[dataOff+1] = z11 + z4;\n                    data[dataOff+7] = z11 - z4;\n\n                    dataOff += 8; /* advance pointer to next row */\n                }\n\n                /* Pass 2: process columns. */\n                dataOff = 0;\n                for (i=0; i<I8; ++i)\n                {\n                    d0 = data[dataOff];\n                    d1 = data[dataOff + 8];\n                    d2 = data[dataOff + 16];\n                    d3 = data[dataOff + 24];\n                    d4 = data[dataOff + 32];\n                    d5 = data[dataOff + 40];\n                    d6 = data[dataOff + 48];\n                    d7 = data[dataOff + 56];\n\n                    var tmp0p2 = d0 + d7;\n                    var tmp7p2 = d0 - d7;\n                    var tmp1p2 = d1 + d6;\n                    var tmp6p2 = d1 - d6;\n                    var tmp2p2 = d2 + d5;\n                    var tmp5p2 = d2 - d5;\n                    var tmp3p2 = d3 + d4;\n                    var tmp4p2 = d3 - d4;\n\n                    /* Even part */\n                    var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */\n                    var tmp13p2 = tmp0p2 - tmp3p2;\n                    var tmp11p2 = tmp1p2 + tmp2p2;\n                    var tmp12p2 = tmp1p2 - tmp2p2;\n\n                    data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */\n                    data[dataOff+32] = tmp10p2 - tmp11p2;\n\n                    var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */\n                    data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */\n                    data[dataOff+48] = tmp13p2 - z1p2;\n\n                    /* Odd part */\n                    tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */\n                    tmp11p2 = tmp5p2 + tmp6p2;\n                    tmp12p2 = tmp6p2 + tmp7p2;\n\n                    /* The rotator is modified from fig 4-8 to avoid extra negations. */\n                    var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */\n                    var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */\n                    var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */\n                    var z3p2 = tmp11p2 * 0.707106781; /* c4 */\n\n                    var z11p2 = tmp7p2 + z3p2;  /* phase 5 */\n                    var z13p2 = tmp7p2 - z3p2;\n\n                    data[dataOff+40] = z13p2 + z2p2; /* phase 6 */\n                    data[dataOff+24] = z13p2 - z2p2;\n                    data[dataOff+ 8] = z11p2 + z4p2;\n                    data[dataOff+56] = z11p2 - z4p2;\n\n                    dataOff++; /* advance pointer to next column */\n                }\n\n                // Quantize/descale the coefficients\n                var fDCTQuant;\n                for (i=0; i<I64; ++i)\n                {\n                    // Apply the quantization and scaling factor & Round to nearest integer\n                    fDCTQuant = data[i]*fdtbl[i];\n                    outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);\n                    //outputfDCTQuant[i] = fround(fDCTQuant);\n\n                }\n                return outputfDCTQuant;\n            }\n\n            function writeAPP0()\n            {\n                writeWord(0xFFE0); // marker\n                writeWord(16); // length\n                writeByte(0x4A); // J\n                writeByte(0x46); // F\n                writeByte(0x49); // I\n                writeByte(0x46); // F\n                writeByte(0); // = \"JFIF\",'\\0'\n                writeByte(1); // versionhi\n                writeByte(1); // versionlo\n                writeByte(0); // xyunits\n                writeWord(1); // xdensity\n                writeWord(1); // ydensity\n                writeByte(0); // thumbnwidth\n                writeByte(0); // thumbnheight\n            }\n\n            function writeSOF0(width, height)\n            {\n                writeWord(0xFFC0); // marker\n                writeWord(17);   // length, truecolor YUV JPG\n                writeByte(8);    // precision\n                writeWord(height);\n                writeWord(width);\n                writeByte(3);    // nrofcomponents\n                writeByte(1);    // IdY\n                writeByte(0x11); // HVY\n                writeByte(0);    // QTY\n                writeByte(2);    // IdU\n                writeByte(0x11); // HVU\n                writeByte(1);    // QTU\n                writeByte(3);    // IdV\n                writeByte(0x11); // HVV\n                writeByte(1);    // QTV\n            }\n\n            function writeDQT()\n            {\n                writeWord(0xFFDB); // marker\n                writeWord(132);    // length\n                writeByte(0);\n                for (var i=0; i<64; i++) {\n                    writeByte(YTable[i]);\n                }\n                writeByte(1);\n                for (var j=0; j<64; j++) {\n                    writeByte(UVTable[j]);\n                }\n            }\n\n            function writeDHT()\n            {\n                writeWord(0xFFC4); // marker\n                writeWord(0x01A2); // length\n\n                writeByte(0); // HTYDCinfo\n                for (var i=0; i<16; i++) {\n                    writeByte(std_dc_luminance_nrcodes[i+1]);\n                }\n                for (var j=0; j<=11; j++) {\n                    writeByte(std_dc_luminance_values[j]);\n                }\n\n                writeByte(0x10); // HTYACinfo\n                for (var k=0; k<16; k++) {\n                    writeByte(std_ac_luminance_nrcodes[k+1]);\n                }\n                for (var l=0; l<=161; l++) {\n                    writeByte(std_ac_luminance_values[l]);\n                }\n\n                writeByte(1); // HTUDCinfo\n                for (var m=0; m<16; m++) {\n                    writeByte(std_dc_chrominance_nrcodes[m+1]);\n                }\n                for (var n=0; n<=11; n++) {\n                    writeByte(std_dc_chrominance_values[n]);\n                }\n\n                writeByte(0x11); // HTUACinfo\n                for (var o=0; o<16; o++) {\n                    writeByte(std_ac_chrominance_nrcodes[o+1]);\n                }\n                for (var p=0; p<=161; p++) {\n                    writeByte(std_ac_chrominance_values[p]);\n                }\n            }\n\n            function writeSOS()\n            {\n                writeWord(0xFFDA); // marker\n                writeWord(12); // length\n                writeByte(3); // nrofcomponents\n                writeByte(1); // IdY\n                writeByte(0); // HTY\n                writeByte(2); // IdU\n                writeByte(0x11); // HTU\n                writeByte(3); // IdV\n                writeByte(0x11); // HTV\n                writeByte(0); // Ss\n                writeByte(0x3f); // Se\n                writeByte(0); // Bf\n            }\n\n            function processDU(CDU, fdtbl, DC, HTDC, HTAC){\n                var EOB = HTAC[0x00];\n                var M16zeroes = HTAC[0xF0];\n                var pos;\n                var I16 = 16;\n                var I63 = 63;\n                var I64 = 64;\n                var DU_DCT = fDCTQuant(CDU, fdtbl);\n                //ZigZag reorder\n                for (var j=0;j<I64;++j) {\n                    DU[ZigZag[j]]=DU_DCT[j];\n                }\n                var Diff = DU[0] - DC; DC = DU[0];\n                //Encode DC\n                if (Diff==0) {\n                    writeBits(HTDC[0]); // Diff might be 0\n                } else {\n                    pos = 32767+Diff;\n                    writeBits(HTDC[category[pos]]);\n                    writeBits(bitcode[pos]);\n                }\n                //Encode ACs\n                var end0pos = 63; // was const... which is crazy\n                for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};\n                //end0pos = first element in reverse order !=0\n                if ( end0pos == 0) {\n                    writeBits(EOB);\n                    return DC;\n                }\n                var i = 1;\n                var lng;\n                while ( i <= end0pos ) {\n                    var startpos = i;\n                    for (; (DU[i]==0) && (i<=end0pos); ++i) {}\n                    var nrzeroes = i-startpos;\n                    if ( nrzeroes >= I16 ) {\n                        lng = nrzeroes>>4;\n                        for (var nrmarker=1; nrmarker <= lng; ++nrmarker)\n                            writeBits(M16zeroes);\n                        nrzeroes = nrzeroes&0xF;\n                    }\n                    pos = 32767+DU[i];\n                    writeBits(HTAC[(nrzeroes<<4)+category[pos]]);\n                    writeBits(bitcode[pos]);\n                    i++;\n                }\n                if ( end0pos != I63 ) {\n                    writeBits(EOB);\n                }\n                return DC;\n            }\n\n            function initCharLookupTable(){\n                var sfcc = String.fromCharCode;\n                for(var i=0; i < 256; i++){ ///// ACHTUNG // 255\n                    clt[i] = sfcc(i);\n                }\n            }\n\n            this.encode = function(image,quality) // image data object\n            {\n                // var time_start = new Date().getTime();\n\n                if(quality) setQuality(quality);\n\n                // Initialize bit writer\n                byteout = new Array();\n                bytenew=0;\n                bytepos=7;\n\n                // Add JPEG headers\n                writeWord(0xFFD8); // SOI\n                writeAPP0();\n                writeDQT();\n                writeSOF0(image.width,image.height);\n                writeDHT();\n                writeSOS();\n\n\n                // Encode 8x8 macroblocks\n                var DCY=0;\n                var DCU=0;\n                var DCV=0;\n\n                bytenew=0;\n                bytepos=7;\n\n\n                this.encode.displayName = \"_encode_\";\n\n                var imageData = image.data;\n                var width = image.width;\n                var height = image.height;\n\n                var quadWidth = width*4;\n                var tripleWidth = width*3;\n\n                var x, y = 0;\n                var r, g, b;\n                var start,p, col,row,pos;\n                while(y < height){\n                    x = 0;\n                    while(x < quadWidth){\n                    start = quadWidth * y + x;\n                    p = start;\n                    col = -1;\n                    row = 0;\n\n                    for(pos=0; pos < 64; pos++){\n                        row = pos >> 3;// /8\n                        col = ( pos & 7 ) * 4; // %8\n                        p = start + ( row * quadWidth ) + col;\n\n                        if(y+row >= height){ // padding bottom\n                            p-= (quadWidth*(y+1+row-height));\n                        }\n\n                        if(x+col >= quadWidth){ // padding right\n                            p-= ((x+col) - quadWidth +4)\n                        }\n\n                        r = imageData[ p++ ];\n                        g = imageData[ p++ ];\n                        b = imageData[ p++ ];\n\n\n                        /* // calculate YUV values dynamically\n                        YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80\n                        UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));\n                        VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));\n                        */\n\n                        // use lookup table (slightly faster)\n                        YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;\n                        UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;\n                        VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;\n\n                    }\n\n                    DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n                    DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n                    DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n                    x+=32;\n                    }\n                    y+=8;\n                }\n\n\n                ////////////////////////////////////////////////////////////////\n\n                // Do the bit alignment of the EOI marker\n                if ( bytepos >= 0 ) {\n                    var fillbits = [];\n                    fillbits[1] = bytepos+1;\n                    fillbits[0] = (1<<(bytepos+1))-1;\n                    writeBits(fillbits);\n                }\n\n                writeWord(0xFFD9); //EOI\n\n                var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));\n\n                byteout = [];\n\n                // benchmarking\n                // var duration = new Date().getTime() - time_start;\n                // console.log('Encoding time: '+ currentQuality + 'ms');\n                //\n\n                return jpegDataUri\n        }\n\n        function setQuality(quality){\n            if (quality <= 0) {\n                quality = 1;\n            }\n            if (quality > 100) {\n                quality = 100;\n            }\n\n            if(currentQuality == quality) return // don't recalc if unchanged\n\n            var sf = 0;\n            if (quality < 50) {\n                sf = Math.floor(5000 / quality);\n            } else {\n                sf = Math.floor(200 - quality*2);\n            }\n\n            initQuantTables(sf);\n            currentQuality = quality;\n            // console.log('Quality set to: '+quality +'%');\n        }\n\n        function init(){\n            // var time_start = new Date().getTime();\n            if(!quality) quality = 50;\n            // Create tables\n            initCharLookupTable()\n            initHuffmanTbl();\n            initCategoryNumber();\n            initRGBYUVTable();\n\n            setQuality(quality);\n            // var duration = new Date().getTime() - time_start;\n            // console.log('Initialization '+ duration + 'ms');\n        }\n\n        init();\n\n    };\n\n    JPEGEncoder.encode = function( data, quality ) {\n        var encoder = new JPEGEncoder( quality );\n\n        return encoder.encode( data );\n    }\n\n    return JPEGEncoder;\n});"
  },
  {
    "path": "src/runtime/html5/md5.js",
    "content": "/**\n * @fileOverview  Transport flash实现\n */\ndefine([\n    './runtime'\n], function( FlashRuntime ) {\n\n    /*\n     * Fastest md5 implementation around (JKM md5)\n     * Credits: Joseph Myers\n     *\n     * @see http://www.myersdaily.org/joseph/javascript/md5-text.html\n     * @see http://jsperf.com/md5-shootout/7\n     */\n\n    /* this function is much faster,\n      so if possible we use it. Some IEs\n      are the only ones I know of that\n      need the idiotic second function,\n      generated by an if clause.  */\n    var add32 = function (a, b) {\n        return (a + b) & 0xFFFFFFFF;\n    },\n\n    cmn = function (q, a, b, x, s, t) {\n        a = add32(add32(a, q), add32(x, t));\n        return add32((a << s) | (a >>> (32 - s)), b);\n    },\n\n    ff = function (a, b, c, d, x, s, t) {\n        return cmn((b & c) | ((~b) & d), a, b, x, s, t);\n    },\n\n    gg = function (a, b, c, d, x, s, t) {\n        return cmn((b & d) | (c & (~d)), a, b, x, s, t);\n    },\n\n    hh = function (a, b, c, d, x, s, t) {\n        return cmn(b ^ c ^ d, a, b, x, s, t);\n    },\n\n    ii = function (a, b, c, d, x, s, t) {\n        return cmn(c ^ (b | (~d)), a, b, x, s, t);\n    },\n\n    md5cycle = function (x, k) {\n        var a = x[0],\n            b = x[1],\n            c = x[2],\n            d = x[3];\n\n        a = ff(a, b, c, d, k[0], 7, -680876936);\n        d = ff(d, a, b, c, k[1], 12, -389564586);\n        c = ff(c, d, a, b, k[2], 17, 606105819);\n        b = ff(b, c, d, a, k[3], 22, -1044525330);\n        a = ff(a, b, c, d, k[4], 7, -176418897);\n        d = ff(d, a, b, c, k[5], 12, 1200080426);\n        c = ff(c, d, a, b, k[6], 17, -1473231341);\n        b = ff(b, c, d, a, k[7], 22, -45705983);\n        a = ff(a, b, c, d, k[8], 7, 1770035416);\n        d = ff(d, a, b, c, k[9], 12, -1958414417);\n        c = ff(c, d, a, b, k[10], 17, -42063);\n        b = ff(b, c, d, a, k[11], 22, -1990404162);\n        a = ff(a, b, c, d, k[12], 7, 1804603682);\n        d = ff(d, a, b, c, k[13], 12, -40341101);\n        c = ff(c, d, a, b, k[14], 17, -1502002290);\n        b = ff(b, c, d, a, k[15], 22, 1236535329);\n\n        a = gg(a, b, c, d, k[1], 5, -165796510);\n        d = gg(d, a, b, c, k[6], 9, -1069501632);\n        c = gg(c, d, a, b, k[11], 14, 643717713);\n        b = gg(b, c, d, a, k[0], 20, -373897302);\n        a = gg(a, b, c, d, k[5], 5, -701558691);\n        d = gg(d, a, b, c, k[10], 9, 38016083);\n        c = gg(c, d, a, b, k[15], 14, -660478335);\n        b = gg(b, c, d, a, k[4], 20, -405537848);\n        a = gg(a, b, c, d, k[9], 5, 568446438);\n        d = gg(d, a, b, c, k[14], 9, -1019803690);\n        c = gg(c, d, a, b, k[3], 14, -187363961);\n        b = gg(b, c, d, a, k[8], 20, 1163531501);\n        a = gg(a, b, c, d, k[13], 5, -1444681467);\n        d = gg(d, a, b, c, k[2], 9, -51403784);\n        c = gg(c, d, a, b, k[7], 14, 1735328473);\n        b = gg(b, c, d, a, k[12], 20, -1926607734);\n\n        a = hh(a, b, c, d, k[5], 4, -378558);\n        d = hh(d, a, b, c, k[8], 11, -2022574463);\n        c = hh(c, d, a, b, k[11], 16, 1839030562);\n        b = hh(b, c, d, a, k[14], 23, -35309556);\n        a = hh(a, b, c, d, k[1], 4, -1530992060);\n        d = hh(d, a, b, c, k[4], 11, 1272893353);\n        c = hh(c, d, a, b, k[7], 16, -155497632);\n        b = hh(b, c, d, a, k[10], 23, -1094730640);\n        a = hh(a, b, c, d, k[13], 4, 681279174);\n        d = hh(d, a, b, c, k[0], 11, -358537222);\n        c = hh(c, d, a, b, k[3], 16, -722521979);\n        b = hh(b, c, d, a, k[6], 23, 76029189);\n        a = hh(a, b, c, d, k[9], 4, -640364487);\n        d = hh(d, a, b, c, k[12], 11, -421815835);\n        c = hh(c, d, a, b, k[15], 16, 530742520);\n        b = hh(b, c, d, a, k[2], 23, -995338651);\n\n        a = ii(a, b, c, d, k[0], 6, -198630844);\n        d = ii(d, a, b, c, k[7], 10, 1126891415);\n        c = ii(c, d, a, b, k[14], 15, -1416354905);\n        b = ii(b, c, d, a, k[5], 21, -57434055);\n        a = ii(a, b, c, d, k[12], 6, 1700485571);\n        d = ii(d, a, b, c, k[3], 10, -1894986606);\n        c = ii(c, d, a, b, k[10], 15, -1051523);\n        b = ii(b, c, d, a, k[1], 21, -2054922799);\n        a = ii(a, b, c, d, k[8], 6, 1873313359);\n        d = ii(d, a, b, c, k[15], 10, -30611744);\n        c = ii(c, d, a, b, k[6], 15, -1560198380);\n        b = ii(b, c, d, a, k[13], 21, 1309151649);\n        a = ii(a, b, c, d, k[4], 6, -145523070);\n        d = ii(d, a, b, c, k[11], 10, -1120210379);\n        c = ii(c, d, a, b, k[2], 15, 718787259);\n        b = ii(b, c, d, a, k[9], 21, -343485551);\n\n        x[0] = add32(a, x[0]);\n        x[1] = add32(b, x[1]);\n        x[2] = add32(c, x[2]);\n        x[3] = add32(d, x[3]);\n    },\n\n    /* there needs to be support for Unicode here,\n       * unless we pretend that we can redefine the MD-5\n       * algorithm for multi-byte characters (perhaps\n       * by adding every four 16-bit characters and\n       * shortening the sum to 32 bits). Otherwise\n       * I suggest performing MD-5 as if every character\n       * was two bytes--e.g., 0040 0025 = @%--but then\n       * how will an ordinary MD-5 sum be matched?\n       * There is no way to standardize text to something\n       * like UTF-8 before transformation; speed cost is\n       * utterly prohibitive. The JavaScript standard\n       * itself needs to look at this: it should start\n       * providing access to strings as preformed UTF-8\n       * 8-bit unsigned value arrays.\n       */\n    md5blk = function (s) {\n        var md5blks = [],\n            i; /* Andy King said do it this way. */\n\n        for (i = 0; i < 64; i += 4) {\n            md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);\n        }\n        return md5blks;\n    },\n\n    md5blk_array = function (a) {\n        var md5blks = [],\n            i; /* Andy King said do it this way. */\n\n        for (i = 0; i < 64; i += 4) {\n            md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);\n        }\n        return md5blks;\n    },\n\n    md51 = function (s) {\n        var n = s.length,\n            state = [1732584193, -271733879, -1732584194, 271733878],\n            i,\n            length,\n            tail,\n            tmp,\n            lo,\n            hi;\n\n        for (i = 64; i <= n; i += 64) {\n            md5cycle(state, md5blk(s.substring(i - 64, i)));\n        }\n        s = s.substring(i - 64);\n        length = s.length;\n        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n        for (i = 0; i < length; i += 1) {\n            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);\n        }\n        tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n        if (i > 55) {\n            md5cycle(state, tail);\n            for (i = 0; i < 16; i += 1) {\n                tail[i] = 0;\n            }\n        }\n\n        // Beware that the final length might not fit in 32 bits so we take care of that\n        tmp = n * 8;\n        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n        lo = parseInt(tmp[2], 16);\n        hi = parseInt(tmp[1], 16) || 0;\n\n        tail[14] = lo;\n        tail[15] = hi;\n\n        md5cycle(state, tail);\n        return state;\n    },\n\n    md51_array = function (a) {\n        var n = a.length,\n            state = [1732584193, -271733879, -1732584194, 271733878],\n            i,\n            length,\n            tail,\n            tmp,\n            lo,\n            hi;\n\n        for (i = 64; i <= n; i += 64) {\n            md5cycle(state, md5blk_array(a.subarray(i - 64, i)));\n        }\n\n        // Not sure if it is a bug, however IE10 will always produce a sub array of length 1\n        // containing the last element of the parent array if the sub array specified starts\n        // beyond the length of the parent array - weird.\n        // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue\n        a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);\n\n        length = a.length;\n        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n        for (i = 0; i < length; i += 1) {\n            tail[i >> 2] |= a[i] << ((i % 4) << 3);\n        }\n\n        tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n        if (i > 55) {\n            md5cycle(state, tail);\n            for (i = 0; i < 16; i += 1) {\n                tail[i] = 0;\n            }\n        }\n\n        // Beware that the final length might not fit in 32 bits so we take care of that\n        tmp = n * 8;\n        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n        lo = parseInt(tmp[2], 16);\n        hi = parseInt(tmp[1], 16) || 0;\n\n        tail[14] = lo;\n        tail[15] = hi;\n\n        md5cycle(state, tail);\n\n        return state;\n    },\n\n    hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'],\n\n    rhex = function (n) {\n        var s = '',\n            j;\n        for (j = 0; j < 4; j += 1) {\n            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];\n        }\n        return s;\n    },\n\n    hex = function (x) {\n        var i;\n        for (i = 0; i < x.length; i += 1) {\n            x[i] = rhex(x[i]);\n        }\n        return x.join('');\n    },\n\n    md5 = function (s) {\n        return hex(md51(s));\n    },\n\n\n\n    ////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * SparkMD5 OOP implementation.\n     *\n     * Use this class to perform an incremental md5, otherwise use the\n     * static methods instead.\n     */\n    SparkMD5 = function () {\n        // call reset to init the instance\n        this.reset();\n    };\n\n\n    // In some cases the fast add32 function cannot be used..\n    if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') {\n        add32 = function (x, y) {\n            var lsw = (x & 0xFFFF) + (y & 0xFFFF),\n                msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n            return (msw << 16) | (lsw & 0xFFFF);\n        };\n    }\n\n\n    /**\n     * Appends a string.\n     * A conversion will be applied if an utf8 string is detected.\n     *\n     * @param {String} str The string to be appended\n     *\n     * @return {SparkMD5} The instance itself\n     */\n    SparkMD5.prototype.append = function (str) {\n        // converts the string to utf8 bytes if necessary\n        if (/[\\u0080-\\uFFFF]/.test(str)) {\n            str = unescape(encodeURIComponent(str));\n        }\n\n        // then append as binary\n        this.appendBinary(str);\n\n        return this;\n    };\n\n    /**\n     * Appends a binary string.\n     *\n     * @param {String} contents The binary string to be appended\n     *\n     * @return {SparkMD5} The instance itself\n     */\n    SparkMD5.prototype.appendBinary = function (contents) {\n        this._buff += contents;\n        this._length += contents.length;\n\n        var length = this._buff.length,\n            i;\n\n        for (i = 64; i <= length; i += 64) {\n            md5cycle(this._state, md5blk(this._buff.substring(i - 64, i)));\n        }\n\n        this._buff = this._buff.substr(i - 64);\n\n        return this;\n    };\n\n    /**\n     * Finishes the incremental computation, reseting the internal state and\n     * returning the result.\n     * Use the raw parameter to obtain the raw result instead of the hex one.\n     *\n     * @param {Boolean} raw True to get the raw result, false to get the hex result\n     *\n     * @return {String|Array} The result\n     */\n    SparkMD5.prototype.end = function (raw) {\n        var buff = this._buff,\n            length = buff.length,\n            i,\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            ret;\n\n        for (i = 0; i < length; i += 1) {\n            tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);\n        }\n\n        this._finish(tail, length);\n        ret = !!raw ? this._state : hex(this._state);\n\n        this.reset();\n\n        return ret;\n    };\n\n    /**\n     * Finish the final calculation based on the tail.\n     *\n     * @param {Array}  tail   The tail (will be modified)\n     * @param {Number} length The length of the remaining buffer\n     */\n    SparkMD5.prototype._finish = function (tail, length) {\n        var i = length,\n            tmp,\n            lo,\n            hi;\n\n        tail[i >> 2] |= 0x80 << ((i % 4) << 3);\n        if (i > 55) {\n            md5cycle(this._state, tail);\n            for (i = 0; i < 16; i += 1) {\n                tail[i] = 0;\n            }\n        }\n\n        // Do the final computation based on the tail and length\n        // Beware that the final length may not fit in 32 bits so we take care of that\n        tmp = this._length * 8;\n        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);\n        lo = parseInt(tmp[2], 16);\n        hi = parseInt(tmp[1], 16) || 0;\n\n        tail[14] = lo;\n        tail[15] = hi;\n        md5cycle(this._state, tail);\n    };\n\n    /**\n     * Resets the internal state of the computation.\n     *\n     * @return {SparkMD5} The instance itself\n     */\n    SparkMD5.prototype.reset = function () {\n        this._buff = \"\";\n        this._length = 0;\n        this._state = [1732584193, -271733879, -1732584194, 271733878];\n\n        return this;\n    };\n\n    /**\n     * Releases memory used by the incremental buffer and other aditional\n     * resources. If you plan to use the instance again, use reset instead.\n     */\n    SparkMD5.prototype.destroy = function () {\n        delete this._state;\n        delete this._buff;\n        delete this._length;\n    };\n\n\n    /**\n     * Performs the md5 hash on a string.\n     * A conversion will be applied if utf8 string is detected.\n     *\n     * @param {String}  str The string\n     * @param {Boolean} raw True to get the raw result, false to get the hex result\n     *\n     * @return {String|Array} The result\n     */\n    SparkMD5.hash = function (str, raw) {\n        // converts the string to utf8 bytes if necessary\n        if (/[\\u0080-\\uFFFF]/.test(str)) {\n            str = unescape(encodeURIComponent(str));\n        }\n\n        var hash = md51(str);\n\n        return !!raw ? hash : hex(hash);\n    };\n\n    /**\n     * Performs the md5 hash on a binary string.\n     *\n     * @param {String}  content The binary string\n     * @param {Boolean} raw     True to get the raw result, false to get the hex result\n     *\n     * @return {String|Array} The result\n     */\n    SparkMD5.hashBinary = function (content, raw) {\n        var hash = md51(content);\n\n        return !!raw ? hash : hex(hash);\n    };\n\n    /**\n     * SparkMD5 OOP implementation for array buffers.\n     *\n     * Use this class to perform an incremental md5 ONLY for array buffers.\n     */\n    SparkMD5.ArrayBuffer = function () {\n        // call reset to init the instance\n        this.reset();\n    };\n\n    ////////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Appends an array buffer.\n     *\n     * @param {ArrayBuffer} arr The array to be appended\n     *\n     * @return {SparkMD5.ArrayBuffer} The instance itself\n     */\n    SparkMD5.ArrayBuffer.prototype.append = function (arr) {\n        // TODO: we could avoid the concatenation here but the algorithm would be more complex\n        //       if you find yourself needing extra performance, please make a PR.\n        var buff = this._concatArrayBuffer(this._buff, arr),\n            length = buff.length,\n            i;\n\n        this._length += arr.byteLength;\n\n        for (i = 64; i <= length; i += 64) {\n            md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i)));\n        }\n\n        // Avoids IE10 weirdness (documented above)\n        this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0);\n\n        return this;\n    };\n\n    /**\n     * Finishes the incremental computation, reseting the internal state and\n     * returning the result.\n     * Use the raw parameter to obtain the raw result instead of the hex one.\n     *\n     * @param {Boolean} raw True to get the raw result, false to get the hex result\n     *\n     * @return {String|Array} The result\n     */\n    SparkMD5.ArrayBuffer.prototype.end = function (raw) {\n        var buff = this._buff,\n            length = buff.length,\n            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            i,\n            ret;\n\n        for (i = 0; i < length; i += 1) {\n            tail[i >> 2] |= buff[i] << ((i % 4) << 3);\n        }\n\n        this._finish(tail, length);\n        ret = !!raw ? this._state : hex(this._state);\n\n        this.reset();\n\n        return ret;\n    };\n\n    SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;\n\n    /**\n     * Resets the internal state of the computation.\n     *\n     * @return {SparkMD5.ArrayBuffer} The instance itself\n     */\n    SparkMD5.ArrayBuffer.prototype.reset = function () {\n        this._buff = new Uint8Array(0);\n        this._length = 0;\n        this._state = [1732584193, -271733879, -1732584194, 271733878];\n\n        return this;\n    };\n\n    /**\n     * Releases memory used by the incremental buffer and other aditional\n     * resources. If you plan to use the instance again, use reset instead.\n     */\n    SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;\n\n    /**\n     * Concats two array buffers, returning a new one.\n     *\n     * @param  {ArrayBuffer} first  The first array buffer\n     * @param  {ArrayBuffer} second The second array buffer\n     *\n     * @return {ArrayBuffer} The new array buffer\n     */\n    SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) {\n        var firstLength = first.length,\n            result = new Uint8Array(firstLength + second.byteLength);\n\n        result.set(first);\n        result.set(new Uint8Array(second), firstLength);\n\n        return result;\n    };\n\n    /**\n     * Performs the md5 hash on an array buffer.\n     *\n     * @param {ArrayBuffer} arr The array buffer\n     * @param {Boolean}     raw True to get the raw result, false to get the hex result\n     *\n     * @return {String|Array} The result\n     */\n    SparkMD5.ArrayBuffer.hash = function (arr, raw) {\n        var hash = md51_array(new Uint8Array(arr));\n\n        return !!raw ? hash : hex(hash);\n    };\n    \n    return FlashRuntime.register( 'Md5', {\n        init: function() {\n            // do nothing.\n        },\n\n        loadFromBlob: function( file ) {\n            var blob = file.getSource(),\n                chunkSize = 2 * 1024 * 1024,\n                chunks = Math.ceil( blob.size / chunkSize ),\n                chunk = 0,\n                owner = this.owner,\n                spark = new SparkMD5.ArrayBuffer(),\n                me = this,\n                blobSlice = blob.mozSlice || blob.webkitSlice || blob.slice,\n                loadNext, fr;\n\n            fr = new FileReader();\n\n            loadNext = function() {\n                var start, end;\n\n                start = chunk * chunkSize;\n                end = Math.min( start + chunkSize, blob.size );\n\n                fr.onload = function( e ) {\n                    spark.append( e.target.result );\n                    owner.trigger( 'progress', {\n                        total: file.size,\n                        loaded: end\n                    });\n                };\n\n                fr.onloadend = function() {\n                    fr.onloadend = fr.onload = null;\n\n                    if ( ++chunk < chunks ) {\n                        setTimeout( loadNext, 1 );\n                    } else {\n                        setTimeout(function(){\n                            owner.trigger('load');\n                            me.result = spark.end();\n                            loadNext = file = blob = spark = null;\n                            owner.trigger('complete');\n                        }, 50 );\n                    }\n                };\n\n                fr.readAsArrayBuffer( blobSlice.call( blob, start, end ) );\n            };\n\n            loadNext();\n        },\n\n        getResult: function() {\n            return this.result;\n        }\n    });\n});"
  },
  {
    "path": "src/runtime/html5/runtime.js",
    "content": "/**\n * @fileOverview Html5Runtime\n */\ndefine([\n    '../../base',\n    '../runtime',\n    '../compbase'\n], function( Base, Runtime, CompBase ) {\n\n    var type = 'html5',\n        components = {};\n\n    function Html5Runtime() {\n        var pool = {},\n            me = this,\n            destroy = this.destroy;\n\n        Runtime.apply( me, arguments );\n        me.type = type;\n\n\n        // 这个方法的调用者，实际上是RuntimeClient\n        me.exec = function( comp, fn/*, args...*/) {\n            var client = this,\n                uid = client.uid,\n                args = Base.slice( arguments, 2 ),\n                instance;\n\n            if ( components[ comp ] ) {\n                instance = pool[ uid ] = pool[ uid ] ||\n                        new components[ comp ]( client, me );\n\n                if ( instance[ fn ] ) {\n                    return instance[ fn ].apply( instance, args );\n                }\n            }\n        };\n\n        me.destroy = function() {\n            // @todo 删除池子中的所有实例\n            return destroy && destroy.apply( this, arguments );\n        };\n    }\n\n    Base.inherits( Runtime, {\n        constructor: Html5Runtime,\n\n        // 不需要连接其他程序，直接执行callback\n        init: function() {\n            var me = this;\n            setTimeout(function() {\n                me.trigger('ready');\n            }, 1 );\n        }\n\n    });\n\n    // 注册Components\n    Html5Runtime.register = function( name, component ) {\n        var klass = components[ name ] = Base.inherits( CompBase, component );\n        return klass;\n    };\n\n    // 注册html5运行时。\n    // 只有在支持的前提下注册。\n    if ( window.Blob && window.FileReader && window.DataView ) {\n        Runtime.addRuntime( type, Html5Runtime );\n    }\n\n    return Html5Runtime;\n});"
  },
  {
    "path": "src/runtime/html5/transport.js",
    "content": "/**\n * @fileOverview Transport\n * @todo 支持chunked传输，优势：\n * 可以将大文件分成小块，挨个传输，可以提高大文件成功率，当失败的时候，也只需要重传那小部分，\n * 而不需要重头再传一次。另外断点续传也需要用chunked方式。\n */\ndefine([\n    '../../base',\n    './runtime'\n], function( Base, Html5Runtime ) {\n\n    var noop = Base.noop,\n        $ = Base.$;\n\n    return Html5Runtime.register( 'Transport', {\n        init: function() {\n            this._status = 0;\n            this._response = null;\n        },\n\n        send: function() {\n            var owner = this.owner,\n                opts = this.options,\n                xhr = this._initAjax(),\n                blob = owner._blob,\n                server = opts.server,\n                formData, binary, fr;\n\n            if ( opts.sendAsBinary ) {\n                server += opts.attachInfoToQuery !== false ? ((/\\?/.test( server ) ? '&' : '?') +\n                        $.param( owner._formData )) : '';\n\n                binary = blob.getSource();\n            } else {\n                formData = new FormData();\n                $.each( owner._formData, function( k, v ) {\n                    formData.append( k, v );\n                });\n\n                formData.append( opts.fileVal, blob.getSource(),\n                        opts.filename || owner._formData.name || '' );\n            }\n\n            if ( opts.withCredentials && 'withCredentials' in xhr ) {\n                xhr.open( opts.method, server, true );\n                xhr.withCredentials = true;\n            } else {\n                xhr.open( opts.method, server );\n            }\n\n            this._setRequestHeader( xhr, opts.headers );\n\n            if ( binary ) {\n                // 强制设置成 content-type 为文件流。\n                xhr.overrideMimeType &&\n                        xhr.overrideMimeType('application/octet-stream');\n\n                // android直接发送blob会导致服务端接收到的是空文件。\n                // bug详情。\n                // https://code.google.com/p/android/issues/detail?id=39882\n                // 所以先用fileReader读取出来再通过arraybuffer的方式发送。\n                if ( Base.os.android ) {\n                    fr = new FileReader();\n\n                    fr.onload = function() {\n                        xhr.send( this.result );\n                        fr = fr.onload = null;\n                    };\n\n                    fr.readAsArrayBuffer( binary );\n                } else {\n                    xhr.send( binary );\n                }\n            } else {\n                xhr.send( formData );\n            }\n        },\n\n        getResponse: function() {\n            return this._response;\n        },\n\n        getResponseAsJson: function() {\n            return this._parseJson( this._response );\n        },\n\n        getResponseHeaders: function() {\n            return this._headers;\n        },\n\n        getStatus: function() {\n            return this._status;\n        },\n\n        abort: function() {\n            var xhr = this._xhr;\n\n            if ( xhr ) {\n                xhr.upload.onprogress = noop;\n                xhr.onreadystatechange = noop;\n                xhr.abort();\n\n                this._xhr = xhr = null;\n            }\n        },\n\n        destroy: function() {\n            this.abort();\n        },\n\n        _parseHeader: function(raw) {\n            var ret = {};\n\n            raw && raw.replace(/^([^\\:]+):(.*)$/mg, function(_, key, value) {\n                ret[key.trim()] = value.trim();\n            });\n\n            return ret;\n        },\n\n        _initAjax: function() {\n            var me = this,\n                xhr = new XMLHttpRequest(),\n                opts = this.options;\n\n            if ( opts.withCredentials && !('withCredentials' in xhr) &&\n                    typeof XDomainRequest !== 'undefined' ) {\n                xhr = new XDomainRequest();\n            }\n\n            xhr.upload.onprogress = function( e ) {\n                var percentage = 0;\n\n                if ( e.lengthComputable ) {\n                    percentage = e.loaded / e.total;\n                }\n\n                return me.trigger( 'progress', percentage );\n            };\n\n            xhr.onreadystatechange = function() {\n\n                if ( xhr.readyState !== 4 ) {\n                    return;\n                }\n\n                xhr.upload.onprogress = noop;\n                xhr.onreadystatechange = noop;\n                me._xhr = null;\n                me._status = xhr.status;\n\n                var separator = '|', // 分隔符\n                     // 拼接的状态，在 widgets/upload.js 会有代码用到这个分隔符\n                    status = separator + xhr.status +\n                             separator + xhr.statusText;\n\n                if ( xhr.status >= 200 && xhr.status < 300 ) {\n                    me._response = xhr.responseText;\n                    me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                    return me.trigger('load');\n                } else if ( xhr.status >= 500 && xhr.status < 600 ) {\n                    me._response = xhr.responseText;\n                    me._headers = me._parseHeader(xhr.getAllResponseHeaders());\n                    return me.trigger( 'error', 'server' + status );\n                }\n\n\n                return me.trigger( 'error', me._status ? 'http' + status : 'abort' );\n            };\n\n            me._xhr = xhr;\n            return xhr;\n        },\n\n        _setRequestHeader: function( xhr, headers ) {\n            $.each( headers, function( key, val ) {\n                xhr.setRequestHeader( key, val );\n            });\n        },\n\n        _parseJson: function( str ) {\n            var json;\n\n            try {\n                json = JSON.parse( str );\n            } catch ( ex ) {\n                json = {};\n            }\n\n            return json;\n        }\n    });\n});\n"
  },
  {
    "path": "src/runtime/html5/util.js",
    "content": "/**\n * Terms:\n *\n * Uint8Array, FileReader, BlobBuilder, atob, ArrayBuffer\n * @fileOverview Image控件\n */\ndefine([\n    '../../base'\n], function( Base ) {\n\n    var urlAPI = window.createObjectURL && window ||\n            window.URL && URL.revokeObjectURL && URL ||\n            window.webkitURL,\n        createObjectURL = Base.noop,\n        revokeObjectURL = createObjectURL;\n\n    if ( urlAPI ) {\n\n        // 更安全的方式调用，比如android里面就能把context改成其他的对象。\n        createObjectURL = function() {\n            return urlAPI.createObjectURL.apply( urlAPI, arguments );\n        };\n\n        revokeObjectURL = function() {\n            return urlAPI.revokeObjectURL.apply( urlAPI, arguments );\n        };\n    }\n\n    return {\n        createObjectURL: createObjectURL,\n        revokeObjectURL: revokeObjectURL,\n\n        dataURL2Blob: function( dataURI ) {\n            var byteStr, intArray, ab, i, mimetype, parts;\n\n            parts = dataURI.split(',');\n\n            if ( ~parts[ 0 ].indexOf('base64') ) {\n                byteStr = atob( parts[ 1 ] );\n            } else {\n                byteStr = decodeURIComponent( parts[ 1 ] );\n            }\n\n            ab = new ArrayBuffer( byteStr.length );\n            intArray = new Uint8Array( ab );\n\n            for ( i = 0; i < byteStr.length; i++ ) {\n                intArray[ i ] = byteStr.charCodeAt( i );\n            }\n\n            mimetype = parts[ 0 ].split(':')[ 1 ].split(';')[ 0 ];\n\n            return this.arrayBufferToBlob( ab, mimetype );\n        },\n\n        dataURL2ArrayBuffer: function( dataURI ) {\n            var byteStr, intArray, i, parts;\n\n            parts = dataURI.split(',');\n\n            if ( ~parts[ 0 ].indexOf('base64') ) {\n                byteStr = atob( parts[ 1 ] );\n            } else {\n                byteStr = decodeURIComponent( parts[ 1 ] );\n            }\n\n            intArray = new Uint8Array( byteStr.length );\n\n            for ( i = 0; i < byteStr.length; i++ ) {\n                intArray[ i ] = byteStr.charCodeAt( i );\n            }\n\n            return intArray.buffer;\n        },\n\n        arrayBufferToBlob: function( buffer, type ) {\n            var builder = window.BlobBuilder || window.WebKitBlobBuilder,\n                bb;\n\n            // android不支持直接new Blob, 只能借助blobbuilder.\n            if ( builder ) {\n                bb = new builder();\n                bb.append( buffer );\n                return bb.getBlob( type );\n            }\n\n            return new Blob([ buffer ], type ? { type: type } : {} );\n        },\n\n        // 抽出来主要是为了解决android下面canvas.toDataUrl不支持jpeg.\n        // 你得到的结果是png.\n        canvasToDataUrl: function( canvas, type, quality ) {\n            return canvas.toDataURL( type, quality / 100 );\n        },\n\n        // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n        parseMeta: function( blob, callback ) {\n            callback( false, {});\n        },\n\n        // imagemeat会复写这个方法，如果用户选择加载那个文件了的话。\n        updateImageHead: function( data ) {\n            return data;\n        }\n    };\n});"
  },
  {
    "path": "src/runtime/runtime.js",
    "content": "/**\n * @fileOverview Runtime管理器，负责Runtime的选择, 连接\n */\ndefine([\n    '../base',\n    '../mediator'\n], function( Base, Mediator ) {\n\n    var $ = Base.$,\n        factories = {},\n\n        // 获取对象的第一个key\n        getFirstKey = function( obj ) {\n            for ( var key in obj ) {\n                if ( obj.hasOwnProperty( key ) ) {\n                    return key;\n                }\n            }\n            return null;\n        };\n\n    // 接口类。\n    function Runtime( options ) {\n        this.options = $.extend({\n            container: document.body\n        }, options );\n        this.uid = Base.guid('rt_');\n    }\n\n    $.extend( Runtime.prototype, {\n\n        getContainer: function() {\n            var opts = this.options,\n                parent, container;\n\n            if ( this._container ) {\n                return this._container;\n            }\n\n            parent = $( opts.container || document.body );\n            container = $( document.createElement('div') );\n\n            container.attr( 'id', 'rt_' + this.uid );\n            container.css({\n                position: 'absolute',\n                top: '0px',\n                left: '0px',\n                width: '1px',\n                height: '1px',\n                overflow: 'hidden'\n            });\n\n            parent.append( container );\n            parent.addClass('webuploader-container');\n            this._container = container;\n            this._parent = parent;\n            return container;\n        },\n\n        init: Base.noop,\n        exec: Base.noop,\n\n        destroy: function() {\n            this._container && this._container.remove();\n            this._parent && this._parent.removeClass('webuploader-container');\n            this.off();\n        }\n    });\n\n    Runtime.orders = 'html5,flash';\n\n\n    /**\n     * 添加Runtime实现。\n     * @param {String} type    类型\n     * @param {Runtime} factory 具体Runtime实现。\n     */\n    Runtime.addRuntime = function( type, factory ) {\n        factories[ type ] = factory;\n    };\n\n    Runtime.hasRuntime = function( type ) {\n        return !!(type ? factories[ type ] : getFirstKey( factories ));\n    };\n\n    Runtime.create = function( opts, orders ) {\n        var type, runtime;\n\n        orders = orders || Runtime.orders;\n        $.each( orders.split( /\\s*,\\s*/g ), function() {\n            if ( factories[ this ] ) {\n                type = this;\n                return false;\n            }\n        });\n\n        type = type || getFirstKey( factories );\n\n        if ( !type ) {\n            throw new Error('Runtime Error');\n        }\n\n        runtime = new factories[ type ]( opts );\n        return runtime;\n    };\n\n    Mediator.installTo( Runtime.prototype );\n    return Runtime;\n});\n"
  },
  {
    "path": "src/uploader.js",
    "content": "/**\n * @fileOverview Uploader上传类\n */\ndefine([\n    './base',\n    './mediator'\n], function( Base, Mediator ) {\n\n    var $ = Base.$;\n\n    /**\n     * 上传入口类。\n     * @class Uploader\n     * @constructor\n     * @grammar new Uploader( opts ) => Uploader\n     * @example\n     * var uploader = WebUploader.Uploader({\n     *     swf: 'path_of_swf/Uploader.swf',\n     *\n     *     // 开起分片上传。\n     *     chunked: true\n     * });\n     */\n    function Uploader( opts ) {\n        this.options = $.extend( true, {}, Uploader.options, opts );\n        this._init( this.options );\n    }\n\n    // default Options\n    // widgets中有相应扩展\n    Uploader.options = {};\n    Mediator.installTo( Uploader.prototype );\n\n    // 批量添加纯命令式方法。\n    $.each({\n        upload: 'start-upload',\n        stop: 'stop-upload',\n        getFile: 'get-file',\n        getFiles: 'get-files',\n        addFile: 'add-file',\n        addFiles: 'add-file',\n        sort: 'sort-files',\n        removeFile: 'remove-file',\n        cancelFile: 'cancel-file',\n        skipFile: 'skip-file',\n        retry: 'retry',\n        isInProgress: 'is-in-progress',\n        makeThumb: 'make-thumb',\n        md5File: 'md5-file',\n        getDimension: 'get-dimension',\n        addButton: 'add-btn',\n        predictRuntimeType: 'predict-runtime-type',\n        refresh: 'refresh',\n        disable: 'disable',\n        enable: 'enable',\n        reset: 'reset'\n    }, function( fn, command ) {\n        Uploader.prototype[ fn ] = function() {\n            return this.request( command, arguments );\n        };\n    });\n\n    $.extend( Uploader.prototype, {\n        state: 'pending',\n\n        _init: function( opts ) {\n            var me = this;\n\n            me.request( 'init', opts, function() {\n                me.state = 'ready';\n                me.trigger('ready');\n            });\n        },\n\n        /**\n         * 获取或者设置Uploader配置项。\n         * @method option\n         * @grammar option( key ) => *\n         * @grammar option( key, val ) => self\n         * @example\n         *\n         * // 初始状态图片上传前不会压缩\n         * var uploader = new WebUploader.Uploader({\n         *     compress: null;\n         * });\n         *\n         * // 修改后图片上传前，尝试将图片压缩到1600 * 1600\n         * uploader.option( 'compress', {\n         *     width: 1600,\n         *     height: 1600\n         * });\n         */\n        option: function( key, val ) {\n            var opts = this.options;\n\n            // setter\n            if ( arguments.length > 1 ) {\n\n                if ( $.isPlainObject( val ) &&\n                        $.isPlainObject( opts[ key ] ) ) {\n                    $.extend( opts[ key ], val );\n                } else {\n                    opts[ key ] = val;\n                }\n\n            } else {    // getter\n                return key ? opts[ key ] : opts;\n            }\n        },\n\n        /**\n         * 获取文件统计信息。返回一个包含一下信息的对象。\n         * * `successNum` 上传成功的文件数\n         * * `progressNum` 上传中的文件数\n         * * `cancelNum` 被删除的文件数\n         * * `invalidNum` 无效的文件数\n         * * `uploadFailNum` 上传失败的文件数\n         * * `queueNum` 还在队列中的文件数\n         * * `interruptNum` 被暂停的文件数\n         * @method getStats\n         * @grammar getStats() => Object\n         */\n        getStats: function() {\n            // return this._mgr.getStats.apply( this._mgr, arguments );\n            var stats = this.request('get-stats');\n\n            return stats ? {\n                successNum: stats.numOfSuccess,\n                progressNum: stats.numOfProgress,\n\n                // who care?\n                // queueFailNum: 0,\n                cancelNum: stats.numOfCancel,\n                invalidNum: stats.numOfInvalid,\n                uploadFailNum: stats.numOfUploadFailed,\n                queueNum: stats.numOfQueue,\n                interruptNum: stats.numOfInterrupt\n            } : {};\n        },\n\n        // 需要重写此方法来来支持opts.onEvent和instance.onEvent的处理器\n        trigger: function( type/*, args...*/ ) {\n            var args = [].slice.call( arguments, 1 ),\n                opts = this.options,\n                name = 'on' + type.substring( 0, 1 ).toUpperCase() +\n                    type.substring( 1 );\n\n            if (\n                    // 调用通过on方法注册的handler.\n                    Mediator.trigger.apply( this, arguments ) === false ||\n\n                    // 调用opts.onEvent\n                    $.isFunction( opts[ name ] ) &&\n                    opts[ name ].apply( this, args ) === false ||\n\n                    // 调用this.onEvent\n                    $.isFunction( this[ name ] ) &&\n                    this[ name ].apply( this, args ) === false ||\n\n                    // 广播所有uploader的事件。\n                    Mediator.trigger.apply( Mediator,\n                    [ this, type ].concat( args ) ) === false ) {\n\n                return false;\n            }\n\n            return true;\n        },\n\n        /**\n         * 销毁 webuploader 实例\n         * @method destroy\n         * @grammar destroy() => undefined\n         */\n        destroy: function() {\n            this.request( 'destroy', arguments );\n            this.off();\n        },\n\n        // widgets/widget.js将补充此方法的详细文档。\n        request: Base.noop\n    });\n\n    /**\n     * 创建Uploader实例，等同于new Uploader( opts );\n     * @method create\n     * @class Base\n     * @static\n     * @grammar Base.create( opts ) => Uploader\n     */\n    Base.create = Uploader.create = function( opts ) {\n        return new Uploader( opts );\n    };\n\n    // 暴露Uploader，可以通过它来扩展业务逻辑。\n    Base.Uploader = Uploader;\n\n    return Uploader;\n});\n"
  },
  {
    "path": "src/webuploader.js",
    "content": "/**\n * @fileOverview Uploader上传类\n */\ndefine([\n    './preset/all',\n    './widgets/log'\n], function( preset ) {\n    return preset;\n});"
  },
  {
    "path": "src/widgets/filednd.js",
    "content": "/**\n * @fileOverview DragAndDrop Widget。\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../lib/dnd',\n    './widget'\n], function( Base, Uploader, Dnd ) {\n    var $ = Base.$;\n\n    Uploader.options.dnd = '';\n\n    /**\n     * @property {Selector} [dnd=undefined]  指定Drag And Drop拖拽的容器，如果不指定，则不启动。\n     * @namespace options\n     * @for Uploader\n     */\n    \n    /**\n     * @property {Selector} [disableGlobalDnd=false]  是否禁掉整个页面的拖拽功能，如果不禁用，图片拖进来的时候会默认被浏览器打开。\n     * @namespace options\n     * @for Uploader\n     */\n\n    /**\n     * @event dndAccept\n     * @param {DataTransferItemList} items DataTransferItem\n     * @description 阻止此事件可以拒绝某些类型的文件拖入进来。目前只有 chrome 提供这样的 API，且只能通过 mime-type 验证。\n     * @for  Uploader\n     */\n    return Uploader.register({\n        name: 'dnd',\n        \n        init: function( opts ) {\n\n            if ( !opts.dnd ||\n                    this.request('predict-runtime-type') !== 'html5' ) {\n                return;\n            }\n\n            var me = this,\n                deferred = Base.Deferred(),\n                options = $.extend({}, {\n                    disableGlobalDnd: opts.disableGlobalDnd,\n                    container: opts.dnd,\n                    accept: opts.accept\n                }),\n                dnd;\n\n            this.dnd = dnd = new Dnd( options );\n\n            dnd.once( 'ready', deferred.resolve );\n            dnd.on( 'drop', function( files ) {\n                me.request( 'add-file', [ files ]);\n            });\n\n            // 检测文件是否全部允许添加。\n            dnd.on( 'accept', function( items ) {\n                return me.owner.trigger( 'dndAccept', items );\n            });\n\n            dnd.init();\n\n            return deferred.promise();\n        },\n\n        destroy: function() {\n            this.dnd && this.dnd.destroy();\n        }\n    });\n});\n"
  },
  {
    "path": "src/widgets/filepaste.js",
    "content": "/**\n * @fileOverview 组件基类。\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../lib/filepaste',\n    './widget'\n], function( Base, Uploader, FilePaste ) {\n    var $ = Base.$;\n\n    /**\n     * @property {Selector} [paste=undefined]  指定监听paste事件的容器，如果不指定，不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为`document.body`.\n     * @namespace options\n     * @for Uploader\n     */\n    return Uploader.register({\n        name: 'paste',\n        \n        init: function( opts ) {\n\n            if ( !opts.paste ||\n                    this.request('predict-runtime-type') !== 'html5' ) {\n                return;\n            }\n\n            var me = this,\n                deferred = Base.Deferred(),\n                options = $.extend({}, {\n                    container: opts.paste,\n                    accept: opts.accept\n                }),\n                paste;\n\n            this.paste = paste = new FilePaste( options );\n\n            paste.once( 'ready', deferred.resolve );\n            paste.on( 'paste', function( files ) {\n                me.owner.request( 'add-file', [ files ]);\n            });\n            paste.init();\n\n            return deferred.promise();\n        },\n\n        destroy: function() {\n            this.paste && this.paste.destroy();\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/filepicker.js",
    "content": "/**\n * @fileOverview 文件选择相关\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../lib/filepicker',\n    './widget'\n], function( Base, Uploader, FilePicker ) {\n    var $ = Base.$;\n\n    $.extend( Uploader.options, {\n\n        /**\n         * @property {Selector | Object} [pick=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 指定选择文件的按钮容器，不指定则不创建按钮。\n         *\n         * * `id` {Seletor|dom} 指定选择文件的按钮容器，不指定则不创建按钮。**注意** 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。\n         * * `label` {String} 请采用 `innerHTML` 代替\n         * * `innerHTML` {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字。\n         * * `multiple` {Boolean} 是否开起同时选择多个文件能力。\n         */\n        pick: null,\n\n        /**\n         * @property {Array} [accept=null]\n         * @namespace options\n         * @for Uploader\n         * @description 指定接受哪些类型的文件。 由于目前还有ext转mimeType表，所以这里需要分开指定。\n         *\n         * * `title` {String} 文字描述\n         * * `extensions` {String} 允许的文件后缀，不带点，多个用逗号分割。\n         * * `mimeTypes` {String} 多个用逗号分割。\n         *\n         * 如：\n         *\n         * ```\n         * {\n         *     title: 'Images',\n         *     extensions: 'gif,jpg,jpeg,bmp,png',\n         *     mimeTypes: 'image/*'\n         * }\n         * ```\n         */\n        accept: null/*{\n            title: 'Images',\n            extensions: 'gif,jpg,jpeg,bmp,png',\n            mimeTypes: 'image/*'\n        }*/\n    });\n\n    return Uploader.register({\n        name: 'picker',\n\n        init: function( opts ) {\n            this.pickers = [];\n            return opts.pick && this.addBtn( opts.pick );\n        },\n\n        refresh: function() {\n            $.each( this.pickers, function() {\n                this.refresh();\n            });\n        },\n\n        /**\n         * @method addButton\n         * @for Uploader\n         * @grammar addButton( pick ) => Promise\n         * @description\n         * 添加文件选择按钮，如果一个按钮不够，需要调用此方法来添加。参数跟[options.pick](#WebUploader:Uploader:options)一致。\n         * @example\n         * uploader.addButton({\n         *     id: '#btnContainer',\n         *     innerHTML: '选择文件'\n         * });\n         */\n        addBtn: function( pick ) {\n            var me = this,\n                opts = me.options,\n                accept = opts.accept,\n                promises = [];\n\n            if ( !pick ) {\n                return;\n            }\n\n            $.isPlainObject( pick ) || (pick = {\n                id: pick\n            });\n\n            $( pick.id ).each(function() {\n                var options, picker, deferred;\n\n                deferred = Base.Deferred();\n\n                options = $.extend({}, pick, {\n                    accept: $.isPlainObject( accept ) ? [ accept ] : accept,\n                    swf: opts.swf,\n                    runtimeOrder: opts.runtimeOrder,\n                    id: this\n                });\n\n                picker = new FilePicker( options );\n\n                picker.once( 'ready', deferred.resolve );\n                picker.on( 'select', function( files ) {\n                    me.owner.request( 'add-file', [ files ]);\n                });\n                picker.on('dialogopen', function() {\n                    me.owner.trigger('dialogOpen', picker.button);\n                });\n                picker.init();\n\n                me.pickers.push( picker );\n\n                promises.push( deferred.promise() );\n            });\n\n            return Base.when.apply( Base, promises );\n        },\n\n        disable: function() {\n            $.each( this.pickers, function() {\n                this.disable();\n            });\n        },\n\n        enable: function() {\n            $.each( this.pickers, function() {\n                this.enable();\n            });\n        },\n\n        destroy: function() {\n            $.each( this.pickers, function() {\n                this.destroy();\n            });\n            this.pickers = null;\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/image.js",
    "content": "/**\n * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../lib/image',\n    './widget'\n], function( Base, Uploader, Image ) {\n\n    var $ = Base.$,\n        throttle;\n\n    // 根据要处理的文件大小来节流，一次不能处理太多，会卡。\n    throttle = (function( max ) {\n        var occupied = 0,\n            waiting = [],\n            tick = function() {\n                var item;\n\n                while ( waiting.length && occupied < max ) {\n                    item = waiting.shift();\n                    occupied += item[ 0 ];\n                    item[ 1 ]();\n                }\n            };\n\n        return function( emiter, size, cb ) {\n            waiting.push([ size, cb ]);\n            emiter.once( 'destroy', function() {\n                occupied -= size;\n                setTimeout( tick, 1 );\n            });\n            setTimeout( tick, 1 );\n        };\n    })( 5 * 1024 * 1024 );\n\n    $.extend( Uploader.options, {\n\n        /**\n         * @property {Object} [thumb]\n         * @namespace options\n         * @for Uploader\n         * @description 配置生成缩略图的选项。\n         *\n         * 默认为：\n         *\n         * ```javascript\n         * {\n         *     width: 110,\n         *     height: 110,\n         *\n         *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n         *     quality: 70,\n         *\n         *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n         *     allowMagnify: true,\n         *\n         *     // 是否允许裁剪。\n         *     crop: true,\n         *\n         *     // 为空的话则保留原有图片格式。\n         *     // 否则强制转换成指定的类型。\n         *     type: 'image/jpeg'\n         * }\n         * ```\n         */\n        thumb: {\n            width: 110,\n            height: 110,\n            quality: 70,\n            allowMagnify: true,\n            crop: true,\n            preserveHeaders: false,\n\n            // 为空的话则保留原有图片格式。\n            // 否则强制转换成指定的类型。\n            // IE 8下面 base64 大小不能超过 32K 否则预览失败，而非 jpeg 编码的图片很可\n            // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg\n            type: 'image/jpeg'\n        },\n\n        /**\n         * @property {Object} [compress]\n         * @namespace options\n         * @for Uploader\n         * @description 配置压缩的图片的选项。如果此选项为`false`, 则图片在上传前不进行压缩。\n         *\n         * 默认为：\n         *\n         * ```javascript\n         * {\n         *     width: 1600,\n         *     height: 1600,\n         *\n         *     // 图片质量，只有type为`image/jpeg`的时候才有效。\n         *     quality: 90,\n         *\n         *     // 是否允许放大，如果想要生成小图的时候不失真，此选项应该设置为false.\n         *     allowMagnify: false,\n         *\n         *     // 是否允许裁剪。\n         *     crop: false,\n         *\n         *     // 是否保留头部meta信息。\n         *     preserveHeaders: true,\n         *\n         *     // 如果发现压缩后文件大小比原来还大，则使用原来图片\n         *     // 此属性可能会影响图片自动纠正功能\n         *     noCompressIfLarger: false,\n         *\n         *     // 单位字节，如果图片大小小于此值，不会采用压缩。\n         *     compressSize: 0\n         * }\n         * ```\n         */\n        compress: {\n            width: 1600,\n            height: 1600,\n            quality: 90,\n            allowMagnify: false,\n            crop: false,\n            preserveHeaders: true\n        }\n    });\n\n    return Uploader.register({\n\n        name: 'image',\n\n\n        /**\n         * 生成缩略图，此过程为异步，所以需要传入`callback`。\n         * 通常情况在图片加入队里后调用此方法来生成预览图以增强交互效果。\n         *\n         * 当 width 或者 height 的值介于 0 - 1 时，被当成百分比使用。\n         *\n         * `callback`中可以接收到两个参数。\n         * * 第一个为error，如果生成缩略图有错误，此error将为真。\n         * * 第二个为ret, 缩略图的Data URL值。\n         *\n         * **注意**\n         * Date URL在IE6/7中不支持，所以不用调用此方法了，直接显示一张暂不支持预览图片好了。\n         * 也可以借助服务端，将 base64 数据传给服务端，生成一个临时文件供预览。\n         *\n         * @method makeThumb\n         * @grammar makeThumb( file, callback ) => undefined\n         * @grammar makeThumb( file, callback, width, height ) => undefined\n         * @for Uploader\n         * @example\n         *\n         * uploader.on( 'fileQueued', function( file ) {\n         *     var $li = ...;\n         *\n         *     uploader.makeThumb( file, function( error, ret ) {\n         *         if ( error ) {\n         *             $li.text('预览错误');\n         *         } else {\n         *             $li.append('<img alt=\"\" src=\"' + ret + '\" />');\n         *         }\n         *     });\n         *\n         * });\n         */\n        makeThumb: function( file, cb, width, height ) {\n            var opts, image;\n\n            file = this.request( 'get-file', file );\n\n            // 只预览图片格式。\n            if ( !file.type.match( /^image/ ) ) {\n                cb( true );\n                return;\n            }\n\n            opts = $.extend({}, this.options.thumb );\n\n            // 如果传入的是object.\n            if ( $.isPlainObject( width ) ) {\n                opts = $.extend( opts, width );\n                width = null;\n            }\n\n            width = width || opts.width;\n            height = height || opts.height;\n\n            image = new Image( opts );\n\n            image.once( 'load', function() {\n                file._info = file._info || image.info();\n                file._meta = file._meta || image.meta();\n\n                // 如果 width 的值介于 0 - 1\n                // 说明设置的是百分比。\n                if ( width <= 1 && width > 0 ) {\n                    width = file._info.width * width;\n                }\n\n                // 同样的规则应用于 height\n                if ( height <= 1 && height > 0 ) {\n                    height = file._info.height * height;\n                }\n\n                image.resize( width, height );\n            });\n\n            // 当 resize 完后\n            image.once( 'complete', function() {\n                cb( false, image.getAsDataUrl( opts.type ) );\n                image.destroy();\n            });\n\n            image.once( 'error', function( reason ) {\n                cb( reason || true );\n                image.destroy();\n            });\n\n            throttle( image, file.source.size, function() {\n                file._info && image.info( file._info );\n                file._meta && image.meta( file._meta );\n                image.loadFromBlob( file.source );\n            });\n        },\n\n        beforeSendFile: function( file ) {\n            var opts = this.options.compress || this.options.resize,\n                compressSize = opts && opts.compressSize || 0,\n                noCompressIfLarger = opts && opts.noCompressIfLarger || false,\n                image, deferred;\n\n            file = this.request( 'get-file', file );\n\n            // 只压缩 jpeg 图片格式。\n            // gif 可能会丢失针\n            // bmp png 基本上尺寸都不大，且压缩比比较小。\n            if ( !opts || !~'image/jpeg,image/jpg'.indexOf( file.type ) ||\n                    file.size < compressSize ||\n                    file._compressed ) {\n                return;\n            }\n\n            opts = $.extend({}, opts );\n            deferred = Base.Deferred();\n\n            image = new Image( opts );\n\n            deferred.always(function() {\n                image.destroy();\n                image = null;\n            });\n            image.once( 'error', deferred.reject );\n            image.once( 'load', function() {\n                var width = opts.width,\n                    height = opts.height;\n\n                file._info = file._info || image.info();\n                file._meta = file._meta || image.meta();\n\n                // 如果 width 的值介于 0 - 1\n                // 说明设置的是百分比。\n                if ( width <= 1 && width > 0 ) {\n                    width = file._info.width * width;\n                }\n\n                // 同样的规则应用于 height\n                if ( height <= 1 && height > 0 ) {\n                    height = file._info.height * height;\n                }\n\n                image.resize( width, height );\n            });\n\n            image.once( 'complete', function() {\n                var blob, size;\n\n                // 移动端 UC / qq 浏览器的无图模式下\n                // ctx.getImageData 处理大图的时候会报 Exception\n                // INDEX_SIZE_ERR: DOM Exception 1\n                try {\n                    blob = image.getAsBlob( opts.type );\n\n                    size = file.size;\n\n                    // 如果压缩后，比原来还大则不用压缩后的。\n                    if ( !noCompressIfLarger || blob.size < size ) {\n                        // file.source.destroy && file.source.destroy();\n                        file.source = blob;\n                        file.size = blob.size;\n\n                        file.trigger( 'resize', blob.size, size );\n                    }\n\n                    // 标记，避免重复压缩。\n                    file._compressed = true;\n                    deferred.resolve();\n                } catch ( e ) {\n                    // 出错了直接继续，让其上传原始图片\n                    deferred.resolve();\n                }\n            });\n\n            file._info && image.info( file._info );\n            file._meta && image.meta( file._meta );\n\n            image.loadFromBlob( file.source );\n            return deferred.promise();\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/log.js",
    "content": "/**\n * @fileOverview 日志组件，主要用来收集错误信息，可以帮助 webuploader 更好的定位问题和发展。\n *\n * 如果您不想要启用此功能，请在打包的时候去掉 log 模块。\n *\n * 或者可以在初始化的时候通过 options.disableWidgets 属性禁用。\n *\n * 如：\n * WebUploader.create({\n *     ...\n *\n *     disableWidgets: 'log',\n *\n *     ...\n * })\n */\ndefine([\n    '../base',\n    '../uploader',\n    './widget'\n], function( Base, Uploader ) {\n    var $ = Base.$,\n        logUrl = ' http://static.tieba.baidu.com/tb/pms/img/st.gif??',\n        product = (location.hostname || location.host || 'protected').toLowerCase(),\n\n        // 只针对 baidu 内部产品用户做统计功能。\n        enable = product && /baidu/i.exec(product),\n        base;\n\n    if (!enable) {\n        return;\n    }\n\n    base = {\n        dv: 3,\n        master: 'webuploader',\n        online: /test/.exec(product) ? 0 : 1,\n        module: '',\n        product: product,\n        type: 0\n    };\n\n    function send(data) {\n        var obj = $.extend({}, base, data),\n            url = logUrl.replace(/^(.*)\\?/, '$1' + $.param( obj )),\n            image = new Image();\n\n        image.src = url;\n    }\n\n    return Uploader.register({\n        name: 'log',\n\n        init: function() {\n            var owner = this.owner,\n                count = 0,\n                size = 0;\n\n            owner\n                .on('error', function(code) {\n                    send({\n                        type: 2,\n                        c_error_code: code\n                    });\n                })\n                .on('uploadError', function(file, reason) {\n                    send({\n                        type: 2,\n                        c_error_code: 'UPLOAD_ERROR',\n                        c_reason: '' + reason\n                    });\n                })\n                .on('uploadComplete', function(file) {\n                    count++;\n                    size += file.size;\n                }).\n                on('uploadFinished', function() {\n                    send({\n                        c_count: count,\n                        c_size: size\n                    });\n                    count = size = 0;\n                });\n\n            send({\n                c_usage: 1\n            });\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/md5.js",
    "content": "/**\n * @fileOverview 图片操作, 负责预览图片和上传前压缩图片\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../lib/md5',\n    '../lib/blob',\n    './widget'\n], function( Base, Uploader, Md5, Blob ) {\n\n    return Uploader.register({\n        name: 'md5',\n\n\n        /**\n         * 计算文件 md5 值，返回一个 promise 对象，可以监听 progress 进度。\n         *\n         *\n         * @method md5File\n         * @grammar md5File( file[, start[, end]] ) => promise\n         * @for Uploader\n         * @example\n         *\n         * uploader.on( 'fileQueued', function( file ) {\n         *     var $li = ...;\n         *\n         *     uploader.md5File( file )\n         *\n         *         // 及时显示进度\n         *         .progress(function(percentage) {\n         *             console.log('Percentage:', percentage);\n         *         })\n         *\n         *         // 完成\n         *         .then(function(val) {\n         *             console.log('md5 result:', val);\n         *         });\n         *\n         * });\n         */\n        md5File: function( file, start, end ) {\n            var md5 = new Md5(),\n                deferred = Base.Deferred(),\n                blob = (file instanceof Blob) ? file :\n                    this.request( 'get-file', file ).source;\n\n            md5.on( 'progress load', function( e ) {\n                e = e || {};\n                deferred.notify( e.total ? e.loaded / e.total : 1 );\n            });\n\n            md5.on( 'complete', function() {\n                deferred.resolve( md5.getResult() );\n            });\n\n            md5.on( 'error', function( reason ) {\n                deferred.reject( reason );\n            });\n\n            if ( arguments.length > 1 ) {\n                start = start || 0;\n                end = end || 0;\n                start < 0 && (start = blob.size + start);\n                end < 0 && (end = blob.size + end);\n                end = Math.min( end, blob.size );\n                blob = blob.slice( start, end );\n            }\n\n            md5.loadFromBlob( blob );\n\n            return deferred.promise();\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/queue.js",
    "content": "/**\n * @fileOverview 队列\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../queue',\n    '../file',\n    '../lib/file',\n    '../runtime/client',\n    './widget'\n], function( Base, Uploader, Queue, WUFile, File, RuntimeClient ) {\n\n    var $ = Base.$,\n        rExt = /\\.\\w+$/,\n        Status = WUFile.Status;\n\n    return Uploader.register({\n        name: 'queue',\n\n        init: function( opts ) {\n            var me = this,\n                deferred, len, i, item, arr, accept, runtime;\n\n            if ( $.isPlainObject( opts.accept ) ) {\n                opts.accept = [ opts.accept ];\n            }\n\n            // accept中的中生成匹配正则。\n            if ( opts.accept ) {\n                arr = [];\n\n                for ( i = 0, len = opts.accept.length; i < len; i++ ) {\n                    item = opts.accept[ i ].extensions;\n                    item && arr.push( item );\n                }\n\n                if ( arr.length ) {\n                    accept = '\\\\.' + arr.join(',')\n                            .replace( /,/g, '$|\\\\.' )\n                            .replace( /\\*/g, '.*' ) + '$';\n                }\n\n                me.accept = new RegExp( accept, 'i' );\n            }\n\n            me.queue = new Queue();\n            me.stats = me.queue.stats;\n\n            // 如果当前不是html5运行时，那就算了。\n            // 不执行后续操作\n            if ( this.request('predict-runtime-type') !== 'html5' ) {\n                return;\n            }\n\n            // 创建一个 html5 运行时的 placeholder\n            // 以至于外部添加原生 File 对象的时候能正确包裹一下供 webuploader 使用。\n            deferred = Base.Deferred();\n            this.placeholder = runtime = new RuntimeClient('Placeholder');\n            runtime.connectRuntime({\n                runtimeOrder: 'html5'\n            }, function() {\n                me._ruid = runtime.getRuid();\n                deferred.resolve();\n            });\n            return deferred.promise();\n        },\n\n\n        // 为了支持外部直接添加一个原生File对象。\n        _wrapFile: function( file ) {\n            if ( !(file instanceof WUFile) ) {\n\n                if ( !(file instanceof File) ) {\n                    if ( !this._ruid ) {\n                        throw new Error('Can\\'t add external files.');\n                    }\n                    file = new File( this._ruid, file );\n                }\n\n                file = new WUFile( file );\n            }\n\n            return file;\n        },\n\n        // 判断文件是否可以被加入队列\n        acceptFile: function( file ) {\n            var invalid = !file || !file.size || this.accept &&\n\n                    // 如果名字中有后缀，才做后缀白名单处理。\n                    rExt.exec( file.name ) && !this.accept.test( file.name );\n\n            return !invalid;\n        },\n\n\n        /**\n         * @event beforeFileQueued\n         * @param {File} file File对象\n         * @description 当文件被加入队列之前触发。如果此事件handler的返回值为`false`，则此文件不会被添加进入队列。\n         * @for  Uploader\n         */\n\n        /**\n         * @event fileQueued\n         * @param {File} file File对象\n         * @description 当文件被加入队列以后触发。\n         * @for  Uploader\n         */\n\n        _addFile: function( file ) {\n            var me = this;\n\n            file = me._wrapFile( file );\n\n            // 不过类型判断允许不允许，先派送 `beforeFileQueued`\n            if ( !me.owner.trigger( 'beforeFileQueued', file ) ) {\n                return;\n            }\n\n            // 类型不匹配，则派送错误事件，并返回。\n            if ( !me.acceptFile( file ) ) {\n                me.owner.trigger( 'error', 'Q_TYPE_DENIED', file );\n                return;\n            }\n\n            me.queue.append( file );\n            me.owner.trigger( 'fileQueued', file );\n            return file;\n        },\n\n        getFile: function( fileId ) {\n            return this.queue.getFile( fileId );\n        },\n\n        /**\n         * @event filesQueued\n         * @param {File} files 数组，内容为原始File(lib/File）对象。\n         * @description 当一批文件添加进队列以后触发。\n         * @for  Uploader\n         */\n        \n        /**\n         * @property {Boolean} [auto=false]\n         * @namespace options\n         * @for Uploader\n         * @description 设置为 true 后，不需要手动调用上传，有文件选择即开始上传。\n         * \n         */\n\n        /**\n         * @method addFiles\n         * @grammar addFiles( file ) => undefined\n         * @grammar addFiles( [file1, file2 ...] ) => undefined\n         * @param {Array of File or File} [files] Files 对象 数组\n         * @description 添加文件到队列\n         * @for  Uploader\n         */\n        addFile: function( files ) {\n            var me = this;\n\n            if ( !files.length ) {\n                files = [ files ];\n            }\n\n            files = $.map( files, function( file ) {\n                return me._addFile( file );\n            });\n\t\t\t\n\t\t\tif ( files.length ) {\n\n                me.owner.trigger( 'filesQueued', files );\n\n\t\t\t\tif ( me.options.auto ) {\n\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\tme.request('start-upload');\n\t\t\t\t\t}, 20 );\n\t\t\t\t}\n            }\n        },\n\n        getStats: function() {\n            return this.stats;\n        },\n\n        /**\n         * @event fileDequeued\n         * @param {File} file File对象\n         * @description 当文件被移除队列后触发。\n         * @for  Uploader\n         */\n\n         /**\n         * @method removeFile\n         * @grammar removeFile( file ) => undefined\n         * @grammar removeFile( id ) => undefined\n         * @grammar removeFile( file, true ) => undefined\n         * @grammar removeFile( id, true ) => undefined\n         * @param {File|id} file File对象或这File对象的id\n         * @description 移除某一文件, 默认只会标记文件状态为已取消，如果第二个参数为 `true` 则会从 queue 中移除。\n         * @for  Uploader\n         * @example\n         *\n         * $li.on('click', '.remove-this', function() {\n         *     uploader.removeFile( file );\n         * })\n         */\n        removeFile: function( file, remove ) {\n            var me = this;\n\n            file = file.id ? file : me.queue.getFile( file );\n\n            this.request( 'cancel-file', file );\n\n            if ( remove ) {\n                this.queue.removeFile( file );\n            }\n        },\n\n        /**\n         * @method getFiles\n         * @grammar getFiles() => Array\n         * @grammar getFiles( status1, status2, status... ) => Array\n         * @description 返回指定状态的文件集合，不传参数将返回所有状态的文件。\n         * @for  Uploader\n         * @example\n         * console.log( uploader.getFiles() );    // => all files\n         * console.log( uploader.getFiles('error') )    // => all error files.\n         */\n        getFiles: function() {\n            return this.queue.getFiles.apply( this.queue, arguments );\n        },\n\n        fetchFile: function() {\n            return this.queue.fetch.apply( this.queue, arguments );\n        },\n\n        /**\n         * @method retry\n         * @grammar retry() => undefined\n         * @grammar retry( file ) => undefined\n         * @description 重试上传，重试指定文件，或者从出错的文件开始重新上传。\n         * @for  Uploader\n         * @example\n         * function retry() {\n         *     uploader.retry();\n         * }\n         */\n        retry: function( file, noForceStart ) {\n            var me = this,\n                files, i, len;\n\n            if ( file ) {\n                file = file.id ? file : me.queue.getFile( file );\n                file.setStatus( Status.QUEUED );\n                noForceStart || me.request('start-upload');\n                return;\n            }\n\n            files = me.queue.getFiles( Status.ERROR );\n            i = 0;\n            len = files.length;\n\n            for ( ; i < len; i++ ) {\n                file = files[ i ];\n                file.setStatus( Status.QUEUED );\n            }\n\n            me.request('start-upload');\n        },\n\n        /**\n         * @method sort\n         * @grammar sort( fn ) => undefined\n         * @description 排序队列中的文件，在上传之前调整可以控制上传顺序。\n         * @for  Uploader\n         */\n        sortFiles: function() {\n            return this.queue.sort.apply( this.queue, arguments );\n        },\n\n        /**\n         * @event reset\n         * @description 当 uploader 被重置的时候触发。\n         * @for  Uploader\n         */\n\n        /**\n         * @method reset\n         * @grammar reset() => undefined\n         * @description 重置uploader。目前只重置了队列。\n         * @for  Uploader\n         * @example\n         * uploader.reset();\n         */\n        reset: function() {\n            this.owner.trigger('reset');\n            this.queue = new Queue();\n            this.stats = this.queue.stats;\n        },\n\n        destroy: function() {\n            this.reset();\n            this.placeholder && this.placeholder.destroy();\n        }\n    });\n\n});"
  },
  {
    "path": "src/widgets/runtime.js",
    "content": "/**\n * @fileOverview 添加获取Runtime相关信息的方法。\n */\ndefine([\n    '../uploader',\n    '../runtime/runtime',\n    './widget'\n], function( Uploader, Runtime ) {\n\n    Uploader.support = function() {\n        return Runtime.hasRuntime.apply( Runtime, arguments );\n    };\n\n    /**\n     * @property {Object} [runtimeOrder=html5,flash]\n     * @namespace options\n     * @for Uploader\n     * @description 指定运行时启动顺序。默认会先尝试 html5 是否支持，如果支持则使用 html5, 否则使用 flash.\n     *\n     * 可以将此值设置成 `flash`，来强制使用 flash 运行时。\n     */\n\n    return Uploader.register({\n        name: 'runtime',\n\n        init: function() {\n            if ( !this.predictRuntimeType() ) {\n                throw Error('Runtime Error');\n            }\n        },\n\n        /**\n         * 预测Uploader将采用哪个`Runtime`\n         * @grammar predictRuntimeType() => String\n         * @method predictRuntimeType\n         * @for  Uploader\n         */\n        predictRuntimeType: function() {\n            var orders = this.options.runtimeOrder || Runtime.orders,\n                type = this.type,\n                i, len;\n\n            if ( !type ) {\n                orders = orders.split( /\\s*,\\s*/g );\n\n                for ( i = 0, len = orders.length; i < len; i++ ) {\n                    if ( Runtime.hasRuntime( orders[ i ] ) ) {\n                        this.type = type = orders[ i ];\n                        break;\n                    }\n                }\n            }\n\n            return type;\n        }\n    });\n});"
  },
  {
    "path": "src/widgets/upload.js",
    "content": "/**\n * @fileOverview 负责文件上传相关。\n */\ndefine([\n    '../base',\n    '../uploader',\n    '../file',\n    '../lib/transport',\n    './widget'\n], function( Base, Uploader, WUFile, Transport ) {\n\n    var $ = Base.$,\n        isPromise = Base.isPromise,\n        Status = WUFile.Status;\n\n    // 添加默认配置项\n    $.extend( Uploader.options, {\n\n\n        /**\n         * @property {Boolean} [prepareNextFile=false]\n         * @namespace options\n         * @for Uploader\n         * @description 是否允许在文件传输时提前把下一个文件准备好。\n         * 某些文件的准备工作比较耗时，比如图片压缩，md5序列化。\n         * 如果能提前在当前文件传输期处理，可以节省总体耗时。\n         */\n        prepareNextFile: false,\n\n        /**\n         * @property {Boolean} [chunked=false]\n         * @namespace options\n         * @for Uploader\n         * @description 是否要分片处理大文件上传。\n         */\n        chunked: false,\n\n        /**\n         * @property {Boolean} [chunkSize=5242880]\n         * @namespace options\n         * @for Uploader\n         * @description 如果要分片，分多大一片？ 默认大小为5M.\n         */\n        chunkSize: 5 * 1024 * 1024,\n\n        /**\n         * @property {Boolean} [chunkRetry=2]\n         * @namespace options\n         * @for Uploader\n         * @description 如果某个分片由于网络问题出错，允许自动重传多少次？\n         */\n        chunkRetry: 2,\n\n        /**\n         * @property {Number} [chunkRetryDelay=1000]\n         * @namespace options\n         * @for Uploader\n         * @description 开启重试后，设置重试延时时间, 单位毫秒。默认1000毫秒，即1秒.\n         */\n        chunkRetryDelay: 1000,\n\n        /**\n         * @property {Boolean} [threads=3]\n         * @namespace options\n         * @for Uploader\n         * @description 上传并发数。允许同时最大上传进程数。\n         */\n        threads: 3,\n\n\n        /**\n         * @property {Object} [formData={}]\n         * @namespace options\n         * @for Uploader\n         * @description 文件上传请求的参数表，每次发送都会发送此对象中的参数。\n         */\n        formData: {}\n\n        /**\n         * @property {Object} [fileVal='file']\n         * @namespace options\n         * @for Uploader\n         * @description 设置文件上传域的name。\n         */\n\n         /**\n         * @property {Object} [method=POST]\n         * @namespace options\n         * @for Uploader\n         * @description 文件上传方式，`POST` 或者 `GET`。\n         */\n\n        /**\n         * @property {Object} [sendAsBinary=false]\n         * @namespace options\n         * @for Uploader\n         * @description 是否已二进制的流的方式发送文件，这样整个上传内容`php://input`都为文件内容，\n         * 其他参数在$_GET数组中。\n         */\n    });\n\n    // 负责将文件切片。\n    function CuteFile( file, chunkSize ) {\n        var pending = [],\n            blob = file.source,\n            total = blob.size,\n            chunks = chunkSize ? Math.ceil( total / chunkSize ) : 1,\n            start = 0,\n            index = 0,\n            len, api;\n\n        api = {\n            file: file,\n\n            has: function() {\n                return !!pending.length;\n            },\n\n            shift: function() {\n                return pending.shift();\n            },\n\n            unshift: function( block ) {\n                pending.unshift( block );\n            }\n        };\n\n        while ( index < chunks ) {\n            len = Math.min( chunkSize, total - start );\n\n            pending.push({\n                file: file,\n                start: start,\n                end: chunkSize ? (start + len) : total,\n                total: total,\n                chunks: chunks,\n                chunk: index++,\n                cuted: api\n            });\n            start += len;\n        }\n\n        file.blocks = pending.concat();\n        file.remaning = pending.length;\n\n        return api;\n    }\n\n    Uploader.register({\n        name: 'upload',\n\n        init: function() {\n            var owner = this.owner,\n                me = this;\n\n            this.runing = false;\n            this.progress = false;\n\n            owner\n                .on( 'startUpload', function() {\n                    me.progress = true;\n                })\n                .on( 'uploadFinished', function() {\n                    me.progress = false;\n                });\n\n            // 记录当前正在传的数据，跟threads相关\n            this.pool = [];\n\n            // 缓存分好片的文件。\n            this.stack = [];\n\n            // 缓存即将上传的文件。\n            this.pending = [];\n\n            // 跟踪还有多少分片在上传中但是没有完成上传。\n            this.remaning = 0;\n            this.__tick = Base.bindFn( this._tick, this );\n\n            // 销毁上传相关的属性。\n            owner.on( 'uploadComplete', function( file ) {\n\n                // 把其他块取消了。\n                file.blocks && $.each( file.blocks, function( _, v ) {\n                    v.transport && (v.transport.abort(), v.transport.destroy());\n                    delete v.transport;\n                });\n\n                delete file.blocks;\n                delete file.remaning;\n            });\n        },\n\n        reset: function() {\n            this.request( 'stop-upload', true );\n            this.runing = false;\n            this.pool = [];\n            this.stack = [];\n            this.pending = [];\n            this.remaning = 0;\n            this._trigged = false;\n            this._promise = null;\n        },\n\n        /**\n         * @event startUpload\n         * @description 当开始上传流程时触发。\n         * @for  Uploader\n         */\n\n        /**\n         * 开始上传。此方法可以从初始状态调用开始上传流程，也可以从暂停状态调用，继续上传流程。\n         *\n         * 可以指定开始某一个文件。\n         * @grammar upload() => undefined\n         * @grammar upload( file | fileId) => undefined\n         * @method upload\n         * @for  Uploader\n         */\n        startUpload: function(file) {\n            var me = this;\n\n            // 移出invalid的文件\n            $.each( me.request( 'get-files', Status.INVALID ), function() {\n                me.request( 'remove-file', this );\n            });\n\n            // 如果指定了开始某个文件，则只开始指定的文件。\n            if ( file ) {\n                file = file.id ? file : me.request( 'get-file', file );\n\n                if (file.getStatus() === Status.INTERRUPT) {\n                    file.setStatus( Status.QUEUED );\n\n                    $.each( me.pool, function( _, v ) {\n\n                        // 之前暂停过。\n                        if (v.file !== file) {\n                            return;\n                        }\n\n                        v.transport && v.transport.send();\n                        file.setStatus( Status.PROGRESS );\n                    });\n\n                    \n                } else if (file.getStatus() !== Status.PROGRESS) {\n                    file.setStatus( Status.QUEUED );\n                }\n            } else {\n                $.each( me.request( 'get-files', [ Status.INITED ] ), function() {\n                    this.setStatus( Status.QUEUED );\n                });\n            }\n\n            if ( me.runing ) {\n                me.owner.trigger('startUpload', file);// 开始上传或暂停恢复的，trigger event\n                return Base.nextTick( me.__tick );\n            }\n\n            me.runing = true;\n            var files = [];\n\n            // 如果有暂停的，则续传\n            file || $.each( me.pool, function( _, v ) {\n                var file = v.file;\n\n                if ( file.getStatus() === Status.INTERRUPT ) {\n                    me._trigged = false;\n                    files.push(file);\n\n                    if (v.waiting) {\n                        return;\n                    }\n                    \n                    // 文件 prepare 完后，如果暂停了，这个时候只会把文件插入 pool, 而不会创建 tranport，\n                    v.transport ? v.transport.send() : me._doSend(v);\n                }\n            });\n\n            $.each(files, function() {\n                this.setStatus( Status.PROGRESS );\n            });\n\n            file || $.each( me.request( 'get-files',\n                    Status.INTERRUPT ), function() {\n                this.setStatus( Status.PROGRESS );\n            });\n\n            me._trigged = false;\n            Base.nextTick( me.__tick );\n            me.owner.trigger('startUpload');\n        },\n\n        /**\n         * @event stopUpload\n         * @description 当开始上传流程暂停时触发。\n         * @for  Uploader\n         */\n\n        /**\n         * 暂停上传。第一个参数为是否中断上传当前正在上传的文件。\n         *\n         * 如果第一个参数是文件，则只暂停指定文件。\n         * @grammar stop() => undefined\n         * @grammar stop( true ) => undefined\n         * @grammar stop( file ) => undefined\n         * @method stop\n         * @for  Uploader\n         */\n        stopUpload: function( file, interrupt ) {\n            var me = this;\n\n            if (file === true) {\n                interrupt = file;\n                file = null;\n            }\n\n            if ( me.runing === false ) {\n                return;\n            }\n\n            // 如果只是暂停某个文件。\n            if ( file ) {\n                file = file.id ? file : me.request( 'get-file', file );\n\n                if ( file.getStatus() !== Status.PROGRESS &&\n                        file.getStatus() !== Status.QUEUED ) {\n                    return;\n                }\n\n                file.setStatus( Status.INTERRUPT );\n\n\n                $.each( me.pool, function( _, v ) {\n\n                    // 只 abort 指定的文件，每一个分片。\n                    if (v.file === file) {\n                        v.transport && v.transport.abort();\n\n                        if (interrupt) {\n                            me._putback(v);\n                            me._popBlock(v);\n                        }\n                    }\n                });\n\n                me.owner.trigger('stopUpload', file);// 暂停，trigger event\n\n                return Base.nextTick( me.__tick );\n            }\n\n            me.runing = false;\n\n            // 正在准备中的文件。\n            if (this._promise && this._promise.file) {\n                this._promise.file.setStatus( Status.INTERRUPT );\n            }\n\n            interrupt && $.each( me.pool, function( _, v ) {\n                v.transport && v.transport.abort();\n                v.file.setStatus( Status.INTERRUPT );\n            });\n\n            me.owner.trigger('stopUpload');\n        },\n\n        /**\n         * @method cancelFile\n         * @grammar cancelFile( file ) => undefined\n         * @grammar cancelFile( id ) => undefined\n         * @param {File|id} file File对象或这File对象的id\n         * @description 标记文件状态为已取消, 同时将中断文件传输。\n         * @for  Uploader\n         * @example\n         *\n         * $li.on('click', '.remove-this', function() {\n         *     uploader.cancelFile( file );\n         * })\n         */\n        cancelFile: function( file ) {\n            file = file.id ? file : this.request( 'get-file', file );\n\n            // 如果正在上传。\n            file.blocks && $.each( file.blocks, function( _, v ) {\n                var _tr = v.transport;\n\n                if ( _tr ) {\n                    _tr.abort();\n                    _tr.destroy();\n                    delete v.transport;\n                }\n            });\n\n            file.setStatus( Status.CANCELLED );\n            this.owner.trigger( 'fileDequeued', file );\n        },\n\n        /**\n         * 判断`Uploader`是否正在上传中。\n         * @grammar isInProgress() => Boolean\n         * @method isInProgress\n         * @for  Uploader\n         */\n        isInProgress: function() {\n            return !!this.progress;\n        },\n\n        _getStats: function() {\n            return this.request('get-stats');\n        },\n\n        /**\n         * 跳过一个文件上传，直接标记指定文件为已上传状态。\n         * @grammar skipFile( file ) => undefined\n         * @method skipFile\n         * @for  Uploader\n         */\n        skipFile: function( file, status ) {\n            file = file.id ? file : this.request( 'get-file', file );\n\n            file.setStatus( status || Status.COMPLETE );\n            file.skipped = true;\n\n            // 如果正在上传。\n            file.blocks && $.each( file.blocks, function( _, v ) {\n                var _tr = v.transport;\n\n                if ( _tr ) {\n                    _tr.abort();\n                    _tr.destroy();\n                    delete v.transport;\n                }\n            });\n\n            this.owner.trigger( 'uploadSkip', file );\n        },\n\n        /**\n         * @event uploadFinished\n         * @description 当所有文件上传结束时触发。\n         * @for  Uploader\n         */\n        _tick: function() {\n            var me = this,\n                opts = me.options,\n                fn, val;\n\n            // 上一个promise还没有结束，则等待完成后再执行。\n            if ( me._promise ) {\n                return me._promise.always( me.__tick );\n            }\n\n            // 还有位置，且还有文件要处理的话。\n            if ( me.pool.length < opts.threads && (val = me._nextBlock()) ) {\n                me._trigged = false;\n\n                fn = function( val ) {\n                    me._promise = null;\n\n                    // 有可能是reject过来的，所以要检测val的类型。\n                    val && val.file && me._startSend( val );\n                    Base.nextTick( me.__tick );\n                };\n\n                me._promise = isPromise( val ) ? val.always( fn ) : fn( val );\n\n            // 没有要上传的了，且没有正在传输的了。\n            } else if ( !me.remaning && !me._getStats().numOfQueue &&\n                !me._getStats().numOfInterrupt ) {\n                me.runing = false;\n\n                me._trigged || Base.nextTick(function() {\n                    me.owner.trigger('uploadFinished');\n                });\n                me._trigged = true;\n            }\n        },\n\n        _putback: function(block) {\n            var idx;\n\n            block.cuted.unshift(block);\n            idx = this.stack.indexOf(block.cuted);\n\n            if (!~idx) {\n                // 如果不在里面，说明移除过，需要把计数还原回去。\n                this.remaning++;\n                block.file.remaning++;\n                this.stack.unshift(block.cuted);\n            }\n        },\n\n        _getStack: function() {\n            var i = 0,\n                act;\n\n            while ( (act = this.stack[ i++ ]) ) {\n                if ( act.has() && act.file.getStatus() === Status.PROGRESS ) {\n                    return act;\n                } else if (!act.has() ||\n                        act.file.getStatus() !== Status.PROGRESS &&\n                        act.file.getStatus() !== Status.INTERRUPT ) {\n\n                    // 把已经处理完了的，或者，状态为非 progress（上传中）、\n                    // interupt（暂停中） 的移除。\n                    this.stack.splice( --i, 1 );\n                }\n            }\n\n            return null;\n        },\n\n        _nextBlock: function() {\n            var me = this,\n                opts = me.options,\n                act, next, done, preparing;\n\n            // 如果当前文件还有没有需要传输的，则直接返回剩下的。\n            if ( (act = this._getStack()) ) {\n\n                // 是否提前准备下一个文件\n                if ( opts.prepareNextFile && !me.pending.length ) {\n                    me._prepareNextFile();\n                }\n\n                return act.shift();\n\n            // 否则，如果正在运行，则准备下一个文件，并等待完成后返回下个分片。\n            } else if ( me.runing ) {\n\n                // 如果缓存中有，则直接在缓存中取，没有则去queue中取。\n                if ( !me.pending.length && me._getStats().numOfQueue ) {\n                    me._prepareNextFile();\n                }\n\n                next = me.pending.shift();\n                done = function( file ) {\n                    if ( !file ) {\n                        return null;\n                    }\n\n                    act = CuteFile( file, opts.chunked ? opts.chunkSize : 0 );\n                    me.stack.push(act);\n                    return act.shift();\n                };\n\n                // 文件可能还在prepare中，也有可能已经完全准备好了。\n                if ( isPromise( next) ) {\n                    preparing = next.file;\n                    next = next[ next.pipe ? 'pipe' : 'then' ]( done );\n                    next.file = preparing;\n                    return next;\n                }\n\n                return done( next );\n            }\n        },\n\n\n        /**\n         * @event uploadStart\n         * @param {File} file File对象\n         * @description 某个文件开始上传前触发，一个文件只会触发一次。\n         * @for  Uploader\n         */\n        _prepareNextFile: function() {\n            var me = this,\n                file = me.request('fetch-file'),\n                pending = me.pending,\n                promise;\n\n            if ( file ) {\n                promise = me.request( 'before-send-file', file, function() {\n\n                    // 有可能文件被skip掉了。文件被skip掉后，状态坑定不是Queued.\n                    if ( file.getStatus() === Status.PROGRESS ||\n                        file.getStatus() === Status.INTERRUPT ) {\n                        return file;\n                    }\n\n                    return me._finishFile( file );\n                });\n\n                me.owner.trigger( 'uploadStart', file );\n                file.setStatus( Status.PROGRESS );\n\n                promise.file = file;\n\n                // 如果还在pending中，则替换成文件本身。\n                promise.done(function() {\n                    var idx = $.inArray( promise, pending );\n\n                    ~idx && pending.splice( idx, 1, file );\n                });\n\n                // befeore-send-file的钩子就有错误发生。\n                promise.fail(function( reason ) {\n                    file.setStatus( Status.ERROR, reason );\n                    me.owner.trigger( 'uploadError', file, reason );\n                    me.owner.trigger( 'uploadComplete', file );\n                });\n\n                pending.push( promise );\n            }\n        },\n\n        // 让出位置了，可以让其他分片开始上传\n        _popBlock: function( block ) {\n            var idx = $.inArray( block, this.pool );\n\n            this.pool.splice( idx, 1 );\n            block.file.remaning--;\n            this.remaning--;\n        },\n\n        // 开始上传，可以被掉过。如果promise被reject了，则表示跳过此分片。\n        _startSend: function( block ) {\n            var me = this,\n                file = block.file,\n                promise;\n\n            // 有可能在 before-send-file 的 promise 期间改变了文件状态。\n            // 如：暂停，取消\n            // 我们不能中断 promise, 但是可以在 promise 完后，不做上传操作。\n            if ( file.getStatus() !== Status.PROGRESS ) {\n\n                // 如果是中断，则还需要放回去。\n                if (file.getStatus() === Status.INTERRUPT) {\n                    me._putback(block);\n                }\n\n                return;\n            }\n\n            me.pool.push( block );\n            me.remaning++;\n\n            // 如果没有分片，则直接使用原始的。\n            // 不会丢失content-type信息。\n            block.blob = block.chunks === 1 ? file.source :\n                    file.source.slice( block.start, block.end );\n\n            // hook, 每个分片发送之前可能要做些异步的事情。\n            block.waiting = promise = me.request( 'before-send', block, function() {\n                delete block.waiting;\n\n                // 有可能文件已经上传出错了，所以不需要再传输了。\n                if ( file.getStatus() === Status.PROGRESS ) {\n                    me._doSend( block );\n                } else if (block.file.getStatus() !== Status.INTERRUPT) {\n                    me._popBlock(block);\n                }\n\n                Base.nextTick(me.__tick);\n            });\n\n            // 如果为fail了，则跳过此分片。\n            promise.fail(function() {\n                delete block.waiting;\n\n                if ( file.remaning === 1 ) {\n                    me._finishFile( file ).always(function() {\n                        block.percentage = 1;\n                        me._popBlock( block );\n                        me.owner.trigger( 'uploadComplete', file );\n                        Base.nextTick( me.__tick );\n                    });\n                } else {\n                    block.percentage = 1;\n                    me.updateFileProgress( file );\n                    me._popBlock( block );\n                    Base.nextTick( me.__tick );\n                }\n            });\n        },\n\n\n        /**\n         * @event uploadBeforeSend\n         * @param {Object} object\n         * @param {Object} data 默认的上传参数，可以扩展此对象来控制上传参数。\n         * @param {Object} headers 可以扩展此对象来控制上传头部。\n         * @description 当某个文件的分块在发送前触发，主要用来询问是否要添加附带参数，大文件在开起分片上传的前提下此事件可能会触发多次。\n         * @for  Uploader\n         */\n\n        /**\n         * @event uploadAccept\n         * @param {Object} object\n         * @param {Object} ret 服务端的返回数据，json格式，如果服务端不是json格式，从ret._raw中取数据，自行解析。\n         * @description 当某个文件上传到服务端响应后，会派送此事件来询问服务端响应是否有效。如果此事件handler返回值为`false`, 则此文件将派送`server`类型的`uploadError`事件。\n         * @for  Uploader\n         */\n\n        /**\n         * @event uploadProgress\n         * @param {File} file File对象\n         * @param {Number} percentage 上传进度\n         * @description 上传过程中触发，携带上传进度。\n         * @for  Uploader\n         */\n\n\n        /**\n         * @event uploadError\n         * @param {File} file File对象\n         * @param {String} reason 出错的code\n         * @description 当文件上传出错时触发。\n         * @for  Uploader\n         */\n\n        /**\n         * @event uploadSuccess\n         * @param {File} file File对象\n         * @param {Object} response 服务端返回的数据\n         * @description 当文件上传成功时触发。\n         * @for  Uploader\n         */\n\n        /**\n         * @event uploadComplete\n         * @param {File} [file] File对象\n         * @description 不管成功或者失败，文件上传完成时触发。\n         * @for  Uploader\n         */\n\n        // 做上传操作。\n        _doSend: function( block ) {\n            var me = this,\n                owner = me.owner,\n                opts = $.extend({}, me.options, block.options),\n                file = block.file,\n                tr = new Transport( opts ),\n                data = $.extend({}, opts.formData ),\n                headers = $.extend({}, opts.headers ),\n                requestAccept, ret;\n\n            block.transport = tr;\n\n            tr.on( 'destroy', function() {\n                delete block.transport;\n                me._popBlock( block );\n                Base.nextTick( me.__tick );\n            });\n\n            // 广播上传进度。以文件为单位。\n            tr.on( 'progress', function( percentage ) {\n                block.percentage = percentage;\n                me.updateFileProgress( file );\n            });\n\n            // 用来询问，是否返回的结果是有错误的。\n            requestAccept = function( reject ) {\n                var fn;\n\n                ret = tr.getResponseAsJson() || {};\n                ret._raw = tr.getResponse();\n                ret._headers = tr.getResponseHeaders();\n                block.response = ret;\n                fn = function( value ) {\n                    reject = value;\n                };\n\n                // 服务端响应了，不代表成功了，询问是否响应正确。\n                if ( !owner.trigger( 'uploadAccept', block, ret, fn ) ) {\n                    reject = reject || 'server';\n                }\n\n                return reject;\n            };\n\n            // 尝试重试，然后广播文件上传出错。\n            tr.on( 'error', function( type, flag ) {\n                // 在 runtime/html5/transport.js 上为 type 加上了状态码，形式：type|status|text（如：http-403-Forbidden）\n                // 这里把状态码解释出来，并还原后面代码所依赖的 type 变量\n                var typeArr = type.split( '|' ), status, statusText;  \n                type = typeArr[0];\n                status = parseFloat( typeArr[1] ),\n                statusText = typeArr[2];\n\n                block.retried = block.retried || 0;\n\n                // 自动重试\n                if ( block.chunks > 1 && ~'http,abort,server'.indexOf( type.replace( /-.*/, '' ) ) &&\n                        block.retried < opts.chunkRetry ) {\n\n                    block.retried++;\n\n                    me.retryTimer = setTimeout(function() {\n                        tr.send();\n                    }, opts.chunkRetryDelay || 1000);\n\n                } else {\n\n                    // http status 500 ~ 600\n                    if ( !flag && type === 'server' ) {\n                        type = requestAccept( type );\n                    }\n\n                    file.setStatus( Status.ERROR, type );\n                    owner.trigger( 'uploadError', file, type, status, statusText );\n                    owner.trigger( 'uploadComplete', file );\n                }\n            });\n\n            // 上传成功\n            tr.on( 'load', function() {\n                var reason;\n\n                // 如果非预期，转向上传出错。\n                if ( (reason = requestAccept()) ) {\n                    tr.trigger( 'error', reason, true );\n                    return;\n                }\n\n                // 全部上传完成。\n                if ( file.remaning === 1 ) {\n                    me._finishFile( file, ret );\n                } else {\n                    tr.destroy();\n                }\n            });\n\n            // 配置默认的上传字段。\n            data = $.extend( data, {\n                id: file.id,\n                name: file.name,\n                type: file.type,\n                lastModifiedDate: file.lastModifiedDate,\n                size: file.size\n            });\n\n            block.chunks > 1 && $.extend( data, {\n                chunks: block.chunks,\n                chunk: block.chunk\n            });\n\n            // 在发送之间可以添加字段什么的。。。\n            // 如果默认的字段不够使用，可以通过监听此事件来扩展\n            owner.trigger( 'uploadBeforeSend', block, data, headers );\n\n            // 开始发送。\n            tr.appendBlob( opts.fileVal, block.blob, file.name );\n            tr.append( data );\n            tr.setRequestHeader( headers );\n            tr.send();\n        },\n\n        // 完成上传。\n        _finishFile: function( file, ret, hds ) {\n            var owner = this.owner;\n\n            return owner\n                    .request( 'after-send-file', arguments, function() {\n                        file.setStatus( Status.COMPLETE );\n                        owner.trigger( 'uploadSuccess', file, ret, hds );\n                    })\n                    .fail(function( reason ) {\n\n                        // 如果外部已经标记为invalid什么的，不再改状态。\n                        if ( file.getStatus() === Status.PROGRESS ) {\n                            file.setStatus( Status.ERROR, reason );\n                        }\n\n                        owner.trigger( 'uploadError', file, reason );\n                    })\n                    .always(function() {\n                        owner.trigger( 'uploadComplete', file );\n                    });\n        },\n\n        updateFileProgress: function(file) {\n            var totalPercent = 0,\n                uploaded = 0;\n\n            if (!file.blocks) {\n                return;\n            }\n\n            $.each( file.blocks, function( _, v ) {\n                uploaded += (v.percentage || 0) * (v.end - v.start);\n            });\n\n            totalPercent = uploaded / file.size;\n            this.owner.trigger( 'uploadProgress', file, totalPercent || 0 );\n        },\n\n        destroy: function() {\n            clearTimeout(this.retryTimer);\n        }\n\n    });\n});\n"
  },
  {
    "path": "src/widgets/validator.js",
    "content": "/**\n * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。\n */\n\ndefine([\n    '../base',\n    '../uploader',\n    '../file',\n    './widget'\n], function( Base, Uploader, WUFile ) {\n\n    var $ = Base.$,\n        validators = {},\n        api;\n\n    /**\n     * @event error\n     * @param {String} type 错误类型。\n     * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。\n     *\n     * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。\n     * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。\n     * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。\n     * @for  Uploader\n     */\n\n    // 暴露给外面的api\n    api = {\n\n        // 添加验证器\n        addValidator: function( type, cb ) {\n            validators[ type ] = cb;\n        },\n\n        // 移除验证器\n        removeValidator: function( type ) {\n            delete validators[ type ];\n        }\n    };\n\n    // 在Uploader初始化的时候启动Validators的初始化\n    Uploader.register({\n        name: 'validator',\n\n        init: function() {\n            var me = this;\n            Base.nextTick(function() {\n                $.each( validators, function() {\n                    this.call( me.owner );\n                });\n            });\n        }\n    });\n\n    /**\n     * @property {int} [fileNumLimit=undefined]\n     * @namespace options\n     * @for Uploader\n     * @description 验证文件总数量, 超出则不允许加入队列。\n     */\n    api.addValidator( 'fileNumLimit', function() {\n        var uploader = this,\n            opts = uploader.options,\n            count = 0,\n            max = parseInt( opts.fileNumLimit, 10 ),\n            flag = true;\n\n        if ( !max ) {\n            return;\n        }\n\n        uploader.on( 'beforeFileQueued', function( file ) {\n                // 增加beforeFileQueuedCheckfileNumLimit验证,主要为了再次加载时(已存在历史文件)验证数量是否超过设置项\n            if (!this.trigger('beforeFileQueuedCheckfileNumLimit', file,count)) {\n                return false;\n            }\n            if ( count >= max && flag ) {\n                flag = false;\n                this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );\n                setTimeout(function() {\n                    flag = true;\n                }, 1 );\n            }\n\n            return count >= max ? false : true;\n        });\n\n        uploader.on( 'fileQueued', function() {\n            count++;\n        });\n\n        uploader.on( 'fileDequeued', function() {\n            count--;\n        });\n\n        uploader.on( 'reset', function() {\n            count = 0;\n        });\n    });\n\n\n    /**\n     * @property {int} [fileSizeLimit=undefined]\n     * @namespace options\n     * @for Uploader\n     * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。\n     */\n    api.addValidator( 'fileSizeLimit', function() {\n        var uploader = this,\n            opts = uploader.options,\n            count = 0,\n            max = parseInt( opts.fileSizeLimit, 10 ),\n            flag = true;\n\n        if ( !max ) {\n            return;\n        }\n\n        uploader.on( 'beforeFileQueued', function( file ) {\n            var invalid = count + file.size > max;\n\n            if ( invalid && flag ) {\n                flag = false;\n                this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );\n                setTimeout(function() {\n                    flag = true;\n                }, 1 );\n            }\n\n            return invalid ? false : true;\n        });\n\n        uploader.on( 'fileQueued', function( file ) {\n            count += file.size;\n        });\n\n        uploader.on( 'fileDequeued', function( file ) {\n            count -= file.size;\n        });\n\n        uploader.on( 'reset', function() {\n            count = 0;\n        });\n    });\n\n    /**\n     * @property {int} [fileSingleSizeLimit=undefined]\n     * @namespace options\n     * @for Uploader\n     * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。\n     */\n    api.addValidator( 'fileSingleSizeLimit', function() {\n        var uploader = this,\n            opts = uploader.options,\n            max = opts.fileSingleSizeLimit;\n\n        if ( !max ) {\n            return;\n        }\n\n        uploader.on( 'beforeFileQueued', function( file ) {\n\n            if ( file.size > max ) {\n                file.setStatus( WUFile.Status.INVALID, 'exceed_size' );\n                this.trigger( 'error', 'F_EXCEED_SIZE', max, file );\n                return false;\n            }\n\n        });\n\n    });\n\n    /**\n     * @property {Boolean} [duplicate=undefined]\n     * @namespace options\n     * @for Uploader\n     * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.\n     */\n    api.addValidator( 'duplicate', function() {\n        var uploader = this,\n            opts = uploader.options,\n            mapping = {};\n\n        if ( opts.duplicate ) {\n            return;\n        }\n\n        function hashString( str ) {\n            var hash = 0,\n                i = 0,\n                len = str.length,\n                _char;\n\n            for ( ; i < len; i++ ) {\n                _char = str.charCodeAt( i );\n                hash = _char + (hash << 6) + (hash << 16) - hash;\n            }\n\n            return hash;\n        }\n\n        uploader.on( 'beforeFileQueued', function( file ) {\n            var hash = file.__hash || (file.__hash = hashString( file.name +\n                    file.size + file.lastModifiedDate ));\n\n            // 已经重复了\n            if ( mapping[ hash ] ) {\n                this.trigger( 'error', 'F_DUPLICATE', file );\n                return false;\n            }\n        });\n\n        uploader.on( 'fileQueued', function( file ) {\n            var hash = file.__hash;\n\n            hash && (mapping[ hash ] = true);\n        });\n\n        uploader.on( 'fileDequeued', function( file ) {\n            var hash = file.__hash;\n\n            hash && (delete mapping[ hash ]);\n        });\n\n        uploader.on( 'reset', function() {\n            mapping = {};\n        });\n    });\n\n    return api;\n});\n"
  },
  {
    "path": "src/widgets/widget.js",
    "content": "/**\n * @fileOverview 组件基类。\n */\ndefine([\n    '../base',\n    '../uploader'\n], function( Base, Uploader ) {\n\n    var $ = Base.$,\n        _init = Uploader.prototype._init,\n        _destroy = Uploader.prototype.destroy,\n        IGNORE = {},\n        widgetClass = [];\n\n    function isArrayLike( obj ) {\n        if ( !obj ) {\n            return false;\n        }\n\n        var length = obj.length,\n            type = $.type( obj );\n\n        if ( obj.nodeType === 1 && length ) {\n            return true;\n        }\n\n        return type === 'array' || type !== 'function' && type !== 'string' &&\n                (length === 0 || typeof length === 'number' && length > 0 &&\n                (length - 1) in obj);\n    }\n\n    function Widget( uploader ) {\n        this.owner = uploader;\n        this.options = uploader.options;\n    }\n\n    $.extend( Widget.prototype, {\n\n        init: Base.noop,\n\n        // 类Backbone的事件监听声明，监听uploader实例上的事件\n        // widget直接无法监听事件，事件只能通过uploader来传递\n        invoke: function( apiName, args ) {\n\n            /*\n                {\n                    'make-thumb': 'makeThumb'\n                }\n             */\n            var map = this.responseMap;\n\n            // 如果无API响应声明则忽略\n            if ( !map || !(apiName in map) || !(map[ apiName ] in this) ||\n                    !$.isFunction( this[ map[ apiName ] ] ) ) {\n\n                return IGNORE;\n            }\n\n            return this[ map[ apiName ] ].apply( this, args );\n\n        },\n\n        /**\n         * 发送命令。当传入`callback`或者`handler`中返回`promise`时。返回一个当所有`handler`中的promise都完成后完成的新`promise`。\n         * @method request\n         * @grammar request( command, args ) => * | Promise\n         * @grammar request( command, args, callback ) => Promise\n         * @for  Uploader\n         */\n        request: function() {\n            return this.owner.request.apply( this.owner, arguments );\n        }\n    });\n\n    // 扩展Uploader.\n    $.extend( Uploader.prototype, {\n\n        /**\n         * @property {String | Array} [disableWidgets=undefined]\n         * @namespace options\n         * @for Uploader\n         * @description 默认所有 Uploader.register 了的 widget 都会被加载，如果禁用某一部分，请通过此 option 指定黑名单。\n         */\n\n        // 覆写_init用来初始化widgets\n        _init: function() {\n            var me = this,\n                widgets = me._widgets = [],\n                deactives = me.options.disableWidgets || '';\n\n            $.each( widgetClass, function( _, klass ) {\n                (!deactives || !~deactives.indexOf( klass._name )) &&\n                    widgets.push( new klass( me ) );\n            });\n\n            return _init.apply( me, arguments );\n        },\n\n        request: function( apiName, args, callback ) {\n            var i = 0,\n                widgets = this._widgets,\n                len = widgets && widgets.length,\n                rlts = [],\n                dfds = [],\n                widget, rlt, promise, key;\n\n            args = isArrayLike( args ) ? args : [ args ];\n\n            for ( ; i < len; i++ ) {\n                widget = widgets[ i ];\n                rlt = widget.invoke( apiName, args );\n\n                if ( rlt !== IGNORE ) {\n\n                    // Deferred对象\n                    if ( Base.isPromise( rlt ) ) {\n                        dfds.push( rlt );\n                    } else {\n                        rlts.push( rlt );\n                    }\n                }\n            }\n\n            // 如果有callback，则用异步方式。\n            if ( callback || dfds.length ) {\n                promise = Base.when.apply( Base, dfds );\n                key = promise.pipe ? 'pipe' : 'then';\n\n                // 很重要不能删除。删除了会死循环。\n                // 保证执行顺序。让callback总是在下一个 tick 中执行。\n                return promise[ key ](function() {\n                            var deferred = Base.Deferred(),\n                                args = arguments;\n\n                            if ( args.length === 1 ) {\n                                args = args[ 0 ];\n                            }\n\n                            setTimeout(function() {\n                                deferred.resolve( args );\n                            }, 1 );\n\n                            return deferred.promise();\n                        })[ callback ? key : 'done' ]( callback || Base.noop );\n            } else {\n                return rlts[ 0 ];\n            }\n        },\n\n        destroy: function() {\n            _destroy.apply( this, arguments );\n            this._widgets = null;\n        }\n    });\n\n    /**\n     * 添加组件\n     * @grammar Uploader.register(proto);\n     * @grammar Uploader.register(map, proto);\n     * @param  {object} responseMap API 名称与函数实现的映射\n     * @param  {object} proto 组件原型，构造函数通过 constructor 属性定义\n     * @method Uploader.register\n     * @for Uploader\n     * @example\n     * Uploader.register({\n     *     'make-thumb': 'makeThumb'\n     * }, {\n     *     init: function( options ) {},\n     *     makeThumb: function() {}\n     * });\n     *\n     * Uploader.register({\n     *     'make-thumb': function() {\n     *         \n     *     }\n     * });\n     */\n    Uploader.register = Widget.register = function( responseMap, widgetProto ) {\n        var map = { init: 'init', destroy: 'destroy', name: 'anonymous' },\n            klass;\n\n        if ( arguments.length === 1 ) {\n            widgetProto = responseMap;\n\n            // 自动生成 map 表。\n            $.each(widgetProto, function(key) {\n                if ( key[0] === '_' || key === 'name' ) {\n                    key === 'name' && (map.name = widgetProto.name);\n                    return;\n                }\n\n                map[key.replace(/[A-Z]/g, '-$&').toLowerCase()] = key;\n            });\n\n        } else {\n            map = $.extend( map, responseMap );\n        }\n\n        widgetProto.responseMap = map;\n        klass = Base.inherits( Widget, widgetProto );\n        klass._name = map.name;\n        widgetClass.push( klass );\n\n        return klass;\n    };\n\n    /**\n     * 删除插件，只有在注册时指定了名字的才能被删除。\n     * @grammar Uploader.unRegister(name);\n     * @param  {string} name 组件名字\n     * @method Uploader.unRegister\n     * @for Uploader\n     * @example\n     *\n     * Uploader.register({\n     *     name: 'custom',\n     *     \n     *     'make-thumb': function() {\n     *         \n     *     }\n     * });\n     *\n     * Uploader.unRegister('custom');\n     */\n    Uploader.unRegister = Widget.unRegister = function( name ) {\n        if ( !name || name === 'anonymous' ) {\n            return;\n        }\n        \n        // 删除指定的插件。\n        for ( var i = widgetClass.length; i--; ) {\n            if ( widgetClass[i]._name === name ) {\n                widgetClass.splice(i, 1)\n            }\n        }\n    };\n\n    return Widget;\n});"
  },
  {
    "path": "test/Qunit/qunit-1.14.0.css",
    "content": "/*!\n * QUnit 1.14.0\n * http://qunitjs.com/\n *\n * Copyright 2013 jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2014-01-31T16:40Z\n */\n\n/** Font Family and Sizes */\n\n#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {\n\tfont-family: \"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial, sans-serif;\n}\n\n#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }\n#qunit-tests { font-size: smaller; }\n\n\n/** Resets */\n\n#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {\n\tmargin: 0;\n\tpadding: 0;\n}\n\n\n/** Header */\n\n#qunit-header {\n\tpadding: 0.5em 0 0.5em 1em;\n\n\tcolor: #8699A4;\n\tbackground-color: #0D3349;\n\n\tfont-size: 1.5em;\n\tline-height: 1em;\n\tfont-weight: 400;\n\n\tborder-radius: 5px 5px 0 0;\n}\n\n#qunit-header a {\n\ttext-decoration: none;\n\tcolor: #C2CCD1;\n}\n\n#qunit-header a:hover,\n#qunit-header a:focus {\n\tcolor: #FFF;\n}\n\n#qunit-testrunner-toolbar label {\n\tdisplay: inline-block;\n\tpadding: 0 0.5em 0 0.1em;\n}\n\n#qunit-banner {\n\theight: 5px;\n}\n\n#qunit-testrunner-toolbar {\n\tpadding: 0.5em 0 0.5em 2em;\n\tcolor: #5E740B;\n\tbackground-color: #EEE;\n\toverflow: hidden;\n}\n\n#qunit-userAgent {\n\tpadding: 0.5em 0 0.5em 2.5em;\n\tbackground-color: #2B81AF;\n\tcolor: #FFF;\n\ttext-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;\n}\n\n#qunit-modulefilter-container {\n\tfloat: right;\n}\n\n/** Tests: Pass/Fail */\n\n#qunit-tests {\n\tlist-style-position: inside;\n}\n\n#qunit-tests li {\n\tpadding: 0.4em 0.5em 0.4em 2.5em;\n\tborder-bottom: 1px solid #FFF;\n\tlist-style-position: inside;\n}\n\n#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  {\n\tdisplay: none;\n}\n\n#qunit-tests li strong {\n\tcursor: pointer;\n}\n\n#qunit-tests li a {\n\tpadding: 0.5em;\n\tcolor: #C2CCD1;\n\ttext-decoration: none;\n}\n#qunit-tests li a:hover,\n#qunit-tests li a:focus {\n\tcolor: #000;\n}\n\n#qunit-tests li .runtime {\n\tfloat: right;\n\tfont-size: smaller;\n}\n\n.qunit-assert-list {\n\tmargin-top: 0.5em;\n\tpadding: 0.5em;\n\n\tbackground-color: #FFF;\n\n\tborder-radius: 5px;\n}\n\n.qunit-collapsed {\n\tdisplay: none;\n}\n\n#qunit-tests table {\n\tborder-collapse: collapse;\n\tmargin-top: 0.2em;\n}\n\n#qunit-tests th {\n\ttext-align: right;\n\tvertical-align: top;\n\tpadding: 0 0.5em 0 0;\n}\n\n#qunit-tests td {\n\tvertical-align: top;\n}\n\n#qunit-tests pre {\n\tmargin: 0;\n\twhite-space: pre-wrap;\n\tword-wrap: break-word;\n}\n\n#qunit-tests del {\n\tbackground-color: #E0F2BE;\n\tcolor: #374E0C;\n\ttext-decoration: none;\n}\n\n#qunit-tests ins {\n\tbackground-color: #FFCACA;\n\tcolor: #500;\n\ttext-decoration: none;\n}\n\n/*** Test Counts */\n\n#qunit-tests b.counts                       { color: #000; }\n#qunit-tests b.passed                       { color: #5E740B; }\n#qunit-tests b.failed                       { color: #710909; }\n\n#qunit-tests li li {\n\tpadding: 5px;\n\tbackground-color: #FFF;\n\tborder-bottom: none;\n\tlist-style-position: inside;\n}\n\n/*** Passing Styles */\n\n#qunit-tests li li.pass {\n\tcolor: #3C510C;\n\tbackground-color: #FFF;\n\tborder-left: 10px solid #C6E746;\n}\n\n#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }\n#qunit-tests .pass .test-name               { color: #366097; }\n\n#qunit-tests .pass .test-actual,\n#qunit-tests .pass .test-expected           { color: #999; }\n\n#qunit-banner.qunit-pass                    { background-color: #C6E746; }\n\n/*** Failing Styles */\n\n#qunit-tests li li.fail {\n\tcolor: #710909;\n\tbackground-color: #FFF;\n\tborder-left: 10px solid #EE5757;\n\twhite-space: pre;\n}\n\n#qunit-tests > li:last-child {\n\tborder-radius: 0 0 5px 5px;\n}\n\n#qunit-tests .fail                          { color: #000; background-color: #EE5757; }\n#qunit-tests .fail .test-name,\n#qunit-tests .fail .module-name             { color: #000; }\n\n#qunit-tests .fail .test-actual             { color: #EE5757; }\n#qunit-tests .fail .test-expected           { color: #008000; }\n\n#qunit-banner.qunit-fail                    { background-color: #EE5757; }\n\n\n/** Result */\n\n#qunit-testresult {\n\tpadding: 0.5em 0.5em 0.5em 2.5em;\n\n\tcolor: #2B81AF;\n\tbackground-color: #D2E0E6;\n\n\tborder-bottom: 1px solid #FFF;\n}\n#qunit-testresult .module-name {\n\tfont-weight: 700;\n}\n\n/** Fixture */\n\n#qunit-fixture {\n\tposition: absolute;\n\ttop: -10000px;\n\tleft: -10000px;\n\twidth: 1000px;\n\theight: 1000px;\n}\n"
  },
  {
    "path": "test/Qunit/qunit-1.14.0.js",
    "content": "/*!\n * QUnit 1.14.0\n * http://qunitjs.com/\n *\n * Copyright 2013 jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2014-01-31T16:40Z\n */\n\n(function( window ) {\n\nvar QUnit,\n\tassert,\n\tconfig,\n\tonErrorFnPrev,\n\ttestId = 0,\n\tfileName = (sourceFromStacktrace( 0 ) || \"\" ).replace(/(:\\d+)+\\)?/, \"\").replace(/.+\\//, \"\"),\n\ttoString = Object.prototype.toString,\n\thasOwn = Object.prototype.hasOwnProperty,\n\t// Keep a local reference to Date (GH-283)\n\tDate = window.Date,\n\tsetTimeout = window.setTimeout,\n\tclearTimeout = window.clearTimeout,\n\tdefined = {\n\t\tdocument: typeof window.document !== \"undefined\",\n\t\tsetTimeout: typeof window.setTimeout !== \"undefined\",\n\t\tsessionStorage: (function() {\n\t\t\tvar x = \"qunit-test-string\";\n\t\t\ttry {\n\t\t\t\tsessionStorage.setItem( x, x );\n\t\t\t\tsessionStorage.removeItem( x );\n\t\t\t\treturn true;\n\t\t\t} catch( e ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}())\n\t},\n\t/**\n\t * Provides a normalized error string, correcting an issue\n\t * with IE 7 (and prior) where Error.prototype.toString is\n\t * not properly implemented\n\t *\n\t * Based on http://es5.github.com/#x15.11.4.4\n\t *\n\t * @param {String|Error} error\n\t * @return {String} error message\n\t */\n\terrorString = function( error ) {\n\t\tvar name, message,\n\t\t\terrorString = error.toString();\n\t\tif ( errorString.substring( 0, 7 ) === \"[object\" ) {\n\t\t\tname = error.name ? error.name.toString() : \"Error\";\n\t\t\tmessage = error.message ? error.message.toString() : \"\";\n\t\t\tif ( name && message ) {\n\t\t\t\treturn name + \": \" + message;\n\t\t\t} else if ( name ) {\n\t\t\t\treturn name;\n\t\t\t} else if ( message ) {\n\t\t\t\treturn message;\n\t\t\t} else {\n\t\t\t\treturn \"Error\";\n\t\t\t}\n\t\t} else {\n\t\t\treturn errorString;\n\t\t}\n\t},\n\t/**\n\t * Makes a clone of an object using only Array or Object as base,\n\t * and copies over the own enumerable properties.\n\t *\n\t * @param {Object} obj\n\t * @return {Object} New object with only the own properties (recursively).\n\t */\n\tobjectValues = function( obj ) {\n\t\t// Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.\n\t\t/*jshint newcap: false */\n\t\tvar key, val,\n\t\t\tvals = QUnit.is( \"array\", obj ) ? [] : {};\n\t\tfor ( key in obj ) {\n\t\t\tif ( hasOwn.call( obj, key ) ) {\n\t\t\t\tval = obj[key];\n\t\t\t\tvals[key] = val === Object(val) ? objectValues(val) : val;\n\t\t\t}\n\t\t}\n\t\treturn vals;\n\t};\n\n\n// Root QUnit object.\n// `QUnit` initialized at top of scope\nQUnit = {\n\n\t// call on start of module test to prepend name to all tests\n\tmodule: function( name, testEnvironment ) {\n\t\tconfig.currentModule = name;\n\t\tconfig.currentModuleTestEnvironment = testEnvironment;\n\t\tconfig.modules[name] = true;\n\t},\n\n\tasyncTest: function( testName, expected, callback ) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tQUnit.test( testName, expected, callback, true );\n\t},\n\n\ttest: function( testName, expected, callback, async ) {\n\t\tvar test,\n\t\t\tnameHtml = \"<span class='test-name'>\" + escapeText( testName ) + \"</span>\";\n\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tif ( config.currentModule ) {\n\t\t\tnameHtml = \"<span class='module-name'>\" + escapeText( config.currentModule ) + \"</span>: \" + nameHtml;\n\t\t}\n\n\t\ttest = new Test({\n\t\t\tnameHtml: nameHtml,\n\t\t\ttestName: testName,\n\t\t\texpected: expected,\n\t\t\tasync: async,\n\t\t\tcallback: callback,\n\t\t\tmodule: config.currentModule,\n\t\t\tmoduleTestEnvironment: config.currentModuleTestEnvironment,\n\t\t\tstack: sourceFromStacktrace( 2 )\n\t\t});\n\n\t\tif ( !validTest( test ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttest.queue();\n\t},\n\n\t// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.\n\texpect: function( asserts ) {\n\t\tif (arguments.length === 1) {\n\t\t\tconfig.current.expected = asserts;\n\t\t} else {\n\t\t\treturn config.current.expected;\n\t\t}\n\t},\n\n\tstart: function( count ) {\n\t\t// QUnit hasn't been initialized yet.\n\t\t// Note: RequireJS (et al) may delay onLoad\n\t\tif ( config.semaphore === undefined ) {\n\t\t\tQUnit.begin(function() {\n\t\t\t\t// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\tQUnit.start( count );\n\t\t\t\t});\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconfig.semaphore -= count || 1;\n\t\t// don't start until equal number of stop-calls\n\t\tif ( config.semaphore > 0 ) {\n\t\t\treturn;\n\t\t}\n\t\t// ignore if start is called more often then stop\n\t\tif ( config.semaphore < 0 ) {\n\t\t\tconfig.semaphore = 0;\n\t\t\tQUnit.pushFailure( \"Called start() while already started (QUnit.config.semaphore was 0 already)\", null, sourceFromStacktrace(2) );\n\t\t\treturn;\n\t\t}\n\t\t// A slight delay, to avoid any current callbacks\n\t\tif ( defined.setTimeout ) {\n\t\t\tsetTimeout(function() {\n\t\t\t\tif ( config.semaphore > 0 ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif ( config.timeout ) {\n\t\t\t\t\tclearTimeout( config.timeout );\n\t\t\t\t}\n\n\t\t\t\tconfig.blocking = false;\n\t\t\t\tprocess( true );\n\t\t\t}, 13);\n\t\t} else {\n\t\t\tconfig.blocking = false;\n\t\t\tprocess( true );\n\t\t}\n\t},\n\n\tstop: function( count ) {\n\t\tconfig.semaphore += count || 1;\n\t\tconfig.blocking = true;\n\n\t\tif ( config.testTimeout && defined.setTimeout ) {\n\t\t\tclearTimeout( config.timeout );\n\t\t\tconfig.timeout = setTimeout(function() {\n\t\t\t\tQUnit.ok( false, \"Test timed out\" );\n\t\t\t\tconfig.semaphore = 1;\n\t\t\t\tQUnit.start();\n\t\t\t}, config.testTimeout );\n\t\t}\n\t}\n};\n\n// We use the prototype to distinguish between properties that should\n// be exposed as globals (and in exports) and those that shouldn't\n(function() {\n\tfunction F() {}\n\tF.prototype = QUnit;\n\tQUnit = new F();\n\t// Make F QUnit's constructor so that we can add to the prototype later\n\tQUnit.constructor = F;\n}());\n\n/**\n * Config object: Maintain internal state\n * Later exposed as QUnit.config\n * `config` initialized at top of scope\n */\nconfig = {\n\t// The queue of tests to run\n\tqueue: [],\n\n\t// block until document ready\n\tblocking: true,\n\n\t// when enabled, show only failing tests\n\t// gets persisted through sessionStorage and can be changed in UI via checkbox\n\thidepassed: false,\n\n\t// by default, run previously failed tests first\n\t// very useful in combination with \"Hide passed tests\" checked\n\treorder: true,\n\n\t// by default, modify document.title when suite is done\n\taltertitle: true,\n\n\t// by default, scroll to top of the page when suite is done\n\tscrolltop: true,\n\n\t// when enabled, all tests must call expect()\n\trequireExpects: false,\n\n\t// add checkboxes that are persisted in the query-string\n\t// when enabled, the id is set to `true` as a `QUnit.config` property\n\turlConfig: [\n\t\t{\n\t\t\tid: \"noglobals\",\n\t\t\tlabel: \"Check for Globals\",\n\t\t\ttooltip: \"Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings.\"\n\t\t},\n\t\t{\n\t\t\tid: \"notrycatch\",\n\t\t\tlabel: \"No try-catch\",\n\t\t\ttooltip: \"Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings.\"\n\t\t}\n\t],\n\n\t// Set of all modules.\n\tmodules: {},\n\n\t// logging callback queues\n\tbegin: [],\n\tdone: [],\n\tlog: [],\n\ttestStart: [],\n\ttestDone: [],\n\tmoduleStart: [],\n\tmoduleDone: []\n};\n\n// Initialize more QUnit.config and QUnit.urlParams\n(function() {\n\tvar i, current,\n\t\tlocation = window.location || { search: \"\", protocol: \"file:\" },\n\t\tparams = location.search.slice( 1 ).split( \"&\" ),\n\t\tlength = params.length,\n\t\turlParams = {};\n\n\tif ( params[ 0 ] ) {\n\t\tfor ( i = 0; i < length; i++ ) {\n\t\t\tcurrent = params[ i ].split( \"=\" );\n\t\t\tcurrent[ 0 ] = decodeURIComponent( current[ 0 ] );\n\n\t\t\t// allow just a key to turn on a flag, e.g., test.html?noglobals\n\t\t\tcurrent[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;\n\t\t\tif ( urlParams[ current[ 0 ] ] ) {\n\t\t\t\turlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );\n\t\t\t} else {\n\t\t\t\turlParams[ current[ 0 ] ] = current[ 1 ];\n\t\t\t}\n\t\t}\n\t}\n\n\tQUnit.urlParams = urlParams;\n\n\t// String search anywhere in moduleName+testName\n\tconfig.filter = urlParams.filter;\n\n\t// Exact match of the module name\n\tconfig.module = urlParams.module;\n\n\tconfig.testNumber = [];\n\tif ( urlParams.testNumber ) {\n\n\t\t// Ensure that urlParams.testNumber is an array\n\t\turlParams.testNumber = [].concat( urlParams.testNumber );\n\t\tfor ( i = 0; i < urlParams.testNumber.length; i++ ) {\n\t\t\tcurrent = urlParams.testNumber[ i ];\n\t\t\tconfig.testNumber.push( parseInt( current, 10 ) );\n\t\t}\n\t}\n\n\t// Figure out if we're running the tests from a server or not\n\tQUnit.isLocal = location.protocol === \"file:\";\n}());\n\nextend( QUnit, {\n\n\tconfig: config,\n\n\t// Initialize the configuration options\n\tinit: function() {\n\t\textend( config, {\n\t\t\tstats: { all: 0, bad: 0 },\n\t\t\tmoduleStats: { all: 0, bad: 0 },\n\t\t\tstarted: +new Date(),\n\t\t\tupdateRate: 1000,\n\t\t\tblocking: false,\n\t\t\tautostart: true,\n\t\t\tautorun: false,\n\t\t\tfilter: \"\",\n\t\t\tqueue: [],\n\t\t\tsemaphore: 1\n\t\t});\n\n\t\tvar tests, banner, result,\n\t\t\tqunit = id( \"qunit\" );\n\n\t\tif ( qunit ) {\n\t\t\tqunit.innerHTML =\n\t\t\t\t\"<h1 id='qunit-header'>\" + escapeText( document.title ) + \"</h1>\" +\n\t\t\t\t\"<h2 id='qunit-banner'></h2>\" +\n\t\t\t\t\"<div id='qunit-testrunner-toolbar'></div>\" +\n\t\t\t\t\"<h2 id='qunit-userAgent'></h2>\" +\n\t\t\t\t\"<ol id='qunit-tests'></ol>\";\n\t\t}\n\n\t\ttests = id( \"qunit-tests\" );\n\t\tbanner = id( \"qunit-banner\" );\n\t\tresult = id( \"qunit-testresult\" );\n\n\t\tif ( tests ) {\n\t\t\ttests.innerHTML = \"\";\n\t\t}\n\n\t\tif ( banner ) {\n\t\t\tbanner.className = \"\";\n\t\t}\n\n\t\tif ( result ) {\n\t\t\tresult.parentNode.removeChild( result );\n\t\t}\n\n\t\tif ( tests ) {\n\t\t\tresult = document.createElement( \"p\" );\n\t\t\tresult.id = \"qunit-testresult\";\n\t\t\tresult.className = \"result\";\n\t\t\ttests.parentNode.insertBefore( result, tests );\n\t\t\tresult.innerHTML = \"Running...<br/>&nbsp;\";\n\t\t}\n\t},\n\n\t// Resets the test setup. Useful for tests that modify the DOM.\n\t/*\n\tDEPRECATED: Use multiple tests instead of resetting inside a test.\n\tUse testStart or testDone for custom cleanup.\n\tThis method will throw an error in 2.0, and will be removed in 2.1\n\t*/\n\treset: function() {\n\t\tvar fixture = id( \"qunit-fixture\" );\n\t\tif ( fixture ) {\n\t\t\tfixture.innerHTML = config.fixture;\n\t\t}\n\t},\n\n\t// Safe object type checking\n\tis: function( type, obj ) {\n\t\treturn QUnit.objectType( obj ) === type;\n\t},\n\n\tobjectType: function( obj ) {\n\t\tif ( typeof obj === \"undefined\" ) {\n\t\t\treturn \"undefined\";\n\t\t}\n\n\t\t// Consider: typeof null === object\n\t\tif ( obj === null ) {\n\t\t\treturn \"null\";\n\t\t}\n\n\t\tvar match = toString.call( obj ).match(/^\\[object\\s(.*)\\]$/),\n\t\t\ttype = match && match[1] || \"\";\n\n\t\tswitch ( type ) {\n\t\t\tcase \"Number\":\n\t\t\t\tif ( isNaN(obj) ) {\n\t\t\t\t\treturn \"nan\";\n\t\t\t\t}\n\t\t\t\treturn \"number\";\n\t\t\tcase \"String\":\n\t\t\tcase \"Boolean\":\n\t\t\tcase \"Array\":\n\t\t\tcase \"Date\":\n\t\t\tcase \"RegExp\":\n\t\t\tcase \"Function\":\n\t\t\t\treturn type.toLowerCase();\n\t\t}\n\t\tif ( typeof obj === \"object\" ) {\n\t\t\treturn \"object\";\n\t\t}\n\t\treturn undefined;\n\t},\n\n\tpush: function( result, actual, expected, message ) {\n\t\tif ( !config.current ) {\n\t\t\tthrow new Error( \"assertion outside test context, was \" + sourceFromStacktrace() );\n\t\t}\n\n\t\tvar output, source,\n\t\t\tdetails = {\n\t\t\t\tmodule: config.current.module,\n\t\t\t\tname: config.current.testName,\n\t\t\t\tresult: result,\n\t\t\t\tmessage: message,\n\t\t\t\tactual: actual,\n\t\t\t\texpected: expected\n\t\t\t};\n\n\t\tmessage = escapeText( message ) || ( result ? \"okay\" : \"failed\" );\n\t\tmessage = \"<span class='test-message'>\" + message + \"</span>\";\n\t\toutput = message;\n\n\t\tif ( !result ) {\n\t\t\texpected = escapeText( QUnit.jsDump.parse(expected) );\n\t\t\tactual = escapeText( QUnit.jsDump.parse(actual) );\n\t\t\toutput += \"<table><tr class='test-expected'><th>Expected: </th><td><pre>\" + expected + \"</pre></td></tr>\";\n\n\t\t\tif ( actual !== expected ) {\n\t\t\t\toutput += \"<tr class='test-actual'><th>Result: </th><td><pre>\" + actual + \"</pre></td></tr>\";\n\t\t\t\toutput += \"<tr class='test-diff'><th>Diff: </th><td><pre>\" + QUnit.diff( expected, actual ) + \"</pre></td></tr>\";\n\t\t\t}\n\n\t\t\tsource = sourceFromStacktrace();\n\n\t\t\tif ( source ) {\n\t\t\t\tdetails.source = source;\n\t\t\t\toutput += \"<tr class='test-source'><th>Source: </th><td><pre>\" + escapeText( source ) + \"</pre></td></tr>\";\n\t\t\t}\n\n\t\t\toutput += \"</table>\";\n\t\t}\n\n\t\trunLoggingCallbacks( \"log\", QUnit, details );\n\n\t\tconfig.current.assertions.push({\n\t\t\tresult: !!result,\n\t\t\tmessage: output\n\t\t});\n\t},\n\n\tpushFailure: function( message, source, actual ) {\n\t\tif ( !config.current ) {\n\t\t\tthrow new Error( \"pushFailure() assertion outside test context, was \" + sourceFromStacktrace(2) );\n\t\t}\n\n\t\tvar output,\n\t\t\tdetails = {\n\t\t\t\tmodule: config.current.module,\n\t\t\t\tname: config.current.testName,\n\t\t\t\tresult: false,\n\t\t\t\tmessage: message\n\t\t\t};\n\n\t\tmessage = escapeText( message ) || \"error\";\n\t\tmessage = \"<span class='test-message'>\" + message + \"</span>\";\n\t\toutput = message;\n\n\t\toutput += \"<table>\";\n\n\t\tif ( actual ) {\n\t\t\toutput += \"<tr class='test-actual'><th>Result: </th><td><pre>\" + escapeText( actual ) + \"</pre></td></tr>\";\n\t\t}\n\n\t\tif ( source ) {\n\t\t\tdetails.source = source;\n\t\t\toutput += \"<tr class='test-source'><th>Source: </th><td><pre>\" + escapeText( source ) + \"</pre></td></tr>\";\n\t\t}\n\n\t\toutput += \"</table>\";\n\n\t\trunLoggingCallbacks( \"log\", QUnit, details );\n\n\t\tconfig.current.assertions.push({\n\t\t\tresult: false,\n\t\t\tmessage: output\n\t\t});\n\t},\n\n\turl: function( params ) {\n\t\tparams = extend( extend( {}, QUnit.urlParams ), params );\n\t\tvar key,\n\t\t\tquerystring = \"?\";\n\n\t\tfor ( key in params ) {\n\t\t\tif ( hasOwn.call( params, key ) ) {\n\t\t\t\tquerystring += encodeURIComponent( key ) + \"=\" +\n\t\t\t\t\tencodeURIComponent( params[ key ] ) + \"&\";\n\t\t\t}\n\t\t}\n\t\treturn window.location.protocol + \"//\" + window.location.host +\n\t\t\twindow.location.pathname + querystring.slice( 0, -1 );\n\t},\n\n\textend: extend,\n\tid: id,\n\taddEvent: addEvent,\n\taddClass: addClass,\n\thasClass: hasClass,\n\tremoveClass: removeClass\n\t// load, equiv, jsDump, diff: Attached later\n});\n\n/**\n * @deprecated: Created for backwards compatibility with test runner that set the hook function\n * into QUnit.{hook}, instead of invoking it and passing the hook function.\n * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.\n * Doing this allows us to tell if the following methods have been overwritten on the actual\n * QUnit object.\n */\nextend( QUnit.constructor.prototype, {\n\n\t// Logging callbacks; all receive a single argument with the listed properties\n\t// run test/logs.html for any related changes\n\tbegin: registerLoggingCallback( \"begin\" ),\n\n\t// done: { failed, passed, total, runtime }\n\tdone: registerLoggingCallback( \"done\" ),\n\n\t// log: { result, actual, expected, message }\n\tlog: registerLoggingCallback( \"log\" ),\n\n\t// testStart: { name }\n\ttestStart: registerLoggingCallback( \"testStart\" ),\n\n\t// testDone: { name, failed, passed, total, runtime }\n\ttestDone: registerLoggingCallback( \"testDone\" ),\n\n\t// moduleStart: { name }\n\tmoduleStart: registerLoggingCallback( \"moduleStart\" ),\n\n\t// moduleDone: { name, failed, passed, total }\n\tmoduleDone: registerLoggingCallback( \"moduleDone\" )\n});\n\nif ( !defined.document || document.readyState === \"complete\" ) {\n\tconfig.autorun = true;\n}\n\nQUnit.load = function() {\n\trunLoggingCallbacks( \"begin\", QUnit, {} );\n\n\t// Initialize the config, saving the execution queue\n\tvar banner, filter, i, j, label, len, main, ol, toolbar, val, selection,\n\t\turlConfigContainer, moduleFilter, userAgent,\n\t\tnumModules = 0,\n\t\tmoduleNames = [],\n\t\tmoduleFilterHtml = \"\",\n\t\turlConfigHtml = \"\",\n\t\toldconfig = extend( {}, config );\n\n\tQUnit.init();\n\textend(config, oldconfig);\n\n\tconfig.blocking = false;\n\n\tlen = config.urlConfig.length;\n\n\tfor ( i = 0; i < len; i++ ) {\n\t\tval = config.urlConfig[i];\n\t\tif ( typeof val === \"string\" ) {\n\t\t\tval = {\n\t\t\t\tid: val,\n\t\t\t\tlabel: val\n\t\t\t};\n\t\t}\n\t\tconfig[ val.id ] = QUnit.urlParams[ val.id ];\n\t\tif ( !val.value || typeof val.value === \"string\" ) {\n\t\t\turlConfigHtml += \"<input id='qunit-urlconfig-\" + escapeText( val.id ) +\n\t\t\t\t\"' name='\" + escapeText( val.id ) +\n\t\t\t\t\"' type='checkbox'\" +\n\t\t\t\t( val.value ? \" value='\" + escapeText( val.value ) + \"'\" : \"\" ) +\n\t\t\t\t( config[ val.id ] ? \" checked='checked'\" : \"\" ) +\n\t\t\t\t\" title='\" + escapeText( val.tooltip ) +\n\t\t\t\t\"'><label for='qunit-urlconfig-\" + escapeText( val.id ) +\n\t\t\t\t\"' title='\" + escapeText( val.tooltip ) + \"'>\" + val.label + \"</label>\";\n\t\t} else {\n\t\t\turlConfigHtml += \"<label for='qunit-urlconfig-\" + escapeText( val.id ) +\n\t\t\t\t\"' title='\" + escapeText( val.tooltip ) +\n\t\t\t\t\"'>\" + val.label +\n\t\t\t\t\": </label><select id='qunit-urlconfig-\" + escapeText( val.id ) +\n\t\t\t\t\"' name='\" + escapeText( val.id ) +\n\t\t\t\t\"' title='\" + escapeText( val.tooltip ) +\n\t\t\t\t\"'><option></option>\";\n\t\t\tselection = false;\n\t\t\tif ( QUnit.is( \"array\", val.value ) ) {\n\t\t\t\tfor ( j = 0; j < val.value.length; j++ ) {\n\t\t\t\t\turlConfigHtml += \"<option value='\" + escapeText( val.value[j] ) + \"'\" +\n\t\t\t\t\t\t( config[ val.id ] === val.value[j] ?\n\t\t\t\t\t\t\t(selection = true) && \" selected='selected'\" :\n\t\t\t\t\t\t\t\"\" ) +\n\t\t\t\t\t\t\">\" + escapeText( val.value[j] ) + \"</option>\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( j in val.value ) {\n\t\t\t\t\tif ( hasOwn.call( val.value, j ) ) {\n\t\t\t\t\t\turlConfigHtml += \"<option value='\" + escapeText( j ) + \"'\" +\n\t\t\t\t\t\t\t( config[ val.id ] === j ?\n\t\t\t\t\t\t\t\t(selection = true) && \" selected='selected'\" :\n\t\t\t\t\t\t\t\t\"\" ) +\n\t\t\t\t\t\t\t\">\" + escapeText( val.value[j] ) + \"</option>\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( config[ val.id ] && !selection ) {\n\t\t\t\turlConfigHtml += \"<option value='\" + escapeText( config[ val.id ] ) +\n\t\t\t\t\t\"' selected='selected' disabled='disabled'>\" +\n\t\t\t\t\tescapeText( config[ val.id ] ) +\n\t\t\t\t\t\"</option>\";\n\t\t\t}\n\t\t\turlConfigHtml += \"</select>\";\n\t\t}\n\t}\n\tfor ( i in config.modules ) {\n\t\tif ( config.modules.hasOwnProperty( i ) ) {\n\t\t\tmoduleNames.push(i);\n\t\t}\n\t}\n\tnumModules = moduleNames.length;\n\tmoduleNames.sort( function( a, b ) {\n\t\treturn a.localeCompare( b );\n\t});\n\tmoduleFilterHtml += \"<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' \" +\n\t\t( config.module === undefined  ? \"selected='selected'\" : \"\" ) +\n\t\t\">< All Modules ></option>\";\n\n\n\tfor ( i = 0; i < numModules; i++) {\n\t\t\tmoduleFilterHtml += \"<option value='\" + escapeText( encodeURIComponent(moduleNames[i]) ) + \"' \" +\n\t\t\t\t( config.module === moduleNames[i] ? \"selected='selected'\" : \"\" ) +\n\t\t\t\t\">\" + escapeText(moduleNames[i]) + \"</option>\";\n\t}\n\tmoduleFilterHtml += \"</select>\";\n\n\t// `userAgent` initialized at top of scope\n\tuserAgent = id( \"qunit-userAgent\" );\n\tif ( userAgent ) {\n\t\tuserAgent.innerHTML = navigator.userAgent;\n\t}\n\n\t// `banner` initialized at top of scope\n\tbanner = id( \"qunit-header\" );\n\tif ( banner ) {\n\t\tbanner.innerHTML = \"<a href='\" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + \"'>\" + banner.innerHTML + \"</a> \";\n\t}\n\n\t// `toolbar` initialized at top of scope\n\ttoolbar = id( \"qunit-testrunner-toolbar\" );\n\tif ( toolbar ) {\n\t\t// `filter` initialized at top of scope\n\t\tfilter = document.createElement( \"input\" );\n\t\tfilter.type = \"checkbox\";\n\t\tfilter.id = \"qunit-filter-pass\";\n\n\t\taddEvent( filter, \"click\", function() {\n\t\t\tvar tmp,\n\t\t\t\tol = id( \"qunit-tests\" );\n\n\t\t\tif ( filter.checked ) {\n\t\t\t\tol.className = ol.className + \" hidepass\";\n\t\t\t} else {\n\t\t\t\ttmp = \" \" + ol.className.replace( /[\\n\\t\\r]/g, \" \" ) + \" \";\n\t\t\t\tol.className = tmp.replace( / hidepass /, \" \" );\n\t\t\t}\n\t\t\tif ( defined.sessionStorage ) {\n\t\t\t\tif (filter.checked) {\n\t\t\t\t\tsessionStorage.setItem( \"qunit-filter-passed-tests\", \"true\" );\n\t\t\t\t} else {\n\t\t\t\t\tsessionStorage.removeItem( \"qunit-filter-passed-tests\" );\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( \"qunit-filter-passed-tests\" ) ) {\n\t\t\tfilter.checked = true;\n\t\t\t// `ol` initialized at top of scope\n\t\t\tol = id( \"qunit-tests\" );\n\t\t\tol.className = ol.className + \" hidepass\";\n\t\t}\n\t\ttoolbar.appendChild( filter );\n\n\t\t// `label` initialized at top of scope\n\t\tlabel = document.createElement( \"label\" );\n\t\tlabel.setAttribute( \"for\", \"qunit-filter-pass\" );\n\t\tlabel.setAttribute( \"title\", \"Only show tests and assertions that fail. Stored in sessionStorage.\" );\n\t\tlabel.innerHTML = \"Hide passed tests\";\n\t\ttoolbar.appendChild( label );\n\n\t\turlConfigContainer = document.createElement(\"span\");\n\t\turlConfigContainer.innerHTML = urlConfigHtml;\n\t\t// For oldIE support:\n\t\t// * Add handlers to the individual elements instead of the container\n\t\t// * Use \"click\" instead of \"change\" for checkboxes\n\t\t// * Fallback from event.target to event.srcElement\n\t\taddEvents( urlConfigContainer.getElementsByTagName(\"input\"), \"click\", function( event ) {\n\t\t\tvar params = {},\n\t\t\t\ttarget = event.target || event.srcElement;\n\t\t\tparams[ target.name ] = target.checked ?\n\t\t\t\ttarget.defaultValue || true :\n\t\t\t\tundefined;\n\t\t\twindow.location = QUnit.url( params );\n\t\t});\n\t\taddEvents( urlConfigContainer.getElementsByTagName(\"select\"), \"change\", function( event ) {\n\t\t\tvar params = {},\n\t\t\t\ttarget = event.target || event.srcElement;\n\t\t\tparams[ target.name ] = target.options[ target.selectedIndex ].value || undefined;\n\t\t\twindow.location = QUnit.url( params );\n\t\t});\n\t\ttoolbar.appendChild( urlConfigContainer );\n\n\t\tif (numModules > 1) {\n\t\t\tmoduleFilter = document.createElement( \"span\" );\n\t\t\tmoduleFilter.setAttribute( \"id\", \"qunit-modulefilter-container\" );\n\t\t\tmoduleFilter.innerHTML = moduleFilterHtml;\n\t\t\taddEvent( moduleFilter.lastChild, \"change\", function() {\n\t\t\t\tvar selectBox = moduleFilter.getElementsByTagName(\"select\")[0],\n\t\t\t\t\tselectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);\n\n\t\t\t\twindow.location = QUnit.url({\n\t\t\t\t\tmodule: ( selectedModule === \"\" ) ? undefined : selectedModule,\n\t\t\t\t\t// Remove any existing filters\n\t\t\t\t\tfilter: undefined,\n\t\t\t\t\ttestNumber: undefined\n\t\t\t\t});\n\t\t\t});\n\t\t\ttoolbar.appendChild(moduleFilter);\n\t\t}\n\t}\n\n\t// `main` initialized at top of scope\n\tmain = id( \"qunit-fixture\" );\n\tif ( main ) {\n\t\tconfig.fixture = main.innerHTML;\n\t}\n\n\tif ( config.autostart ) {\n\t\tQUnit.start();\n\t}\n};\n\nif ( defined.document ) {\n\taddEvent( window, \"load\", QUnit.load );\n}\n\n// `onErrorFnPrev` initialized at top of scope\n// Preserve other handlers\nonErrorFnPrev = window.onerror;\n\n// Cover uncaught exceptions\n// Returning true will suppress the default browser handler,\n// returning false will let it run.\nwindow.onerror = function ( error, filePath, linerNr ) {\n\tvar ret = false;\n\tif ( onErrorFnPrev ) {\n\t\tret = onErrorFnPrev( error, filePath, linerNr );\n\t}\n\n\t// Treat return value as window.onerror itself does,\n\t// Only do our handling if not suppressed.\n\tif ( ret !== true ) {\n\t\tif ( QUnit.config.current ) {\n\t\t\tif ( QUnit.config.current.ignoreGlobalErrors ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tQUnit.pushFailure( error, filePath + \":\" + linerNr );\n\t\t} else {\n\t\t\tQUnit.test( \"global failure\", extend( function() {\n\t\t\t\tQUnit.pushFailure( error, filePath + \":\" + linerNr );\n\t\t\t}, { validTest: validTest } ) );\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn ret;\n};\n\nfunction done() {\n\tconfig.autorun = true;\n\n\t// Log the last module results\n\tif ( config.previousModule ) {\n\t\trunLoggingCallbacks( \"moduleDone\", QUnit, {\n\t\t\tname: config.previousModule,\n\t\t\tfailed: config.moduleStats.bad,\n\t\t\tpassed: config.moduleStats.all - config.moduleStats.bad,\n\t\t\ttotal: config.moduleStats.all\n\t\t});\n\t}\n\tdelete config.previousModule;\n\n\tvar i, key,\n\t\tbanner = id( \"qunit-banner\" ),\n\t\ttests = id( \"qunit-tests\" ),\n\t\truntime = +new Date() - config.started,\n\t\tpassed = config.stats.all - config.stats.bad,\n\t\thtml = [\n\t\t\t\"Tests completed in \",\n\t\t\truntime,\n\t\t\t\" milliseconds.<br/>\",\n\t\t\t\"<span class='passed'>\",\n\t\t\tpassed,\n\t\t\t\"</span> assertions of <span class='total'>\",\n\t\t\tconfig.stats.all,\n\t\t\t\"</span> passed, <span class='failed'>\",\n\t\t\tconfig.stats.bad,\n\t\t\t\"</span> failed.\"\n\t\t].join( \"\" );\n\n\tif ( banner ) {\n\t\tbanner.className = ( config.stats.bad ? \"qunit-fail\" : \"qunit-pass\" );\n\t}\n\n\tif ( tests ) {\n\t\tid( \"qunit-testresult\" ).innerHTML = html;\n\t}\n\n\tif ( config.altertitle && defined.document && document.title ) {\n\t\t// show ✖ for good, ✔ for bad suite result in title\n\t\t// use escape sequences in case file gets loaded with non-utf-8-charset\n\t\tdocument.title = [\n\t\t\t( config.stats.bad ? \"\\u2716\" : \"\\u2714\" ),\n\t\t\tdocument.title.replace( /^[\\u2714\\u2716] /i, \"\" )\n\t\t].join( \" \" );\n\t}\n\n\t// clear own sessionStorage items if all tests passed\n\tif ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {\n\t\t// `key` & `i` initialized at top of scope\n\t\tfor ( i = 0; i < sessionStorage.length; i++ ) {\n\t\t\tkey = sessionStorage.key( i++ );\n\t\t\tif ( key.indexOf( \"qunit-test-\" ) === 0 ) {\n\t\t\t\tsessionStorage.removeItem( key );\n\t\t\t}\n\t\t}\n\t}\n\n\t// scroll back to top to show results\n\tif ( config.scrolltop && window.scrollTo ) {\n\t\twindow.scrollTo(0, 0);\n\t}\n\n\trunLoggingCallbacks( \"done\", QUnit, {\n\t\tfailed: config.stats.bad,\n\t\tpassed: passed,\n\t\ttotal: config.stats.all,\n\t\truntime: runtime\n\t});\n}\n\n/** @return Boolean: true if this test should be ran */\nfunction validTest( test ) {\n\tvar include,\n\t\tfilter = config.filter && config.filter.toLowerCase(),\n\t\tmodule = config.module && config.module.toLowerCase(),\n\t\tfullName = ( test.module + \": \" + test.testName ).toLowerCase();\n\n\t// Internally-generated tests are always valid\n\tif ( test.callback && test.callback.validTest === validTest ) {\n\t\tdelete test.callback.validTest;\n\t\treturn true;\n\t}\n\n\tif ( config.testNumber.length > 0 ) {\n\t\tif ( inArray( test.testNumber, config.testNumber ) < 0 ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {\n\t\treturn false;\n\t}\n\n\tif ( !filter ) {\n\t\treturn true;\n\t}\n\n\tinclude = filter.charAt( 0 ) !== \"!\";\n\tif ( !include ) {\n\t\tfilter = filter.slice( 1 );\n\t}\n\n\t// If the filter matches, we need to honour include\n\tif ( fullName.indexOf( filter ) !== -1 ) {\n\t\treturn include;\n\t}\n\n\t// Otherwise, do the opposite\n\treturn !include;\n}\n\n// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)\n// Later Safari and IE10 are supposed to support error.stack as well\n// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack\nfunction extractStacktrace( e, offset ) {\n\toffset = offset === undefined ? 3 : offset;\n\n\tvar stack, include, i;\n\n\tif ( e.stacktrace ) {\n\t\t// Opera\n\t\treturn e.stacktrace.split( \"\\n\" )[ offset + 3 ];\n\t} else if ( e.stack ) {\n\t\t// Firefox, Chrome\n\t\tstack = e.stack.split( \"\\n\" );\n\t\tif (/^error$/i.test( stack[0] ) ) {\n\t\t\tstack.shift();\n\t\t}\n\t\tif ( fileName ) {\n\t\t\tinclude = [];\n\t\t\tfor ( i = offset; i < stack.length; i++ ) {\n\t\t\t\tif ( stack[ i ].indexOf( fileName ) !== -1 ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tinclude.push( stack[ i ] );\n\t\t\t}\n\t\t\tif ( include.length ) {\n\t\t\t\treturn include.join( \"\\n\" );\n\t\t\t}\n\t\t}\n\t\treturn stack[ offset ];\n\t} else if ( e.sourceURL ) {\n\t\t// Safari, PhantomJS\n\t\t// hopefully one day Safari provides actual stacktraces\n\t\t// exclude useless self-reference for generated Error objects\n\t\tif ( /qunit.js$/.test( e.sourceURL ) ) {\n\t\t\treturn;\n\t\t}\n\t\t// for actual exceptions, this is useful\n\t\treturn e.sourceURL + \":\" + e.line;\n\t}\n}\nfunction sourceFromStacktrace( offset ) {\n\ttry {\n\t\tthrow new Error();\n\t} catch ( e ) {\n\t\treturn extractStacktrace( e, offset );\n\t}\n}\n\n/**\n * Escape text for attribute or text content.\n */\nfunction escapeText( s ) {\n\tif ( !s ) {\n\t\treturn \"\";\n\t}\n\ts = s + \"\";\n\t// Both single quotes and double quotes (for attributes)\n\treturn s.replace( /['\"<>&]/g, function( s ) {\n\t\tswitch( s ) {\n\t\t\tcase \"'\":\n\t\t\t\treturn \"&#039;\";\n\t\t\tcase \"\\\"\":\n\t\t\t\treturn \"&quot;\";\n\t\t\tcase \"<\":\n\t\t\t\treturn \"&lt;\";\n\t\t\tcase \">\":\n\t\t\t\treturn \"&gt;\";\n\t\t\tcase \"&\":\n\t\t\t\treturn \"&amp;\";\n\t\t}\n\t});\n}\n\nfunction synchronize( callback, last ) {\n\tconfig.queue.push( callback );\n\n\tif ( config.autorun && !config.blocking ) {\n\t\tprocess( last );\n\t}\n}\n\nfunction process( last ) {\n\tfunction next() {\n\t\tprocess( last );\n\t}\n\tvar start = new Date().getTime();\n\tconfig.depth = config.depth ? config.depth + 1 : 1;\n\n\twhile ( config.queue.length && !config.blocking ) {\n\t\tif ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {\n\t\t\tconfig.queue.shift()();\n\t\t} else {\n\t\t\tsetTimeout( next, 13 );\n\t\t\tbreak;\n\t\t}\n\t}\n\tconfig.depth--;\n\tif ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {\n\t\tdone();\n\t}\n}\n\nfunction saveGlobal() {\n\tconfig.pollution = [];\n\n\tif ( config.noglobals ) {\n\t\tfor ( var key in window ) {\n\t\t\tif ( hasOwn.call( window, key ) ) {\n\t\t\t\t// in Opera sometimes DOM element ids show up here, ignore them\n\t\t\t\tif ( /^qunit-test-output/.test( key ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconfig.pollution.push( key );\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkPollution() {\n\tvar newGlobals,\n\t\tdeletedGlobals,\n\t\told = config.pollution;\n\n\tsaveGlobal();\n\n\tnewGlobals = diff( config.pollution, old );\n\tif ( newGlobals.length > 0 ) {\n\t\tQUnit.pushFailure( \"Introduced global variable(s): \" + newGlobals.join(\", \") );\n\t}\n\n\tdeletedGlobals = diff( old, config.pollution );\n\tif ( deletedGlobals.length > 0 ) {\n\t\tQUnit.pushFailure( \"Deleted global variable(s): \" + deletedGlobals.join(\", \") );\n\t}\n}\n\n// returns a new Array with the elements that are in a but not in b\nfunction diff( a, b ) {\n\tvar i, j,\n\t\tresult = a.slice();\n\n\tfor ( i = 0; i < result.length; i++ ) {\n\t\tfor ( j = 0; j < b.length; j++ ) {\n\t\t\tif ( result[i] === b[j] ) {\n\t\t\t\tresult.splice( i, 1 );\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction extend( a, b ) {\n\tfor ( var prop in b ) {\n\t\tif ( hasOwn.call( b, prop ) ) {\n\t\t\t// Avoid \"Member not found\" error in IE8 caused by messing with window.constructor\n\t\t\tif ( !( prop === \"constructor\" && a === window ) ) {\n\t\t\t\tif ( b[ prop ] === undefined ) {\n\t\t\t\t\tdelete a[ prop ];\n\t\t\t\t} else {\n\t\t\t\t\ta[ prop ] = b[ prop ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a;\n}\n\n/**\n * @param {HTMLElement} elem\n * @param {string} type\n * @param {Function} fn\n */\nfunction addEvent( elem, type, fn ) {\n\tif ( elem.addEventListener ) {\n\n\t\t// Standards-based browsers\n\t\telem.addEventListener( type, fn, false );\n\t} else if ( elem.attachEvent ) {\n\n\t\t// support: IE <9\n\t\telem.attachEvent( \"on\" + type, fn );\n\t} else {\n\n\t\t// Caller must ensure support for event listeners is present\n\t\tthrow new Error( \"addEvent() was called in a context without event listener support\" );\n\t}\n}\n\n/**\n * @param {Array|NodeList} elems\n * @param {string} type\n * @param {Function} fn\n */\nfunction addEvents( elems, type, fn ) {\n\tvar i = elems.length;\n\twhile ( i-- ) {\n\t\taddEvent( elems[i], type, fn );\n\t}\n}\n\nfunction hasClass( elem, name ) {\n\treturn (\" \" + elem.className + \" \").indexOf(\" \" + name + \" \") > -1;\n}\n\nfunction addClass( elem, name ) {\n\tif ( !hasClass( elem, name ) ) {\n\t\telem.className += (elem.className ? \" \" : \"\") + name;\n\t}\n}\n\nfunction removeClass( elem, name ) {\n\tvar set = \" \" + elem.className + \" \";\n\t// Class name may appear multiple times\n\twhile ( set.indexOf(\" \" + name + \" \") > -1 ) {\n\t\tset = set.replace(\" \" + name + \" \" , \" \");\n\t}\n\t// If possible, trim it for prettiness, but not necessarily\n\telem.className = typeof set.trim === \"function\" ? set.trim() : set.replace(/^\\s+|\\s+$/g, \"\");\n}\n\nfunction id( name ) {\n\treturn defined.document && document.getElementById && document.getElementById( name );\n}\n\nfunction registerLoggingCallback( key ) {\n\treturn function( callback ) {\n\t\tconfig[key].push( callback );\n\t};\n}\n\n// Supports deprecated method of completely overwriting logging callbacks\nfunction runLoggingCallbacks( key, scope, args ) {\n\tvar i, callbacks;\n\tif ( QUnit.hasOwnProperty( key ) ) {\n\t\tQUnit[ key ].call(scope, args );\n\t} else {\n\t\tcallbacks = config[ key ];\n\t\tfor ( i = 0; i < callbacks.length; i++ ) {\n\t\t\tcallbacks[ i ].call( scope, args );\n\t\t}\n\t}\n}\n\n// from jquery.js\nfunction inArray( elem, array ) {\n\tif ( array.indexOf ) {\n\t\treturn array.indexOf( elem );\n\t}\n\n\tfor ( var i = 0, length = array.length; i < length; i++ ) {\n\t\tif ( array[ i ] === elem ) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nfunction Test( settings ) {\n\textend( this, settings );\n\tthis.assertions = [];\n\tthis.testNumber = ++Test.count;\n}\n\nTest.count = 0;\n\nTest.prototype = {\n\tinit: function() {\n\t\tvar a, b, li,\n\t\t\ttests = id( \"qunit-tests\" );\n\n\t\tif ( tests ) {\n\t\t\tb = document.createElement( \"strong\" );\n\t\t\tb.innerHTML = this.nameHtml;\n\n\t\t\t// `a` initialized at top of scope\n\t\t\ta = document.createElement( \"a\" );\n\t\t\ta.innerHTML = \"Rerun\";\n\t\t\ta.href = QUnit.url({ testNumber: this.testNumber });\n\n\t\t\tli = document.createElement( \"li\" );\n\t\t\tli.appendChild( b );\n\t\t\tli.appendChild( a );\n\t\t\tli.className = \"running\";\n\t\t\tli.id = this.id = \"qunit-test-output\" + testId++;\n\n\t\t\ttests.appendChild( li );\n\t\t}\n\t},\n\tsetup: function() {\n\t\tif (\n\t\t\t// Emit moduleStart when we're switching from one module to another\n\t\t\tthis.module !== config.previousModule ||\n\t\t\t\t// They could be equal (both undefined) but if the previousModule property doesn't\n\t\t\t\t// yet exist it means this is the first test in a suite that isn't wrapped in a\n\t\t\t\t// module, in which case we'll just emit a moduleStart event for 'undefined'.\n\t\t\t\t// Without this, reporters can get testStart before moduleStart  which is a problem.\n\t\t\t\t!hasOwn.call( config, \"previousModule\" )\n\t\t) {\n\t\t\tif ( hasOwn.call( config, \"previousModule\" ) ) {\n\t\t\t\trunLoggingCallbacks( \"moduleDone\", QUnit, {\n\t\t\t\t\tname: config.previousModule,\n\t\t\t\t\tfailed: config.moduleStats.bad,\n\t\t\t\t\tpassed: config.moduleStats.all - config.moduleStats.bad,\n\t\t\t\t\ttotal: config.moduleStats.all\n\t\t\t\t});\n\t\t\t}\n\t\t\tconfig.previousModule = this.module;\n\t\t\tconfig.moduleStats = { all: 0, bad: 0 };\n\t\t\trunLoggingCallbacks( \"moduleStart\", QUnit, {\n\t\t\t\tname: this.module\n\t\t\t});\n\t\t}\n\n\t\tconfig.current = this;\n\n\t\tthis.testEnvironment = extend({\n\t\t\tsetup: function() {},\n\t\t\tteardown: function() {}\n\t\t}, this.moduleTestEnvironment );\n\n\t\tthis.started = +new Date();\n\t\trunLoggingCallbacks( \"testStart\", QUnit, {\n\t\t\tname: this.testName,\n\t\t\tmodule: this.module\n\t\t});\n\n\t\t/*jshint camelcase:false */\n\n\n\t\t/**\n\t\t * Expose the current test environment.\n\t\t *\n\t\t * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.\n\t\t */\n\t\tQUnit.current_testEnvironment = this.testEnvironment;\n\n\t\t/*jshint camelcase:true */\n\n\t\tif ( !config.pollution ) {\n\t\t\tsaveGlobal();\n\t\t}\n\t\tif ( config.notrycatch ) {\n\t\t\tthis.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );\n\t\t} catch( e ) {\n\t\t\tQUnit.pushFailure( \"Setup failed on \" + this.testName + \": \" + ( e.message || e ), extractStacktrace( e, 1 ) );\n\t\t}\n\t},\n\trun: function() {\n\t\tconfig.current = this;\n\n\t\tvar running = id( \"qunit-testresult\" );\n\n\t\tif ( running ) {\n\t\t\trunning.innerHTML = \"Running: <br/>\" + this.nameHtml;\n\t\t}\n\n\t\tif ( this.async ) {\n\t\t\tQUnit.stop();\n\t\t}\n\n\t\tthis.callbackStarted = +new Date();\n\n\t\tif ( config.notrycatch ) {\n\t\t\tthis.callback.call( this.testEnvironment, QUnit.assert );\n\t\t\tthis.callbackRuntime = +new Date() - this.callbackStarted;\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.callback.call( this.testEnvironment, QUnit.assert );\n\t\t\tthis.callbackRuntime = +new Date() - this.callbackStarted;\n\t\t} catch( e ) {\n\t\t\tthis.callbackRuntime = +new Date() - this.callbackStarted;\n\n\t\t\tQUnit.pushFailure( \"Died on test #\" + (this.assertions.length + 1) + \" \" + this.stack + \": \" + ( e.message || e ), extractStacktrace( e, 0 ) );\n\t\t\t// else next test will carry the responsibility\n\t\t\tsaveGlobal();\n\n\t\t\t// Restart the tests if they're blocking\n\t\t\tif ( config.blocking ) {\n\t\t\t\tQUnit.start();\n\t\t\t}\n\t\t}\n\t},\n\tteardown: function() {\n\t\tconfig.current = this;\n\t\tif ( config.notrycatch ) {\n\t\t\tif ( typeof this.callbackRuntime === \"undefined\" ) {\n\t\t\t\tthis.callbackRuntime = +new Date() - this.callbackStarted;\n\t\t\t}\n\t\t\tthis.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );\n\t\t\treturn;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tthis.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );\n\t\t\t} catch( e ) {\n\t\t\t\tQUnit.pushFailure( \"Teardown failed on \" + this.testName + \": \" + ( e.message || e ), extractStacktrace( e, 1 ) );\n\t\t\t}\n\t\t}\n\t\tcheckPollution();\n\t},\n\tfinish: function() {\n\t\tconfig.current = this;\n\t\tif ( config.requireExpects && this.expected === null ) {\n\t\t\tQUnit.pushFailure( \"Expected number of assertions to be defined, but expect() was not called.\", this.stack );\n\t\t} else if ( this.expected !== null && this.expected !== this.assertions.length ) {\n\t\t\tQUnit.pushFailure( \"Expected \" + this.expected + \" assertions, but \" + this.assertions.length + \" were run\", this.stack );\n\t\t} else if ( this.expected === null && !this.assertions.length ) {\n\t\t\tQUnit.pushFailure( \"Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.\", this.stack );\n\t\t}\n\n\t\tvar i, assertion, a, b, time, li, ol,\n\t\t\ttest = this,\n\t\t\tgood = 0,\n\t\t\tbad = 0,\n\t\t\ttests = id( \"qunit-tests\" );\n\n\t\tthis.runtime = +new Date() - this.started;\n\t\tconfig.stats.all += this.assertions.length;\n\t\tconfig.moduleStats.all += this.assertions.length;\n\n\t\tif ( tests ) {\n\t\t\tol = document.createElement( \"ol\" );\n\t\t\tol.className = \"qunit-assert-list\";\n\n\t\t\tfor ( i = 0; i < this.assertions.length; i++ ) {\n\t\t\t\tassertion = this.assertions[i];\n\n\t\t\t\tli = document.createElement( \"li\" );\n\t\t\t\tli.className = assertion.result ? \"pass\" : \"fail\";\n\t\t\t\tli.innerHTML = assertion.message || ( assertion.result ? \"okay\" : \"failed\" );\n\t\t\t\tol.appendChild( li );\n\n\t\t\t\tif ( assertion.result ) {\n\t\t\t\t\tgood++;\n\t\t\t\t} else {\n\t\t\t\t\tbad++;\n\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store result when possible\n\t\t\tif ( QUnit.config.reorder && defined.sessionStorage ) {\n\t\t\t\tif ( bad ) {\n\t\t\t\t\tsessionStorage.setItem( \"qunit-test-\" + this.module + \"-\" + this.testName, bad );\n\t\t\t\t} else {\n\t\t\t\t\tsessionStorage.removeItem( \"qunit-test-\" + this.module + \"-\" + this.testName );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( bad === 0 ) {\n\t\t\t\taddClass( ol, \"qunit-collapsed\" );\n\t\t\t}\n\n\t\t\t// `b` initialized at top of scope\n\t\t\tb = document.createElement( \"strong\" );\n\t\t\tb.innerHTML = this.nameHtml + \" <b class='counts'>(<b class='failed'>\" + bad + \"</b>, <b class='passed'>\" + good + \"</b>, \" + this.assertions.length + \")</b>\";\n\n\t\t\taddEvent(b, \"click\", function() {\n\t\t\t\tvar next = b.parentNode.lastChild,\n\t\t\t\t\tcollapsed = hasClass( next, \"qunit-collapsed\" );\n\t\t\t\t( collapsed ? removeClass : addClass )( next, \"qunit-collapsed\" );\n\t\t\t});\n\n\t\t\taddEvent(b, \"dblclick\", function( e ) {\n\t\t\t\tvar target = e && e.target ? e.target : window.event.srcElement;\n\t\t\t\tif ( target.nodeName.toLowerCase() === \"span\" || target.nodeName.toLowerCase() === \"b\" ) {\n\t\t\t\t\ttarget = target.parentNode;\n\t\t\t\t}\n\t\t\t\tif ( window.location && target.nodeName.toLowerCase() === \"strong\" ) {\n\t\t\t\t\twindow.location = QUnit.url({ testNumber: test.testNumber });\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// `time` initialized at top of scope\n\t\t\ttime = document.createElement( \"span\" );\n\t\t\ttime.className = \"runtime\";\n\t\t\ttime.innerHTML = this.runtime + \" ms\";\n\n\t\t\t// `li` initialized at top of scope\n\t\t\tli = id( this.id );\n\t\t\tli.className = bad ? \"fail\" : \"pass\";\n\t\t\tli.removeChild( li.firstChild );\n\t\t\ta = li.firstChild;\n\t\t\tli.appendChild( b );\n\t\t\tli.appendChild( a );\n\t\t\tli.appendChild( time );\n\t\t\tli.appendChild( ol );\n\n\t\t} else {\n\t\t\tfor ( i = 0; i < this.assertions.length; i++ ) {\n\t\t\t\tif ( !this.assertions[i].result ) {\n\t\t\t\t\tbad++;\n\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trunLoggingCallbacks( \"testDone\", QUnit, {\n\t\t\tname: this.testName,\n\t\t\tmodule: this.module,\n\t\t\tfailed: bad,\n\t\t\tpassed: this.assertions.length - bad,\n\t\t\ttotal: this.assertions.length,\n\t\t\truntime: this.runtime,\n\t\t\t// DEPRECATED: this property will be removed in 2.0.0, use runtime instead\n\t\t\tduration: this.runtime\n\t\t});\n\n\t\tQUnit.reset();\n\n\t\tconfig.current = undefined;\n\t},\n\n\tqueue: function() {\n\t\tvar bad,\n\t\t\ttest = this;\n\n\t\tsynchronize(function() {\n\t\t\ttest.init();\n\t\t});\n\t\tfunction run() {\n\t\t\t// each of these can by async\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.setup();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.run();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.teardown();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.finish();\n\t\t\t});\n\t\t}\n\n\t\t// `bad` initialized at top of scope\n\t\t// defer when previous test run passed, if storage is available\n\t\tbad = QUnit.config.reorder && defined.sessionStorage &&\n\t\t\t\t\t\t+sessionStorage.getItem( \"qunit-test-\" + this.module + \"-\" + this.testName );\n\n\t\tif ( bad ) {\n\t\t\trun();\n\t\t} else {\n\t\t\tsynchronize( run, true );\n\t\t}\n\t}\n};\n\n// `assert` initialized at top of scope\n// Assert helpers\n// All of these must either call QUnit.push() or manually do:\n// - runLoggingCallbacks( \"log\", .. );\n// - config.current.assertions.push({ .. });\nassert = QUnit.assert = {\n\t/**\n\t * Asserts rough true-ish result.\n\t * @name ok\n\t * @function\n\t * @example ok( \"asdfasdf\".length > 5, \"There must be at least 5 chars\" );\n\t */\n\tok: function( result, msg ) {\n\t\tif ( !config.current ) {\n\t\t\tthrow new Error( \"ok() assertion outside test context, was \" + sourceFromStacktrace(2) );\n\t\t}\n\t\tresult = !!result;\n\t\tmsg = msg || ( result ? \"okay\" : \"failed\" );\n\n\t\tvar source,\n\t\t\tdetails = {\n\t\t\t\tmodule: config.current.module,\n\t\t\t\tname: config.current.testName,\n\t\t\t\tresult: result,\n\t\t\t\tmessage: msg\n\t\t\t};\n\n\t\tmsg = \"<span class='test-message'>\" + escapeText( msg ) + \"</span>\";\n\n\t\tif ( !result ) {\n\t\t\tsource = sourceFromStacktrace( 2 );\n\t\t\tif ( source ) {\n\t\t\t\tdetails.source = source;\n\t\t\t\tmsg += \"<table><tr class='test-source'><th>Source: </th><td><pre>\" +\n\t\t\t\t\tescapeText( source ) +\n\t\t\t\t\t\"</pre></td></tr></table>\";\n\t\t\t}\n\t\t}\n\t\trunLoggingCallbacks( \"log\", QUnit, details );\n\t\tconfig.current.assertions.push({\n\t\t\tresult: result,\n\t\t\tmessage: msg\n\t\t});\n\t},\n\n\t/**\n\t * Assert that the first two arguments are equal, with an optional message.\n\t * Prints out both actual and expected values.\n\t * @name equal\n\t * @function\n\t * @example equal( format( \"Received {0} bytes.\", 2), \"Received 2 bytes.\", \"format() replaces {0} with next argument\" );\n\t */\n\tequal: function( actual, expected, message ) {\n\t\t/*jshint eqeqeq:false */\n\t\tQUnit.push( expected == actual, actual, expected, message );\n\t},\n\n\t/**\n\t * @name notEqual\n\t * @function\n\t */\n\tnotEqual: function( actual, expected, message ) {\n\t\t/*jshint eqeqeq:false */\n\t\tQUnit.push( expected != actual, actual, expected, message );\n\t},\n\n\t/**\n\t * @name propEqual\n\t * @function\n\t */\n\tpropEqual: function( actual, expected, message ) {\n\t\tactual = objectValues(actual);\n\t\texpected = objectValues(expected);\n\t\tQUnit.push( QUnit.equiv(actual, expected), actual, expected, message );\n\t},\n\n\t/**\n\t * @name notPropEqual\n\t * @function\n\t */\n\tnotPropEqual: function( actual, expected, message ) {\n\t\tactual = objectValues(actual);\n\t\texpected = objectValues(expected);\n\t\tQUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );\n\t},\n\n\t/**\n\t * @name deepEqual\n\t * @function\n\t */\n\tdeepEqual: function( actual, expected, message ) {\n\t\tQUnit.push( QUnit.equiv(actual, expected), actual, expected, message );\n\t},\n\n\t/**\n\t * @name notDeepEqual\n\t * @function\n\t */\n\tnotDeepEqual: function( actual, expected, message ) {\n\t\tQUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );\n\t},\n\n\t/**\n\t * @name strictEqual\n\t * @function\n\t */\n\tstrictEqual: function( actual, expected, message ) {\n\t\tQUnit.push( expected === actual, actual, expected, message );\n\t},\n\n\t/**\n\t * @name notStrictEqual\n\t * @function\n\t */\n\tnotStrictEqual: function( actual, expected, message ) {\n\t\tQUnit.push( expected !== actual, actual, expected, message );\n\t},\n\n\t\"throws\": function( block, expected, message ) {\n\t\tvar actual,\n\t\t\texpectedOutput = expected,\n\t\t\tok = false;\n\n\t\t// 'expected' is optional\n\t\tif ( !message && typeof expected === \"string\" ) {\n\t\t\tmessage = expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tconfig.current.ignoreGlobalErrors = true;\n\t\ttry {\n\t\t\tblock.call( config.current.testEnvironment );\n\t\t} catch (e) {\n\t\t\tactual = e;\n\t\t}\n\t\tconfig.current.ignoreGlobalErrors = false;\n\n\t\tif ( actual ) {\n\n\t\t\t// we don't want to validate thrown error\n\t\t\tif ( !expected ) {\n\t\t\t\tok = true;\n\t\t\t\texpectedOutput = null;\n\n\t\t\t// expected is an Error object\n\t\t\t} else if ( expected instanceof Error ) {\n\t\t\t\tok = actual instanceof Error &&\n\t\t\t\t\t actual.name === expected.name &&\n\t\t\t\t\t actual.message === expected.message;\n\n\t\t\t// expected is a regexp\n\t\t\t} else if ( QUnit.objectType( expected ) === \"regexp\" ) {\n\t\t\t\tok = expected.test( errorString( actual ) );\n\n\t\t\t// expected is a string\n\t\t\t} else if ( QUnit.objectType( expected ) === \"string\" ) {\n\t\t\t\tok = expected === errorString( actual );\n\n\t\t\t// expected is a constructor\n\t\t\t} else if ( actual instanceof expected ) {\n\t\t\t\tok = true;\n\n\t\t\t// expected is a validation function which returns true is validation passed\n\t\t\t} else if ( expected.call( {}, actual ) === true ) {\n\t\t\t\texpectedOutput = null;\n\t\t\t\tok = true;\n\t\t\t}\n\n\t\t\tQUnit.push( ok, actual, expectedOutput, message );\n\t\t} else {\n\t\t\tQUnit.pushFailure( message, null, \"No exception was thrown.\" );\n\t\t}\n\t}\n};\n\n/**\n * @deprecated since 1.8.0\n * Kept assertion helpers in root for backwards compatibility.\n */\nextend( QUnit.constructor.prototype, assert );\n\n/**\n * @deprecated since 1.9.0\n * Kept to avoid TypeErrors for undefined methods.\n */\nQUnit.constructor.prototype.raises = function() {\n\tQUnit.push( false, false, false, \"QUnit.raises has been deprecated since 2012 (fad3c1ea), use QUnit.throws instead\" );\n};\n\n/**\n * @deprecated since 1.0.0, replaced with error pushes since 1.3.0\n * Kept to avoid TypeErrors for undefined methods.\n */\nQUnit.constructor.prototype.equals = function() {\n\tQUnit.push( false, false, false, \"QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead\" );\n};\nQUnit.constructor.prototype.same = function() {\n\tQUnit.push( false, false, false, \"QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead\" );\n};\n\n// Test for equality any JavaScript type.\n// Author: Philippe Rathé <prathe@gmail.com>\nQUnit.equiv = (function() {\n\n\t// Call the o related callback with the given arguments.\n\tfunction bindCallbacks( o, callbacks, args ) {\n\t\tvar prop = QUnit.objectType( o );\n\t\tif ( prop ) {\n\t\t\tif ( QUnit.objectType( callbacks[ prop ] ) === \"function\" ) {\n\t\t\t\treturn callbacks[ prop ].apply( callbacks, args );\n\t\t\t} else {\n\t\t\t\treturn callbacks[ prop ]; // or undefined\n\t\t\t}\n\t\t}\n\t}\n\n\t// the real equiv function\n\tvar innerEquiv,\n\t\t// stack to decide between skip/abort functions\n\t\tcallers = [],\n\t\t// stack to avoiding loops from circular referencing\n\t\tparents = [],\n\t\tparentsB = [],\n\n\t\tgetProto = Object.getPrototypeOf || function ( obj ) {\n\t\t\t/*jshint camelcase:false */\n\t\t\treturn obj.__proto__;\n\t\t},\n\t\tcallbacks = (function () {\n\n\t\t\t// for string, boolean, number and null\n\t\t\tfunction useStrictEquality( b, a ) {\n\t\t\t\t/*jshint eqeqeq:false */\n\t\t\t\tif ( b instanceof a.constructor || a instanceof b.constructor ) {\n\t\t\t\t\t// to catch short annotation VS 'new' annotation of a\n\t\t\t\t\t// declaration\n\t\t\t\t\t// e.g. var i = 1;\n\t\t\t\t\t// var j = new Number(1);\n\t\t\t\t\treturn a == b;\n\t\t\t\t} else {\n\t\t\t\t\treturn a === b;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t\"string\": useStrictEquality,\n\t\t\t\t\"boolean\": useStrictEquality,\n\t\t\t\t\"number\": useStrictEquality,\n\t\t\t\t\"null\": useStrictEquality,\n\t\t\t\t\"undefined\": useStrictEquality,\n\n\t\t\t\t\"nan\": function( b ) {\n\t\t\t\t\treturn isNaN( b );\n\t\t\t\t},\n\n\t\t\t\t\"date\": function( b, a ) {\n\t\t\t\t\treturn QUnit.objectType( b ) === \"date\" && a.valueOf() === b.valueOf();\n\t\t\t\t},\n\n\t\t\t\t\"regexp\": function( b, a ) {\n\t\t\t\t\treturn QUnit.objectType( b ) === \"regexp\" &&\n\t\t\t\t\t\t// the regex itself\n\t\t\t\t\t\ta.source === b.source &&\n\t\t\t\t\t\t// and its modifiers\n\t\t\t\t\t\ta.global === b.global &&\n\t\t\t\t\t\t// (gmi) ...\n\t\t\t\t\t\ta.ignoreCase === b.ignoreCase &&\n\t\t\t\t\t\ta.multiline === b.multiline &&\n\t\t\t\t\t\ta.sticky === b.sticky;\n\t\t\t\t},\n\n\t\t\t\t// - skip when the property is a method of an instance (OOP)\n\t\t\t\t// - abort otherwise,\n\t\t\t\t// initial === would have catch identical references anyway\n\t\t\t\t\"function\": function() {\n\t\t\t\t\tvar caller = callers[callers.length - 1];\n\t\t\t\t\treturn caller !== Object && typeof caller !== \"undefined\";\n\t\t\t\t},\n\n\t\t\t\t\"array\": function( b, a ) {\n\t\t\t\t\tvar i, j, len, loop, aCircular, bCircular;\n\n\t\t\t\t\t// b could be an object literal here\n\t\t\t\t\tif ( QUnit.objectType( b ) !== \"array\" ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tlen = a.length;\n\t\t\t\t\tif ( len !== b.length ) {\n\t\t\t\t\t\t// safe and faster\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\t// track reference to avoid circular references\n\t\t\t\t\tparents.push( a );\n\t\t\t\t\tparentsB.push( b );\n\t\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\t\tloop = false;\n\t\t\t\t\t\tfor ( j = 0; j < parents.length; j++ ) {\n\t\t\t\t\t\t\taCircular = parents[j] === a[i];\n\t\t\t\t\t\t\tbCircular = parentsB[j] === b[i];\n\t\t\t\t\t\t\tif ( aCircular || bCircular ) {\n\t\t\t\t\t\t\t\tif ( a[i] === b[i] || aCircular && bCircular ) {\n\t\t\t\t\t\t\t\t\tloop = true;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tparents.pop();\n\t\t\t\t\t\t\t\t\tparentsB.pop();\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( !loop && !innerEquiv(a[i], b[i]) ) {\n\t\t\t\t\t\t\tparents.pop();\n\t\t\t\t\t\t\tparentsB.pop();\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tparents.pop();\n\t\t\t\t\tparentsB.pop();\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\n\t\t\t\t\"object\": function( b, a ) {\n\t\t\t\t\t/*jshint forin:false */\n\t\t\t\t\tvar i, j, loop, aCircular, bCircular,\n\t\t\t\t\t\t// Default to true\n\t\t\t\t\t\teq = true,\n\t\t\t\t\t\taProperties = [],\n\t\t\t\t\t\tbProperties = [];\n\n\t\t\t\t\t// comparing constructors is more strict than using\n\t\t\t\t\t// instanceof\n\t\t\t\t\tif ( a.constructor !== b.constructor ) {\n\t\t\t\t\t\t// Allow objects with no prototype to be equivalent to\n\t\t\t\t\t\t// objects with Object as their constructor.\n\t\t\t\t\t\tif ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||\n\t\t\t\t\t\t\t( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// stack constructor before traversing properties\n\t\t\t\t\tcallers.push( a.constructor );\n\n\t\t\t\t\t// track reference to avoid circular references\n\t\t\t\t\tparents.push( a );\n\t\t\t\t\tparentsB.push( b );\n\n\t\t\t\t\t// be strict: don't ensure hasOwnProperty and go deep\n\t\t\t\t\tfor ( i in a ) {\n\t\t\t\t\t\tloop = false;\n\t\t\t\t\t\tfor ( j = 0; j < parents.length; j++ ) {\n\t\t\t\t\t\t\taCircular = parents[j] === a[i];\n\t\t\t\t\t\t\tbCircular = parentsB[j] === b[i];\n\t\t\t\t\t\t\tif ( aCircular || bCircular ) {\n\t\t\t\t\t\t\t\tif ( a[i] === b[i] || aCircular && bCircular ) {\n\t\t\t\t\t\t\t\t\tloop = true;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\teq = false;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\taProperties.push(i);\n\t\t\t\t\t\tif ( !loop && !innerEquiv(a[i], b[i]) ) {\n\t\t\t\t\t\t\teq = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tparents.pop();\n\t\t\t\t\tparentsB.pop();\n\t\t\t\t\tcallers.pop(); // unstack, we are done\n\n\t\t\t\t\tfor ( i in b ) {\n\t\t\t\t\t\tbProperties.push( i ); // collect b's properties\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensures identical properties name\n\t\t\t\t\treturn eq && innerEquiv( aProperties.sort(), bProperties.sort() );\n\t\t\t\t}\n\t\t\t};\n\t\t}());\n\n\tinnerEquiv = function() { // can take multiple arguments\n\t\tvar args = [].slice.apply( arguments );\n\t\tif ( args.length < 2 ) {\n\t\t\treturn true; // end transition\n\t\t}\n\n\t\treturn (function( a, b ) {\n\t\t\tif ( a === b ) {\n\t\t\t\treturn true; // catch the most you can\n\t\t\t} else if ( a === null || b === null || typeof a === \"undefined\" ||\n\t\t\t\t\ttypeof b === \"undefined\" ||\n\t\t\t\t\tQUnit.objectType(a) !== QUnit.objectType(b) ) {\n\t\t\t\treturn false; // don't lose time with error prone cases\n\t\t\t} else {\n\t\t\t\treturn bindCallbacks(a, callbacks, [ b, a ]);\n\t\t\t}\n\n\t\t\t// apply transition with (1..n) arguments\n\t\t}( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );\n\t};\n\n\treturn innerEquiv;\n}());\n\n/**\n * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |\n * http://flesler.blogspot.com Licensed under BSD\n * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008\n *\n * @projectDescription Advanced and extensible data dumping for Javascript.\n * @version 1.0.0\n * @author Ariel Flesler\n * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}\n */\nQUnit.jsDump = (function() {\n\tfunction quote( str ) {\n\t\treturn \"\\\"\" + str.toString().replace( /\"/g, \"\\\\\\\"\" ) + \"\\\"\";\n\t}\n\tfunction literal( o ) {\n\t\treturn o + \"\";\n\t}\n\tfunction join( pre, arr, post ) {\n\t\tvar s = jsDump.separator(),\n\t\t\tbase = jsDump.indent(),\n\t\t\tinner = jsDump.indent(1);\n\t\tif ( arr.join ) {\n\t\t\tarr = arr.join( \",\" + s + inner );\n\t\t}\n\t\tif ( !arr ) {\n\t\t\treturn pre + post;\n\t\t}\n\t\treturn [ pre, inner + arr, base + post ].join(s);\n\t}\n\tfunction array( arr, stack ) {\n\t\tvar i = arr.length, ret = new Array(i);\n\t\tthis.up();\n\t\twhile ( i-- ) {\n\t\t\tret[i] = this.parse( arr[i] , undefined , stack);\n\t\t}\n\t\tthis.down();\n\t\treturn join( \"[\", ret, \"]\" );\n\t}\n\n\tvar reName = /^function (\\w+)/,\n\t\tjsDump = {\n\t\t\t// type is used mostly internally, you can fix a (custom)type in advance\n\t\t\tparse: function( obj, type, stack ) {\n\t\t\t\tstack = stack || [ ];\n\t\t\t\tvar inStack, res,\n\t\t\t\t\tparser = this.parsers[ type || this.typeOf(obj) ];\n\n\t\t\t\ttype = typeof parser;\n\t\t\t\tinStack = inArray( obj, stack );\n\n\t\t\t\tif ( inStack !== -1 ) {\n\t\t\t\t\treturn \"recursion(\" + (inStack - stack.length) + \")\";\n\t\t\t\t}\n\t\t\t\tif ( type === \"function\" )  {\n\t\t\t\t\tstack.push( obj );\n\t\t\t\t\tres = parser.call( this, obj, stack );\n\t\t\t\t\tstack.pop();\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t\treturn ( type === \"string\" ) ? parser : this.parsers.error;\n\t\t\t},\n\t\t\ttypeOf: function( obj ) {\n\t\t\t\tvar type;\n\t\t\t\tif ( obj === null ) {\n\t\t\t\t\ttype = \"null\";\n\t\t\t\t} else if ( typeof obj === \"undefined\" ) {\n\t\t\t\t\ttype = \"undefined\";\n\t\t\t\t} else if ( QUnit.is( \"regexp\", obj) ) {\n\t\t\t\t\ttype = \"regexp\";\n\t\t\t\t} else if ( QUnit.is( \"date\", obj) ) {\n\t\t\t\t\ttype = \"date\";\n\t\t\t\t} else if ( QUnit.is( \"function\", obj) ) {\n\t\t\t\t\ttype = \"function\";\n\t\t\t\t} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== \"undefined\" && typeof obj.nodeType === \"undefined\" ) {\n\t\t\t\t\ttype = \"window\";\n\t\t\t\t} else if ( obj.nodeType === 9 ) {\n\t\t\t\t\ttype = \"document\";\n\t\t\t\t} else if ( obj.nodeType ) {\n\t\t\t\t\ttype = \"node\";\n\t\t\t\t} else if (\n\t\t\t\t\t// native arrays\n\t\t\t\t\ttoString.call( obj ) === \"[object Array]\" ||\n\t\t\t\t\t// NodeList objects\n\t\t\t\t\t( typeof obj.length === \"number\" && typeof obj.item !== \"undefined\" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === \"undefined\" ) ) )\n\t\t\t\t) {\n\t\t\t\t\ttype = \"array\";\n\t\t\t\t} else if ( obj.constructor === Error.prototype.constructor ) {\n\t\t\t\t\ttype = \"error\";\n\t\t\t\t} else {\n\t\t\t\t\ttype = typeof obj;\n\t\t\t\t}\n\t\t\t\treturn type;\n\t\t\t},\n\t\t\tseparator: function() {\n\t\t\t\treturn this.multiline ?\tthis.HTML ? \"<br />\" : \"\\n\" : this.HTML ? \"&nbsp;\" : \" \";\n\t\t\t},\n\t\t\t// extra can be a number, shortcut for increasing-calling-decreasing\n\t\t\tindent: function( extra ) {\n\t\t\t\tif ( !this.multiline ) {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t\tvar chr = this.indentChar;\n\t\t\t\tif ( this.HTML ) {\n\t\t\t\t\tchr = chr.replace( /\\t/g, \"   \" ).replace( / /g, \"&nbsp;\" );\n\t\t\t\t}\n\t\t\t\treturn new Array( this.depth + ( extra || 0 ) ).join(chr);\n\t\t\t},\n\t\t\tup: function( a ) {\n\t\t\t\tthis.depth += a || 1;\n\t\t\t},\n\t\t\tdown: function( a ) {\n\t\t\t\tthis.depth -= a || 1;\n\t\t\t},\n\t\t\tsetParser: function( name, parser ) {\n\t\t\t\tthis.parsers[name] = parser;\n\t\t\t},\n\t\t\t// The next 3 are exposed so you can use them\n\t\t\tquote: quote,\n\t\t\tliteral: literal,\n\t\t\tjoin: join,\n\t\t\t//\n\t\t\tdepth: 1,\n\t\t\t// This is the list of parsers, to modify them, use jsDump.setParser\n\t\t\tparsers: {\n\t\t\t\twindow: \"[Window]\",\n\t\t\t\tdocument: \"[Document]\",\n\t\t\t\terror: function(error) {\n\t\t\t\t\treturn \"Error(\\\"\" + error.message + \"\\\")\";\n\t\t\t\t},\n\t\t\t\tunknown: \"[Unknown]\",\n\t\t\t\t\"null\": \"null\",\n\t\t\t\t\"undefined\": \"undefined\",\n\t\t\t\t\"function\": function( fn ) {\n\t\t\t\t\tvar ret = \"function\",\n\t\t\t\t\t\t// functions never have name in IE\n\t\t\t\t\t\tname = \"name\" in fn ? fn.name : (reName.exec(fn) || [])[1];\n\n\t\t\t\t\tif ( name ) {\n\t\t\t\t\t\tret += \" \" + name;\n\t\t\t\t\t}\n\t\t\t\t\tret += \"( \";\n\n\t\t\t\t\tret = [ ret, QUnit.jsDump.parse( fn, \"functionArgs\" ), \"){\" ].join( \"\" );\n\t\t\t\t\treturn join( ret, QUnit.jsDump.parse(fn,\"functionCode\" ), \"}\" );\n\t\t\t\t},\n\t\t\t\tarray: array,\n\t\t\t\tnodelist: array,\n\t\t\t\t\"arguments\": array,\n\t\t\t\tobject: function( map, stack ) {\n\t\t\t\t\t/*jshint forin:false */\n\t\t\t\t\tvar ret = [ ], keys, key, val, i;\n\t\t\t\t\tQUnit.jsDump.up();\n\t\t\t\t\tkeys = [];\n\t\t\t\t\tfor ( key in map ) {\n\t\t\t\t\t\tkeys.push( key );\n\t\t\t\t\t}\n\t\t\t\t\tkeys.sort();\n\t\t\t\t\tfor ( i = 0; i < keys.length; i++ ) {\n\t\t\t\t\t\tkey = keys[ i ];\n\t\t\t\t\t\tval = map[ key ];\n\t\t\t\t\t\tret.push( QUnit.jsDump.parse( key, \"key\" ) + \": \" + QUnit.jsDump.parse( val, undefined, stack ) );\n\t\t\t\t\t}\n\t\t\t\t\tQUnit.jsDump.down();\n\t\t\t\t\treturn join( \"{\", ret, \"}\" );\n\t\t\t\t},\n\t\t\t\tnode: function( node ) {\n\t\t\t\t\tvar len, i, val,\n\t\t\t\t\t\topen = QUnit.jsDump.HTML ? \"&lt;\" : \"<\",\n\t\t\t\t\t\tclose = QUnit.jsDump.HTML ? \"&gt;\" : \">\",\n\t\t\t\t\t\ttag = node.nodeName.toLowerCase(),\n\t\t\t\t\t\tret = open + tag,\n\t\t\t\t\t\tattrs = node.attributes;\n\n\t\t\t\t\tif ( attrs ) {\n\t\t\t\t\t\tfor ( i = 0, len = attrs.length; i < len; i++ ) {\n\t\t\t\t\t\t\tval = attrs[i].nodeValue;\n\t\t\t\t\t\t\t// IE6 includes all attributes in .attributes, even ones not explicitly set.\n\t\t\t\t\t\t\t// Those have values like undefined, null, 0, false, \"\" or \"inherit\".\n\t\t\t\t\t\t\tif ( val && val !== \"inherit\" ) {\n\t\t\t\t\t\t\t\tret += \" \" + attrs[i].nodeName + \"=\" + QUnit.jsDump.parse( val, \"attribute\" );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tret += close;\n\n\t\t\t\t\t// Show content of TextNode or CDATASection\n\t\t\t\t\tif ( node.nodeType === 3 || node.nodeType === 4 ) {\n\t\t\t\t\t\tret += node.nodeValue;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn ret + open + \"/\" + tag + close;\n\t\t\t\t},\n\t\t\t\t// function calls it internally, it's the arguments part of the function\n\t\t\t\tfunctionArgs: function( fn ) {\n\t\t\t\t\tvar args,\n\t\t\t\t\t\tl = fn.length;\n\n\t\t\t\t\tif ( !l ) {\n\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t}\n\n\t\t\t\t\targs = new Array(l);\n\t\t\t\t\twhile ( l-- ) {\n\t\t\t\t\t\t// 97 is 'a'\n\t\t\t\t\t\targs[l] = String.fromCharCode(97+l);\n\t\t\t\t\t}\n\t\t\t\t\treturn \" \" + args.join( \", \" ) + \" \";\n\t\t\t\t},\n\t\t\t\t// object calls it internally, the key part of an item in a map\n\t\t\t\tkey: quote,\n\t\t\t\t// function calls it internally, it's the content of the function\n\t\t\t\tfunctionCode: \"[code]\",\n\t\t\t\t// node calls it internally, it's an html attribute value\n\t\t\t\tattribute: quote,\n\t\t\t\tstring: quote,\n\t\t\t\tdate: quote,\n\t\t\t\tregexp: literal,\n\t\t\t\tnumber: literal,\n\t\t\t\t\"boolean\": literal\n\t\t\t},\n\t\t\t// if true, entities are escaped ( <, >, \\t, space and \\n )\n\t\t\tHTML: false,\n\t\t\t// indentation unit\n\t\t\tindentChar: \"  \",\n\t\t\t// if true, items in a collection, are separated by a \\n, else just a space.\n\t\t\tmultiline: true\n\t\t};\n\n\treturn jsDump;\n}());\n\n/*\n * Javascript Diff Algorithm\n *  By John Resig (http://ejohn.org/)\n *  Modified by Chu Alan \"sprite\"\n *\n * Released under the MIT license.\n *\n * More Info:\n *  http://ejohn.org/projects/javascript-diff-algorithm/\n *\n * Usage: QUnit.diff(expected, actual)\n *\n * QUnit.diff( \"the quick brown fox jumped over\", \"the quick fox jumps over\" ) == \"the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over\"\n */\nQUnit.diff = (function() {\n\t/*jshint eqeqeq:false, eqnull:true */\n\tfunction diff( o, n ) {\n\t\tvar i,\n\t\t\tns = {},\n\t\t\tos = {};\n\n\t\tfor ( i = 0; i < n.length; i++ ) {\n\t\t\tif ( !hasOwn.call( ns, n[i] ) ) {\n\t\t\t\tns[ n[i] ] = {\n\t\t\t\t\trows: [],\n\t\t\t\t\to: null\n\t\t\t\t};\n\t\t\t}\n\t\t\tns[ n[i] ].rows.push( i );\n\t\t}\n\n\t\tfor ( i = 0; i < o.length; i++ ) {\n\t\t\tif ( !hasOwn.call( os, o[i] ) ) {\n\t\t\t\tos[ o[i] ] = {\n\t\t\t\t\trows: [],\n\t\t\t\t\tn: null\n\t\t\t\t};\n\t\t\t}\n\t\t\tos[ o[i] ].rows.push( i );\n\t\t}\n\n\t\tfor ( i in ns ) {\n\t\t\tif ( hasOwn.call( ns, i ) ) {\n\t\t\t\tif ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {\n\t\t\t\t\tn[ ns[i].rows[0] ] = {\n\t\t\t\t\t\ttext: n[ ns[i].rows[0] ],\n\t\t\t\t\t\trow: os[i].rows[0]\n\t\t\t\t\t};\n\t\t\t\t\to[ os[i].rows[0] ] = {\n\t\t\t\t\t\ttext: o[ os[i].rows[0] ],\n\t\t\t\t\t\trow: ns[i].rows[0]\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor ( i = 0; i < n.length - 1; i++ ) {\n\t\t\tif ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&\n\t\t\t\t\t\tn[ i + 1 ] == o[ n[i].row + 1 ] ) {\n\n\t\t\t\tn[ i + 1 ] = {\n\t\t\t\t\ttext: n[ i + 1 ],\n\t\t\t\t\trow: n[i].row + 1\n\t\t\t\t};\n\t\t\t\to[ n[i].row + 1 ] = {\n\t\t\t\t\ttext: o[ n[i].row + 1 ],\n\t\t\t\t\trow: i + 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tfor ( i = n.length - 1; i > 0; i-- ) {\n\t\t\tif ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&\n\t\t\t\t\t\tn[ i - 1 ] == o[ n[i].row - 1 ]) {\n\n\t\t\t\tn[ i - 1 ] = {\n\t\t\t\t\ttext: n[ i - 1 ],\n\t\t\t\t\trow: n[i].row - 1\n\t\t\t\t};\n\t\t\t\to[ n[i].row - 1 ] = {\n\t\t\t\t\ttext: o[ n[i].row - 1 ],\n\t\t\t\t\trow: i - 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\to: o,\n\t\t\tn: n\n\t\t};\n\t}\n\n\treturn function( o, n ) {\n\t\to = o.replace( /\\s+$/, \"\" );\n\t\tn = n.replace( /\\s+$/, \"\" );\n\n\t\tvar i, pre,\n\t\t\tstr = \"\",\n\t\t\tout = diff( o === \"\" ? [] : o.split(/\\s+/), n === \"\" ? [] : n.split(/\\s+/) ),\n\t\t\toSpace = o.match(/\\s+/g),\n\t\t\tnSpace = n.match(/\\s+/g);\n\n\t\tif ( oSpace == null ) {\n\t\t\toSpace = [ \" \" ];\n\t\t}\n\t\telse {\n\t\t\toSpace.push( \" \" );\n\t\t}\n\n\t\tif ( nSpace == null ) {\n\t\t\tnSpace = [ \" \" ];\n\t\t}\n\t\telse {\n\t\t\tnSpace.push( \" \" );\n\t\t}\n\n\t\tif ( out.n.length === 0 ) {\n\t\t\tfor ( i = 0; i < out.o.length; i++ ) {\n\t\t\t\tstr += \"<del>\" + out.o[i] + oSpace[i] + \"</del>\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif ( out.n[0].text == null ) {\n\t\t\t\tfor ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {\n\t\t\t\t\tstr += \"<del>\" + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( i = 0; i < out.n.length; i++ ) {\n\t\t\t\tif (out.n[i].text == null) {\n\t\t\t\t\tstr += \"<ins>\" + out.n[i] + nSpace[i] + \"</ins>\";\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// `pre` initialized at top of scope\n\t\t\t\t\tpre = \"\";\n\n\t\t\t\t\tfor ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {\n\t\t\t\t\t\tpre += \"<del>\" + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t\t}\n\t\t\t\t\tstr += \" \" + out.n[i].text + nSpace[i] + pre;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn str;\n\t};\n}());\n\n// For browser, export only select globals\nif ( typeof window !== \"undefined\" ) {\n\textend( window, QUnit.constructor.prototype );\n\twindow.QUnit = QUnit;\n}\n\n// For CommonJS environments, export everything\nif ( typeof module !== \"undefined\" && module.exports ) {\n\tmodule.exports = QUnit;\n}\n\n\n// Get a reference to the global object, like window in browsers\n}( (function() {\n\treturn this;\n})() ));\n"
  },
  {
    "path": "test/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>WebUploader test units.</title>\n  <link rel=\"stylesheet\" href=\"./Qunit/qunit-1.14.0.css\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"../css/webuploader.css\" />\n</head>\n<body>\n  <div id=\"qunit\"></div>\n  <div id=\"qunit-fixture\"></div>\n  <script src=\"./jquery-1.10.1.min.js\"></script>\n  <script src=\"./Qunit/qunit-1.14.0.js\"></script>\n  <script src=\"./Qunit/qunit-1.14.0.js\"></script>\n  <script src=\"./require.js\"></script>\n<script type=\"text/javascript\">\n//<![CDATA[\n(function($) {\n    var units;\n\n\n    units = [\n        'base',\n        'filepicker'\n    ];\n\n\n    requirejs.config({\n        baseUrl: './units',\n\n        paths: {\n            webuploader: '../../src'\n        }\n    });\n\n\n    QUnit.config.autostart = false;\n    requirejs(units, function() {\n        QUnit.start();\n    });\n\n})(jQuery);\n//]]>\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "test/require.js",
    "content": "/*\n RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.\n Available via the MIT or new BSD license.\n see: http://github.com/jrburke/requirejs for details\n*/\nvar requirejs,require,define;\n(function(ca){function G(b){return\"[object Function]\"===N.call(b)}function H(b){return\"[object Array]\"===N.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&\"object\"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof\nRegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split(\".\"),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+\"\\nhttp://requirejs.org/docs/errors.html#\"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split(\"/\");n=I;var m=l.map,k=m&&m[\"*\"];if(a&&\".\"===a.charAt(0))if(e){n=\nI.slice(0,I.length-1);a=a.split(\"/\");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,\"\"));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],\".\"===c)n.splice(e,1),e-=1;else if(\"..\"===c)if(1===e&&(\"..\"===n[2]||\"..\"===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join(\"/\")}else 0===a.indexOf(\"./\")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split(\"/\");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join(\"/\");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join(\"/\")))if(b=j(b,d)){f=b;\ng=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join(\"/\"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName(\"script\"),function(e){if(e.getAttribute(\"data-requiremodule\")===a&&e.getAttribute(\"data-requirecontext\")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf(\"!\"):-1;-1<b&&(e=a.substring(0,\nb),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k=\"\";a||(m=!1,a=\"_@r\"+(N+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?\"_unnormalized\"+(Q+=1):\"\";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+\"!\"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));\nreturn b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))\"defined\"===e&&b(p[f]);else if(n=q(a),n.error&&\"error\"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit(\"error\",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit(\"error\",a.error):(e[f]=\n!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C(\"timeout\",\"Load timeout for modules: \"+\nf,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function L(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent(\"onreadystatechange\",e):a.removeEventListener(\"load\",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener(\"error\",e,!1);return{node:a,id:a&&a.getAttribute(\"data-requiremodule\")}}function M(){var a;\nfor(x();A.length;){a=A.shift();if(null===a[0])return w(C(\"mismatch\",\"Mismatched anonymous define() module: \"+a[a.length-1]));E(a)}}var X,$,i,K,Y,l={waitSeconds:7,baseUrl:\"./\",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},N=1,Q=1;K={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?a.module:\na.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:K.exports(a)}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on(\"error\",b);else this.events.error&&(b=t(this,function(a){this.emit(\"error\",a)}));this.depMaps=a&&a.slice(0);this.errback=b;this.inited=\n!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=\nthis.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit(\"error\",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&\n(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?\"define\":\"require\",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit(\"defined\",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=\nthis.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,\"defined\",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||\"\"),f=m(a.prefix+\"!\"+J,this.map.parentMap),r(f,\"defined\",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);\nif(this.events.error)g.on(\"error\",t(this,function(a){this.emit(\"error\",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+\"_unnormalized\")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C(\"fromtexteval\",\n\"fromText eval for \"+b+\" failed: \"+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if(\"string\"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(K,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,\"defined\",t(this,function(a){this.defineDep(b,\na);this.check()}));this.errback&&r(a,\"error\",t(this,this.errback))}c=a.id;f=k[c];!s(K,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});\"error\"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,\nnextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&\"/\"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+=\"/\");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,\na=\"string\"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+\"/\"+(a.main||\"main\").replace(ja,\"\").replace(R,\"\")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=\n!0);if(\"string\"===typeof f){if(G(c))return w(C(\"requireargs\",\"Invalid require call\"),d);if(a&&s(K,f))return K[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C(\"notloaded\",'Module name \"'+j+'\" has not been loaded yet for context: '+b+(a?\"\":\". Use require([])\"))):p[j]}M();i.nextTick(function(){M();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf(\".\"),g=b.split(\"/\")[0];if(-1!==\nd&&(!(\".\"===g||\"..\"===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,\nc,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C(\"nodefine\",\"No define call for \"+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||\"\");else{f=l.paths;a=a.split(\"/\");for(d=a.length;0<d;d-=1)if(g=a.slice(0,\nd).join(\"/\"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join(\"/\");f+=b||(/^data\\:|\\?/.test(f)||c?\"\":\".js\");f=(\"/\"===f.charAt(0)||f.match(/^[\\w\\+\\.\\-]+:/)?\"\":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf(\"?\")?\"?\":\"&\")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if(\"load\"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=L(a),i.completeLoad(a.id)},onScriptError:function(a){var b=L(a);if(!g(b.id))return w(C(\"scripterror\",\n\"Script error for: \"+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,L,E,P,M,q,Q,la=/(\\/\\*([\\s\\S]*?)\\*\\/|([^:]|^)\\/\\/(.*)$)/mg,ma=/[^.]\\s*require\\s*\\(\\s*[\"']([^'\"\\s]+)[\"']\\s*\\)/g,R=/\\.js$/,ja=/^\\.\\//;x=Object.prototype;var N=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!(\"undefined\"!==typeof window&&\"undefined\"!==typeof navigator&&window.document),fa=!z&&\"undefined\"!==typeof importScripts,ka=z&&\"PLAYSTATION 3\"===navigator.platform?/^complete$/:/^(complete|loaded)$/,\nZ=\"undefined\"!==typeof opera&&\"[object Opera]\"===opera.toString(),F={},r={},S=[],O=!1;if(\"undefined\"===typeof define){if(\"undefined\"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}\"undefined\"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m=\"_\";!H(b)&&\"string\"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};\nh.nextTick=\"undefined\"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version=\"2.1.10\";h.jsExtRegExp=/^\\/|:|\\?|\\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v([\"toUrl\",\"undef\",\"defined\",\"specified\"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName(\"head\")[0],D=document.getElementsByTagName(\"base\")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=\nb.xhtml?document.createElementNS(\"http://www.w3.org/1999/xhtml\",\"html:script\"):document.createElement(\"script\");c.type=b.scriptType||\"text/javascript\";c.charset=\"utf-8\";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute(\"data-requirecontext\",b.contextName),g.setAttribute(\"data-requiremodule\",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf(\"[native code\"))&&!Z?(O=!0,g.attachEvent(\"onreadystatechange\",b.onScriptLoad)):\n(g.addEventListener(\"load\",b.onScriptLoad,!1),g.addEventListener(\"error\",b.onScriptError,!1)),g.src=d,M=g,D?y.insertBefore(g,D):y.appendChild(g),M=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C(\"importscripts\",\"importScripts failed for \"+c+\" at \"+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName(\"script\"),function(b){y||(y=b.parentNode);if(L=b.getAttribute(\"data-main\"))return q=L,r.baseUrl||(E=q.split(\"/\"),q=E.pop(),Q=E.length?E.join(\"/\")+\"/\":\"./\",r.baseUrl=\nQ),q=q.replace(R,\"\"),h.jsExtRegExp.test(q)&&(q=L),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;\"string\"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,\"\").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?[\"require\"]:[\"require\",\"exports\",\"module\"]).concat(c)));if(O){if(!(g=M))P&&\"interactive\"===P.readyState||U(document.getElementsByTagName(\"script\"),function(b){if(\"interactive\"===b.readyState)return P=b}),g=P;g&&(b||\n(b=g.getAttribute(\"data-requiremodule\")),h=F[g.getAttribute(\"data-requirecontext\")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);\n"
  },
  {
    "path": "test/units/base.js",
    "content": "define([\n    'webuploader/base'\n], function( Base ) {\n\n    module('Base');\n\n    test( 'Test dollar', 1, function() {\n        ok( Base.$, 'ok' );\n    });\n\n    test( 'Test bindFn', 2, function() {\n        var obj = {\n                name: '123'\n            },\n\n            fn = function( arg1 ) {\n                ok( this.name, '123', 'The name should be `123`,' );\n                ok( arg1, '456', 'The value of first arg should be `456`. ');\n            },\n\n            proxyFn = Base.bindFn( fn, obj );\n\n        proxyFn('456');\n    });\n\n});"
  },
  {
    "path": "test/units/filepicker.js",
    "content": "define([\n    'webuploader/preset/all'\n], function( Base ) {\n    var $fixture;\n\n    module( 'FilePicker', {\n        setup: function() {\n            $fixture = $('#qunit-fixture');\n        },\n\n        teardown: function() {\n            $fixture.empty();\n            $fixture = null;\n        }\n    });\n\n    test( 'Test initialize', 3, function() {\n        var $btns, $pickers;\n\n        $fixture.append('<div class=\"btn\">Button One</div><div class=\"btn\">Button Two</div>');\n\n\n        $btns = $fixture.find('.btn').each(function( i ) {\n            Base.create({\n                pick: this\n            });\n        });\n\n        $pickers = $btns.find('.webuploader-pick');\n        equal( $pickers.length, 2, 'The length should be 2.' );\n        equal( $pickers.eq(0).text(), 'Button One', 'ok' );\n        equal( $pickers.eq(1).text(), 'Button Two', 'ok' );\n\n    });\n\n});"
  }
]