├── .gitignore ├── tests ├── files │ ├── hello.txt │ ├── 1px.gif │ ├── dino.png │ ├── image.jpg │ └── lebowski.json ├── grunt-task │ ├── phantomjs │ │ ├── bridge.js │ │ ├── grunt-lib-phantomjs-main.js │ │ └── grunt-lib-phantomjs.js │ └── qunit.js ├── index.html ├── qunit │ └── qunit.css └── tests.js ├── jcrop ├── Jcrop.gif ├── jquery.Jcrop.min.css └── jquery.Jcrop.min.js ├── statics ├── body.png ├── content.png ├── body__top.png ├── click-here.png ├── logo_small.png ├── content__head.png ├── splash__blind.png ├── splash__logo.png ├── uploader │ ├── rotate.png │ ├── userpic.gif │ ├── webcam.png │ └── file-icon.png ├── view-on-github.png ├── jquery.modal.js └── main.css ├── FileAPI ├── FileAPI.flash.swf ├── FileAPI.flash.camera.swf ├── FileAPI.flash.image.swf ├── crossdomain.xml ├── FileAPI.exif.js └── FileAPI.min.js ├── composer.json ├── bower.json ├── package.json ├── Gruntfile.js ├── README.md ├── jquery.fileapi.min.js ├── index.html └── jquery.fileapi.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /tests/files/hello.txt: -------------------------------------------------------------------------------- 1 | Hello FileAPI! 2 | -------------------------------------------------------------------------------- /jcrop/Jcrop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/jcrop/Jcrop.gif -------------------------------------------------------------------------------- /statics/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/body.png -------------------------------------------------------------------------------- /statics/content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/content.png -------------------------------------------------------------------------------- /tests/files/1px.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/tests/files/1px.gif -------------------------------------------------------------------------------- /tests/files/dino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/tests/files/dino.png -------------------------------------------------------------------------------- /statics/body__top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/body__top.png -------------------------------------------------------------------------------- /statics/click-here.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/click-here.png -------------------------------------------------------------------------------- /statics/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/logo_small.png -------------------------------------------------------------------------------- /tests/files/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/tests/files/image.jpg -------------------------------------------------------------------------------- /FileAPI/FileAPI.flash.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/FileAPI/FileAPI.flash.swf -------------------------------------------------------------------------------- /statics/content__head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/content__head.png -------------------------------------------------------------------------------- /statics/splash__blind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/splash__blind.png -------------------------------------------------------------------------------- /statics/splash__logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/splash__logo.png -------------------------------------------------------------------------------- /statics/uploader/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/uploader/rotate.png -------------------------------------------------------------------------------- /statics/uploader/userpic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/uploader/userpic.gif -------------------------------------------------------------------------------- /statics/uploader/webcam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/uploader/webcam.png -------------------------------------------------------------------------------- /statics/view-on-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/view-on-github.png -------------------------------------------------------------------------------- /statics/uploader/file-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/statics/uploader/file-icon.png -------------------------------------------------------------------------------- /FileAPI/FileAPI.flash.camera.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/FileAPI/FileAPI.flash.camera.swf -------------------------------------------------------------------------------- /FileAPI/FileAPI.flash.image.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubaXa/jquery.fileapi/HEAD/FileAPI/FileAPI.flash.image.swf -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rubaxa/fileapi", 3 | "description": "jQuery plugin for FileAPI (multiupload, image upload, crop, resize and etc.)", 4 | "keywords": ["jquery", "fileapi", "multiupload", "file upload", "upload"], 5 | "homepage": "https://github.com/RubaXa/jquery.fileapi", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Konstantin Lebedev", 10 | "email": "ibnRubaXa@gmail.com" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /FileAPI/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.fileapi", 3 | "main": "jquery.fileapi.js", 4 | "version": "0.4.11", 5 | "homepage": "http://rubaxa.github.io/jquery.fileapi/", 6 | "authors": [ 7 | "RubaXa " 8 | ], 9 | "description": "jQuery plugin for FileAPI (multiupload, image upload, crop, resize and etc.)", 10 | "keywords": [ 11 | "FileAPI", 12 | "multiupload", 13 | "upload", 14 | "file", 15 | "html5", 16 | "chunked", 17 | "jquery" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.fileapi", 3 | "version": "0.4.11", 4 | "devDependencies": { 5 | "grunt": "~0.4.0", 6 | "grunt-version": "*", 7 | "grunt-contrib-jshint": "~0.2.0", 8 | "grunt-contrib-uglify": "*", 9 | 10 | "eventemitter2": "~0.4.9", 11 | "semver": "~1.0.14", 12 | "temporary": "~0.0.4", 13 | "phantomjs": "latest" 14 | }, 15 | "description": "jQuery plugin for FileAPI (multiupload, image upload, crop, resize and etc.)", 16 | "main": "jquery.fileapi.js", 17 | "scripts": { 18 | "test": "grunt test" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/rubaxa/jquery.fileapi.git" 23 | }, 24 | "keywords": [ 25 | "FileAPI", 26 | "upload", 27 | "multiupload", 28 | "file", 29 | "html5", 30 | "chunked", 31 | "jquery", 32 | "plugin" 33 | ], 34 | "author": "Konstantin Lebedev ", 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /tests/grunt-task/phantomjs/bridge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-contrib-qunit 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2012 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | /*global QUnit:true, alert:true*/ 10 | 11 | 'use strict'; 12 | 13 | 14 | // Don't re-order tests. 15 | QUnit.config.reorder = false; 16 | // Run tests serially, not in parallel. 17 | QUnit.config.autorun = false; 18 | 19 | // Send messages to the parent PhantomJS process via alert! Good times!! 20 | function sendMessage() { 21 | var args = [].slice.call(arguments); 22 | alert(JSON.stringify(args)); 23 | } 24 | 25 | // These methods connect QUnit to PhantomJS. 26 | QUnit.log(function(obj) { 27 | // What is this I don’t even 28 | if (obj.message === '[object Object], undefined:undefined') { return; } 29 | // Parse some stuff before sending it. 30 | var actual = QUnit.jsDump.parse(obj.actual); 31 | var expected = QUnit.jsDump.parse(obj.expected); 32 | // Send it. 33 | sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source); 34 | }); 35 | 36 | QUnit.testStart(function(obj) { 37 | sendMessage('qunit.testStart', obj.name); 38 | }); 39 | 40 | QUnit.testDone(function(obj) { 41 | sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total); 42 | }); 43 | 44 | QUnit.moduleStart(function(obj) { 45 | sendMessage('qunit.moduleStart', obj.name); 46 | }); 47 | 48 | QUnit.moduleDone(function(obj) { 49 | sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total); 50 | }); 51 | 52 | QUnit.begin(function() { 53 | sendMessage('qunit.begin'); 54 | }); 55 | 56 | QUnit.done(function(obj) { 57 | sendMessage('qunit.done', obj.failed, obj.passed, obj.total, obj.runtime); 58 | }); 59 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt){ 4 | // Project configuration. 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | 8 | jshint: { 9 | all: ['jquery.fileapi.js'], 10 | 11 | options: { 12 | curly: true // + "Expected '{' and instead saw 'XXXX'." 13 | , immed: true 14 | , latedef: true 15 | , newcap: true // "Tolerate uncapitalized constructors" 16 | , noarg: true 17 | , sub: true 18 | , undef: true 19 | , unused: true 20 | , boss: true 21 | , eqnull: true 22 | 23 | , node: true 24 | , es5: true 25 | , expr: true // - "Expected an assignment or function call and instead saw an expression." 26 | , supernew: true // - "Missing '()' invoking a constructor." 27 | , laxcomma: true 28 | , laxbreak: true 29 | , smarttabs: true 30 | } 31 | }, 32 | 33 | qunit: { 34 | options: { 35 | files: { 36 | 'one': ['tests/files/image.jpg'] 37 | , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json'] 38 | } 39 | }, 40 | all: ['tests/*.html'] 41 | }, 42 | 43 | version: { 44 | src: ['<%= pkg.name %>.js', 'bower.json'] 45 | }, 46 | 47 | uglify: { 48 | options: { 49 | banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' 50 | }, 51 | dist: { 52 | files: { 53 | '<%= pkg.name %>.min.js': ['<%= pkg.main %>'] 54 | } 55 | } 56 | } 57 | }); 58 | 59 | 60 | // These plugins provide necessary tasks. 61 | grunt.loadNpmTasks('grunt-version'); 62 | grunt.loadNpmTasks('grunt-contrib-jshint'); 63 | grunt.loadNpmTasks('grunt-contrib-uglify'); 64 | 65 | // Load custom QUnit task, based on grunt-contrib-qunit, but support "files" option. 66 | grunt.loadTasks('./tests/grunt-task/'); 67 | 68 | // "npm test" runs these tasks 69 | grunt.registerTask('test', ['jshint', 'qunit']); 70 | 71 | 72 | grunt.registerTask('build', ['version', 'uglify']); 73 | 74 | // Default task. 75 | grunt.registerTask('default', ['test', 'build']); 76 | }; 77 | -------------------------------------------------------------------------------- /jcrop/jquery.Jcrop.min.css: -------------------------------------------------------------------------------- 1 | /* jquery.Jcrop.min.css v0.9.12 (build:20130126) */ 2 | .jcrop-holder{direction:ltr;text-align:left;} 3 | .jcrop-vline,.jcrop-hline{background:#FFF url(Jcrop.gif);font-size:0;position:absolute;} 4 | .jcrop-vline{height:100%;width:1px!important;} 5 | .jcrop-vline.right{right:0;} 6 | .jcrop-hline{height:1px!important;width:100%;} 7 | .jcrop-hline.bottom{bottom:0;} 8 | .jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%;} 9 | .jcrop-handle{background-color:#333;border:1px #EEE solid;font-size:1px;height:7px;width:7px;} 10 | .jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;} 11 | .jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;} 12 | .jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;} 13 | .jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;} 14 | .jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;} 15 | .jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;} 16 | .jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;} 17 | .jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;} 18 | .jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;} 19 | .jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;} 20 | .jcrop-dragbar.ord-n{margin-top:-4px;} 21 | .jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;} 22 | .jcrop-dragbar.ord-e{margin-right:-4px;right:0;} 23 | .jcrop-dragbar.ord-w{margin-left:-4px;} 24 | .jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline{background:#FFF;filter:alpha(opacity=70)!important;opacity:.70!important;} 25 | .jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#FFF;border-radius:3px;} 26 | .jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important;} 27 | .jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#FFF;border-color:#000;border-radius:3px;} 28 | .solid-line .jcrop-vline,.solid-line .jcrop-hline{background:#FFF;} 29 | .jcrop-holder img,img.jcrop-preview{max-width:none;} 30 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jQuery.FileAPI :: Tests 8 | 9 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 |
43 | Choose 44 | 45 |
46 |
47 |
48 | 49 |
50 |
empty.show
51 |
empty.hide
52 | 53 |
emptyQueue.show
54 |
emptyQueue.hide
55 | 56 |
active.show
57 |
active.hide
58 | 59 |
60 |
61 |
<%=$idx%>
62 |
<%=uid%>
63 |
<%=name%>
64 |
<%=type%>
65 |
<%=size%>
66 |
67 |
file.active.show
68 |
file.active.hide
69 |
70 |
71 |
72 | 73 |
74 | 75 |
active.hide
76 |
77 |
78 | 79 |
80 |
81 |
82 |
83 | 84 | 85 | 86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /statics/jquery.modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Modal jQuery plugin 3 | * 4 | * @author Alexander Makarov 5 | * @link https://github.com/samdark/the-modal 6 | * @version 1.0 7 | */ 8 | 9 | /*global jQuery, window, document*/ 10 | ;(function($, window, document, undefined) { 11 | "use strict"; 12 | /*jshint smarttabs:true*/ 13 | 14 | var pluginNamespace = 'the-modal', 15 | // global defaults 16 | defaults = { 17 | overlayClass: 'themodal-overlay', 18 | 19 | closeOnEsc: true, 20 | closeOnOverlayClick: true, 21 | 22 | onClose: null, 23 | onOpen: null 24 | }; 25 | 26 | function lockContainer() { 27 | $('html,body').addClass('lock'); 28 | } 29 | 30 | function unlockContainer() { 31 | $('html,body').removeClass('lock'); 32 | } 33 | 34 | function init(els, options) { 35 | var modalOptions = options, _this; 36 | 37 | if(els.length) { 38 | els.each(function(){ 39 | $(this).data(pluginNamespace+'.options', modalOptions); 40 | }); 41 | } 42 | else { 43 | $.extend(defaults, modalOptions); 44 | } 45 | 46 | return _this = { 47 | open: function(options) { 48 | var el = els.get(0); 49 | var localOptions = $.extend({}, defaults, $(el).data(pluginNamespace+'.options'), options); 50 | 51 | // close modal if opened 52 | if($('.'+localOptions.overlayClass).length) { 53 | $.modal().close(); 54 | } 55 | 56 | lockContainer(); 57 | 58 | var overlay = $('
').addClass(localOptions.overlayClass).prependTo('body'); 59 | overlay.data(pluginNamespace+'.options', options); 60 | 61 | if(el) { 62 | el = $(el).clone(true).appendTo(overlay).show(); 63 | } 64 | 65 | if(localOptions.closeOnEsc) { 66 | $(document).bind('keyup.'+pluginNamespace, function(e){ 67 | if(e.keyCode === 27) { 68 | _this.close(); 69 | } 70 | }); 71 | } 72 | 73 | if(localOptions.closeOnOverlayClick) { 74 | $('.' + localOptions.overlayClass).on('click.' + pluginNamespace, function(e){ 75 | if (e.target === this) { 76 | _this.close(); 77 | } 78 | }); 79 | } 80 | 81 | $(document).bind('touchmove.'+pluginNamespace,function(e){ 82 | if(!$(e).parents('.' + localOptions.overlayClass)) { 83 | e.preventDefault(); 84 | } 85 | }); 86 | 87 | if(localOptions.onOpen) { 88 | localOptions.onOpen(overlay, localOptions); 89 | } 90 | 91 | el.on('resize', function (){ 92 | el.css('marginLeft', ($(window).width() - el.outerWidth())/2); 93 | }).triggerHandler('resize'); 94 | }, 95 | close: function() { 96 | var el = els.get(0); 97 | 98 | var localOptions = $.extend({}, defaults, options); 99 | var overlay = $('.' + localOptions.overlayClass); 100 | $.extend(localOptions, overlay.data(pluginNamespace+'.options')); 101 | 102 | if(localOptions.onClose) { 103 | localOptions.onClose(overlay, localOptions); 104 | } 105 | 106 | overlay.remove(); 107 | unlockContainer(); 108 | 109 | if(localOptions.closeOnEsc) { 110 | $(document).unbind('keyup.'+pluginNamespace); 111 | } 112 | } 113 | }; 114 | } 115 | 116 | $.modal = function(options){ 117 | return init($(), options); 118 | }; 119 | 120 | $.fn.modal = function(options) { 121 | return init(this, options); 122 | }; 123 | 124 | 125 | $.modal.getAvailableWidth = function (width){ 126 | return Math.min($(window).width() - 100, width); 127 | }; 128 | 129 | $.modal.getAvailableHeight = function (height){ 130 | return Math.min($(window).height() - 100, height); 131 | }; 132 | 133 | })(jQuery, window, document); 134 | -------------------------------------------------------------------------------- /tests/grunt-task/phantomjs/grunt-lib-phantomjs-main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-lib-phantomjs 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2012 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | /*global phantom:true*/ 10 | 11 | 'use strict'; 12 | 13 | var fs = require('fs'); 14 | 15 | // The temporary file used for communications. 16 | var tmpfile = phantom.args[0]; 17 | // The page .html file to load. 18 | var url = phantom.args[1]; 19 | // Extra, optionally overridable stuff. 20 | var options = JSON.parse(phantom.args[2] || {}); 21 | 22 | // Default options. 23 | if (!options.timeout) { options.timeout = 5000; } 24 | 25 | // Keep track of the last time a client message was sent. 26 | var last = new Date(); 27 | 28 | // Messages are sent to the parent by appending them to the tempfile. 29 | var sendMessage = function(arg) { 30 | var args = Array.isArray(arg) ? arg : [].slice.call(arguments); 31 | last = new Date(); 32 | fs.write(tmpfile, JSON.stringify(args) + '\n', 'a'); 33 | }; 34 | 35 | // This allows grunt to abort if the PhantomJS version isn't adequate. 36 | sendMessage('private', 'version', phantom.version); 37 | 38 | // Abort if the page doesn't send any messages for a while. 39 | setInterval(function() { 40 | if (new Date() - last > options.timeout) { 41 | sendMessage('fail.timeout'); 42 | phantom.exit(); 43 | } 44 | }, 100); 45 | 46 | // Create a new page. 47 | var page = require('webpage').create(); 48 | 49 | // Inject bridge script into client page. 50 | var injected; 51 | var inject = function() { 52 | if (injected) { return; } 53 | // Inject client-side helper script. 54 | var scripts = Array.isArray(options.inject) ? options.inject : [options.inject]; 55 | sendMessage('inject', options.inject); 56 | scripts.forEach(page.injectJs); 57 | injected = true; 58 | }; 59 | 60 | // Keep track if the client-side helper script already has been injected. 61 | page.onUrlChanged = function(newUrl) { 62 | injected = false; 63 | sendMessage('onUrlChanged', newUrl); 64 | }; 65 | 66 | // The client page must send its messages via alert(jsonstring). 67 | page.onAlert = function(str) { 68 | // The only thing that should ever alert "inject" is the custom event 69 | // handler this script adds to be executed on DOMContentLoaded. 70 | if (str === 'inject') { 71 | inject(); 72 | return; 73 | } 74 | 75 | if (str.indexOf('qunit.testStart') == 2) { 76 | // Setup files 77 | var files = options.files, name; 78 | if( files ){ 79 | for( name in files ){ 80 | page.uploadFile('input[name="'+name+'"]', files[name]); 81 | } 82 | } 83 | } 84 | // Otherwise, parse the specified message string and send it back to grunt. 85 | // Unless there's a parse error. Then, complain. 86 | try { 87 | sendMessage(JSON.parse(str)); 88 | } catch(err) { 89 | sendMessage('error.invalidJSON', str); 90 | } 91 | }; 92 | 93 | // Relay console logging messages. 94 | page.onConsoleMessage = function(message) { 95 | sendMessage('console', message); 96 | }; 97 | 98 | // For debugging. 99 | page.onResourceRequested = function(request) { 100 | sendMessage('onResourceRequested', request.url); 101 | }; 102 | 103 | page.onResourceReceived = function(request) { 104 | if (request.stage === 'end') { 105 | sendMessage('onResourceReceived', request.url); 106 | } 107 | }; 108 | 109 | page.onError = function(msg, trace) { 110 | sendMessage('error.onError', msg, trace); 111 | }; 112 | 113 | // Run before the page is loaded. 114 | page.onInitialized = function() { 115 | sendMessage('onInitialized'); 116 | // Abort if there is no bridge to inject. 117 | if (!options.inject) { return; } 118 | // Tell the client that when DOMContentLoaded fires, it needs to tell this 119 | // script to inject the bridge. This should ensure that the bridge gets 120 | // injected before any other DOMContentLoaded or window.load event handler. 121 | page.evaluate(function() { 122 | /*jshint browser:true, devel:true */ 123 | document.addEventListener('DOMContentLoaded', function() { 124 | alert('inject'); 125 | }, false); 126 | }); 127 | }; 128 | 129 | // Run when the page has finished loading. 130 | page.onLoadFinished = function(status) { 131 | // The window has loaded. 132 | sendMessage('onLoadFinished', status); 133 | if (status !== 'success') { 134 | // File loading failure. 135 | sendMessage('fail.load', url); 136 | phantom.exit(); 137 | } 138 | }; 139 | 140 | // Actually load url. 141 | page.open(url); 142 | -------------------------------------------------------------------------------- /tests/qunit/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.11.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests li .runtime { 115 | float: right; 116 | font-size: smaller; 117 | } 118 | 119 | .qunit-assert-list { 120 | margin-top: 0.5em; 121 | padding: 0.5em; 122 | 123 | background-color: #fff; 124 | 125 | border-radius: 5px; 126 | -moz-border-radius: 5px; 127 | -webkit-border-radius: 5px; 128 | } 129 | 130 | .qunit-collapsed { 131 | display: none; 132 | } 133 | 134 | #qunit-tests table { 135 | border-collapse: collapse; 136 | margin-top: .2em; 137 | } 138 | 139 | #qunit-tests th { 140 | text-align: right; 141 | vertical-align: top; 142 | padding: 0 .5em 0 0; 143 | } 144 | 145 | #qunit-tests td { 146 | vertical-align: top; 147 | } 148 | 149 | #qunit-tests pre { 150 | margin: 0; 151 | white-space: pre-wrap; 152 | word-wrap: break-word; 153 | } 154 | 155 | #qunit-tests del { 156 | background-color: #e0f2be; 157 | color: #374e0c; 158 | text-decoration: none; 159 | } 160 | 161 | #qunit-tests ins { 162 | background-color: #ffcaca; 163 | color: #500; 164 | text-decoration: none; 165 | } 166 | 167 | /*** Test Counts */ 168 | 169 | #qunit-tests b.counts { color: black; } 170 | #qunit-tests b.passed { color: #5E740B; } 171 | #qunit-tests b.failed { color: #710909; } 172 | 173 | #qunit-tests li li { 174 | padding: 5px; 175 | background-color: #fff; 176 | border-bottom: none; 177 | list-style-position: inside; 178 | } 179 | 180 | /*** Passing Styles */ 181 | 182 | #qunit-tests li li.pass { 183 | color: #3c510c; 184 | background-color: #fff; 185 | border-left: 10px solid #C6E746; 186 | } 187 | 188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 189 | #qunit-tests .pass .test-name { color: #366097; } 190 | 191 | #qunit-tests .pass .test-actual, 192 | #qunit-tests .pass .test-expected { color: #999999; } 193 | 194 | #qunit-banner.qunit-pass { background-color: #C6E746; } 195 | 196 | /*** Failing Styles */ 197 | 198 | #qunit-tests li li.fail { 199 | color: #710909; 200 | background-color: #fff; 201 | border-left: 10px solid #EE5757; 202 | white-space: pre; 203 | } 204 | 205 | #qunit-tests > li:last-child { 206 | border-radius: 0 0 5px 5px; 207 | -moz-border-radius: 0 0 5px 5px; 208 | -webkit-border-bottom-right-radius: 5px; 209 | -webkit-border-bottom-left-radius: 5px; 210 | } 211 | 212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 213 | #qunit-tests .fail .test-name, 214 | #qunit-tests .fail .module-name { color: #000000; } 215 | 216 | #qunit-tests .fail .test-actual { color: #EE5757; } 217 | #qunit-tests .fail .test-expected { color: green; } 218 | 219 | #qunit-banner.qunit-fail { background-color: #EE5757; } 220 | 221 | 222 | /** Result */ 223 | 224 | #qunit-testresult { 225 | padding: 0.5em 0.5em 0.5em 2.5em; 226 | 227 | color: #2b81af; 228 | background-color: #D2E0E6; 229 | 230 | border-bottom: 1px solid white; 231 | } 232 | #qunit-testresult .module-name { 233 | font-weight: bold; 234 | } 235 | 236 | /** Fixture */ 237 | 238 | #qunit-fixture { 239 | position: absolute; 240 | top: -10000px; 241 | left: -10000px; 242 | width: 1000px; 243 | height: 1000px; 244 | } 245 | -------------------------------------------------------------------------------- /tests/files/lebowski.json: -------------------------------------------------------------------------------- 1 | { 2 | "adverts":[ 3 | { 4 | "title":"Walter", 5 | "text":"You see what happens, Larry?", 6 | "url":"http://www.imdb.com/title/tt0118715/quotes" 7 | }, 8 | { 9 | "title":"Walter", 10 | "text":"I don't roll on Shabbos!", 11 | "url":"http://www.imdb.com/title/tt0118715/quotes" 12 | }, 13 | { 14 | "title":"Blond Thug", 15 | "text":"Where's the money, Lebowski?", 16 | "url":"http://www.imdb.com/title/tt0118715/quotes" 17 | }, 18 | { 19 | "title":"Nihilist", 20 | "text":"We believe in nothing, Lebowski.", 21 | "url":"http://www.imdb.com/title/tt0118715/quotes" 22 | }, 23 | { 24 | "title":"Walter", 25 | "text":"Is this your homework, Larry?", 26 | "url":"http://www.imdb.com/title/tt0118715/quotes" 27 | }, 28 | { 29 | "title":"Nihilist", 30 | "text":"Ve vant ze money, Lebowski", 31 | "url":"http://www.imdb.com/title/tt0118715/quotes" 32 | } 33 | ], 34 | 35 | "sections":[ 36 | { 37 | "id":1234, 38 | "title":"The Dude", 39 | "rip":0 40 | }, 41 | { 42 | "id":2345, 43 | "title":"Walter Sobchak", 44 | "rip":0 45 | }, 46 | { 47 | "id":3456, 48 | "title":"Donny", 49 | "rip":1 50 | }, 51 | { 52 | "id":4567, 53 | "title":"Maude Lebowski", 54 | "rip":0 55 | }, 56 | { 57 | "id":5678, 58 | "title":"The Big Lebowski", 59 | "rip":0 60 | }, 61 | { 62 | "id":6789, 63 | "title":"Brandt", 64 | "rip":0 65 | }, 66 | { 67 | "id":7890, 68 | "title":"Jesus Quintana", 69 | "rip":0 70 | } 71 | ], 72 | 73 | "total":654329, 74 | "online":[ 75 | { "name":"true" }, 76 | { "name":"false" }, 77 | { "name":"short" }, 78 | { "name":"long" }, 79 | { "name":"apha" }, 80 | { "name":"omega" }, 81 | { "name":"drag" }, 82 | { "name":"drop" }, 83 | { "name":"make" }, 84 | { "name":"clean" }, 85 | { "name":"east" }, 86 | { "name":"west" }, 87 | { "name":"up" }, 88 | { "name":"down" }, 89 | { "name":"sun" }, 90 | { "name":"rain" }, 91 | { "name":"secondary" }, 92 | { "name":"main" } 93 | ], 94 | 95 | "news":[ 96 | { 97 | "time":"03:45", 98 | "id":987, 99 | "title":"The Stranger", 100 | "text":"See, they call Los Angeles the \"City Of Angels\"; but I didn't find it to be that, exactly. But I'll allow it as there are s ome nice folks there. 'Course I ain't never been to London, and I ain't never seen France. And I ain't never seen no queen in her damned undies, so the feller says. But I'll tell you what - after seeing Los Angeles, and this here story I'm about to unfold, well, I guess I seen somethin' every bit as stupefyin' as you'd seen in any of them other places. And in English , too. So I can die with a smile on my face, without feelin' like the good Lord gypped me. Now this here story I'm about to unfold took place in the early '90s - just about the time of our conflict with Sad'm and the I-raqis. I only mention it be cause sometimes there's a man..." 101 | }, 102 | { 103 | "time":"03:48", 104 | "id":876, 105 | "title":"The Stranger", 106 | "text":"...I won't say a hero, 'cause, what's a hero? Sometimes, there's a man. And I'm talkin' about the Dude here - the Dude from Los Angeles. Sometimes, there's a man, well, he's the man for his time and place. He fits right in there. And that's the Dude. The Dude, from Los Angeles. And even if he's a lazy man - and the Dude was most certainly that. Quite possibly the laziest in all of Los Angeles County, which would place him high in the runnin' for laziest worldwide. Sometimes there's a man , sometimes, there's a man. Well, I lost my train of thought here. But... aw, hell. I've done introduced it enough." 107 | }, 108 | { 109 | "time":"03:50", 110 | "id":765, 111 | "title":"Walter Sobchak", 112 | "text":"Donny was a good bowler, and a good man. He was one of us. He was a man who loved the outdoors... and bowling, and as a surfer he explored the beaches of Southern California, from La Jolla to Leo Carrillo and... up to... Pismo. He died, like so many young men of his generation, he died before his time. In your wisdom, Lord, you took him, as you took so many bright flowering young men at Khe Sanh, at Langdok, at Hill 364. These young men gave their lives. And so would Donny. Donny, who loved bowling. And so, Theodore Donald Karabotsos, in accordance with what we think your dying wishes might well have been, we commit your final mortal remains to the bosom of the Pacific Ocean, which you loved so well. Good night, sweet prince." 113 | }, 114 | { 115 | "time":"03:52", 116 | "id":654, 117 | "title":"The Dude", 118 | "text":"God damn you Walter! You fuckin' asshole! Everything's a fuckin' travesty with you, man! And what was all that shit about Vietnam? What the FUCK, has anything got to do with Vietnam? What the fuck are you talking about?" 119 | }, 120 | { 121 | "time":"03:57", 122 | "id":543, 123 | "title":"Jesus Quintana", 124 | "text":"What's this day of rest shit? What's this bullshit? I don't fuckin' care! It don't matter to Jesus. But you're not foolin'me, man. You might fool the fucks in the league office, but you don't fool Jesus. This bush league psyche-out stuff. Laughable, man - ha ha! I would have fucked you in the ass Saturday. I fuck you in the ass next Wednesday instead. Wooo! You gotadate Wednesday, baby!" 125 | }, 126 | { 127 | "time":"03:59", 128 | "id":432, 129 | "title":"Jesus Quintana", 130 | "text":"Let me tell you something, pendejo. You pull any of your crazy shit with us, you flash a piece out on the lanes, I'll take it away from you, stick it up your ass and pull the fucking trigger 'til it goes \"click.\"" 131 | }, 132 | { 133 | "time":"04:01", 134 | "id":321, 135 | "title":"The Dude", 136 | "text":"Let me explain something to you. Um, I am not \"Mr. Lebowski\". You're Mr. Lebowski. I'm the Dude. So that's what you call me. You know, that or, uh, His Dudeness, or uh, Duder, or El Duderino if you're not into the whole brevity thing." 137 | } 138 | ] 139 | } 140 | -------------------------------------------------------------------------------- /tests/grunt-task/phantomjs/grunt-lib-phantomjs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-lib-phantomjs 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2012 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | exports.init = function(grunt) { 12 | 13 | // Nodejs libs. 14 | var path = require('path'); 15 | 16 | // External libs. 17 | var semver = require('semver'); 18 | var Tempfile = require('temporary/lib/file'); 19 | var EventEmitter2 = require('eventemitter2').EventEmitter2; 20 | 21 | // Get path to phantomjs binary 22 | var binPath = require('phantomjs').path; 23 | 24 | // The module to be exported is an event emitter. 25 | var exports = new EventEmitter2({wildcard: true}); 26 | 27 | // Get an asset file, local to the root of the project. 28 | var asset = path.join.bind(null, __dirname, '..'); 29 | 30 | // Call this when everything has finished successfully... or when something 31 | // horrible happens, and you need to clean up and abort. 32 | var halted; 33 | exports.halt = function() { 34 | halted = true; 35 | }; 36 | 37 | // Start PhantomJS process. 38 | exports.spawn = function(pageUrl, options) { 39 | // Create temporary file to be used for grunt-phantom communication. 40 | var tempfile = new Tempfile(); 41 | // Timeout ID. 42 | var id; 43 | // The number of tempfile lines already read. 44 | var n = 0; 45 | // Reset halted flag. 46 | halted = null; 47 | 48 | // All done? Clean up! 49 | var cleanup = function() { 50 | clearTimeout(id); 51 | tempfile.unlink(); 52 | }; 53 | 54 | // Internal methods. 55 | var privates = { 56 | // Abort if PhantomJS version isn't adequate. 57 | version: function(version) { 58 | var current = [version.major, version.minor, version.patch].join('.'); 59 | var required = '>= 1.6.0'; 60 | if (!semver.satisfies(current, required)) { 61 | exports.halt(); 62 | grunt.log.writeln(); 63 | grunt.log.errorlns( 64 | 'In order for this task to work properly, PhantomJS version ' + 65 | required + ' must be installed, but version ' + current + 66 | ' was detected.' 67 | ); 68 | grunt.warn('The correct version of PhantomJS needs to be installed.', 127); 69 | } 70 | } 71 | }; 72 | 73 | // It's simple. As the page running in PhantomJS alerts messages, they 74 | // are written as JSON to a temporary file. This polling loop checks that 75 | // file for new lines, and for each one parses its JSON and emits the 76 | // corresponding event with the specified arguments. 77 | (function loopy() { 78 | // Disable logging temporarily. 79 | grunt.log.muted = true; 80 | // Read the file, splitting lines on \n, and removing a trailing line. 81 | var lines = grunt.file.read(tempfile.path).split('\n').slice(0, -1); 82 | // Re-enable logging. 83 | grunt.log.muted = false; 84 | // Iterate over all lines that haven't already been processed. 85 | var done = lines.slice(n).some(function(line) { 86 | // Get args and method. 87 | var args = JSON.parse(line); 88 | var eventName = args[0]; 89 | // Debugging messages. 90 | grunt.log.debug(JSON.stringify(['phantomjs'].concat(args)).magenta); 91 | if (eventName === 'private') { 92 | // If a private (internal) message is passed, execute the 93 | // corresponding method. 94 | privates[args[1]].apply(null, args.slice(2)); 95 | } else { 96 | // Otherwise, emit the event with its arguments. 97 | exports.emit.apply(exports, args); 98 | } 99 | // If halted, return true. Because the Array#some method was used, 100 | // this not only sets "done" to true, but stops further iteration 101 | // from occurring. 102 | return halted; 103 | }); 104 | 105 | if (done) { 106 | // All done. 107 | cleanup(); 108 | options.done(null); 109 | } else { 110 | // Update n so previously processed lines are ignored. 111 | n = lines.length; 112 | // Check back in a little bit. 113 | id = setTimeout(loopy, 100); 114 | } 115 | }()); 116 | 117 | // Process options. 118 | var failCode = options.failCode || 0; 119 | 120 | // An array of optional PhantomJS --args. 121 | var args = []; 122 | // Additional options for the PhantomJS main.js script. 123 | var opts = {}; 124 | 125 | // Build args array / opts object. 126 | Object.keys(options.options).forEach(function(key) { 127 | if (/^\-\-/.test(key)) { 128 | args.push(key + '=' + options.options[key]); 129 | } else { 130 | opts[key] = options.options[key]; 131 | } 132 | }); 133 | 134 | // Keep -- PhantomJS args first, followed by grunt-specific args. 135 | args.push( 136 | // The main PhantomJS script file. 137 | __dirname + '/grunt-lib-phantomjs-main.js', 138 | // The temporary file used for communications. 139 | tempfile.path, 140 | // URL or path to the page .html test file to run. 141 | pageUrl, 142 | // Additional PhantomJS options. 143 | JSON.stringify(opts) 144 | ); 145 | 146 | grunt.log.debug(JSON.stringify(args)); 147 | 148 | // Actually spawn PhantomJS. 149 | return grunt.util.spawn({ 150 | cmd: binPath, 151 | args: args 152 | }, function(err, result, code) { 153 | if (!err) { return; } 154 | // Something went horribly wrong. 155 | cleanup(); 156 | grunt.verbose.or.writeln(); 157 | grunt.log.write('Running PhantomJS...').error(); 158 | if (code === 127) { 159 | grunt.log.errorlns( 160 | 'In order for this task to work properly, PhantomJS must be installed locally via NPM. ' + 161 | 'If you\'re seeing this message, generally that means the NPM install has failed. ' + 162 | 'Please submit an issue providing as much detail as possible at: ' + 163 | 'https://github.com/gruntjs/grunt-lib-phantomjs/issues' 164 | ); 165 | grunt.warn('PhantomJS not found.', failCode); 166 | } else { 167 | String(result).split('\n').forEach(grunt.log.error, grunt.log); 168 | grunt.warn('PhantomJS exited unexpectedly with exit code ' + code + '.', failCode); 169 | } 170 | options.done(code); 171 | }); 172 | }; 173 | 174 | return exports; 175 | }; 176 | -------------------------------------------------------------------------------- /tests/grunt-task/qunit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-contrib-qunit 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2012 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | 13 | // Nodejs libs. 14 | var path = require('path'); 15 | 16 | // External lib. 17 | var phantomjs = require('./phantomjs/grunt-lib-phantomjs').init(grunt); 18 | 19 | // Keep track of the last-started module, test and status. 20 | var currentModule, currentTest, status; 21 | // Keep track of the last-started test(s). 22 | var unfinished = {}; 23 | 24 | // Get an asset file, local to the root of the project. 25 | var asset = path.join.bind(null, __dirname, '..'); 26 | 27 | // Allow an error message to retain its color when split across multiple lines. 28 | var formatMessage = function(str) { 29 | return String(str).split('\n').map(function(s) { return s.magenta; }).join('\n'); 30 | }; 31 | 32 | // Keep track of failed assertions for pretty-printing. 33 | var failedAssertions = []; 34 | var logFailedAssertions = function() { 35 | var assertion; 36 | // Print each assertion error. 37 | while (assertion = failedAssertions.shift()) { 38 | grunt.verbose.or.error(assertion.testName); 39 | grunt.log.error('Message: ' + formatMessage(assertion.message)); 40 | if (assertion.actual !== assertion.expected) { 41 | grunt.log.error('Actual: ' + formatMessage(assertion.actual)); 42 | grunt.log.error('Expected: ' + formatMessage(assertion.expected)); 43 | } 44 | if (assertion.source) { 45 | grunt.log.error(assertion.source.replace(/ {4}(at)/g, ' $1')); 46 | } 47 | grunt.log.writeln(); 48 | } 49 | }; 50 | 51 | // QUnit hooks. 52 | phantomjs.on('qunit.moduleStart', function(name) { 53 | unfinished[name] = true; 54 | currentModule = name; 55 | }); 56 | 57 | phantomjs.on('qunit.moduleDone', function(name/*, failed, passed, total*/) { 58 | delete unfinished[name]; 59 | }); 60 | 61 | phantomjs.on('qunit.log', function(result, actual, expected, message, source) { 62 | if (!result) { 63 | failedAssertions.push({ 64 | actual: actual, expected: expected, message: message, source: source, 65 | testName: currentTest 66 | }); 67 | } 68 | }); 69 | 70 | phantomjs.on('qunit.testStart', function(name) { 71 | currentTest = (currentModule ? currentModule + ' - ' : '') + name; 72 | grunt.verbose.write(currentTest + '...'); 73 | }); 74 | 75 | phantomjs.on('qunit.testDone', function(name, failed/*, passed, total*/) { 76 | // Log errors if necessary, otherwise success. 77 | if (failed > 0) { 78 | // list assertions 79 | if (grunt.option('verbose')) { 80 | grunt.log.error(); 81 | logFailedAssertions(); 82 | } else { 83 | grunt.log.write('F'.red); 84 | } 85 | } else { 86 | grunt.verbose.ok().or.write('.'); 87 | } 88 | }); 89 | 90 | phantomjs.on('qunit.done', function(failed, passed, total, duration) { 91 | phantomjs.halt(); 92 | status.failed += failed; 93 | status.passed += passed; 94 | status.total += total; 95 | status.duration += duration; 96 | // Print assertion errors here, if verbose mode is disabled. 97 | if (!grunt.option('verbose')) { 98 | if (failed > 0) { 99 | grunt.log.writeln(); 100 | logFailedAssertions(); 101 | } else { 102 | grunt.log.ok(); 103 | } 104 | } 105 | }); 106 | 107 | // Re-broadcast qunit events on grunt.event. 108 | phantomjs.on('qunit.*', function() { 109 | var args = [this.event].concat(grunt.util.toArray(arguments)); 110 | grunt.event.emit.apply(grunt.event, args); 111 | }); 112 | 113 | // Built-in error handlers. 114 | phantomjs.on('fail.load', function(url) { 115 | phantomjs.halt(); 116 | grunt.verbose.write('Running PhantomJS...').or.write('...'); 117 | grunt.log.error(); 118 | grunt.warn('PhantomJS unable to load "' + url + '" URI.'); 119 | }); 120 | 121 | phantomjs.on('fail.timeout', function() { 122 | phantomjs.halt(); 123 | grunt.log.writeln(); 124 | grunt.warn('PhantomJS timed out, possibly due to a missing QUnit start() call.'); 125 | }); 126 | 127 | // Pass-through console.log statements. 128 | phantomjs.on('console', console.log.bind(console)); 129 | 130 | grunt.registerMultiTask('qunit', 'Run QUnit unit tests in a headless PhantomJS instance.', function() { 131 | // Merge task-specific and/or target-specific options with these defaults. 132 | var options = this.options({ 133 | // Default PhantomJS timeout. 134 | timeout: 5000, 135 | // QUnit-PhantomJS bridge file to be injected. 136 | inject: __dirname + '/phantomjs/bridge.js', 137 | // Explicit non-file URLs to test. 138 | urls: [], 139 | }); 140 | 141 | // Combine any specified URLs with src files. 142 | var urls = options.urls.concat(this.filesSrc); 143 | 144 | // This task is asynchronous. 145 | var done = this.async(); 146 | 147 | // Reset status. 148 | status = {failed: 0, passed: 0, total: 0, duration: 0}; 149 | 150 | // Process each filepath in-order. 151 | grunt.util.async.forEachSeries(urls, function(url, next) { 152 | var basename = path.basename(url); 153 | grunt.verbose.subhead('Testing ' + url).or.write('Testing ' + url); 154 | 155 | // Reset current module. 156 | currentModule = null; 157 | 158 | // Launch PhantomJS. 159 | grunt.event.emit('qunit.spawn', url); 160 | phantomjs.spawn(url, { 161 | // Additional PhantomJS options. 162 | options: options, 163 | // Do stuff when done. 164 | done: function(err) { 165 | if (err) { 166 | // If there was an error, abort the series. 167 | done(); 168 | } else { 169 | // Otherwise, process next url. 170 | next(); 171 | } 172 | }, 173 | }); 174 | }, 175 | // All tests have been run. 176 | function() { 177 | // Log results. 178 | if (status.failed > 0) { 179 | grunt.warn(status.failed + '/' + status.total + ' assertions failed (' + 180 | status.duration + 'ms)'); 181 | } else if (status.total === 0) { 182 | grunt.warn('0/0 assertions ran (' + status.duration + 'ms)'); 183 | } else { 184 | grunt.verbose.writeln(); 185 | grunt.log.ok(status.total + ' assertions passed (' + status.duration + 'ms)'); 186 | } 187 | // All done! 188 | done(); 189 | }); 190 | }); 191 | 192 | }; 193 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | module('jQuery.FileAPI'); 2 | 3 | (function (){ 4 | var serverUrl = 'http://rubaxa.org/FileAPI/server/ctrl.php'; 5 | 6 | QUnit.config.autostart = isPhantomJS; 7 | 8 | 9 | FileAPI.event.on(startBtn, 'click', function (){ 10 | QUnit.start(); 11 | }); 12 | 13 | 14 | var $el = $('#uploader'); 15 | var $one = $('#one'); 16 | var $multiple = $('#multiple'); 17 | 18 | 19 | function checkDisabledCtrls(disabled, postfix){ 20 | equal($('#uploadBtn').prop('disabled'), disabled, 'upload - '+postfix); 21 | equal($el.find('[data-fileapi="ctrl.reset"]').prop('disabled'), disabled, 'reset - '+postfix); 22 | 23 | equal($el.find('[data-fileapi="empty.show"]').is(':visible'), disabled, 'empty.show - '+postfix); 24 | equal($el.find('[data-fileapi="empty.hide"]').is(':visible'), !disabled, 'empty.hide - '+postfix); 25 | 26 | equal($el.find('[data-fileapi="emptyQueue.show"]').is(':visible'), disabled, 'emptyQueue.show - '+postfix); 27 | equal($el.find('[data-fileapi="emptyQueue.hide"]').is(':visible'), !disabled, 'emptyQueue.hide - '+postfix); 28 | } 29 | 30 | 31 | function _checkFileListView(files, $files, postfix){ 32 | $files.each(function (i){ 33 | var $file = $(this), file = files[i]; 34 | equal($file.find('.idx').html(), i, '$idx - '+i+' - '+postfix); 35 | equal($file.find('.uid').html(), FileAPI.uid(file), 'uid - '+i+' - '+postfix); 36 | equal($file.find('.name').html(), file.name, 'name - '+i+' - '+postfix); 37 | equal($file.find('.type').html(), file.type, 'type - '+i+' - '+postfix); 38 | equal($file.find('.size').html(), file.size, 'size - '+i+' - '+postfix); 39 | }); 40 | } 41 | 42 | 43 | 44 | asyncTest('files.multiple:false', function (){ 45 | var $el = $('#userpic'); 46 | 47 | $el.fileapi('destroy').fileapi({ 48 | url: serverUrl, 49 | multiple: false, 50 | elements: { 51 | preview: { 52 | el: '.js-preview', 53 | width: 200, 54 | height: 200 55 | } 56 | }, 57 | files: [{ 58 | src: './files/dino.png', 59 | name: 'dino.png' 60 | }] 61 | }); 62 | 63 | setTimeout(function () { 64 | equal($el.find('.js-preview canvas').width(), 200, 'width'); 65 | equal($el.find('.js-preview canvas').height(), 200, 'height'); 66 | start(); 67 | }, 500); 68 | }); 69 | 70 | 71 | test('files.multiple:true', function (){ 72 | $el.fileapi('destroy').fileapi({ 73 | url: serverUrl, 74 | multiple: true, 75 | elements: { 76 | ctrl: { upload: '#uploadBtn' }, 77 | file: { 78 | preview: { 79 | el: '.img', 80 | width: 100, 81 | height: 100 82 | } 83 | } 84 | }, 85 | files: ['./files/image.jpg', { 86 | src: './files/dino.png', 87 | type: 'image/png', 88 | name: 'dino.png', 89 | size: 315 90 | }] 91 | }); 92 | 93 | ok($('#uploadBtn').prop('disabled'), 'upload btn'); 94 | _checkFileListView($el.fileapi('files'), $el.find('[data-fileapi="list"]').children()); 95 | }); 96 | 97 | 98 | test('UI', function (){ 99 | function checkFiles(inpFiles, postfix){ 100 | ok(inpFiles.length > 0, 'files > 0 - '+postfix); 101 | equal(files.length, inpFiles.length, 'selected files - '+postfix); 102 | 103 | var names = [], size = 0; 104 | FileAPI.each(inpFiles, function (file){ 105 | size += file.size; 106 | names.push(file.name); 107 | }); 108 | 109 | equal($el.find('[data-fileapi="name"]').text(), names.join(', '), 'ui.names - '+postfix); 110 | equal($el.find('[data-fileapi="size"]').text(), widget._getFormatedSize(size), 'ui.size - '+postfix); 111 | 112 | var $files = $el.find('[data-fileapi="list"]').children(); 113 | equal($files.length, files.length, 'list - '+postfix); 114 | 115 | _checkFileListView(files, $files, postfix); 116 | } 117 | 118 | 119 | // TEST 120 | stop(); 121 | 122 | var 123 | files 124 | , prepareIdx = 0 125 | , onFileComplete = false 126 | , onTestedEvents = {} 127 | , testingEvents = 'Upload Progress FilePrepare FileUpload FileProgress FileComplete' 128 | ; 129 | 130 | 131 | $el 132 | .fileapi('destroy') 133 | .fileapi({ 134 | url: serverUrl, 135 | data: {}, 136 | onSelect: function (evt, ui){ 137 | files = ui.files 138 | }, 139 | elements: { 140 | ctrl: { upload: '#uploadBtn' } 141 | }, 142 | onUpload: function (){ 143 | ok(!$el.find('[data-fileapi="ctrl.abort"]').prop('disabled'), 'abort after "upload"'); 144 | onTestedEvents.onUpload = true; 145 | 146 | FileAPI.each(testingEvents.split(' '), function (name){ 147 | $el.fileapi('option', 'on'+name, function (){ 148 | onTestedEvents['on'+name] = true; 149 | }); 150 | }, this); 151 | }, 152 | onComplete: function (evt, uiEvt){ 153 | FileAPI.each(testingEvents.split(' '), function (name){ 154 | ok(onTestedEvents['on'+name], 'on'+name); 155 | ok(onTestedEvents[name.toLowerCase()], name); 156 | }); 157 | 158 | ok(onFileComplete, 'filecomplete listener'); 159 | 160 | setTimeout(function (){ 161 | start(); 162 | ok($('#uploadBtn').prop('disabled'), 'upload after "complete"'); 163 | ok($el.find('[data-fileapi="ctrl.reset"]').prop('disabled'), 'reset after "complete"'); 164 | ok($el.find('[data-fileapi="ctrl.abort"]').prop('disabled'), 'abort after "complete"'); 165 | }, 500); 166 | } 167 | }) 168 | .on(testingEvents.toLowerCase(), function (evt){ 169 | onTestedEvents[evt.type] = true; 170 | }) 171 | .on('fileprepare', function (evt, ui){ 172 | prepareIdx++; 173 | ui.file.foo = prepareIdx; 174 | ui.options.data.foo = prepareIdx; 175 | }) 176 | .on('filecomplete', function (evt, ui){ 177 | onFileComplete = true; 178 | equal(ui.xhr.options.data.foo, prepareIdx, "xhr.options.data.foo: "+prepareIdx); 179 | equal(ui.result.data._REQUEST.foo, prepareIdx, "result.foo: "+prepareIdx); 180 | }) 181 | ; 182 | 183 | 184 | 185 | var widget = $el.fileapi('widget'); 186 | equal(widget.toString(), '[jQuery.FileAPI object]', 'widget'); 187 | 188 | // controls before "change" 189 | checkDisabledCtrls(true, 'before "change"'); 190 | ok($el.find('[data-fileapi="ctrl.abort"]').prop('disabled'), 'abort - before "change"'); 191 | 192 | // simulate "change" 193 | $one.appendTo($el); 194 | $el.find('input').trigger('change'); 195 | 196 | // controls after "change" 197 | checkDisabledCtrls(false, 'after "change"'); 198 | 199 | // one 200 | checkFiles($one.prop('files'), 'one'); 201 | 202 | // click on reset 203 | $el.find('[data-fileapi="ctrl.reset"]').trigger('click'); 204 | 205 | // controls after "reset" 206 | checkDisabledCtrls(true, 'after "change"'); 207 | ok($el.find('[data-fileapi="ctrl.abort"]').prop('disabled'), 'abort - after "reset"'); 208 | 209 | equal($el.find('[data-fileapi="name"]').text(), '', 'fileName (after reset)'); 210 | equal($el.find('[data-fileapi="size"]').text(), '', 'fileSize (after reset)'); 211 | 212 | // simulate "change" 213 | $one.detach(); 214 | $multiple.appendTo($el); 215 | $el.find('input').trigger('change'); 216 | 217 | // controls after "change" 218 | checkDisabledCtrls(false, 'after "change"'); 219 | 220 | // multiple 221 | checkFiles($multiple.prop('files'), 'multiple'); 222 | 223 | 224 | // Start upload 225 | $('#uploadBtn').trigger('click'); 226 | ok($el.find('[data-fileapi="active.show"]').is(':visible'), 'active.show after "start"'); 227 | ok(!$el.find('[data-fileapi="active.hide"]').is(':visible'), 'active.show after "start"'); 228 | }); 229 | })(); 230 | -------------------------------------------------------------------------------- /FileAPI/FileAPI.exif.js: -------------------------------------------------------------------------------- 1 | (function (){ 2 | /**! 3 | * Binary Ajax 0.1.10 4 | * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ 5 | * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt] 6 | * 7 | * 8 | * Javascript EXIF Reader 0.1.4 9 | * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/ 10 | * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt] 11 | */ 12 | 13 | 14 | var BinaryFile=function(j,k,l){var h=j,i=k||0,b=0;this.getRawData=function(){return h};"string"==typeof j&&(b=l||h.length,this.getByteAt=function(a){return h.charCodeAt(a+i)&255},this.getBytesAt=function(a,b){for(var c=[],f=0;fc&&(c+=65536);return c};this.getSShortAt=function(a,b){var c=this.getShortAt(a,b);return 32767c&&(c+=4294967296);return c};this.getSLongAt=function(a,b){var c=this.getLongAt(a,b);return 2147483647 128*1024) { 45 | try { 46 | var size = Math.min(blob.size, 128 * 1024); 47 | blob = (blob.slice || blob.mozSlice || blob.webkitSlice).call(blob, 0, size); 48 | } catch (e) { 49 | FileAPI.log("exception "+ e); 50 | } 51 | } 52 | 53 | FileAPI.readAsBinaryString(blob, function (evt){ 54 | if( evt.type == 'load' ){ 55 | var binaryString = evt.result; 56 | var oFile = new BinaryFile(binaryString, 0, blob.size); 57 | var exif = EXIF.readFromBinaryFile(oFile); 58 | 59 | defer.resolve(false, { 'exif': exif || {} }); 60 | } 61 | else if( evt.type == 'error' ){ 62 | defer.resolve('read_as_binary_string_exif'); 63 | } 64 | }); 65 | } 66 | 67 | file.__exif.then(callback); 68 | }); 69 | })(); 70 | -------------------------------------------------------------------------------- /jcrop/jquery.Jcrop.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.Jcrop.min.js v0.9.12 (build:20130202) 3 | * jQuery Image Cropping Plugin - released under MIT License 4 | * Copyright (c) 2008-2013 Tapmodo Interactive LLC 5 | * https://github.com/tapmodo/Jcrop 6 | */ 7 | (function(a){a.Jcrop=function(b,c){function i(a){return Math.round(a)+"px"}function j(a){return d.baseClass+"-"+a}function k(){return a.fx.step.hasOwnProperty("backgroundColor")}function l(b){var c=a(b).offset();return[c.left,c.top]}function m(a){return[a.pageX-e[0],a.pageY-e[1]]}function n(b){typeof b!="object"&&(b={}),d=a.extend(d,b),a.each(["onChange","onSelect","onRelease","onDblClick"],function(a,b){typeof d[b]!="function"&&(d[b]=function(){})})}function o(a,b,c){e=l(D),bc.setCursor(a==="move"?a:a+"-resize");if(a==="move")return bc.activateHandlers(q(b),v,c);var d=_.getFixed(),f=r(a),g=_.getCorner(r(f));_.setPressed(_.getCorner(f)),_.setCurrent(g),bc.activateHandlers(p(a,d),v,c)}function p(a,b){return function(c){if(!d.aspectRatio)switch(a){case"e":c[1]=b.y2;break;case"w":c[1]=b.y2;break;case"n":c[0]=b.x2;break;case"s":c[0]=b.x2}else switch(a){case"e":c[1]=b.y+1;break;case"w":c[1]=b.y+1;break;case"n":c[0]=b.x+1;break;case"s":c[0]=b.x+1}_.setCurrent(c),bb.update()}}function q(a){var b=a;return bd.watchKeys 8 | (),function(a){_.moveOffset([a[0]-b[0],a[1]-b[1]]),b=a,bb.update()}}function r(a){switch(a){case"n":return"sw";case"s":return"nw";case"e":return"nw";case"w":return"ne";case"ne":return"sw";case"nw":return"se";case"se":return"nw";case"sw":return"ne"}}function s(a){return function(b){return d.disabled?!1:a==="move"&&!d.allowMove?!1:(e=l(D),W=!0,o(a,m(b)),b.stopPropagation(),b.preventDefault(),!1)}}function t(a,b,c){var d=a.width(),e=a.height();d>b&&b>0&&(d=b,e=b/a.width()*a.height()),e>c&&c>0&&(e=c,d=c/a.height()*a.width()),T=a.width()/d,U=a.height()/e,a.width(d).height(e)}function u(a){return{x:a.x*T,y:a.y*U,x2:a.x2*T,y2:a.y2*U,w:a.w*T,h:a.h*U}}function v(a){var b=_.getFixed();b.w>d.minSelect[0]&&b.h>d.minSelect[1]?(bb.enableHandles(),bb.done()):bb.release(),bc.setCursor(d.allowSelect?"crosshair":"default")}function w(a){if(d.disabled)return!1;if(!d.allowSelect)return!1;W=!0,e=l(D),bb.disableHandles(),bc.setCursor("crosshair");var b=m(a);return _.setPressed(b),bb.update(),bc.activateHandlers(x,v,a.type.substring 9 | (0,5)==="touch"),bd.watchKeys(),a.stopPropagation(),a.preventDefault(),!1}function x(a){_.setCurrent(a),bb.update()}function y(){var b=a("
").addClass(j("tracker"));return g&&b.css({opacity:0,backgroundColor:"white"}),b}function be(a){G.removeClass().addClass(j("holder")).addClass(a)}function bf(a,b){function t(){window.setTimeout(u,l)}var c=a[0]/T,e=a[1]/U,f=a[2]/T,g=a[3]/U;if(X)return;var h=_.flipCoords(c,e,f,g),i=_.getFixed(),j=[i.x,i.y,i.x2,i.y2],k=j,l=d.animationDelay,m=h[0]-j[0],n=h[1]-j[1],o=h[2]-j[2],p=h[3]-j[3],q=0,r=d.swingSpeed;c=k[0],e=k[1],f=k[2],g=k[3],bb.animMode(!0);var s,u=function(){return function(){q+=(100-q)/r,k[0]=Math.round(c+q/100*m),k[1]=Math.round(e+q/100*n),k[2]=Math.round(f+q/100*o),k[3]=Math.round(g+q/100*p),q>=99.8&&(q=100),q<100?(bh(k),t()):(bb.done(),bb.animMode(!1),typeof b=="function"&&b.call(bs))}}();t()}function bg(a){bh([a[0]/T,a[1]/U,a[2]/T,a[3]/U]),d.onSelect.call(bs,u(_.getFixed())),bb.enableHandles()}function bh(a){_.setPressed([a[0],a[1]]),_.setCurrent([a[2], 10 | a[3]]),bb.update()}function bi(){return u(_.getFixed())}function bj(){return _.getFixed()}function bk(a){n(a),br()}function bl(){d.disabled=!0,bb.disableHandles(),bb.setCursor("default"),bc.setCursor("default")}function bm(){d.disabled=!1,br()}function bn(){bb.done(),bc.activateHandlers(null,null)}function bo(){G.remove(),A.show(),A.css("visibility","visible"),a(b).removeData("Jcrop")}function bp(a,b){bb.release(),bl();var c=new Image;c.onload=function(){var e=c.width,f=c.height,g=d.boxWidth,h=d.boxHeight;D.width(e).height(f),D.attr("src",a),H.attr("src",a),t(D,g,h),E=D.width(),F=D.height(),H.width(E).height(F),M.width(E+L*2).height(F+L*2),G.width(E).height(F),ba.resize(E,F),bm(),typeof b=="function"&&b.call(bs)},c.src=a}function bq(a,b,c){var e=b||d.bgColor;d.bgFade&&k()&&d.fadeTime&&!c?a.animate({backgroundColor:e},{queue:!1,duration:d.fadeTime}):a.css("backgroundColor",e)}function br(a){d.allowResize?a?bb.enableOnly():bb.enableHandles():bb.disableHandles(),bc.setCursor(d.allowSelect?"crosshair":"default"),bb 11 | .setCursor(d.allowMove?"move":"default"),d.hasOwnProperty("trueSize")&&(T=d.trueSize[0]/E,U=d.trueSize[1]/F),d.hasOwnProperty("setSelect")&&(bg(d.setSelect),bb.done(),delete d.setSelect),ba.refresh(),d.bgColor!=N&&(bq(d.shade?ba.getShades():G,d.shade?d.shadeColor||d.bgColor:d.bgColor),N=d.bgColor),O!=d.bgOpacity&&(O=d.bgOpacity,d.shade?ba.refresh():bb.setBgOpacity(O)),P=d.maxSize[0]||0,Q=d.maxSize[1]||0,R=d.minSize[0]||0,S=d.minSize[1]||0,d.hasOwnProperty("outerImage")&&(D.attr("src",d.outerImage),delete d.outerImage),bb.refresh()}var d=a.extend({},a.Jcrop.defaults),e,f=navigator.userAgent.toLowerCase(),g=/msie/.test(f),h=/msie [1-6]\./.test(f);typeof b!="object"&&(b=a(b)[0]),typeof c!="object"&&(c={}),n(c);var z={border:"none",visibility:"visible",margin:0,padding:0,position:"absolute",top:0,left:0},A=a(b),B=!0;if(b.tagName=="IMG"){if(A[0].width!=0&&A[0].height!=0)A.width(A[0].width),A.height(A[0].height);else{var C=new Image;C.src=A[0].src,A.width(C.width),A.height(C.height)}var D=A.clone().removeAttr("id"). 12 | css(z).show();D.width(A.width()),D.height(A.height()),A.after(D).hide()}else D=A.css(z).show(),B=!1,d.shade===null&&(d.shade=!0);t(D,d.boxWidth,d.boxHeight);var E=D.width(),F=D.height(),G=a("
").width(E).height(F).addClass(j("holder")).css({position:"relative",backgroundColor:d.bgColor}).insertAfter(A).append(D);d.addClass&&G.addClass(d.addClass);var H=a("
"),I=a("
").width("100%").height("100%").css({zIndex:310,position:"absolute",overflow:"hidden"}),J=a("
").width("100%").height("100%").css("zIndex",320),K=a("
").css({position:"absolute",zIndex:600}).dblclick(function(){var a=_.getFixed();d.onDblClick.call(bs,a)}).insertBefore(D).append(I,J);B&&(H=a("").attr("src",D.attr("src")).css(z).width(E).height(F),I.append(H)),h&&K.css({overflowY:"hidden"});var L=d.boundary,M=y().width(E+L*2).height(F+L*2).css({position:"absolute",top:i(-L),left:i(-L),zIndex:290}).mousedown(w),N=d.bgColor,O=d.bgOpacity,P,Q,R,S,T,U,V=!0,W,X,Y;e=l(D);var Z=function(){function a(){var a={},b=["touchstart" 13 | ,"touchmove","touchend"],c=document.createElement("div"),d;try{for(d=0;da+f&&(f-=f+a),0>b+g&&(g-=g+b),FE&&(r=E,u=Math.abs((r-a)/f),s=k<0?b-u:u+b)):(r=c,u=l/f,s=k<0?b-u:b+u,s<0?(s=0,t=Math.abs((s-b)*f),r=j<0?a-t:t+a):s>F&&(s=F,t=Math.abs(s-b)*f,r=j<0?a-t:t+a)),r>a?(r-ah&&(r=a+h),s>b?s=b+(r-a)/f:s=b-(r-a)/f):rh&&(r=a-h),s>b?s=b+(a-r)/f:s=b-(a-r)/f),r<0?(a-=r,r=0):r>E&&(a-=r-E,r=E),s<0?(b-=s,s=0):s>F&&(b-=s-F,s=F),q(o(a,b,r,s))}function n(a){return a[0]<0&&(a[0]=0),a[1]<0&&(a[1]=0),a[0]>E&&(a[0]=E),a[1]>F&&(a[1]=F),[Math.round(a[0]),Math.round(a[1])]}function o(a,b,c,d){var e=a,f=c,g=b,h=d;return cP&&(c=d>0?a+P:a-P),Q&&Math.abs 15 | (f)>Q&&(e=f>0?b+Q:b-Q),S/U&&Math.abs(f)0?b+S/U:b-S/U),R/T&&Math.abs(d)0?a+R/T:a-R/T),a<0&&(c-=a,a-=a),b<0&&(e-=b,b-=b),c<0&&(a-=c,c-=c),e<0&&(b-=e,e-=e),c>E&&(g=c-E,a-=g,c-=g),e>F&&(g=e-F,b-=g,e-=g),a>E&&(g=a-F,e-=g,b-=g),b>F&&(g=b-F,e-=g,b-=g),q(o(a,b,c,e))}function q(a){return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]}}var a=0,b=0,c=0,e=0,f,g;return{flipCoords:o,setPressed:h,setCurrent:i,getOffset:j,moveOffset:k,getCorner:l,getFixed:m}}(),ba=function(){function f(a,b){e.left.css({height:i(b)}),e.right.css({height:i(b)})}function g(){return h(_.getFixed())}function h(a){e.top.css({left:i(a.x),width:i(a.w),height:i(a.y)}),e.bottom.css({top:i(a.y2),left:i(a.x),width:i(a.w),height:i(F-a.y2)}),e.right.css({left:i(a.x2),width:i(E-a.x2)}),e.left.css({width:i(a.x)})}function j(){return a("
").css({position:"absolute",backgroundColor:d.shadeColor||d.bgColor}).appendTo(c)}function k(){b||(b=!0,c.insertBefore(D),g(),bb.setBgOpacity(1,0,1),H.hide(),l(d.shadeColor||d.bgColor,1),bb. 16 | isAwake()?n(d.bgOpacity,1):n(1,1))}function l(a,b){bq(p(),a,b)}function m(){b&&(c.remove(),H.show(),b=!1,bb.isAwake()?bb.setBgOpacity(d.bgOpacity,1,1):(bb.setBgOpacity(1,1,1),bb.disableHandles()),bq(G,0,1))}function n(a,e){b&&(d.bgFade&&!e?c.animate({opacity:1-a},{queue:!1,duration:d.fadeTime}):c.css({opacity:1-a}))}function o(){d.shade?k():m(),bb.isAwake()&&n(d.bgOpacity)}function p(){return c.children()}var b=!1,c=a("
").css({position:"absolute",zIndex:240,opacity:0}),e={top:j(),left:j().height(F),right:j().height(F),bottom:j()};return{update:g,updateRaw:h,getShades:p,setBgColor:l,enable:k,disable:m,resize:f,refresh:o,opacity:n}}(),bb=function(){function k(b){var c=a("
").css({position:"absolute",opacity:d.borderOpacity}).addClass(j(b));return I.append(c),c}function l(b,c){var d=a("
").mousedown(s(b)).css({cursor:b+"-resize",position:"absolute",zIndex:c}).addClass("ord-"+b);return Z.support&&d.bind("touchstart.jcrop",Z.createDragger(b)),J.append(d),d}function m(a){var b=d.handleSize,e=l(a,c++ 17 | ).css({opacity:d.handleOpacity}).addClass(j("handle"));return b&&e.width(b).height(b),e}function n(a){return l(a,c++).addClass("jcrop-dragbar")}function o(a){var b;for(b=0;b').css({position:"fixed",left:"-120px",width:"12px"}).addClass("jcrop-keymgr"),c=a("
").css({position:"absolute",overflow:"hidden"}).append(b);return d.keySupport&&(b.keydown(i).blur(f),h||!d.fixedSupport?(b.css({position:"absolute",left:"-20px"}),c.append(b).insertBefore(D)):b.insertBefore(D)),{watchKeys:e}}();Z.support&&M.bind("touchstart.jcrop",Z.newSelection),J.hide(),br(!0);var bs={setImage:bp,animateTo:bf,setSelect:bg,setOptions:bk,tellSelect:bi,tellScaled:bj,setClass:be,disable:bl,enable:bm,cancel:bn,release:bb.release,destroy:bo,focus:bd.watchKeys,getBounds:function(){return[E*T,F*U]},getWidgetSize:function(){return[E,F]},getScaleFactor:function(){return[T,U]},getOptions:function(){return d},ui:{holder:G,selection:K}};return g&&G.bind("selectstart",function(){return!1}),A.data("Jcrop",bs),bs},a.fn.Jcrop=function(b,c){var d;return this.each(function(){if(a(this).data("Jcrop")){if( 21 | b==="api")return a(this).data("Jcrop");a(this).data("Jcrop").setOptions(b)}else this.tagName=="IMG"?a.Jcrop.Loader(this,function(){a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d)}):(a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d))}),this},a.Jcrop.Loader=function(b,c,d){function g(){f.complete?(e.unbind(".jcloader"),a.isFunction(c)&&c.call(f)):window.setTimeout(g,50)}var e=a(b),f=e[0];e.bind("load.jcloader",g).bind("error.jcloader",function(b){e.unbind(".jcloader"),a.isFunction(d)&&d.call(f)}),f.complete&&a.isFunction(c)&&(e.unbind(".jcloader"),c.call(f))},a.Jcrop.defaults={allowSelect:!0,allowMove:!0,allowResize:!0,trackDocument:!0,baseClass:"jcrop",addClass:null,bgColor:"black",bgOpacity:.6,bgFade:!1,borderOpacity:.4,handleOpacity:.5,handleSize:null,aspectRatio:0,keySupport:!0,createHandles:["n","s","e","w","nw","ne","se","sw"],createDragbars:["n","s","e","w"],createBorders:["n","s","e","w"],drawBorders:!0,dragEdges 22 | :!0,fixedSupport:!0,touchSupport:null,shade:null,boxWidth:0,boxHeight:0,boundary:2,fadeTime:400,animationDelay:20,swingSpeed:3,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){},onDblClick:function(){},onRelease:function(){}}})(jQuery); -------------------------------------------------------------------------------- /statics/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | img { 7 | border: 0; 8 | vertical-align: middle; 9 | } 10 | 11 | body { 12 | font-family: "Helvetica Neue"; 13 | background-color: #202227; 14 | background-image: url('body.png'); 15 | } 16 | .body__top { 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 321px; 21 | z-index: -1; 22 | position: absolute; 23 | background-image: url('body__top.png'); 24 | } 25 | 26 | 27 | 28 | h2 { 29 | margin: 0 0 10px; 30 | font-size: 40px; 31 | font-family: 'Roboto', sans-serif; 32 | font-weight: 300; 33 | text-shadow: 0 1px 1px #fff; 34 | } 35 | 36 | 37 | .view-on-github { 38 | top: 160px; 39 | right: 5%; 40 | width: 114px; 41 | height: 80px; 42 | display: block; 43 | position: absolute; 44 | background: url('view-on-github.png') no-repeat; 45 | } 46 | 47 | 48 | .logo { 49 | display: inline-block; 50 | background-position: no-repeat; 51 | } 52 | .logo_small { 53 | width: 43px; 54 | height: 15px; 55 | background-image: url('logo_small.png'); 56 | } 57 | 58 | 59 | .splash { 60 | width: 650px; 61 | height: 370px; 62 | margin: 30px auto 0; 63 | box-shadow: 0 1px 1px rgba(255,255,255,.2); 64 | border-radius: 4px; 65 | background-color: #000; 66 | position: relative; 67 | } 68 | .splash__inner { 69 | top: 3px; 70 | left: 3px; 71 | right: 3px; 72 | bottom: 3px; 73 | overflow: hidden; 74 | position: absolute; 75 | } 76 | 77 | .splash__blind { 78 | width: 100%; 79 | height: 100%; 80 | cursor: pointer; 81 | position: absolute; 82 | border-radius: 4px; 83 | background-image: url('splash__blind.png'); 84 | } 85 | 86 | .splash__logo { 87 | top: 50%; 88 | left: 50%; 89 | width: 284px; 90 | height: 201px; 91 | margin: -120px 0 0 -142px; 92 | position: absolute; 93 | background: url('splash__logo.png') no-repeat; 94 | } 95 | 96 | .splash__click-here { 97 | right: 60px; 98 | bottom: 40px; 99 | width: 66px; 100 | height: 25px; 101 | position: absolute; 102 | background: url('click-here.png') no-repeat; 103 | cursor: pointer; 104 | } 105 | 106 | 107 | .content { 108 | width: 80%; 109 | max-width: 1200px; 110 | margin: 60px auto; 111 | border: 1px solid #fff; 112 | position: relative; 113 | min-height: 400px; 114 | background-color: #F4F7F1; 115 | background-image: url('content.png'); 116 | } 117 | .content__head { 118 | top: -21px; 119 | left: 36px; 120 | right: 36px; 121 | height: 20px; 122 | position: absolute; 123 | text-align: center; 124 | background-image: url('content__head.png'); 125 | } 126 | 127 | .content__head:before, .content__head:after { 128 | top: 0; 129 | left: 0; 130 | width: 100%; 131 | bottom: 0; 132 | z-index: -1; 133 | content: ''; 134 | position: absolute; 135 | background-image: url('content__head.png'); 136 | } 137 | .content__head:before { 138 | left: 20px; 139 | -webkit-transform: skew(60deg); 140 | -moz-transform: skew(60deg); 141 | transform: skew(60deg); 142 | } 143 | .content__head:after { 144 | left: auto; 145 | right: 20px; 146 | -webkit-transform: skew(-60deg); 147 | -moz-transform: skew(-60deg); 148 | transform: skew(-60deg); 149 | } 150 | 151 | 152 | 153 | 154 | 155 | .example { 156 | margin: 25px; 157 | } 158 | .example__left { 159 | float: left; 160 | width: 290px; 161 | text-align: center; 162 | } 163 | 164 | .example__right { 165 | margin-left: 320px; 166 | } 167 | 168 | .example__code { 169 | padding: 1px 20px 0 20px; 170 | overflow: auto; 171 | max-height: 400px; 172 | border-right: 4px solid #DDE0DA; 173 | border-bottom: 4px solid #DDE0DA; 174 | background-color: #fff; 175 | } 176 | 177 | .example h2 { 178 | overflow: hidden; 179 | } 180 | .example h2 span { 181 | float: left; 182 | display: block; 183 | } 184 | 185 | .example__tabs { 186 | display: block; 187 | font-size: 14px; 188 | vertical-align: top; 189 | } 190 | .example__tabs a { 191 | color: #36c; 192 | cursor: pointer; 193 | margin: 0 5px 2px; 194 | border-bottom: 1px dotted #36c; 195 | } 196 | .example__tabs a.active { 197 | color: #333; 198 | cursor: default; 199 | margin: 0; 200 | padding: 0 5px 2px; 201 | border-bottom: 0; 202 | background-color: rgba(0,0,0,.1); 203 | *background-color: #eee; 204 | } 205 | 206 | 207 | 208 | .btn { 209 | cursor: pointer; 210 | display: inline-block; 211 | *zoom: 1; 212 | *display: inline; 213 | position: relative; 214 | overflow: hidden; 215 | font-size: 20px; 216 | font-family: Arial, Helvetica, sans-serif; 217 | border-radius: 4px; 218 | vertical-align: middle; 219 | } 220 | .btn-success { 221 | border: 1px solid rgba(0,0,0,.2); 222 | padding: 8px 20px; 223 | background-color: #FFDC73; 224 | background: -moz-linear-gradient(top,#FFE599 0%,#FFDC73); 225 | background: -webkit-gradient(linear, left top, left bottom,from(#FFE599),to(#FFDC73)); 226 | box-shadow: 0 1px 1px rgba(255,255,255,.5); 227 | } 228 | 229 | .btn-success.btn-small[aria-disabled], 230 | .btn-primary[disabled], 231 | .btn-warning[disabled] { 232 | opacity: .7; 233 | } 234 | 235 | 236 | .btn-primary { 237 | color: #333; 238 | border: 1px solid rgba(0,0,0,.2); 239 | padding: 8px 20px; 240 | background-color: #62c462; 241 | background: -moz-linear-gradient(top,#62c462 0%,#51a351); 242 | background: -webkit-gradient(linear, left top, left bottom,from(#62c462),to(#51a351)); 243 | box-shadow: 0 1px 1px rgba(255,255,255,.5); 244 | } 245 | 246 | .btn-warning { 247 | color: #333; 248 | border: 1px solid rgba(0,0,0,.2); 249 | padding: 8px 20px; 250 | background-color: #bd362f; 251 | background: -moz-linear-gradient(top,#ee5f5b 0%,#bd362f); 252 | background: -webkit-gradient(linear, left top, left bottom,from(#ee5f5b),to(#bd362f)); 253 | box-shadow: 0 1px 1px rgba(255,255,255,.5); 254 | } 255 | 256 | .btn-small { 257 | padding: 5px 10px; 258 | font-size: 16px; 259 | } 260 | 261 | .btn input { 262 | top: -10px; 263 | right: -40px; 264 | z-index: 2; 265 | position: absolute; 266 | cursor: pointer; 267 | opacity: 0; 268 | filter: alpha(opacity=0); 269 | font-size: 50px; 270 | cursor: pointer; 271 | } 272 | 273 | .btn-txt { 274 | position: relative; 275 | } 276 | 277 | .btn .progress { 278 | top: 0; 279 | left: 0; 280 | right: 0; 281 | bottom: 0; 282 | opacity: .5; 283 | position: absolute; 284 | } 285 | .progress .bar { 286 | width: 0; 287 | top: 0; 288 | left: 0; 289 | bottom: 0; 290 | position: absolute; 291 | background-color: #f60; 292 | } 293 | 294 | 295 | .progress-small { 296 | height: 5px; 297 | padding: 1px; 298 | box-shadow: 0 0 1px 1px rgba(255, 255, 255, 0.3); 299 | border-radius: 10px; 300 | background-color: rgba(0,0,0,.5); 301 | } 302 | .progress-small .bar { 303 | width: 0; 304 | height: 100%; 305 | position: static; 306 | border-radius: 10px; 307 | background-color: orange; 308 | } 309 | 310 | 311 | 312 | 313 | .webcam, 314 | .userpic { 315 | width: 200px; 316 | height: 200px; 317 | border: 2px solid #aaa; 318 | display: inline-block; 319 | position: relative; 320 | background: url('uploader/userpic.gif') no-repeat; 321 | background-size: cover; 322 | } 323 | .webcam .btn, 324 | .userpic .btn { 325 | margin-top: 150px; 326 | } 327 | 328 | .webcam__preview, 329 | .userpic__preview { 330 | position: absolute; 331 | } 332 | 333 | .webcam { 334 | background-size: auto; 335 | background-image: url('uploader/webcam.png'); 336 | background-position: center 10px; 337 | } 338 | 339 | 340 | .b-upload { 341 | white-space: nowrap; 342 | } 343 | .b-upload__name, 344 | .b-upload__size { 345 | display: inline-block; 346 | position: relative; 347 | overflow: hidden; 348 | max-width: 150px; 349 | vertical-align: middle; 350 | } 351 | .b-upload__size { 352 | color: #666; 353 | font-size: 12px; 354 | } 355 | 356 | .b-upload .js-files:after { 357 | clear: both; 358 | content: ''; 359 | display: block; 360 | } 361 | 362 | .b-upload__dnd { 363 | padding: 30px; 364 | border-radius: 5px; 365 | margin-bottom: 10px; 366 | background-color: rgba(0,0,0,.1); 367 | } 368 | .b-upload__dnd_hover { 369 | color: #fff; 370 | background-color: orange; 371 | } 372 | 373 | .b-upload__hint { 374 | padding: 5px 8px; 375 | font-size: 12px; 376 | white-space: normal; 377 | border-radius: 3px; 378 | background-color: rgba(0,0,0,.08); 379 | } 380 | 381 | 382 | 383 | 384 | 385 | .b-thumb { 386 | float: left; 387 | margin: 3px; 388 | padding: 5px; 389 | overflow: hidden; 390 | position: relative; 391 | box-shadow: 0 0 2px rgba(0,0,0,.4); 392 | background-color: #fff; 393 | } 394 | .b-thumb__del { 395 | top: -6px; 396 | right: -1px; 397 | color: #FF0000; 398 | cursor: pointer; 399 | opacity: 0; 400 | z-index: 999; 401 | position: absolute; 402 | font-size: 20px; 403 | -webkit-transition: opacity .1s ease-in; 404 | -moz-transition: opacity .1s ease-in; 405 | transition: opacity .1s ease-in; 406 | } 407 | .b-thumb:hover .b-thumb__del { 408 | opacity: 1; 409 | } 410 | 411 | .b-thumb__rotate { 412 | top: 40%; 413 | left: 50%; 414 | width: 32px; 415 | height: 32px; 416 | cursor: pointer; 417 | margin: -16px 0 0 -16px; 418 | position: absolute; 419 | background: url('uploader/rotate.png'); 420 | } 421 | 422 | .b-thumb__preview { 423 | width: 80px; 424 | height: 80px; 425 | -webkit-transition: -webkit-transform .2s ease-in; 426 | -moz-transition: -moz-transform .2s ease-in; 427 | transition: transform .2s ease-in; 428 | } 429 | .b-thumb__preview__pic { 430 | width: 100%; 431 | height: 100%; 432 | background: url('uploader/file-icon.png') 50% 50% no-repeat; 433 | } 434 | 435 | .b-thumb__name { 436 | width: 80px; 437 | overflow: hidden; 438 | font-size: 12px; 439 | } 440 | 441 | .b-thumb__progress { 442 | top: 75px; 443 | left: 10px; 444 | right: 10px; 445 | position: absolute; 446 | } 447 | 448 | 449 | 450 | .btn { 451 | cursor: pointer; 452 | *zoom: 1; 453 | *display: inline; 454 | display: inline-block; 455 | position: relative; 456 | overflow: hidden; 457 | font-size: 20px; 458 | font-family: Arial; 459 | border-radius: 4px; 460 | vertical-align: middle; 461 | } 462 | .btn_browse { 463 | border: 1px solid rgba(0,0,0,.2); 464 | padding: 8px 20px; 465 | background-color: #FFDC73; 466 | background: -moz-linear-gradient(top,#FFE599 0%,#FFDC73); 467 | background: -webkit-gradient(linear, left top, left bottom,from(#FFE599),to(#FFDC73)); 468 | box-shadow: 0 1px 1px rgba(255,255,255,.5); 469 | } 470 | .btn_browse_small { 471 | padding: 5px 10px; 472 | font-size: 16px; 473 | } 474 | 475 | .btn_browse_small[aria-disabled] { 476 | opacity: .5; 477 | } 478 | 479 | .btn_choose { 480 | color: #fff; 481 | border: 2px solid rgba(255,255,255,.4); 482 | padding: 5px 10px; 483 | text-shadow: 0 1px 1px rgba(0,0,0,.3); 484 | background-color: rgba(0,0,0,.4); 485 | *background-color: #aaa; 486 | } 487 | 488 | .btn__inp { 489 | top: -10px; 490 | right: -10px; 491 | cursor: pointer; 492 | filter: alpha(opacity=0); 493 | opacity: 0; 494 | font-size: 50px; 495 | position: absolute; 496 | } 497 | 498 | .btn__progress { 499 | top: 0; 500 | left: 0; 501 | height: 100%; 502 | opacity: .5; 503 | position: absolute; 504 | background-color: #f60; 505 | } 506 | 507 | 508 | .fileprogress { 509 | padding: 1px; 510 | height: 5px; 511 | box-shadow: 0 0 1px 1px rgba(255, 255, 255, 0.3); 512 | border-radius: 10px; 513 | background-color: rgba(0,0,0,.5); 514 | } 515 | .fileprogress__bar { 516 | width: 0; 517 | height: 100%; 518 | border-radius: 10px; 519 | background-color: orange; 520 | } 521 | 522 | 523 | 524 | 525 | .userpic { 526 | width: 200px; 527 | height: 200px; 528 | border: 2px solid rgba(0,0,0,.3); 529 | display: inline-block; 530 | position: relative; 531 | } 532 | .userpic .btn { 533 | margin-top: 150px; 534 | } 535 | 536 | .userpic__preview { 537 | position: absolute; 538 | } 539 | 540 | 541 | .b-upload { 542 | white-space: nowrap; 543 | } 544 | .b-upload__name, 545 | .b-upload__size { 546 | display: inline-block; 547 | position: relative; 548 | overflow: hidden; 549 | max-width: 150px; 550 | vertical-align: middle; 551 | } 552 | .b-upload__size { 553 | color: #666; 554 | font-size: 12px; 555 | } 556 | 557 | .b-upload .js-files:after { 558 | clear: both; 559 | content: ''; 560 | display: block; 561 | } 562 | 563 | .b-upload__dnd { 564 | padding: 30px; 565 | border-radius: 5px; 566 | margin-bottom: 10px; 567 | background-color: rgba(0,0,0,.1); 568 | } 569 | .b-upload__dnd_hover { 570 | color: #fff; 571 | background-color: orange; 572 | } 573 | 574 | 575 | 576 | .themodal-overlay { 577 | position: fixed; 578 | bottom: 0; 579 | left: 0; 580 | top: 0; 581 | right: 0; 582 | z-index: 100000; 583 | overflow: auto; 584 | background-color: rgba(0, 0, 0, 0.5); 585 | } 586 | 587 | .lock { 588 | overflow: hidden; 589 | } 590 | 591 | .popup { 592 | margin: 25px; 593 | float: left; 594 | display: inline-block; 595 | box-shadow: 0 0 5px #000; 596 | background-color: #fff; 597 | } 598 | .popup__body { 599 | margin: 10px 10px 5px; 600 | } 601 | 602 | 603 | 604 | 605 | /** 606 | * XCode style (c) Angel Garcia 607 | */ 608 | 609 | pre code { 610 | display: block; padding: 0.5em; 611 | color: black; 612 | } 613 | 614 | pre .comment, 615 | pre .template_comment, 616 | pre .javadoc, 617 | pre .comment * { 618 | color: rgb(0,106,0); 619 | } 620 | 621 | pre .keyword, 622 | pre .literal, 623 | pre .nginx .title { 624 | color: rgb(170,13,145); 625 | } 626 | pre .method, 627 | pre .list .title, 628 | pre .tag .title, 629 | pre .setting .value, 630 | pre .winutils, 631 | pre .tex .command, 632 | pre .http .title, 633 | pre .request, 634 | pre .status { 635 | color: #008; 636 | } 637 | 638 | pre .envvar, 639 | pre .tex .special { 640 | color: #660; 641 | } 642 | 643 | pre .string { 644 | color: rgb(196,26,22); 645 | } 646 | pre .tag .value, 647 | pre .cdata, 648 | pre .filter .argument, 649 | pre .attr_selector, 650 | pre .apache .cbracket, 651 | pre .date, 652 | pre .regexp { 653 | color: #080; 654 | } 655 | 656 | pre .sub .identifier, 657 | pre .pi, 658 | pre .tag, 659 | pre .tag .keyword, 660 | pre .decorator, 661 | pre .ini .title, 662 | pre .shebang, 663 | pre .prompt, 664 | pre .hexcolor, 665 | pre .rules .value, 666 | pre .css .value .number, 667 | pre .symbol, 668 | pre .symbol .string, 669 | pre .number, 670 | pre .css .function, 671 | pre .clojure .title, 672 | pre .clojure .built_in { 673 | color: rgb(28,0,207); 674 | } 675 | 676 | pre .class .title, 677 | pre .haskell .type, 678 | pre .smalltalk .class, 679 | pre .javadoctag, 680 | pre .yardoctag, 681 | pre .phpdoc, 682 | pre .typename, 683 | pre .tag .attribute, 684 | pre .doctype, 685 | pre .class .id, 686 | pre .built_in, 687 | pre .setting, 688 | pre .params, 689 | pre .clojure .attribute { 690 | color: rgb(92,38,153); 691 | } 692 | 693 | pre .variable { 694 | color: rgb(63,110,116); 695 | } 696 | pre .css .tag, 697 | pre .rules .property, 698 | pre .pseudo, 699 | pre .subst { 700 | color: #000; 701 | } 702 | 703 | pre .css .class, pre .css .id { 704 | color: #9B703F; 705 | } 706 | 707 | pre .value .important { 708 | color: #ff7700; 709 | font-weight: bold; 710 | } 711 | 712 | pre .rules .keyword { 713 | color: #C5AF75; 714 | } 715 | 716 | pre .annotation, 717 | pre .apache .sqbracket, 718 | pre .nginx .built_in { 719 | color: #9B859D; 720 | } 721 | 722 | pre .preprocessor, 723 | pre .preprocessor * { 724 | color: rgb(100,56,32); 725 | } 726 | 727 | pre .tex .formula { 728 | background-color: #EEE; 729 | font-style: italic; 730 | } 731 | 732 | pre .diff .header, 733 | pre .chunk { 734 | color: #808080; 735 | font-weight: bold; 736 | } 737 | 738 | pre .diff .change { 739 | background-color: #BCCFF9; 740 | } 741 | 742 | pre .addition { 743 | background-color: #BAEEBA; 744 | } 745 | 746 | pre .deletion { 747 | background-color: #FFC8BD; 748 | } 749 | 750 | pre .comment .yardoctag { 751 | font-weight: bold; 752 | } 753 | 754 | pre .method .id { 755 | color: #000; 756 | } 757 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### WARNING: This plugin is not maintained 2 | If you have a desire to continue to develop and support, email me, with pleasure I will give access to the repository. 3 | 4 | --- 5 | 6 | ### ВНИМАНИЕ: Этот плагин больше не поддерживается 7 | Если у вас есть желание продолжить разработку и поддержку, напишите мне, с удовольствием предоставлю весь необходимый доступ. 8 | 9 | --- 10 | 11 | 12 | # $.fn.fileapi 13 | jQuery plugin for [FileAPI](https://github.com/mailru/FileAPI/) (multiupload, image upload, crop, resize and etc.) 14 | 15 | 16 | ## Install 17 | ```html 18 | 19 | 20 | 26 | 27 | 28 | 29 | ``` 30 | 31 | --- 32 | 33 | 34 | ## Example 35 | ```html 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 |
44 | 55 | ``` 56 | 57 | 58 | --- 59 | 60 | 61 | ## Options 62 | 63 | ### url`:String` 64 | URL to which the request is sent.
65 | If `undefined` or empty, it is set to the action property of the file upload form if available. 66 | 67 | 68 | ### autoUpload`:Boolean` 69 | To enable automatic uploads, set this option to `true`. 70 | 71 | 72 | ### data`:Object` 73 | Additional form data to be sent along with the file uploads can be set using this option. 74 | 75 | 76 | ### headers`:Object` 77 | Additional request headers. 78 | 79 | 80 | ### multiple`:Boolean` 81 | It specifies that multiple files can be selected at once, default `true`. 82 | 83 | 84 | ### accept`:String` 85 | If the value of the type attribute is file, this attribute indicates the types of files that the server accepts; 86 | otherwise it is ignored. The value must be a comma-separated list of unique content type specifiers: `image/*`, `audio/*`, `video/*`, etc. 87 | 88 | 89 | ### duplicate`:Boolean` 90 | The ability to upload duplicates, default `false`. 91 | 92 | 93 | ### paramName`:String` 94 | The parameter name for the file form data (the request argument name).
95 | If `undefined` or empty, the name property of the file input field is used, or `files[]` if the file input name property is also empty. 96 | 97 | 98 | ### dataType`:String` 99 | The type of data that is expected back from the server, default `json`. 100 | 101 | 102 | ### chunkSize`:Number` 103 | Chunk size in bytes, eg: `.5 * FileAPI.MB`. 104 | 105 | 106 | ### chunkUploadRetry`:Number` 107 | Number of retries during upload chunks. 108 | 109 | 110 | ### maxSize`:Number` 111 | The maximum allowed file size in bytes, by default unlimited. 112 | 113 | 114 | ### maxFiles`:Number` 115 | This option limits the number of files that are allowed to be uploaded using this plugin. 116 | 117 | 118 | ### imageSize`:Object` 119 | Allowable size of uploaded images, eg: `{ minWidth: 320, minHeight: 240, maxWidth: 3840, maxHeight: 2160 }`. 120 | 121 | 122 | ### sortFn`:Function` 123 | Sort function of selected files. 124 | 125 | 126 | ### filterFn`:Function` 127 | Filter function of selected files, eg: `function (file, info){ return /^image/.test(file.type) && info.width > 320 }`. 128 | 129 | 130 | ### imageTransform`:Object` 131 | Rules of changes the original image on the client (see [details](https://github.com/mailru/FileAPI#imagetransformobject)). 132 | ```js 133 | imageTransform: { 134 | // resize by max side 135 | maxWidth: 800, 136 | maxHeight: 600 137 | } 138 | ``` 139 | 140 | 141 | ### imageOriginal`:Boolean` 142 | Sent to the server the original image or not, if defined imageTransform option. 143 | 144 | 145 | ### elements`:Object` 146 | ```js 147 | // Default options 148 | elements: { 149 | // Controls 150 | ctrl: { 151 | upload: '[data-fileapi="ctrl.upload"]', 152 | reset: '[data-fileapi="ctrl.reset"]', 153 | abort: '[data-fileapi="ctrl.abort"]' 154 | }, 155 | // Display element depending on files 156 | empty: { 157 | show: '[data-fileapi="empty.show"]', 158 | hide: '[data-fileapi="empty.hide"]' 159 | }, 160 | // Display element depending on queue state 161 | emptyQueue: { 162 | show: '[data-fileapi="emptyQueue.show"]', 163 | hide: '[data-fileapi="emptyQueue.hide"]' 164 | }, 165 | // Display element depending on upload state 166 | active: { 167 | show: '[data-fileapi="active.show"]', 168 | hide: '[data-fileapi="active.hide"]' 169 | }, 170 | // Preview file (single upload) 171 | preview: { 172 | el: 0, // css selector 173 | width: 0, 174 | height: 0, 175 | keepAspectRatio: false // optional: false to stretch cropped image to preview area, true scale image proportionally 176 | }, 177 | // Total size of queue 178 | size: '[data-fileapi="size"]', 179 | // Selected file name 180 | name: '[data-fileapi="name"]', 181 | // Progress bar total 182 | progress: '[data-fileapi="progress"]', 183 | // Filelist options 184 | file: { 185 | // Template 186 | tpl: '[data-fileapi="file.tpl"]', 187 | // Progress bar 188 | progress: '[data-fileapi="file.progress"]', 189 | // Display element depending on upload state 190 | active: { 191 | show: '[data-fileapi="active.show"]', 192 | hide: '[data-fileapi="active.hide"]' 193 | }, 194 | // Preview file or icon 195 | preview: { 196 | el: 0, // css selector 197 | get: 0, // eg: function($el, file){ $el.append(''); } 198 | width: 0, 199 | height: 0, 200 | keepAspectRatio: false // optional: false to stretch cropped image to preview area, true scale image proportionally 201 | } 202 | }, 203 | // Drag and drop 204 | dnd: { 205 | // DropZone: selector or element 206 | el: '[data-fileapi="dnd"]', 207 | // Hover class 208 | hover: 'dnd_hover' 209 | } 210 | } 211 | ``` 212 | 213 | --- 214 | 215 | ## Events 216 | ### onSelect`:Function`(evt`:$.Event`, data`:FilesObject`) 217 | Retrieve file List, takes two arguments. 218 | 219 | ```js 220 | $('...').fileapi({ 221 | onSelect: function (evt, data){ 222 | data.all; // All files 223 | data.files; // Correct files 224 | if( data.other.length ){ 225 | // errors 226 | var errors = data.other[0].errors; 227 | if( errors ){ 228 | errors.maxSize; // File size exceeds the maximum size `@see maxSize` 229 | errors.maxFiles; // Number of files selected exceeds the maximum `@see maxFiles` 230 | errors.minWidth; // Width of the image is smaller than the specified `@see imageSize` 231 | errors.minHeight; 232 | errors.maxWidth; // Width of the image greater than the specified `@see imageSize` 233 | errors.maxHeight; 234 | } 235 | } 236 | } 237 | }); 238 | ``` 239 | 240 | ### onBeforeUpload`:Function`(evt`:$.Event`, uiEvt`:Object`) 241 | Before start uploading. 242 | ```js 243 | function (evt, uiEvt){ 244 | var files = uiEvt.files; 245 | var widget = uiEvt.widget; 246 | if (files.length > 1000) { 247 | return false; // prevent uploading 248 | } 249 | } 250 | ``` 251 | 252 | ### onUpload`:Function`(evt`:$.Event`, uiEvt`:Object`) 253 | Start uploading. 254 | ```js 255 | function (evt, uiEvt){ 256 | // Base properties 257 | var file = uiEvt.file; 258 | var files = uiEvt.files; 259 | var widget = uiEvt.widget; 260 | var xhr = uiEvt.xhr; 261 | } 262 | ``` 263 | 264 | ### onFilePrepare`:Function`(evt`:$.Event`, uiEvt`:Object`) 265 | Preparation of data before uploading. 266 | 267 | ```js 268 | function (evt, uiEvt){ 269 | var file = uiEvt.file; 270 | uiEvt.options.data.fileType = file.type; 271 | } 272 | ``` 273 | 274 | ### onFileUpload`:Function`(evt`:$.Event`, uiEvt`:Object`) 275 | Start upload the same file. 276 | 277 | ### onProgress`:Function`(evt`:$.Event`, uiEvt`:Object`) 278 | Common uploading progress. 279 | ```js 280 | function (evt, uiEvt){ 281 | var part = uiEvt.loaded / uiEvt.total; 282 | } 283 | ``` 284 | 285 | ### onFileProgress`:Function`(evt`:$.Event`, uiEvt`:Object`) 286 | Progress upload the same file. 287 | ```js 288 | function (evt, uiEvt){ 289 | var file = uiEvt.file; 290 | var part = uiEvt.loaded / uiEvt.total; 291 | } 292 | ``` 293 | 294 | ### onComplete`:Function`(evt`:$.Event`, uiEvt`:Object`) 295 | Completion of the entire uploading. 296 | 297 | ### onFileComplete`:Function`(evt`:$.Event`, uiEvt`:Object`) 298 | Completion of uploading the file. 299 | ```js 300 | function (evt, uiEvt){ 301 | var error = uiEvt.error; 302 | var result = uiEvt.result; // server response 303 | } 304 | ``` 305 | 306 | ### onDrop`:Function`(evt`:$.Event`, data`:FilesObject`) 307 | Retrieve file List, takes two arguments. 308 | 309 | ### onDropHover`:Function`(evt`:$.Event`, uiEvt`:Object`) 310 | ```js 311 | $('#box').fileapi({ 312 | onDropHover: function (evt, uiEvt){ 313 | $(this).toggleClass('dnd_hover', uiEvt.state); 314 | } 315 | }); 316 | ``` 317 | 318 | ### onFileRemove(evt`:$.Event`, file`:File`) 319 | Removing a file from the queue 320 | ```js 321 | function (evt, file){ 322 | if( !confirm('Remove "'+file.name+'"?') ){ 323 | return false; 324 | } 325 | } 326 | ``` 327 | 328 | ### onFileRemoveCompleted(evt`:$.Event`, file`:File`) 329 | Removing a file from the queue 330 | ```js 331 | function (evt, file){ 332 | // Send ajax-request 333 | $.post('/remove-ctrl.php', { uid: FileAPI.uid(file) }); 334 | } 335 | ``` 336 | 337 | 338 | ## Cropper 339 | Based on [Jсrop](http://deepliquid.com/content/Jcrop.html). 340 | 341 | ```html 342 | 343 | 344 | 345 | 346 | 347 | 348 | ``` 349 | 350 | Usage: 351 | ```js 352 | $('#userpic').fileapi({ 353 | url: '...', 354 | accept: 'image/*', 355 | onSelect: function (imageFile){ 356 | $('#userpic-upload-btn') 357 | .unbind('.fileapi') 358 | .bind('click.fileapi', function (){ 359 | $('#userpic').fileapi('upload'); 360 | }) 361 | ; 362 | 363 | $('#image-preview').cropper({ 364 | file: imageFile 365 | , bgColor: '#fff' 366 | , maxSize: [320, 240] // viewport max size 367 | , minSize: [100, 100] // crop min size 368 | , aspectRatio: 1 // optional, aspect ratio: 0 - disable, >0 - fixed, remove this option: autocalculation from minSize 369 | , onSelect: function (coords){ 370 | $('#userpic').fileapi('crop', imageFile, coords); 371 | } 372 | }); 373 | } 374 | }); 375 | ``` 376 | 377 | --- 378 | 379 | 380 | ## Customization 381 | 382 | ```js 383 | $('#upload').fileapi({ 384 | multiple: true, 385 | 386 | // Restores the list of files uploaded earlier *** IE < 9 — NOT SUPPORTED *** 387 | files: [{ 388 | src: "http://path/to/filename.png", 389 | type: "image/png", 390 | name: "filename.png", 391 | size: 31409, 392 | data: { id: 999, token: "..." } 393 | }], 394 | 395 | // Remove a file from the upload queue 396 | onFileRemove: function (evt, file){ 397 | if( !confirm("Are you sure?") ){ 398 | // Cancel remove 399 | evt.preventDefault(); 400 | } 401 | }, 402 | 403 | onFileComplete: function (evt, uiEvt){ 404 | var file = uiEvt.file; 405 | var json = uiEvt.result; 406 | 407 | file.data = { 408 | id: json.id, 409 | token: json.token 410 | }; 411 | }, 412 | 413 | onFileRemoveCompleted: function (evt, file){ 414 | evt.preventDefault(); 415 | 416 | file.$el 417 | .attr('disabled', true) 418 | .addClass('my_disabled') 419 | ; 420 | 421 | new ModalConfirm('Delete "'+file.name+'"?') 422 | .then(function (){ 423 | $.post('/api/remove', file.data); 424 | 425 | $('#upload').fileapi("remove", file); 426 | // or so 427 | evt.widget.remove(file); 428 | }, function (){ 429 | file.$el 430 | .attr('disabled', false) 431 | .removeClass('my_disabled') 432 | ; 433 | }) 434 | ; 435 | } 436 | 437 | }) 438 | ``` 439 | 440 | 441 | --- 442 | 443 | ## Using with Bootstrap 444 | 445 | You can use this uploader with Bootstrap framework without writing much additional CSS. Just add the following CSS to your page to hide the browser's "browse" button: 446 | 447 | ```css 448 | #id-of-uploader .btn { 449 | cursor: pointer; 450 | display: inline-block; 451 | position: relative; 452 | overflow: hidden; 453 | } 454 | 455 | #id-of-uploader .btn input { 456 | top: -10px; 457 | right: -40px; 458 | z-index: 2; 459 | position: absolute; 460 | cursor: pointer; 461 | opacity: 0; 462 | filter: alpha(opacity=0); 463 | font-size: 50px; 464 | } 465 | ``` 466 | 467 | 468 | --- 469 | 470 | 471 | ## MIT LICENSE 472 | Copyright 2013 Lebedev Konstantin 473 | http://rubaxa.github.io/jquery.fileapi/ 474 | 475 | Permission is hereby granted, free of charge, to any person obtaining 476 | a copy of this software and associated documentation files (the 477 | "Software"), to deal in the Software without restriction, including 478 | without limitation the rights to use, copy, modify, merge, publish, 479 | distribute, sublicense, and/or sell copies of the Software, and to 480 | permit persons to whom the Software is furnished to do so, subject to 481 | the following conditions: 482 | 483 | The above copyright notice and this permission notice shall be 484 | included in all copies or substantial portions of the Software. 485 | 486 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 487 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 488 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 489 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 490 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 491 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 492 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 493 | 494 | 495 | --- 496 | 497 | 498 | ## Changelog 499 | 500 | ### 0.4.6 501 | * FileAPI up to 2.0.9 502 | * #12: `onRemoveCompleted` -> `onFileRemoveCompleted` 503 | * #100: fixed `maxSize` option 504 | 505 | ### 0.4.5 506 | * #95: fixed `rotate` method 507 | * #94: fixed `redraw` method 508 | 509 | ### 0.4.4 510 | * #93: `files` option and userpic 511 | * #90: fixed rotate + imageAutoOrientation 512 | 513 | ### 0.4.3 514 | * #84: fixed modal.js 515 | * #82: clear(all: true) 516 | * #61: always parse result (dataType === 'json') 517 | 518 | ### 0.4.2 519 | * #73: git -> gif (fixed typo) 520 | 521 | ### 0.4.1 522 | * #67: `resize` method 523 | * #63: `remove` method 524 | * - console.log 525 | * `modal` close 526 | 527 | ### 0.4.0 528 | * #57: + `onBeforeUpload` event 529 | * support `disabled` dom-attribute 530 | * #34: fixed `imageTransform` 531 | * + FileAPI v2.0.3 532 | * #35: + `imageOriginal` option 533 | 534 | ### 0.3.1 535 | * fixed `crop` method 536 | * + `onFilePrepare` event 537 | 538 | ### 0.3.0 539 | * + QUnit tests 540 | * + `onFileRemove` and `onRemoveCompleted` events 541 | * + `abort(text)` method 542 | * + `remove(file)` method 543 | * fixed `serialize()` method 544 | 545 | ### 0.2.0 546 | * enhancement `ui event` in onSelect 547 | * + `maxFiles` option support 548 | * fixed `onFileUpload` & `onFileProgress` events 549 | * + #9: Preview with aspect ratio keeping support (optional) 550 | 551 | ### 0.1.4 552 | * + `headers:Object` 553 | * + `queue()`; * `clear()`; 554 | * `clearOnComplete: false` 555 | * * `resetOnSelect` -> `clearOnSelect` 556 | 557 | ### 0.1.1 558 | * + `resetOnSelect` option, default `!multiple` 559 | * fix $.fn.cropper reinit 560 | 561 | 562 | ### 0.1.0 563 | * Inital commit 564 | 565 | 566 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/RubaXa/jquery.fileapi/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 567 | 568 | -------------------------------------------------------------------------------- /jquery.fileapi.min.js: -------------------------------------------------------------------------------- 1 | /*! jquery.fileapi 0.4.11 - MIT | git://github.com/rubaxa/jquery.fileapi.git */ 2 | !function(a,b){"use strict";function c(a){var b;for(b in a)if(a.hasOwnProperty(b)&&!(a[b]instanceof Object||"overlay"===b))return!0;return!1}function d(a,b,c,d){if(c&&d){var e=c>0?c:c[b],f=d>0?d:d[b.substr(3).toLowerCase()],g=e-f,h=/max/.test(b);(h&&0>g||!h&&g>0)&&(a.errors||(a.errors={}),a.errors[b]=Math.abs(g))}}function e(a,b,c,e){if(a){var f=c.length-(a-b);f>0&&m(c.splice(0,f),function(a,b){d(a,"maxFiles",-1,b),e.push(a)})}}var f=a.noop,g=!a.fn.prop,h=g?"attr":"prop",i=g?"removeAttr":"removeProp",j="data-fileapi",k="data-fileapi-id",l=[].slice,m=b.each,n=b.extend,o=function(a,b){var c=l.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(l.call(arguments)))}},p=function(a){return"["+j+'="'+a+'"]'},q=function(a){return 0===a.indexOf("#")},r=function(b,c){if(this.$el=b=a(b).on("change.fileapi",'input[type="file"]',o(this,this._onSelect)),this.el=b[0],this._options={},this.options={url:0,data:{},accept:0,multiple:!1,paramName:0,dataType:"json",duplicate:!1,uploadRetry:0,networkDownRetryTimeout:5e3,chunkSize:0,chunkUploadRetry:3,chunkNetworkDownRetryTimeout:2e3,maxSize:0,maxFiles:0,imageSize:0,sortFn:0,filterFn:0,autoUpload:!1,clearOnSelect:void 0,clearOnComplete:void 0,lang:{B:"bytes",KB:"KB",MB:"MB",GB:"GB",TB:"TB"},sizeFormat:"0.00",imageOriginal:!0,imageTransform:0,imageAutoOrientation:!!FileAPI.support.exif,elements:{ctrl:{upload:p("ctrl.upload"),reset:p("ctrl.reset"),abort:p("ctrl.abort")},empty:{show:p("empty.show"),hide:p("empty.hide")},emptyQueue:{show:p("emptyQueue.show"),hide:p("emptyQueue.hide")},active:{show:p("active.show"),hide:p("active.hide")},size:p("size"),name:p("name"),progress:p("progress"),list:p("list"),file:{tpl:p("file.tpl"),progress:p("file.progress"),active:{show:p("file.active.show"),hide:p("file.active.hide")},preview:{el:0,get:0,width:0,height:0,processing:0},abort:p("file.abort"),remove:p("file.remove"),rotate:p("file.rotate")},dnd:{el:p("dnd"),hover:"dnd_hover",fallback:p("dnd.fallback")}},onDrop:f,onDropHover:f,onSelect:f,onBeforeUpload:f,onUpload:f,onProgress:f,onComplete:f,onFilePrepare:f,onFileUpload:f,onFileProgress:f,onFileComplete:f,onFileRemove:null,onFileRemoveCompleted:null},a.extend(!0,this.options,c),c=this.options,this.option("elements.file.preview.rotate",c.imageAutoOrientation),!c.url){var d=this.$el.attr("action")||this.$el.find("form").attr("action");d?c.url=d:this._throw("url — is not defined")}this.$files=this.$elem("list"),this.$fileTpl=this.$elem("file.tpl"),this.itemTplFn=a.fn.fileapi.tpl(a("
").append(this.$elem("file.tpl")).html()),m(c,function(a,b){this._setOption(b,a)},this),this.$el.on("reset.fileapi",o(this,this._onReset)).on("submit.fileapi",o(this,this._onSubmit)).on("upload.fileapi progress.fileapi complete.fileapi",o(this,this._onUploadEvent)).on("fileupload.fileapi fileprogress.fileapi filecomplete.fileapi",o(this,this._onFileUploadEvent)).on("click","["+j+"]",o(this,this._onActionClick));var e=c.elements.ctrl;e&&(this._listen("click",e.reset,o(this,this._onReset)),this._listen("click",e.upload,o(this,this._onSubmit)),this._listen("click",e.abort,o(this,this._onAbort)));var g=FileAPI.support.dnd;this.$elem("dnd.el",!0).toggle(g),this.$elem("dnd.fallback").toggle(!g),g&&this.$elem("dnd.el",!0).dnd(o(this,this._onDropHover),o(this,this._onDrop)),this.$progress=this.$elem("progress"),void 0===c.clearOnSelect&&(c.clearOnSelect=!c.multiple),this.clear(),a.isArray(c.files)&&(this.files=a.map(c.files,function(b){return"string"===a.type(b)&&(b={src:b,size:0}),b.name=b.name||b.src.split("/").pop(),b.type=b.type||/\.(jpe?g|png|bmp|gif|tiff?)/i.test(b.src)&&"image/"+b.src.split(".").pop(),b.complete=!0,b}),this._redraw())};r.prototype={constructor:r,_throw:function(a){throw"jquery.fileapi: "+a},_getFiles:function(a,c){var f=this.options,g=f.maxSize,h=f.maxFiles,i=f.filterFn,j=this.files.length,k=b.getFiles(a),l={all:k,files:[],other:[],duplicate:f.duplicate?[]:this._extractDuplicateFiles(k)},n=f.imageSize,o=this;n||i?b.filterFiles(k,function(a,b){return b&&n&&(d(a,"minWidth",n,b),d(a,"minHeight",n,b),d(a,"maxWidth",n,b),d(a,"maxHeight",n,b)),d(a,"maxSize",g,a.size),!a.errors&&(!i||i(a,b))},function(a,b){e(h,j,a,b),l.other=b,l.files=a,c.call(o,l)}):(m(k,function(a){d(a,"maxSize",g,a.size),l[a.errors?"other":"files"].push(a)}),e(h,j,l.files,l.other),c.call(o,l))},_extractDuplicateFiles:function(a){for(var b,c=[],d=a.length,e=this.files;d--;)for(b=e.length;b--;)if(this._fileCompare(a[d],e[b])){c.push(a.splice(d,1));break}return c},_fileCompare:function(a,b){return a.size==b.size&&a.name==b.name},_getFormatedSize:function(a){var c=this.options,d="B";return a>=b.TB?a/=b[d="TB"]:a>=b.GB?a/=b[d="GB"]:a>=b.MB?a/=b[d="MB"]:a>=b.KB&&(a/=b[d="KB"]),c.sizeFormat.replace(/^\d+([^\d]+)(\d*)/,function(b,e,f){return a=(parseFloat(a)||0).toFixed(f.length),(a+"").replace(".",e)+" "+c.lang[d]})},_onSelect:function(a){this.options.clearOnSelect&&(this.queue=[],this.files=[]),this._getFiles(a,o(this,function(b){b.all.length&&this.emit("select",b)!==!1&&this.add(b.files),FileAPI.reset(a.target)}))},_onActionClick:function(b){var c=b.currentTarget,d=a.attr(c,j),e=a(c).closest("["+k+"]",this.$el).attr(k),f=this._getFile(e),g=!0;this.$file(e).attr("disabled")||("file.remove"==d?f&&this.emit("fileRemove"+(f.complete?"Completed":""),f)&&this.remove(e):/^file\.rotate/.test(d)?this.rotate(e,/ccw/.test(d)?"-=90":"+=90"):g=!1),g&&b.preventDefault()},_listen:function(b,c,d){c&&m(a.trim(c).split(","),function(c){c=a.trim(c),q(c)?a(c).on(b+".fileapi",d):this.$el.on(b+".fileapi",c,d)},this)},_onSubmit:function(a){a.preventDefault(),this.upload()},_onReset:function(a){a.preventDefault(),this.clear(!0)},_onAbort:function(a){a.preventDefault(),this.abort()},_onDrop:function(a){this._getFiles(a,function(a){this.emit("drop",a)!==!1&&this.add(a.files)})},_onDropHover:function(b,c){if(this.emit("dropHover",{state:b,event:c})!==!1){var d=this.option("elements.dnd.hover");d&&a(c.currentTarget).toggleClass(d,b)}},_getFile:function(a){return b.filter(this.files,function(c){return b.uid(c)==a})[0]},_getUploadEvent:function(a,b){a=this.xhr||a;var c={xhr:a,file:this.xhr.currentFile,files:this.xhr.files,widget:this};return n(c,b)},_emitUploadEvent:function(a,b,c){var d=this._getUploadEvent(c);this.emit(a+"Upload",d)},_emitProgressEvent:function(a,b,c,d){var e=this._getUploadEvent(d,b);this.emit(a+"Progress",e)},_emitCompleteEvent:function(b,c,d,e){var f=this._getUploadEvent(d,{error:c,status:d.status,statusText:d.statusText,result:d.responseText});if("file"==b&&(e.complete=!0),"json"==this.options.dataType)try{f.result=a.parseJSON(f.result)}catch(c){f.error=c}this.emit(b+"Complete",f)},_onUploadEvent:function(a,b){var c=this,d=c.$progress,e=a.type;if("progress"==e)d.stop().animate({width:b.loaded/b.total*100+"%"},300);else if("upload"==e)d.width(0);else{var f=function(){d.dequeue(),c[c.options.clearOnComplete?"clear":"dequeue"]()};this.xhr=null,this.active=!1,d.length?d.queue(f):f()}},_onFileUploadPrepare:function(d,e){var f=b.uid(d),g=this._rotate[f],h=this._crop[f],i=this._resize[f],j=this._getUploadEvent(this.xhr);if(g||h||i){var k=a.extend(!0,{},e.imageTransform||{});g=null!=g?g:this.options.imageAutoOrientation?"auto":void 0,a.isEmptyObject(k)||c(k)?(n(k,i),k.crop=h,k.rotate=g):m(k,function(a){n(a,i),a.crop=h,a.rotate=g}),e.imageTransform=k}j.file=d,j.options=e,this.emit("filePrepare",j)},_onFileUploadEvent:function(a,c){var d=this,e=a.type.substr(4),f=b.uid(c.file),g=this.$file(f),h=this._$fileprogress,i=this.options;if(this.__fileId!==f&&(this.__fileId=f,this._$fileprogress=h=this.$elem("file.progress",g)),"progress"==e)h.stop().animate({width:c.loaded/c.total*100+"%"},300);else if("upload"==e||"complete"==e){var j=function(){var a="file."+e,b=d.$elem("file.remove",g);"upload"==e?(b.hide(),h.width(0)):i.onFileRemoveCompleted&&b.show(),h.dequeue(),d.$elem(a+".show",g).show(),d.$elem(a+".hide",g).hide()};h.length?h.queue(j):j(),"complete"==e&&(this.uploaded.push(c.file),delete this._rotate[f])}},_redraw:function(c,d){var e=this.files,f=!!this.active,g=!e.length&&!f,i=!this.queue.length&&!f,j=[],l=0,n=this.$files,o=n.children().length,p=this.option("elements.preview"),q=this.option("elements.file.preview");c&&this.$files.empty(),d&&p&&p.el&&!this.queue.length&&this.$(p.el).empty(),m(e,function(c,d){var e=b.uid(c);if(j.push(c.name),l+=c.complete?0:c.size,p&&p.el)this._makeFilePreview(e,c,p,!0);else if(n.length&&!this.$file(e).length){var f=this.itemTplFn({$idx:o+d,uid:e,name:c.name,type:c.type,size:c.size,complete:!!c.complete,sizeText:this._getFormatedSize(c.size)}),g=a(f).attr(k,e);c.$el=g,n.append(g),c.complete&&(this.$elem("file.upload.hide",g).hide(),this.$elem("file.complete.hide",g).hide()),q.el&&this._makeFilePreview(e,c,q)}},this),this.$elem("name").text(j.join(", ")),this.$elem("size").text(i?"":this._getFormatedSize(l)),this.$elem("empty.show").toggle(g),this.$elem("empty.hide").toggle(!g),this.$elem("emptyQueue.show").toggle(i),this.$elem("emptyQueue.hide").toggle(!i),this.$elem("active.show").toggle(f),this.$elem("active.hide").toggle(!f),this.$(".js-fileapi-wrapper,:file")[f?"attr":"removeAttr"]("aria-disabled",f)[h]("disabled",f),this._disableElem("ctrl.upload",i||f),this._disableElem("ctrl.reset",i||f),this._disableElem("ctrl.abort",!f)},_disableElem:function(a,b){this.$elem(a)[b?"attr":"removeAttr"]("aria-disabled","disabled")[h]("disabled",b)},_makeFilePreview:function(a,c,d,e){var f=this,g=e?f.$(d.el):f.$file(a).find(d.el);if(!f._crop[a])if(/^image/.test(c.type)){var h=b.Image(c.src||c),i=function(){h.get(function(b,e){f._crop[a]||(b?(d.get&&d.get(g,c),f.emit("previewError",[b,c])):g.html(e))})};d.width&&h.preview(d.width,d.height),d.rotate&&h.rotate("auto"),d.processing?d.processing(c,h,i):i()}else d.get&&d.get(g,c)},emit:function(b,c){var d,e=this.options,f=a.Event(b.toLowerCase());return f.widget=this,b=a.camelCase("on-"+b),a.isFunction(e[b])&&(d=e[b].call(this.el,f,c)),this.$el.triggerHandler(f,c),d!==!1&&!f.isDefaultPrevented()},add:function(a,b){if(a=[].concat(a),a.length){var c=this.options,d=c.sortFn;d&&a.sort(d),this.xhr&&this.xhr.append(a),this.queue=b?a:this.queue.concat(a),this.files=b?a:this.files.concat(a),this.active?(this.xhr.append(a),this._redraw(b)):(this._redraw(b),this.options.autoUpload&&this.upload())}},$:function(b,c){return"string"==typeof b&&(b=/^#/.test(b)?b:(c?a(c):this.$el).find(b)),a(b)},$elem:function(b,c,d){c&&c.jquery&&(d=c,c=!1);var e=this.option("elements."+b);return void 0===e&&c&&(e=this.option("elements."+b.substr(0,b.lastIndexOf(".")))),this.$("string"!=a.type(e)&&a.isEmptyObject(e)?[]:e,d)},$file:function(a){return this.$("["+k+'="'+a+'"]')},option:function(b,c){if(void 0!==c&&a.isPlainObject(c))return m(c,function(a,c){this.option(b+"."+c,a)},this),this;var d,e,f=this.options,g=f[b],h=0;if(-1!=b.indexOf("."))for(g=f,b=b.split("."),d=b.length;d>h;h++){if(e=b[h],void 0!==c&&d-h===1){g[e]=c;break}g[e]||(g[e]={}),g=g[e]}else void 0!==c&&(f[b]=c);return void 0!==c&&(this._setOption(b,c,this._options[b]),this._options[b]=c),void 0!==c?c:g},_setOption:function(a,b){switch(a){case"accept":case"multiple":this.$(":file")[b?h:i](a,b);break;case"paramName":b&&this.$(":file")[h]("name",b)}},serialize:function(){var b,c={};return this.$el.find(":input").each(function(d,e){(d=e.name)&&!e.disabled&&(e.checked||/select|textarea|input/i.test(e.nodeName)&&!/checkbox|radio|file/i.test(e.type))&&(b=a(e).val(),void 0!==c[d]?(c[d].push||(c[d]=[c[d]]),c[d].push(b)):c[d]=b)}),c},upload:function(){if(!this.active&&this.emit("beforeUpload",{widget:this,files:this.queue})){this.active=!0;var a=this.$el,c=this.options,d={},e={url:c.url,data:n({},this.serialize(),c.data),headers:c.headers,files:d,uploadRetry:c.uploadRetry,networkDownRetryTimeout:c.networkDownRetryTimeout,chunkSize:c.chunkSize,chunkUploadRetry:c.chunkUploadRetry,chunkNetworkDownRetryTimeout:c.chunkNetworkDownRetryTimeout,prepare:o(this,this._onFileUploadPrepare),imageOriginal:c.imageOriginal,imageTransform:c.imageTransform,imageAutoOrientation:c.imageAutoOrientation};d[a.find(":file").attr("name")||"files[]"]=this.queue,m(["Upload","Progress","Complete"],function(a){var b=a.toLowerCase();e[b]=o(this,this["_emit"+a+"Event"],""),e["file"+b]=o(this,this["_emit"+a+"Event"],"file")},this),this.xhr=b.upload(e),this._redraw()}},abort:function(a){this.active&&this.xhr&&this.xhr.abort(a)},crop:function(c,d){var e=b.uid(c),f=this.options,g=f.multiple?this.option("elements.file.preview"):f.elements.preview,h=(f.multiple?this.$file(e):this.$el).find(g&&g.el);h.length&&b.getInfo(c,o(this,function(e,i){if(e)this.emit("previewError",[e,c]);else{h.find("div>div").length||h.html(a("
").css(g).css("overflow","hidden")),this.__cropFile!==c&&(this.__cropFile=c,b.Image(c).rotate(f.imageAutoOrientation?"auto":0).get(function(b,c){h.find(">div>div").html(a(c).width("100%").height("100%"))},"exactFit"));var j=g.width,k=g.height,l=j,m=k,n=j/d.rw,o=k/d.rh;g.keepAspectRatio&&(n>1&&o>1?(n=o=1,m=d.h,l=d.w):o>n?(o=n,m=j*d.rh/d.rw):(n=o,l=k*d.rw/d.rh)),h.find(">div>div").css({width:Math.round(n*i[d.flip?"height":"width"]),height:Math.round(o*i[d.flip?"width":"height"]),marginLeft:-Math.round(n*d.rx),marginTop:-Math.round(o*d.ry)}),g.keepAspectRatio&&h.find(">div").css({width:Math.round(l),height:Math.round(m),marginLeft:j>l?Math.round((j-l)/2):0,marginTop:k>m?Math.round((k-m)/2):0})}})),this._crop[e]=d},resize:function(a,c,d,e){this._resize[b.uid(a)]={type:e,width:c,height:d}},rotate:function(c,d){var e="string"==a.type(c)?c:b.uid(c),f=this.options,g=f.multiple?this.option("elements.file.preview"):f.elements.preview,h=(f.multiple?this.$file(e):this.$el).find(g&&g.el),i=this._rotate;c=this._getFile(e),b.getInfo(c,function(a,g){var j=g&&g.exif&&g.exif.Orientation,k=f.imageAutoOrientation&&b.Image.exifOrientation[j]||0;null==i[e]&&(i[e]=k||0),i[e]=/([+-])=/.test(d)?d=i[e]+("+"==RegExp.$1?1:-1)*d.substr(2):d,c.rotate=d,d-=k,h.css({"-webkit-transform":"rotate("+d+"deg)","-moz-transform":"rotate("+d+"deg)",transform:"rotate("+d+"deg)"})})},remove:function(a){var c="object"==typeof a?b.uid(a):a;this.$file(c).remove(),this.queue=b.filter(this.queue,function(a){return b.uid(a)!=c}),this.files=b.filter(this.files,function(a){return b.uid(a)!=c}),this._redraw()},clear:function(a){this._crop={},this._resize={},this._rotate={},this.queue=[],this.files=[],this.uploaded=[],a=void 0===a?!0:a,this._redraw(a,a)},dequeue:function(){this.queue=[],this._redraw()},widget:function(){return this},toString:function(){return"[jQuery.FileAPI object]"},destroy:function(){this.$files.empty().append(this.$fileTpl),this.$el.off(".fileapi").removeData("fileapi"),m(this.options.elements.ctrl,function(b){q(b)&&a(b).off("click.fileapi")})}},a.fn.fileapi=function(b,c){var d=this.data("fileapi");if(d){if("widget"===b)return d;if("string"==typeof b){var e,f=d[b];return a.isFunction(f)?e=f.apply(d,l.call(arguments,1)):void 0===f?e=d.option(b,c):"files"===b&&(e=f),void 0===e?this:e}}else(null==b||"object"==typeof b)&&this.data("fileapi",new r(this,b));return this},a.fn.fileapi.version="0.4.11",a.fn.fileapi.tpl=function(a){var b=0,c="__b+='";return a.replace(/(?:<|<)%([-=])?([\s\S]+?)%(?:>|>)|$/g,function(d,e,f,g){return c+=a.slice(b,g).replace(/[\r\n"']/g,function(a){return"\\"+a}),f&&(c+=e?"'+\n((__x=("+f+"))==null?'':"+("-"==e?"__esc(__x)":"__x")+")\n+'":"';\n"+f+"\n__b+='"),b=g+d.length,d}),new Function("ctx","var __x,__b='',__esc=function(val){return typeof val=='string'?val.replace(/0&&(c.aspectRatio=g),b.getInfo(e,function(h,i){var j=b.Image(e),k=c.maxSize,l=e.rotate;k&&j.resize(Math.max(k[0],f[0]),Math.max(k[1],f[1]),"max"),j.rotate(void 0===l?"auto":l).get(function(b,e){var f=c.selection,h=Math.min(e.width,e.height),j=h,k=h/g,l=FileAPI.Image.exifOrientation[i.exif&&i.exif.Orientation]||0;if(f){(/%/.test(f)||f>0&&1>f)&&(f=parseFloat(f,10)/(f>0?1:100),j*=f,k*=f);var n=(e.width-j)/2,o=(e.height-k)/2;c.setSelect=[0|n,0|o,n+j|0,o+k|0]}m(["onSelect","onChange"],function(a,b){(b=c[a])&&(c[a]=function(a){var c=l%180,d=i.width,f=i.height,g=a.x/e.width,h=a.y/e.height,j=a.w/e.width,k=a.h/e.height,m=d*(c?h:g),n=f*(c?1-(a.x+a.w)/e.width:h),o=d*(c?k:j),p=f*(c?j:k);b({x:m,y:n,w:o,h:p,rx:g*(c?f:d),ry:h*(c?d:f),rw:j*(c?f:d),rh:k*(c?d:f),lx:a.x,ly:a.y,lw:a.w,lh:a.h,lx2:a.x2,ly2:a.y2,deg:l,flip:c})})});var p=a("
").css("lineHeight",0).append(a(e).css("margin",0));d.html(p),p.Jcrop(c).trigger("resize")})})}return d}}(jQuery,FileAPI); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jQuery plugin for FileAPI 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | Browse 48 | 49 |
50 | 56 |
57 |
58 | 59 |
60 |

Simple button

61 |
62 | 63 | 78 |
79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 | Choose 87 | 88 |
89 | 93 |
94 |
95 |
96 | 97 |
98 |

Userpic + crop

99 |
100 | 101 | 149 |
150 | 151 |
152 |
153 |
154 |
155 |
156 |
157 | Webcam 158 |
159 | 163 |
164 |
165 |
166 | 167 |
168 |

Webcam

169 |
170 | 171 | 218 |
219 | 220 |
221 |
222 |
223 |
224 |
225 | Browse 226 | 227 |
228 | 229 | 230 | () 231 | 232 | 233 |
234 | 235 | 236 | 237 |
238 |
239 |
240 |
241 |

File upload

242 |
243 | 256 |
257 | 258 |
259 |
260 |
261 |
262 |
Добавить файлы в очередь загрузки, например изображения ;]
263 | 264 |
265 |
266 |
267 |
268 |
269 |
270 | <% if( /^image/.test(type) ){ %> 271 |
272 | <% } %> 273 |
274 |
<%-name%>
275 |
276 |
277 | 278 |
279 |
280 | Add 281 | 282 |
283 |
284 | Upload 285 |
286 |
287 |
288 |
289 |
290 |

Multiupload

291 |
292 | 316 |
317 | 318 |
319 |
320 |
321 |
Drag and drop, automatic upload
322 |
323 |
324 | Choose files 325 | 326 |
327 |
328 | 329 |
330 |
331 |
332 |
333 |
334 |
335 |
<%-name%>
336 |
337 |
338 |
339 |
340 | 341 |
342 |

Drag'n'drop upload

343 |
344 | 345 | 373 |
374 | 375 | 376 |
377 |
Loading…
378 | 379 |
380 |
381 | 382 |
383 | 384 | 385 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 508 | 509 | 518 | 519 | 520 | -------------------------------------------------------------------------------- /jquery.fileapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery plugin for FileAPI v2+ 3 | * @auhtor RubaXa 4 | */ 5 | 6 | /*jslint evil: true */ 7 | /*global jQuery, FileAPI*/ 8 | (function ($, api){ 9 | "use strict"; 10 | 11 | var 12 | noop = $.noop 13 | , oldJQ = !$.fn.prop 14 | , propFn = oldJQ ? 'attr' : 'prop' 15 | , removePropFn = oldJQ ? 'removeAttr' : 'removeProp' 16 | 17 | , _dataAttr = 'data-fileapi' 18 | , _dataFileId = 'data-fileapi-id' 19 | 20 | , _slice = [].slice 21 | , _each = api.each 22 | , _extend = api.extend 23 | 24 | , _bind = function (ctx, fn) { 25 | var args = _slice.call(arguments, 2); 26 | return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function (){ 27 | return fn.apply(ctx, args.concat(_slice.call(arguments))); 28 | }; 29 | } 30 | 31 | , _optDataAttr = function (name){ 32 | return '['+_dataAttr+'="'+name+'"]'; 33 | } 34 | 35 | , _isID = function (selector){ 36 | return selector.indexOf('#') === 0; 37 | } 38 | ; 39 | 40 | 41 | 42 | var Plugin = function (el, options){ 43 | this.$el = el = $(el).on('change.fileapi', 'input[type="file"]', _bind(this, this._onSelect)); 44 | this.el = el[0]; 45 | 46 | this._options = {}; // previous options 47 | this.options = { // current options 48 | url: 0, 49 | data: {}, // additional POST-data 50 | accept: 0, // accept mime types, "*" — unlimited 51 | multiple: false, // single or multiple mode upload mode 52 | paramName: 0, // POST-parameter name 53 | dataType: 'json', 54 | duplicate: false, // ignore duplicate 55 | 56 | uploadRetry: 0, 57 | networkDownRetryTimeout: 5000, 58 | 59 | chunkSize: 0, // or chunk size in bytes, eg: .5 * FileAPI.MB 60 | chunkUploadRetry: 3, // number of retries during upload chunks (html5) 61 | chunkNetworkDownRetryTimeout: 2000, 62 | 63 | maxSize: 0, // max file size, 0 — unlimited 64 | maxFiles: 0, // 0 unlimited 65 | imageSize: 0, // { minWidth: 320, minHeight: 240, maxWidth: 3840, maxHeight: 2160 } 66 | 67 | sortFn: 0, 68 | filterFn: 0, 69 | autoUpload: false, 70 | 71 | clearOnSelect: void 0, 72 | clearOnComplete: void 0, 73 | 74 | lang: { 75 | B: 'bytes' 76 | , KB: 'KB' 77 | , MB: 'MB' 78 | , GB: 'GB' 79 | , TB: 'TB' 80 | }, 81 | sizeFormat: '0.00', 82 | 83 | imageOriginal: true, 84 | imageTransform: 0, 85 | imageAutoOrientation: !!FileAPI.support.exif, 86 | 87 | elements: { 88 | ctrl: { 89 | upload: _optDataAttr('ctrl.upload'), 90 | reset: _optDataAttr('ctrl.reset'), 91 | abort: _optDataAttr('ctrl.abort') 92 | }, 93 | empty: { 94 | show: _optDataAttr('empty.show'), 95 | hide: _optDataAttr('empty.hide') 96 | }, 97 | emptyQueue: { 98 | show: _optDataAttr('emptyQueue.show'), 99 | hide: _optDataAttr('emptyQueue.hide') 100 | }, 101 | active: { 102 | show: _optDataAttr('active.show'), 103 | hide: _optDataAttr('active.hide') 104 | }, 105 | size: _optDataAttr('size'), 106 | name: _optDataAttr('name'), 107 | progress: _optDataAttr('progress'), 108 | list: _optDataAttr('list'), 109 | file: { 110 | tpl: _optDataAttr('file.tpl'), 111 | progress: _optDataAttr('file.progress'), 112 | active: { 113 | show: _optDataAttr('file.active.show'), 114 | hide: _optDataAttr('file.active.hide') 115 | }, 116 | preview: { 117 | el: 0, 118 | get: 0, 119 | width: 0, 120 | height: 0, 121 | processing: 0 122 | }, 123 | abort: _optDataAttr('file.abort'), 124 | remove: _optDataAttr('file.remove'), 125 | rotate: _optDataAttr('file.rotate') 126 | }, 127 | dnd: { 128 | el: _optDataAttr('dnd'), 129 | hover: 'dnd_hover', 130 | fallback: _optDataAttr('dnd.fallback') 131 | } 132 | }, 133 | 134 | onDrop: noop, 135 | onDropHover: noop, 136 | 137 | onSelect: noop, 138 | 139 | onBeforeUpload: noop, 140 | onUpload: noop, 141 | onProgress: noop, 142 | onComplete: noop, 143 | 144 | onFilePrepare: noop, 145 | onFileUpload: noop, 146 | onFileProgress: noop, 147 | onFileComplete: noop, 148 | 149 | onFileRemove: null, 150 | onFileRemoveCompleted: null 151 | }; 152 | 153 | $.extend(true, this.options, options); // deep extend 154 | options = this.options; 155 | 156 | this.option('elements.file.preview.rotate', options.imageAutoOrientation); 157 | 158 | if( !options.url ){ 159 | var url = this.$el.attr('action') || this.$el.find('form').attr('action'); 160 | if( url ){ 161 | options.url = url; 162 | } else { 163 | this._throw('url — is not defined'); 164 | } 165 | } 166 | 167 | 168 | this.$files = this.$elem('list'); 169 | 170 | this.$fileTpl = this.$elem('file.tpl'); 171 | this.itemTplFn = $.fn.fileapi.tpl( $('
').append( this.$elem('file.tpl') ).html() ); 172 | 173 | 174 | _each(options, function (value, option){ 175 | this._setOption(option, value); 176 | }, this); 177 | 178 | 179 | this.$el 180 | .on('reset.fileapi', _bind(this, this._onReset)) 181 | .on('submit.fileapi', _bind(this, this._onSubmit)) 182 | .on('upload.fileapi progress.fileapi complete.fileapi', _bind(this, this._onUploadEvent)) 183 | .on('fileupload.fileapi fileprogress.fileapi filecomplete.fileapi', _bind(this, this._onFileUploadEvent)) 184 | .on('click', '['+_dataAttr+']', _bind(this, this._onActionClick)) 185 | ; 186 | 187 | 188 | // Controls 189 | var ctrl = options.elements.ctrl; 190 | if( ctrl ){ 191 | this._listen('click', ctrl.reset, _bind(this, this._onReset)); 192 | this._listen('click', ctrl.upload, _bind(this, this._onSubmit)); 193 | this._listen('click', ctrl.abort, _bind(this, this._onAbort)); 194 | } 195 | 196 | // Drag'n'Drop 197 | var dnd = FileAPI.support.dnd; 198 | this.$elem('dnd.el', true).toggle(dnd); 199 | this.$elem('dnd.fallback').toggle(!dnd); 200 | 201 | if( dnd ){ 202 | this.$elem('dnd.el', true).dnd(_bind(this, this._onDropHover), _bind(this, this._onDrop)); 203 | } 204 | 205 | 206 | this.$progress = this.$elem('progress'); 207 | 208 | if( options.clearOnSelect === void 0 ){ 209 | options.clearOnSelect = !options.multiple; 210 | } 211 | 212 | this.clear(); 213 | 214 | if( $.isArray(options.files) ){ 215 | this.files = $.map(options.files, function (file){ 216 | if( $.type(file) === 'string' ){ 217 | file = { src: file, size: 0 }; 218 | } 219 | 220 | file.name = file.name || file.src.split('/').pop(); 221 | file.type = file.type || /\.(jpe?g|png|bmp|gif|tiff?)/i.test(file.src) && 'image/' + file.src.split('.').pop(); // @todo: use FileAPI.getMimeType (v2.1+) 222 | file.complete = true; 223 | 224 | return file; 225 | }); 226 | 227 | this._redraw(); 228 | } 229 | }; 230 | 231 | 232 | Plugin.prototype = { 233 | constructor: Plugin, 234 | 235 | _throw: function (msg){ 236 | throw "jquery.fileapi: " + msg; 237 | }, 238 | 239 | _getFiles: function (evt, fn){ 240 | var 241 | opts = this.options 242 | , maxSize = opts.maxSize 243 | , maxFiles = opts.maxFiles 244 | , filterFn = opts.filterFn 245 | , countFiles = this.files.length 246 | , files = api.getFiles(evt) 247 | , data = { 248 | all: files 249 | , files: [] 250 | , other: [] 251 | , duplicate: opts.duplicate ? [] : this._extractDuplicateFiles(files) 252 | } 253 | , imageSize = opts.imageSize 254 | , _this = this 255 | ; 256 | 257 | if( imageSize || filterFn ){ 258 | api.filterFiles(files, function (file, info){ 259 | if( info && imageSize ){ 260 | _checkFileByCriteria(file, 'minWidth', imageSize, info); 261 | _checkFileByCriteria(file, 'minHeight', imageSize, info); 262 | _checkFileByCriteria(file, 'maxWidth', imageSize, info); 263 | _checkFileByCriteria(file, 'maxHeight', imageSize, info); 264 | } 265 | 266 | _checkFileByCriteria(file, 'maxSize', maxSize, file.size); 267 | 268 | return !file.errors && (!filterFn || filterFn(file, info)); 269 | }, function (success, rejected){ 270 | _extractFilesOverLimit(maxFiles, countFiles, success, rejected); 271 | 272 | data.other = rejected; 273 | data.files = success; 274 | 275 | fn.call(_this, data); 276 | }); 277 | } else { 278 | _each(files, function (file){ 279 | _checkFileByCriteria(file, 'maxSize', maxSize, file.size); 280 | data[file.errors ? 'other' : 'files'].push(file); 281 | }); 282 | 283 | _extractFilesOverLimit(maxFiles, countFiles, data.files, data.other); 284 | fn.call(_this, data); 285 | } 286 | }, 287 | 288 | _extractDuplicateFiles: function (list/**Array*/){ 289 | var duplicates = [], i = list.length, files = this.files, j; 290 | 291 | while( i-- ){ 292 | j = files.length; 293 | while( j-- ){ 294 | if( this._fileCompare(list[i], files[j]) ){ 295 | duplicates.push( list.splice(i, 1) ); 296 | break; 297 | } 298 | } 299 | } 300 | 301 | return duplicates; 302 | }, 303 | 304 | _fileCompare: function (A/**File*/, B/**File*/){ 305 | return (A.size == B.size) && (A.name == B.name); 306 | }, 307 | 308 | _getFormatedSize: function (size){ 309 | var opts = this.options, postfix = 'B'; 310 | 311 | if( size >= api.TB ){ 312 | size /= api[postfix = 'TB']; 313 | } 314 | else if( size >= api.GB ){ 315 | size /= api[postfix = 'GB']; 316 | } 317 | else if( size >= api.MB ){ 318 | size /= api[postfix = 'MB']; 319 | } 320 | else if( size >= api.KB ){ 321 | size /= api[postfix = 'KB']; 322 | } 323 | 324 | return opts.sizeFormat.replace(/^\d+([^\d]+)(\d*)/, function (_, separator, fix){ 325 | size = (parseFloat(size) || 0).toFixed(fix.length); 326 | return (size + '').replace('.', separator) +' '+ opts.lang[postfix]; 327 | }); 328 | }, 329 | 330 | _onSelect: function (evt){ 331 | if( this.options.clearOnSelect ){ 332 | this.queue = []; 333 | this.files = []; 334 | } 335 | 336 | this._getFiles(evt, _bind(this, function (data){ 337 | if( data.all.length && this.emit('select', data) !== false ){ 338 | this.add(data.files); 339 | } 340 | 341 | // Reset input 342 | FileAPI.reset(evt.target); 343 | })); 344 | }, 345 | 346 | _onActionClick: function (evt){ 347 | var 348 | el = evt.currentTarget 349 | , act = $.attr(el, _dataAttr) 350 | , uid = $(el).closest('['+_dataFileId+']', this.$el).attr(_dataFileId) 351 | , file = this._getFile(uid) 352 | , prevent = true 353 | ; 354 | 355 | if( !this.$file(uid).attr('disabled') ){ 356 | if( 'file.remove' == act ){ 357 | if( file && this.emit('fileRemove' + (file.complete ? 'Completed' : ''), file) ){ 358 | this.remove(uid); 359 | } 360 | } 361 | else if( /^file\.rotate/.test(act) ){ 362 | this.rotate(uid, (/ccw/.test(act) ? '-=90' : '+=90')); 363 | } 364 | else { 365 | prevent = false; 366 | } 367 | } 368 | 369 | if( prevent ){ 370 | evt.preventDefault(); 371 | } 372 | }, 373 | 374 | _listen: function (name, selector, fn){ 375 | selector && _each($.trim(selector).split(','), function (selector){ 376 | selector = $.trim(selector); 377 | 378 | if( _isID(selector) ){ 379 | $(selector).on(name+'.fileapi', fn); 380 | } else { 381 | this.$el.on(name+'.fileapi', selector, fn); 382 | } 383 | }, this); 384 | }, 385 | 386 | _onSubmit: function (evt){ 387 | evt.preventDefault(); 388 | this.upload(); 389 | }, 390 | 391 | _onReset: function (evt){ 392 | evt.preventDefault(); 393 | this.clear(true); 394 | }, 395 | 396 | _onAbort: function (evt){ 397 | evt.preventDefault(); 398 | this.abort(); 399 | }, 400 | 401 | _onDrop: function (files){ 402 | this._getFiles(files, function (data){ 403 | if( this.emit('drop', data) !== false ){ 404 | this.add(data.files); 405 | } 406 | }); 407 | }, 408 | 409 | _onDropHover: function (state, evt){ 410 | if( this.emit('dropHover', { state: state, event: evt }) !== false ){ 411 | var hover = this.option('elements.dnd.hover'); 412 | if( hover ){ 413 | $(evt.currentTarget).toggleClass(hover, state); 414 | } 415 | } 416 | }, 417 | 418 | _getFile: function (uid){ 419 | return api.filter(this.files, function (file){ return api.uid(file) == uid; })[0]; 420 | }, 421 | 422 | _getUploadEvent: function (xhr, extra){ 423 | xhr = this.xhr || xhr; 424 | 425 | var evt = { 426 | xhr: xhr 427 | , file: this.xhr.currentFile 428 | , files: this.xhr.files 429 | , widget: this 430 | }; 431 | 432 | return _extend(evt, extra); 433 | }, 434 | 435 | _emitUploadEvent: function (prefix, file, xhr){ 436 | var evt = this._getUploadEvent(xhr); 437 | this.emit(prefix+'Upload', evt); 438 | }, 439 | 440 | _emitProgressEvent: function (prefix, event, file, xhr){ 441 | var evt = this._getUploadEvent(xhr, event); 442 | this.emit(prefix+'Progress', evt); 443 | }, 444 | 445 | _emitCompleteEvent: function (prefix, err, xhr, file){ 446 | var evt = this._getUploadEvent(xhr, { 447 | error: err 448 | , status: xhr.status 449 | , statusText: xhr.statusText 450 | , result: xhr.responseText 451 | }); 452 | 453 | if( prefix == 'file' ){ 454 | file.complete = true; 455 | } 456 | 457 | if( this.options.dataType == 'json' ){ 458 | try { 459 | evt.result = $.parseJSON(evt.result); 460 | } catch (err){ 461 | evt.error = err; 462 | } 463 | } 464 | 465 | this.emit(prefix+'Complete', evt); 466 | }, 467 | 468 | _onUploadEvent: function (evt, ui){ 469 | var _this = this, $progress = _this.$progress, type = evt.type; 470 | 471 | if( type == 'progress' ){ 472 | $progress.stop().animate({ width: ui.loaded/ui.total*100 + '%' }, 300); 473 | } 474 | else if( type == 'upload' ){ 475 | // Начало загрузки 476 | $progress.width(0); 477 | } 478 | else { 479 | // Завершение загрузки 480 | var fn = function (){ 481 | $progress.dequeue(); 482 | _this[_this.options.clearOnComplete ? 'clear' : 'dequeue'](); 483 | }; 484 | 485 | this.xhr = null; 486 | this.active = false; 487 | 488 | if( $progress.length ){ 489 | $progress.queue(fn); 490 | } else { 491 | fn(); 492 | } 493 | } 494 | }, 495 | 496 | _onFileUploadPrepare: function (file, opts){ 497 | var 498 | uid = api.uid(file) 499 | , deg = this._rotate[uid] 500 | , crop = this._crop[uid] 501 | , resize = this._resize[uid] 502 | , evt = this._getUploadEvent(this.xhr) 503 | ; 504 | 505 | if( deg || crop || resize ){ 506 | var trans = $.extend(true, {}, opts.imageTransform || {}); 507 | deg = (deg != null) ? deg : (this.options.imageAutoOrientation ? 'auto' : void 0); 508 | 509 | if( $.isEmptyObject(trans) || _isOriginTransform(trans) ){ 510 | _extend(trans, resize); 511 | 512 | trans.crop = crop; 513 | trans.rotate = deg; 514 | } 515 | else { 516 | _each(trans, function (opts){ 517 | _extend(opts, resize); 518 | 519 | opts.crop = crop; 520 | opts.rotate = deg; 521 | }); 522 | } 523 | 524 | opts.imageTransform = trans; 525 | } 526 | 527 | evt.file = file; 528 | evt.options = opts; 529 | 530 | this.emit('filePrepare', evt); 531 | }, 532 | 533 | _onFileUploadEvent: function (evt, ui){ 534 | var 535 | _this = this 536 | , type = evt.type.substr(4) 537 | , uid = api.uid(ui.file) 538 | , $file = this.$file(uid) 539 | , $progress = this._$fileprogress 540 | , opts = this.options 541 | ; 542 | 543 | if( this.__fileId !== uid ){ 544 | this.__fileId = uid; 545 | this._$fileprogress = $progress = this.$elem('file.progress', $file); 546 | } 547 | 548 | if( type == 'progress' ){ 549 | $progress.stop().animate({ width: ui.loaded/ui.total*100 + '%' }, 300); 550 | } 551 | else if( type == 'upload' || type == 'complete' ){ 552 | var fn = function (){ 553 | var 554 | elem = 'file.'+ type 555 | , $remove = _this.$elem('file.remove', $file) 556 | ; 557 | 558 | if( type == 'upload' ){ 559 | $remove.hide(); 560 | $progress.width(0); 561 | } else if( opts.onFileRemoveCompleted ){ 562 | $remove.show(); 563 | } 564 | 565 | $progress.dequeue(); 566 | 567 | _this.$elem(elem + '.show', $file).show(); 568 | _this.$elem(elem + '.hide', $file).hide(); 569 | }; 570 | 571 | if( $progress.length ){ 572 | $progress.queue(fn); 573 | } else { 574 | fn(); 575 | } 576 | 577 | if( type == 'complete' ){ 578 | this.uploaded.push(ui.file); 579 | delete this._rotate[uid]; 580 | } 581 | } 582 | }, 583 | 584 | _redraw: function (clearFiles/**Boolean*/, clearPreview/**Boolean*/){ 585 | var 586 | files = this.files 587 | , active = !!this.active 588 | , empty = !files.length && !active 589 | , emptyQueue = !this.queue.length && !active 590 | , name = [] 591 | , size = 0 592 | , $files = this.$files 593 | , offset = $files.children().length 594 | , preview = this.option('elements.preview') 595 | , filePreview = this.option('elements.file.preview') 596 | ; 597 | 598 | if( clearFiles ){ 599 | this.$files.empty(); 600 | } 601 | 602 | if( clearPreview && preview && preview.el && !this.queue.length ) { 603 | this.$(preview.el).empty(); 604 | } 605 | 606 | _each(files, function (file, i){ 607 | var uid = api.uid(file); 608 | 609 | name.push(file.name); 610 | size += file.complete ? 0 : file.size; 611 | 612 | if( preview && preview.el ){ 613 | this._makeFilePreview(uid, file, preview, true); 614 | } 615 | else if( $files.length && !this.$file(uid).length ){ 616 | var 617 | html = this.itemTplFn({ 618 | $idx: offset + i 619 | , uid: uid 620 | , name: file.name 621 | , type: file.type 622 | , size: file.size 623 | , complete: !!file.complete 624 | , sizeText: this._getFormatedSize(file.size) 625 | }) 626 | 627 | , $file = $(html).attr(_dataFileId, uid) 628 | ; 629 | 630 | file.$el = $file; 631 | $files.append( $file ); 632 | 633 | if( file.complete ){ 634 | this.$elem('file.upload.hide', $file).hide(); 635 | this.$elem('file.complete.hide', $file).hide(); 636 | } 637 | 638 | if( filePreview.el ){ 639 | this._makeFilePreview(uid, file, filePreview); 640 | } 641 | } 642 | }, this); 643 | 644 | 645 | this.$elem('name').text( name.join(', ') ); 646 | this.$elem('size').text( !emptyQueue ? this._getFormatedSize(size) : '' ); 647 | 648 | 649 | this.$elem('empty.show').toggle( empty ); 650 | this.$elem('empty.hide').toggle( !empty ); 651 | 652 | 653 | this.$elem('emptyQueue.show').toggle( emptyQueue ); 654 | this.$elem('emptyQueue.hide').toggle( !emptyQueue ); 655 | 656 | 657 | this.$elem('active.show').toggle( active ); 658 | this.$elem('active.hide').toggle( !active ); 659 | 660 | 661 | this.$('.js-fileapi-wrapper,:file') 662 | [active ? 'attr' : 'removeAttr']('aria-disabled', active) 663 | [propFn]('disabled', active) 664 | ; 665 | 666 | // Upload control 667 | this._disableElem('ctrl.upload', emptyQueue || active); 668 | 669 | // Reset control 670 | this._disableElem('ctrl.reset', emptyQueue || active); 671 | 672 | // Abort control 673 | this._disableElem('ctrl.abort', !active); 674 | }, 675 | 676 | _disableElem: function (name, state){ 677 | this.$elem(name) 678 | [state ? 'attr' : 'removeAttr']('aria-disabled', 'disabled') 679 | [propFn]('disabled', state) 680 | ; 681 | }, 682 | 683 | _makeFilePreview: function (uid, file, opts, global){ 684 | var 685 | _this = this 686 | , $el = global ? _this.$(opts.el) : _this.$file(uid).find(opts.el) 687 | ; 688 | 689 | if( !_this._crop[uid] ){ 690 | if( /^image/.test(file.type) ){ 691 | var 692 | image = api.Image(file.src || file) 693 | , doneFn = function (){ 694 | image.get(function (err, img){ 695 | if( !_this._crop[uid] ){ 696 | if( err ){ 697 | opts.get && opts.get($el, file); 698 | _this.emit('previewError', [err, file]); 699 | } else { 700 | $el.html(img); 701 | } 702 | } 703 | }); 704 | } 705 | ; 706 | 707 | if( opts.width ){ 708 | image.preview(opts.width, opts.height); 709 | } 710 | 711 | if( opts.rotate ){ 712 | image.rotate('auto'); 713 | } 714 | 715 | if( opts.processing ){ 716 | opts.processing(file, image, doneFn); 717 | } else { 718 | doneFn(); 719 | } 720 | } 721 | else { 722 | opts.get && opts.get($el, file); 723 | } 724 | } 725 | }, 726 | 727 | emit: function (name, arg){ 728 | var opts = this.options, evt = $.Event(name.toLowerCase()), res; 729 | evt.widget = this; 730 | name = $.camelCase('on-'+name); 731 | if( $.isFunction(opts[name]) ){ 732 | res = opts[name].call(this.el, evt, arg); 733 | } 734 | this.$el.triggerHandler(evt, arg); 735 | return (res !== false) && !evt.isDefaultPrevented(); 736 | }, 737 | 738 | /** 739 | * Add files to queue 740 | * @param {Array} files 741 | * @param {Boolean} [clear] 742 | */ 743 | add: function (files, clear){ 744 | files = [].concat(files); 745 | 746 | if( files.length ){ 747 | var 748 | opts = this.options 749 | , sortFn = opts.sortFn 750 | ; 751 | 752 | if( sortFn ){ 753 | files.sort(sortFn); 754 | } 755 | 756 | if( this.xhr ){ 757 | this.xhr.append(files); 758 | } 759 | 760 | this.queue = clear ? files : this.queue.concat(files); 761 | this.files = clear ? files : this.files.concat(files); 762 | 763 | if( this.active ){ 764 | this.xhr.append(files); 765 | this._redraw(clear); 766 | } 767 | else { 768 | this._redraw(clear); 769 | if( this.options.autoUpload ){ 770 | this.upload(); 771 | } 772 | } 773 | } 774 | }, 775 | 776 | /** 777 | * Find element 778 | * @param {String} sel 779 | * @param {jQuery} [ctx] 780 | * @return {jQuery} 781 | */ 782 | $: function (sel, ctx){ 783 | if( typeof sel === 'string' ){ 784 | sel = /^#/.test(sel) ? sel : (ctx ? $(ctx) : this.$el).find(sel); 785 | } 786 | return $(sel); 787 | }, 788 | 789 | /** 790 | * @param {String} name 791 | * @param {Boolean|jQuery} [up] 792 | * @param {jQuery} [ctx] 793 | * @return {jQuery} 794 | */ 795 | $elem: function (name, up, ctx){ 796 | if( up && up.jquery ){ 797 | ctx = up; 798 | up = false; 799 | } 800 | 801 | var sel = this.option('elements.'+name); 802 | if( sel === void 0 && up ){ 803 | sel = this.option('elements.'+name.substr(0, name.lastIndexOf('.'))); 804 | } 805 | 806 | return this.$($.type(sel) != 'string' && $.isEmptyObject(sel) ? [] : sel, ctx); 807 | }, 808 | 809 | /** 810 | * @param {String} uid 811 | * @return {jQuery} 812 | */ 813 | $file: function (uid){ 814 | return this.$('['+_dataFileId+'="'+uid+'"]'); 815 | }, 816 | 817 | /** 818 | * Get/set options 819 | * @param {String} name 820 | * @param {*} [value] 821 | * @return {*} 822 | */ 823 | option: function (name, value){ 824 | if( value !== void 0 && $.isPlainObject(value) ){ 825 | _each(value, function (val, key){ 826 | this.option(name+'.'+key, val); 827 | }, this); 828 | 829 | return this; 830 | } 831 | 832 | var opts = this.options, val = opts[name], i = 0, len, part; 833 | 834 | if( name.indexOf('.') != -1 ){ 835 | val = opts; 836 | name = name.split('.'); 837 | len = name.length; 838 | 839 | for( ; i < len; i++ ){ 840 | part = name[i]; 841 | 842 | if( (value !== void 0) && (len - i === 1) ){ 843 | val[part] = value; 844 | break; 845 | } 846 | else if( !val[part] ){ 847 | val[part] = {}; 848 | } 849 | 850 | val = val[part]; 851 | } 852 | } 853 | else if( value !== void 0 ){ 854 | opts[name] = value; 855 | } 856 | 857 | if( value !== void 0 ){ 858 | this._setOption(name, value, this._options[name]); 859 | this._options[name] = value; 860 | } 861 | 862 | return value !== void 0 ? value : val; 863 | }, 864 | 865 | _setOption: function (name, nVal){ 866 | switch( name ){ 867 | case 'accept': 868 | case 'multiple': 869 | this.$(':file')[nVal ? propFn : removePropFn](name, nVal); 870 | break; 871 | 872 | 873 | case 'paramName': 874 | nVal && this.$(':file')[propFn]('name', nVal); 875 | break; 876 | } 877 | }, 878 | 879 | serialize: function (){ 880 | var obj = {}, val; 881 | 882 | this.$el.find(':input').each(function(name, node){ 883 | if( 884 | (name = node.name) && !node.disabled 885 | && (node.checked || /select|textarea|input/i.test(node.nodeName) && !/checkbox|radio|file/i.test(node.type)) 886 | ){ 887 | val = $(node).val(); 888 | if( obj[name] !== void 0 ){ 889 | if( !obj[name].push ){ 890 | obj[name] = [obj[name]]; 891 | } 892 | 893 | obj[name].push(val); 894 | } else { 895 | obj[name] = val; 896 | } 897 | } 898 | }); 899 | 900 | return obj; 901 | }, 902 | 903 | upload: function (){ 904 | if( !this.active && this.emit('beforeUpload', { widget: this, files: this.queue }) ){ 905 | this.active = true; 906 | 907 | var 908 | $el = this.$el 909 | , opts = this.options 910 | , files = {} 911 | , uploadOpts = { 912 | url: opts.url 913 | , data: _extend({}, this.serialize(), opts.data) 914 | , headers: opts.headers 915 | , files: files 916 | 917 | , uploadRetry: opts.uploadRetry 918 | , networkDownRetryTimeout: opts.networkDownRetryTimeout 919 | , chunkSize: opts.chunkSize 920 | , chunkUploadRetry: opts.chunkUploadRetry 921 | , chunkNetworkDownRetryTimeout: opts.chunkNetworkDownRetryTimeout 922 | 923 | , prepare: _bind(this, this._onFileUploadPrepare) 924 | , imageOriginal: opts.imageOriginal 925 | , imageTransform: opts.imageTransform 926 | , imageAutoOrientation: opts.imageAutoOrientation 927 | } 928 | ; 929 | 930 | // Set files 931 | files[$el.find(':file').attr('name') || 'files[]'] = this.queue; 932 | 933 | // Add event listeners 934 | _each(['Upload', 'Progress', 'Complete'], function (name){ 935 | var lowerName = name.toLowerCase(); 936 | uploadOpts[lowerName] = _bind(this, this['_emit'+name+'Event'], ''); 937 | uploadOpts['file'+lowerName] = _bind(this, this['_emit'+name+'Event'], 'file'); 938 | }, this); 939 | 940 | // Start uploading 941 | this.xhr = api.upload(uploadOpts); 942 | this._redraw(); 943 | } 944 | }, 945 | 946 | abort: function (text){ 947 | if( this.active && this.xhr ){ 948 | this.xhr.abort(text); 949 | } 950 | }, 951 | 952 | crop: function (file, coords){ 953 | var 954 | uid = api.uid(file) 955 | , opts = this.options 956 | , preview = opts.multiple ? this.option('elements.file.preview') : opts.elements.preview 957 | , $el = (opts.multiple ? this.$file(uid) : this.$el).find(preview && preview.el) 958 | ; 959 | 960 | if( $el.length ){ 961 | api.getInfo(file, _bind(this, function (err, info){ 962 | if( err ){ 963 | this.emit('previewError', [err, file]); 964 | } else { 965 | if( !$el.find('div>div').length ){ 966 | $el.html( 967 | $('
') 968 | .css(preview) 969 | .css('overflow', 'hidden') 970 | ); 971 | } 972 | 973 | if( this.__cropFile !== file ){ 974 | this.__cropFile = file; 975 | api.Image(file).rotate(opts.imageAutoOrientation ? 'auto' : 0).get(function (err, img){ 976 | $el.find('>div>div').html($(img).width('100%').height('100%')); 977 | }, 'exactFit'); 978 | } 979 | 980 | 981 | var 982 | pw = preview.width, ph = preview.height 983 | , mx = pw, my = ph 984 | , rx = pw/coords.rw, ry = ph/coords.rh 985 | ; 986 | 987 | if( preview.keepAspectRatio ){ 988 | if (rx > 1 && ry > 1){ // image is smaller than preview (no scale) 989 | rx = ry = 1; 990 | my = coords.h; 991 | mx = coords.w; 992 | 993 | } else { // image is bigger than preview (scale) 994 | if( rx < ry ){ 995 | ry = rx; 996 | my = pw * coords.rh / coords.rw; 997 | } else { 998 | rx = ry; 999 | mx = ph * coords.rw / coords.rh; 1000 | } 1001 | } 1002 | } 1003 | 1004 | $el.find('>div>div').css({ 1005 | width: Math.round(rx * info[coords.flip ? 'height' : 'width']) 1006 | , height: Math.round(ry * info[coords.flip ? 'width' : 'height']) 1007 | , marginLeft: -Math.round(rx * coords.rx) 1008 | , marginTop: -Math.round(ry * coords.ry) 1009 | }); 1010 | 1011 | if( preview.keepAspectRatio ){ // create side gaps 1012 | $el.find('>div').css({ 1013 | width: Math.round(mx) 1014 | , height: Math.round(my) 1015 | , marginLeft: mx < pw ? Math.round((pw - mx) / 2) : 0 1016 | , marginTop: my < ph ? Math.round((ph - my) / 2) : 0 1017 | }); 1018 | } 1019 | } 1020 | })); 1021 | } 1022 | 1023 | this._crop[uid] = coords; 1024 | }, 1025 | 1026 | resize: function (file, width, height, type){ 1027 | this._resize[api.uid(file)] = { 1028 | type: type 1029 | , width: width 1030 | , height: height 1031 | }; 1032 | }, 1033 | 1034 | rotate: function (file, deg){ 1035 | var 1036 | uid = $.type(file) == 'string' ? file : api.uid(file) 1037 | , opts = this.options 1038 | , preview = opts.multiple ? this.option('elements.file.preview') : opts.elements.preview 1039 | , $el = (opts.multiple ? this.$file(uid) : this.$el).find(preview && preview.el) 1040 | , _rotate = this._rotate 1041 | ; 1042 | 1043 | file = this._getFile(uid); 1044 | 1045 | api.getInfo(file, function (err, info) { 1046 | var orientation = info && info.exif && info.exif.Orientation, 1047 | startDeg = opts.imageAutoOrientation && api.Image.exifOrientation[orientation] || 0; 1048 | 1049 | if (_rotate[uid] == null) { 1050 | _rotate[uid] = startDeg || 0; 1051 | } 1052 | 1053 | if( /([+-])=/.test(deg) ){ 1054 | _rotate[uid] = deg = (_rotate[uid] + (RegExp.$1 == '+' ? 1 : -1) * deg.substr(2)); 1055 | } else { 1056 | _rotate[uid] = deg; 1057 | } 1058 | 1059 | // Store deg 1060 | file.rotate = deg; 1061 | 1062 | // Fix exif.rotate.auto 1063 | deg -= startDeg; 1064 | $el.css({ 1065 | '-webkit-transform': 'rotate('+deg+'deg)' 1066 | , '-moz-transform': 'rotate('+deg+'deg)' 1067 | , 'transform': 'rotate('+deg+'deg)' 1068 | }); 1069 | }); 1070 | }, 1071 | 1072 | remove: function (file){ 1073 | var uid = typeof file == 'object' ? api.uid(file) : file; 1074 | 1075 | this.$file(uid).remove(); 1076 | 1077 | this.queue = api.filter(this.queue, function (file){ return api.uid(file) != uid; }); 1078 | this.files = api.filter(this.files, function (file){ return api.uid(file) != uid; }); 1079 | 1080 | this._redraw(); 1081 | }, 1082 | 1083 | clear: function (all) { 1084 | this._crop = {}; 1085 | this._resize = {}; 1086 | this._rotate = {}; // rotate deg 1087 | 1088 | this.queue = []; 1089 | this.files = []; // all files 1090 | this.uploaded = []; // uploaded files 1091 | 1092 | all = all === void 0 ? true : all; 1093 | this._redraw(all, all); 1094 | }, 1095 | 1096 | dequeue: function (){ 1097 | this.queue = []; 1098 | this._redraw(); 1099 | }, 1100 | 1101 | widget: function (){ 1102 | return this; 1103 | }, 1104 | 1105 | toString: function (){ 1106 | return '[jQuery.FileAPI object]'; 1107 | }, 1108 | 1109 | destroy: function (){ 1110 | this.$files.empty().append(this.$fileTpl); 1111 | 1112 | this.$el 1113 | .off('.fileapi') 1114 | .removeData('fileapi') 1115 | ; 1116 | 1117 | _each(this.options.elements.ctrl, function (selector){ 1118 | _isID(selector) && $(selector).off('click.fileapi'); 1119 | }); 1120 | } 1121 | }; 1122 | 1123 | 1124 | function _isOriginTransform(trans){ 1125 | var key; 1126 | for( key in trans ){ 1127 | if( trans.hasOwnProperty(key) ){ 1128 | if( !(trans[key] instanceof Object || key === 'overlay') ){ 1129 | return true; 1130 | } 1131 | } 1132 | } 1133 | return false; 1134 | } 1135 | 1136 | 1137 | function _checkFileByCriteria(file, name, imageSize, info){ 1138 | if( imageSize && info ){ 1139 | var excepted = imageSize > 0 ? imageSize : imageSize[name], 1140 | actual = info > 0 ? info : info[name.substr(3).toLowerCase()], 1141 | val = (excepted - actual), 1142 | isMax = /max/.test(name) 1143 | ; 1144 | 1145 | if( (isMax && val < 0) || (!isMax && val > 0) ){ 1146 | if( !file.errors ){ 1147 | file.errors = {}; 1148 | } 1149 | file.errors[name] = Math.abs(val); 1150 | } 1151 | } 1152 | } 1153 | 1154 | 1155 | function _extractFilesOverLimit(limit, countFiles, files, other){ 1156 | if( limit ){ 1157 | var delta = files.length - (limit - countFiles); 1158 | if( delta > 0 ){ 1159 | _each(files.splice(0, delta), function (file, i){ 1160 | _checkFileByCriteria(file, 'maxFiles', -1, i); 1161 | other.push(file); 1162 | }); 1163 | } 1164 | } 1165 | } 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | /** 1172 | * @export 1173 | * @param {Object} options 1174 | * @param {String} [value] 1175 | */ 1176 | $.fn.fileapi = function (options, value){ 1177 | var plugin = this.data('fileapi'); 1178 | 1179 | if( plugin ){ 1180 | if( options === 'widget' ){ 1181 | return plugin; 1182 | } 1183 | 1184 | if( typeof options == 'string' ){ 1185 | var fn = plugin[options], res; 1186 | 1187 | if( $.isFunction(fn) ){ 1188 | res = fn.apply(plugin, _slice.call(arguments, 1)); 1189 | } 1190 | else if( fn === void 0 ){ 1191 | res = plugin.option(options, value); 1192 | } 1193 | else if( options === 'files' ){ 1194 | res = fn; 1195 | } 1196 | 1197 | return res === void 0 ? this : res; 1198 | } 1199 | } else if( options == null || typeof options == 'object' ){ 1200 | this.data('fileapi', new Plugin(this, options)); 1201 | } 1202 | 1203 | return this; 1204 | }; 1205 | 1206 | 1207 | $.fn.fileapi.version = '0.4.11'; 1208 | $.fn.fileapi.tpl = function (text){ 1209 | var index = 0; 1210 | var source = "__b+='"; 1211 | 1212 | text.replace(/(?:<|<)%([-=])?([\s\S]+?)%(?:>|>)|$/g, function (match, mode, expr, offset){ 1213 | source += text.slice(index, offset).replace(/[\r\n"']/g, function (match){ return '\\'+match; }); 1214 | 1215 | if( expr ){ 1216 | if( mode ){ 1217 | source += "'+\n((__x=("+ expr +"))==null?'':" + (mode == "-" ? "__esc(__x)" : "__x")+")\n+'"; 1218 | } else { 1219 | source += "';\n"+ expr +"\n__b+='"; 1220 | } 1221 | } 1222 | 1223 | index = offset + match.length; 1224 | return match; 1225 | }); 1226 | 1227 | return new Function("ctx", "var __x,__b=''," + 1228 | "__esc=function(val){return typeof val=='string'?val.replace(/ 0 ){ 1289 | opts.aspectRatio = ratio; 1290 | } 1291 | 1292 | api.getInfo(file, function (err, info){ 1293 | var 1294 | Image = api.Image(file) 1295 | , maxSize = opts.maxSize 1296 | , deg = file.rotate 1297 | ; 1298 | 1299 | if( maxSize ){ 1300 | Image.resize( 1301 | Math.max(maxSize[0], minSize[0]) 1302 | , Math.max(maxSize[1], minSize[1]) 1303 | , 'max' 1304 | ); 1305 | } 1306 | 1307 | Image.rotate(deg === void 0 ? 'auto' : deg).get(function (err, img){ 1308 | var 1309 | selection = opts.selection 1310 | , minSide = Math.min(img.width, img.height) 1311 | 1312 | , selWidth = minSide 1313 | , selHeight = minSide / ratio 1314 | 1315 | , deg = FileAPI.Image.exifOrientation[info.exif && info.exif.Orientation] || 0 1316 | ; 1317 | 1318 | if( selection ){ 1319 | if( /%/.test(selection) || (selection > 0 && selection < 1) ){ 1320 | selection = parseFloat(selection, 10) / (selection > 0 ? 1 : 100); 1321 | selWidth *= selection; 1322 | selHeight *= selection; 1323 | } 1324 | 1325 | var 1326 | selLeft = (img.width - selWidth)/2 1327 | , selTop = (img.height - selHeight)/2 1328 | ; 1329 | 1330 | opts.setSelect = [selLeft|0, selTop|0, (selLeft + selWidth)|0, (selTop + selHeight)|0]; 1331 | } 1332 | 1333 | _each(['onSelect', 'onChange'], function (name, fn){ 1334 | if( fn = opts[name] ){ 1335 | opts[name] = function (coords){ 1336 | var 1337 | flip = deg % 180 1338 | , ow = info.width 1339 | , oh = info.height 1340 | , fx = coords.x/img.width 1341 | , fy = coords.y/img.height 1342 | , fw = coords.w/img.width 1343 | , fh = coords.h/img.height 1344 | , x = ow * (flip ? fy : fx) 1345 | , y = oh * (flip ? 1 - (coords.x + coords.w)/img.width : fy) 1346 | , w = ow * (flip ? fh : fw) 1347 | , h = oh * (flip ? fw : fh) 1348 | ; 1349 | 1350 | 1351 | fn({ 1352 | x: x 1353 | , y: y 1354 | , w: w 1355 | , h: h 1356 | , rx: fx * (flip ? oh : ow) 1357 | , ry: fy * (flip ? ow : oh) 1358 | , rw: fw * (flip ? oh : ow) 1359 | , rh: fh * (flip ? ow : oh) 1360 | , lx: coords.x // local coords 1361 | , ly: coords.y 1362 | , lw: coords.w 1363 | , lh: coords.h 1364 | , lx2: coords.x2 1365 | , ly2: coords.y2 1366 | , deg: deg 1367 | , flip: flip 1368 | }); 1369 | }; 1370 | } 1371 | }); 1372 | 1373 | var $inner = $('
').css('lineHeight', 0).append( $(img).css('margin', 0) ); 1374 | $el.html($inner); 1375 | $inner.Jcrop(opts).trigger('resize'); 1376 | }); 1377 | }); 1378 | } 1379 | 1380 | return $el; 1381 | }; 1382 | })(jQuery, FileAPI); 1383 | -------------------------------------------------------------------------------- /FileAPI/FileAPI.min.js: -------------------------------------------------------------------------------- 1 | /*! FileAPI 2.0.15 - BSD | git://github.com/mailru/FileAPI.git */ 2 | !function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;for(b=a.split(",")[0].indexOf("base64")>=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;h0,E=a.dataURLtoBlob,F=/img/i,G=/canvas/i,H=/img|canvas/i,I=/input/i,J=/^data:[^,]+,/,K={}.toString,L=a.Math,M=function(b){return b=new a.Number(L.pow(1024,b)),b.from=function(a){return L.round(a*this)},b},N={},O=[],P="abort progress error load loadend",Q="status statusText readyState response responseXML responseText responseBody".split(" "),R="currentTarget",S="preventDefault",T=function(a){return a&&"length"in a},U=function(a,b,c){if(a)if(T(a))for(var d=0,e=a.length;e>d;d++)d in a&&b.call(c,a[d],d,a);else for(var f in a)a.hasOwnProperty(f)&&b.call(c,a[f],f,a)},V=function(a){for(var b=arguments,c=1,d=function(b,c){a[c]=b};c=c&&!d&&f.end()},isFail:function(){return d},fail:function(){!d&&a(d=!0)},end:function(){e||(e=!0,a())}};return f},each:U,afor:function(a,b){var c=0,d=a.length;T(a)&&d--?!function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:V,isFile:function(a){return"[object File]"===K.call(a)},isBlob:function(a){return this.isFile(a)||"[object Blob]"===K.call(a)},isCanvas:function(a){return a&&G.test(a.nodeName)},getFilesFilter:function(a){return a="string"==typeof a?a:a.getAttribute&&a.getAttribute("accept")||"",a?new RegExp("("+a.replace(/\./g,"\\.").replace(/,/g,"|")+")$","i"):/./},readAsDataURL:function(a,b){_.isCanvas(a)?c(a,b,"load",_.toDataURL(a)):e(a,b,"DataURL")},readAsBinaryString:function(a,b){d("BinaryString")?e(a,b,"BinaryString"):e(a,function(a){if("load"==a.type)try{a.result=_.toBinaryString(a.result)}catch(c){a.type="error",a.message=c.toString()}b(a)},"DataURL")},readAsArrayBuffer:function(a,b){e(a,b,"ArrayBuffer")},readAsText:function(a,b,c){c||(c=b,b="utf-8"),e(a,c,"Text",b)},toDataURL:function(a,b){return"string"==typeof a?a:a.toDataURL?a.toDataURL(b||"image/png"):void 0},toBinaryString:function(b){return a.atob(_.toDataURL(b).replace(J,""))},readAsImage:function(a,d,e){if(_.isFile(a))if(t){var f=t.createObjectURL(a);f===b?c(a,d,"error"):_.readAsImage(f,d,e)}else _.readAsDataURL(a,function(b){"load"==b.type?_.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(_.isCanvas(a))c(a,d,"load",a);else if(F.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";Y(a,g,function i(b){"load"==b.type&&t&&t.revokeObjectURL(a.src),X(a,g,i),c(a,d,b,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=_.newImage(a.dataURL||a);_.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=_.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),U(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||_.ext2mime[b.type])&&(b.type=_.ext2mime[b.type]||c.split("/")[0]+"/"+b.type)}),b},getDropFiles:function(a,b){var c,d=[],e=[],j=l(a),k=j.files,m=j.items,n=T(m)&&m[0]&&h(m[0]),o=_.queue(function(){b(d,e)});if(n)if(D&&k){var p,q,r=k.length;for(c=new Array(r);r--;){p=k[r];try{q=h(m[r])}catch(s){_.log("[err] getDropFiles: ",s),q=null}g(q)&&(q.isDirectory||q.isFile&&p.name==p.name.normalize("NFC"))?c[r]=q:c[r]=p}}else c=m;else c=k;U(c||[],function(a){o.inc();try{n&&g(a)?i(a,function(a,b,c){a?_.log("[err] getDropFiles:",a):d.push.apply(d,b),e.push.apply(e,c),o.next()}):f(a,function(b,c){b?d.push(a):a.error=c,e.push(a),o.next()})}catch(b){o.next(),_.log("[err] getDropFiles: ",b)}}),o.check()},getFiles:function(a,b,c){var d=[];return c?(_.filterFiles(_.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(_.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=_.getFilesFilter(b)),a.originalEvent?a=Z(a.originalEvent):a.srcElement&&(a=Z(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,A||(d[0].blob=a,d[0].iframe=!0)):!A&&k(a)?_.trim(a.value)&&(d=[_.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):T(a)&&(d=a),_.filter(d,function(a){return!b||b.test(a.name)}))},getTotalSize:function(a){for(var b=0,c=a&&a.length;c--;)b+=a[c].size;return b},getInfo:function(a,b){var c={},d=O.concat();_.isFile(a)?!function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(V(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},O.push(b)},filter:function(a,b){for(var c,d=[],e=0,f=a.length;f>e;e++)e in a&&(c=a[e],b.call(c,c,e,a)&&d.push(c));return d},filterFiles:function(a,b,c){if(a.length){var d,e=a.concat(),f=[],g=[];!function h(){e.length?(d=e.shift(),_.getInfo(d,function(a,c){(b(d,a?!1:c)?f:g).push(d),h()})):c(f,g)}()}else c([],a)},upload:function(a){a=V({jsonp:"callback",prepare:_.F,beforeupload:_.F,upload:_.F,fileupload:_.F,fileprogress:_.F,filecomplete:_.F,progress:_.F,complete:_.F,pause:_.F,imageOriginal:!0,chunkSize:_.chunkSize,chunkUploadRetry:_.chunkUploadRetry,uploadRetry:_.uploadRetry},a),a.imageAutoOrientation&&!a.imageTransform&&(a.imageTransform={rotate:"auto"});var b,c=new _.XHR(a),d=this._getFilesDataArray(a.files),e=this,f=0,g=0,h=!1;return U(d,function(a){f+=a.size}),c.files=[],U(d,function(a){c.files.push(a.file)}),c.total=f,c.loaded=0,c.filesLeft=d.length,a.beforeupload(c,a),b=function(){var i=d.shift(),k=i&&i.file,l=!1,m=j(a);if(c.filesLeft=d.length,k&&k.name===_.expando&&(k=null,_.log("[warn] FileAPI.upload() — called without files")),("abort"!=c.statusText||c.current)&&i){if(h=!1,c.currentFile=k,k&&a.prepare(k,m)===!1)return void b.call(e);m.file=k,e._getFormData(m,i,function(h){g||a.upload(c,a);var j=new _.XHR(V({},m,{upload:k?function(){a.fileupload(k,j,m)}:o,progress:k?function(b){l||(l=b.loaded===b.total,a.fileprogress({type:"progress",total:i.total=b.total,loaded:i.loaded=b.loaded},k,j,m),a.progress({type:"progress",total:f,loaded:c.loaded=g+i.size*(b.loaded/b.total)||0},k,j,m))}:o,complete:function(d){U(Q,function(a){c[a]=j[a]}),k&&(i.total=i.total||i.size,i.loaded=i.total,d||(this.progress(i),l=!0,g+=i.size,c.loaded=g),a.filecomplete(d,j,k,m)),setTimeout(function(){b.call(e)},0)}}));c.abort=function(a){a||(d.length=0),this.current=a,j.abort()},j.send(h)})}else{var n=200==c.status||201==c.status||204==c.status;a.complete(n?!1:c.statusText||"error",c,a),h=!0}},setTimeout(b,0),c.append=function(a,g){a=_._getFilesDataArray([].concat(a)),U(a,function(a){f+=a.size,c.files.push(a.file),g?d.unshift(a):d.push(a)}),c.statusText="",h&&b.call(e)},c.remove=function(a){for(var b,c=d.length;c--;)d[c].file==a&&(b=d.splice(c,1),f-=b.size);return b},c},_getFilesDataArray:function(a){var b=[],c={};if(k(a)){var d=_.getFiles(a);c[a.name||"file"]=null!==a.getAttribute("multiple")?d:d[0]}else T(a)&&k(a[0])?U(a,function(a){c[a.name||"file"]=_.getFiles(a)}):c=a;return U(c,function e(a,c){T(a)?U(a,function(a){e(a,c)}):a&&(a.name||a.image)&&b.push({name:c,file:a,size:a.size,total:a.size,loaded:0})}),b.length||b.push({file:{name:_.expando}}),b},_getFormData:function(a,b,c){var d=b.file,e=b.name,f=d.name,g=d.type,h=_.support.transform&&a.imageTransform,i=new _.Form,j=_.queue(function(){c(i)}),k=h&&m(h),l=_.postNameConcat;U(a.data,function n(a,b){"object"==typeof a?U(a,function(a,c){n(a,l(b,c))}):i.append(b,a)}),function o(b){b.image?(j.inc(),b.toData(function(a,b){f=f||(new Date).getTime()+".png",o(b),j.next()})):_.Image&&h&&(/^image/.test(b.type)||H.test(b.nodeName))?(j.inc(),k&&(h=[h]),_.Image.transform(b,h,a.imageAutoOrientation,function(c,d){if(k&&!c)E||_.flashEngine||(i.multipart=!0),i.append(e,d[0],f,h[0].type||g);else{var m=0;c||U(d,function(a,b){E||_.flashEngine||(i.multipart=!0),h[b].postName||(m=1),i.append(h[b].postName||l(e,b),a,f,h[b].type||g)}),(c||a.imageOriginal)&&i.append(l(e,m?"original":null),b,f,g)}j.next()})):f!==_.expando&&i.append(e,b,f)}(d),j.check()},reset:function(a,b){var c,d;return z?(d=z(a).clone(!0).insertBefore(a).val("")[0],b||z(a).remove()):(c=a.parentNode,d=c.insertBefore(a.cloneNode(!0),a),d.value="",b||c.removeChild(a),U(N[_.uid(a)],function(b,c){U(b,function(b){X(a,c,b),W(d,c,b)})})),d},load:function(a,b){var c=_.getXHR();return c?(c.open("GET",a,!0),c.overrideMimeType&&c.overrideMimeType("text/plain; charset=x-user-defined"),W(c,"progress",function(a){a.lengthComputable&&b({type:a.type,loaded:a.loaded,total:a.total},c)}),c.onreadystatechange=function(){if(4==c.readyState)if(c.onreadystatechange=null,200==c.status){a=a.split("/");var d={name:a[a.length-1],size:c.getResponseHeader("Content-Length"),type:c.getResponseHeader("Content-Type")};d.dataURL="data:"+d.type+";base64,"+_.encode64(c.responseBody||c.responseText),b({type:"load",result:d},c)}else b({type:"error"},c)},c.send(null)):b({type:"error"}),c},encode64:function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",c="",d=0;for("string"!=typeof a&&(a=String(a));d>2,k=(3&g)<<4|h>>4;isNaN(h)?e=f=64:(e=(15&h)<<2|i>>6,f=isNaN(i)?64:63&i),c+=b.charAt(j)+b.charAt(k)+b.charAt(e)+b.charAt(f)}return c}};_.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=_.defer();_.readAsImage(a,function(a){var b=a.target;c.resolve("load"==a.type?!1:"error",{width:b.width,height:b.height}),b.src=_.EMPTY_PNG,b=null})}a.__dimensions.then(b)}),_.event.dnd=function(a,b,c){var d,e;c||(c=b,b=_.F),w?(W(a,"dragenter dragleave dragover",b.ff=b.ff||function(a){for(var c=l(a).types,f=c&&c.length,g=!1;f--;)if(~c[f].indexOf("File")){a[S](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[R],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[R],"dragleave"!=e,a)},50))}),W(a,"drop",c.ff=c.ff||function(a){a[S](),e=0,b.call(a[R],!1,a),_.getDropFiles(a,function(b,d){c.call(a[R],b,d,a)})})):_.log("Drag'n'Drop -- not supported")},_.event.dnd.off=function(a,b,c){X(a,"dragenter dragleave dragover",b.ff),X(a,"drop",c.ff)},z&&!z.fn.dnd&&(z.fn.dnd=function(a,b){return this.each(function(){_.event.dnd(this,a,b)})},z.fn.offdnd=function(a,b){return this.each(function(){_.event.dnd.off(this,a,b)})}),a.FileAPI=V(_,a.FileAPI),_.log("FileAPI: "+_.version),_.log("protocol: "+a.location.protocol),_.log("doctype: ["+q.name+"] "+q.publicId+" "+q.systemId),U(p.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&_.log("meta.http-equiv: "+a.getAttribute("content"))}),_.flashUrl||(_.flashUrl=_.staticPath+"FileAPI.flash.swf"),_.flashImageUrl||(_.flashImageUrl=_.staticPath+"FileAPI.flash.image.swf"),_.flashWebcamUrl||(_.flashWebcamUrl=_.staticPath+"FileAPI.flash.camera.swf")}(window,void 0),function(a,b,c){"use strict";function d(b){if(b instanceof d){var c=new d(b.file);return a.extend(c.matrix,b.matrix),c}return this instanceof d?(this.file=b,this.size=b.size||100,void(this.matrix={sx:0,sy:0,sw:0,sh:0,dx:0,dy:0,dw:0,dh:0,resize:0,deg:0,quality:1,filter:0})):new d(b)}var e=Math.min,f=Math.round,g=function(){return b.createElement("canvas")},h=!1,i={8:270,3:180,6:90,7:270,4:180,5:90};try{h=g().toDataURL("image/png").indexOf("data:image/png")>-1}catch(j){}d.prototype={image:!0,constructor:d,set:function(b){return a.extend(this.matrix,b),this},crop:function(a,b,d,e){return d===c&&(d=a,e=b,a=b=0),this.set({sx:a,sy:b,sw:d,sh:e||d})},resize:function(a,b,c){return/min|max|height|width/.test(b)&&(c=b,b=a),this.set({dw:a,dh:b||a,resize:c})},preview:function(a,b){return this.resize(a,b||a,"preview")},rotate:function(a){return this.set({deg:a})},filter:function(a){return this.set({filter:a})},overlay:function(a){return this.set({overlay:a})},clone:function(){return new d(this)},_load:function(b,c){var d=this;/img|video/i.test(b.nodeName)?c.call(d,null,b):a.readAsImage(b,function(a){c.call(d,"load"!=a.type,a.result)})},_apply:function(b,c){var f,h=g(),i=this.getMatrix(b),j=h.getContext("2d"),k=b.videoWidth||b.width,l=b.videoHeight||b.height,m=i.deg,n=i.dw,o=i.dh,p=k,q=l,r=i.filter,s=b,t=i.overlay,u=a.queue(function(){b.src=a.EMPTY_PNG,c(!1,h)}),v=a.renderImageToCanvas;for(m-=360*Math.floor(m/360),b._type=this.file.type;i.multipass&&e(p/n,q/o)>2;)p=p/2+.5|0,q=q/2+.5|0,f=g(),f.width=p,f.height=q,s!==b?(v(f,s,0,0,s.width,s.height,0,0,p,q),s=f):(s=f,v(s,b,i.sx,i.sy,i.sw,i.sh,0,0,p,q),i.sx=i.sy=i.sw=i.sh=0);h.width=m%180?o:n,h.height=m%180?n:o,h.type=i.type,h.quality=i.quality,j.rotate(m*Math.PI/180),v(j.canvas,s,i.sx,i.sy,i.sw||s.width,i.sh||s.height,180==m||270==m?-n:0,90==m||180==m?-o:0,n,o),n=h.width,o=h.height,t&&a.each([].concat(t),function(b){u.inc();var c=new window.Image,d=function(){var e=0|b.x,f=0|b.y,g=b.w||c.width,h=b.h||c.height,i=b.rel;e=1==i||4==i||7==i?(n-g+e)/2:2==i||5==i||8==i?n-(g+e):e,f=3==i||4==i||5==i?(o-h+f)/2:i>=6?o-(h+f):f,a.event.off(c,"error load abort",d);try{j.globalAlpha=b.opacity||1,j.drawImage(c,e,f,g,h)}catch(k){}u.next()};a.event.on(c,"error load abort",d),c.src=b.src,c.complete&&d()}),r&&(u.inc(),d.applyFilter(h,r,u.next)),u.check()},getMatrix:function(b){var c=a.extend({},this.matrix),d=c.sw=c.sw||b.videoWidth||b.naturalWidth||b.width,g=c.sh=c.sh||b.videoHeight||b.naturalHeight||b.height,h=c.dw=c.dw||d,i=c.dh=c.dh||g,j=d/g,k=h/i,l=c.resize;if("preview"==l){if(h!=d||i!=g){var m,n;k>=j?(m=d,n=m/k):(n=g,m=n*k),(m!=d||n!=g)&&(c.sx=~~((d-m)/2),c.sy=~~((g-n)/2),d=m,g=n)}}else"height"==l?h=i*j:"width"==l?i=h/j:l&&(d>h||g>i?"min"==l?(h=f(k>j?e(d,h):i*j),i=f(k>j?h/j:e(g,i))):(h=f(j>=k?e(d,h):i*j),i=f(j>=k?h/j:e(g,i))):(h=d,i=g));return c.sw=d,c.sh=g,c.dw=h,c.dh=i,c.multipass=a.multiPassResize,c},_trans:function(b){this._load(this.file,function(c,d){if(c)b(c);else try{this._apply(d,b)}catch(c){a.log("[err] FileAPI.Image.fn._apply:",c),b(c)}})},get:function(b){if(a.support.transform){var c=this,d=c.matrix;"auto"==d.deg?a.getInfo(c.file,function(a,e){d.deg=i[e&&e.exif&&e.exif.Orientation]||0,c._trans(b)}):c._trans(b)}else b("not_support_transform");return this},toData:function(a){return this.get(a)}},d.exifOrientation=i,d.transform=function(b,e,f,g){function h(h,i){var j={},k=a.queue(function(a){g(a,j)});h?k.fail():a.each(e,function(a,e){if(!k.isFail()){var g=new d(i.nodeType?i:b),h="function"==typeof a;if(h?a(i,g):a.width?g[a.preview?"preview":"resize"](a.width,a.height,a.strategy):a.maxWidth&&(i.width>a.maxWidth||i.height>a.maxHeight)&&g.resize(a.maxWidth,a.maxHeight,"max"),a.crop){var l=a.crop;g.crop(0|l.x,0|l.y,l.w||l.width,l.h||l.height)}a.rotate===c&&f&&(a.rotate="auto"),g.set({type:g.matrix.type||a.type||b.type||"image/png"}),h||g.set({deg:a.rotate,overlay:a.overlay,filter:a.filter,quality:a.quality||1}),k.inc(),g.toData(function(a,b){a?k.fail():(j[e]=b,k.next())})}})}b.width?h(!1,b):a.getInfo(b,h)},a.each(["TOP","CENTER","BOTTOM"],function(b,c){a.each(["LEFT","CENTER","RIGHT"],function(a,e){d[b+"_"+a]=3*c+e,d[a+"_"+b]=3*c+e})}),d.toCanvas=function(a){var c=b.createElement("canvas");return c.width=a.videoWidth||a.width,c.height=a.videoHeight||a.height,c.getContext("2d").drawImage(a,0,0),c},d.fromDataURL=function(b,c,d){var e=a.newImage(b);a.extend(e,c),d(e)},d.applyFilter=function(b,c,e){"function"==typeof c?c(b,e):window.Caman&&window.Caman("IMG"==b.tagName?d.toCanvas(b):b,function(){"string"==typeof c?this[c]():a.each(c,function(a,b){this[b](a)},this),this.render(e)})},a.renderImageToCanvas=function(b,c,d,e,f,g,h,i,j,k){try{return b.getContext("2d").drawImage(c,d,e,f,g,h,i,j,k)}catch(l){throw a.log("renderImageToCanvas failed"),l}},a.support.canvas=a.support.transform=h,a.Image=d}(FileAPI,document),function(a){"use strict";a(FileAPI)}(function(a){"use strict";if(window.navigator&&window.navigator.platform&&/iP(hone|od|ad)/.test(window.navigator.platform)){var b=a.renderImageToCanvas;a.detectSubsampling=function(a){var b,c;return a.width*a.height>1048576?(b=document.createElement("canvas"),b.width=b.height=1,c=b.getContext("2d"),c.drawImage(a,-a.width+1,0),0===c.getImageData(0,0,1,1).data[3]):!1},a.detectVerticalSquash=function(a,b){var c,d,e,f,g,h=a.naturalHeight||a.height,i=document.createElement("canvas"),j=i.getContext("2d");for(b&&(h/=2),i.width=1,i.height=h,j.drawImage(a,0,0),c=j.getImageData(0,0,1,h).data,d=0,e=h,f=h;f>d;)g=c[4*(f-1)+3],0===g?e=f:d=f,f=e+d>>1;return f/h||1},a.renderImageToCanvas=function(c,d,e,f,g,h,i,j,k,l){if("image/jpeg"===d._type){var m,n,o,p,q=c.getContext("2d"),r=document.createElement("canvas"),s=1024,t=r.getContext("2d");if(r.width=s,r.height=s,q.save(),m=a.detectSubsampling(d),m&&(e/=2,f/=2,g/=2,h/=2),n=a.detectVerticalSquash(d,m),m||1!==n){for(f*=n,k=Math.ceil(s*k/g),l=Math.ceil(s*l/h/n),j=0,p=0;h>p;){for(i=0,o=0;g>o;)t.clearRect(0,0,s,s),t.drawImage(d,e,f,g,h,-o,-p,g,h),q.drawImage(r,0,0,s,s,i,j,k,l),o+=s,i+=k;p+=s,j+=l}return q.restore(),c}}return b(c,d,e,f,g,h,i,j,k,l)}}}),function(a,b){"use strict";function c(b,c,d){var e=b.blob,f=b.file;if(f){if(!e.toDataURL)return void a.readAsBinaryString(e,function(a){"load"==a.type&&c(b,a.result)});var g={"image/jpeg":".jpe?g","image/png":".png"},h=g[b.type]?b.type:"image/png",i=g[h]||".png",j=e.quality||1;f.match(new RegExp(i+"$","i"))||(f+=i.replace("?","")),b.file=f,b.type=h,!d&&e.toBlob?e.toBlob(function(a){c(b,a)},h,j):c(b,a.toBinaryString(e.toDataURL(h,j)))}else c(b,e)}var d=b.document,e=b.FormData,f=function(){this.items=[]},g=b.encodeURIComponent;f.prototype={append:function(a,b,c,d){this.items.push({name:a,blob:b&&b.blob||(void 0==b?"":b),file:b&&(c||b.name),type:b&&(d||b.type)})},each:function(a){for(var b=0,c=this.items.length;c>b;b++)a.call(this,this.items[b])},toData:function(b,c){c._chunked=a.support.chunked&&c.chunkSize>0&&1==a.filter(this.items,function(a){return a.file}).length,a.support.html5?a.formData&&!this.multipart&&e?c._chunked?(a.log("FileAPI.Form.toPlainData"),this.toPlainData(b)):(a.log("FileAPI.Form.toFormData"),this.toFormData(b)):(a.log("FileAPI.Form.toMultipartData"),this.toMultipartData(b)):(a.log("FileAPI.Form.toHtmlData"),this.toHtmlData(b))},_to:function(b,c,d,e){var f=a.queue(function(){c(b)});this.each(function(g){try{d(g,b,f,e)}catch(h){a.log("FileAPI.Form._to: "+h.message),c(h)}}),f.check()},toHtmlData:function(b){this._to(d.createDocumentFragment(),b,function(b,c){var e,f=b.blob;b.file?(a.reset(f,!0),f.name=b.name,f.disabled=!1,c.appendChild(f)):(e=d.createElement("input"),e.name=b.name,e.type="hidden",e.value=f,c.appendChild(e))})},toPlainData:function(a){this._to({},a,function(a,b,d){a.file&&(b.type=a.file),a.blob.toBlob?(d.inc(),c(a,function(a,c){b.name=a.name,b.file=c,b.size=c.length,b.type=a.type,d.next()})):a.file?(b.name=a.blob.name,b.file=a.blob,b.size=a.blob.size,b.type=a.type):(b.params||(b.params=[]),b.params.push(g(a.name)+"="+g(a.blob))),b.start=-1,b.end=b.file&&b.file.FileAPIReadPosition||-1,b.retry=0})},toFormData:function(a){this._to(new e,a,function(a,b,d){a.blob&&a.blob.toBlob?(d.inc(),c(a,function(a,c){b.append(a.name,c,a.file),d.next()})):a.file?b.append(a.name,a.blob,a.file):b.append(a.name,a.blob),a.file&&b.append("_"+a.name,a.file)})},toMultipartData:function(b){this._to([],b,function(a,b,d,e){d.inc(),c(a,function(a,c){b.push("--_"+e+('\r\nContent-Disposition: form-data; name="'+a.name+'"'+(a.file?'; filename="'+g(a.file)+'"':"")+(a.file?"\r\nContent-Type: "+(a.type||"application/octet-stream"):"")+"\r\n\r\n"+(a.file?c:g(c))+"\r\n")),d.next()},!0)},a.expando)}},a.Form=f}(FileAPI,window),function(a,b){"use strict";var c=function(){},d=a.document,e=function(a){this.uid=b.uid(),this.xhr={abort:c,getResponseHeader:c,getAllResponseHeaders:c},this.options=a},f={"":1,XML:1,Text:1,Body:1};e.prototype={status:0,statusText:"",constructor:e,getResponseHeader:function(a){return this.xhr.getResponseHeader(a)},getAllResponseHeaders:function(){return this.xhr.getAllResponseHeaders()||{}},end:function(d,e){var f=this,g=f.options;f.end=f.abort=c,f.status=d,e&&(f.statusText=e),b.log("xhr.end:",d,e),g.complete(200==d||201==d?!1:f.statusText||"unknown",f),f.xhr&&f.xhr.node&&setTimeout(function(){var b=f.xhr.node;try{b.parentNode.removeChild(b)}catch(c){}try{delete a[f.uid]}catch(c){}a[f.uid]=f.xhr.node=null},9)},abort:function(){this.end(0,"abort"),this.xhr&&(this.xhr.aborted=!0,this.xhr.abort())},send:function(a){var b=this,c=this.options;a.toData(function(a){a instanceof Error?b.end(0,a.message):(c.upload(c,b),b._send.call(b,c,a))},c)},_send:function(c,e){var g,h=this,i=h.uid,j=h.uid+"Load",k=c.url;if(b.log("XHR._send:",e),c.cache||(k+=(~k.indexOf("?")?"&":"?")+b.uid()),e.nodeName){var l=c.jsonp;k=k.replace(/([a-z]+)=(\?)/i,"$1="+i),c.upload(c,h);var m=function(a){if(~k.indexOf(a.origin))try{var c=b.parseJSON(a.data);c.id==i&&n(c.status,c.statusText,c.response)}catch(d){n(0,d.message)}},n=a[i]=function(c,d,e){h.readyState=4,h.responseText=e,h.end(c,d),b.event.off(a,"message",m),a[i]=g=p=a[j]=null};h.xhr.abort=function(){try{p.stop?p.stop():p.contentWindow.stop?p.contentWindow.stop():p.contentWindow.document.execCommand("Stop")}catch(a){}n(0,"abort")},b.event.on(a,"message",m),a[j]=function(){try{var a=p.contentWindow,c=a.document,d=a.result||b.parseJSON(c.body.innerHTML);n(d.status,d.statusText,d.response)}catch(e){b.log("[transport.onload]",e)}},g=d.createElement("div"),g.innerHTML='
'+(l&&c.url.indexOf("=?")<0?'':"")+"
";var o=g.getElementsByTagName("form")[0],p=g.getElementsByTagName("iframe")[0];o.appendChild(e),b.log(o.parentNode.innerHTML),d.body.appendChild(g),h.xhr.node=g,h.readyState=2;try{o.submit()}catch(q){b.log("iframe.error: "+q)}o=null}else{if(k=k.replace(/([a-z]+)=(\?)&?/i,""),this.xhr&&this.xhr.aborted)return void b.log("Error: already aborted");if(g=h.xhr=b.getXHR(),e.params&&(k+=(k.indexOf("?")<0?"?":"&")+e.params.join("&")),g.open("POST",k,!0),b.withCredentials&&(g.withCredentials="true"),c.headers&&c.headers["X-Requested-With"]||g.setRequestHeader("X-Requested-With","XMLHttpRequest"),b.each(c.headers,function(a,b){g.setRequestHeader(b,a)}),c._chunked){g.upload&&g.upload.addEventListener("progress",b.throttle(function(a){e.retry||c.progress({type:a.type,total:e.size,loaded:e.start+a.loaded,totalSize:e.size},h,c)},100),!1),g.onreadystatechange=function(){var a=parseInt(g.getResponseHeader("X-Last-Known-Byte"),10);if(h.status=g.status,h.statusText=g.statusText,h.readyState=g.readyState,4==g.readyState){for(var d in f)h["response"+d]=g["response"+d];if(g.onreadystatechange=null,!g.status||g.status-201>0)if(b.log("Error: "+g.status),(!g.status&&!g.aborted||500==g.status||416==g.status)&&++e.retry<=c.chunkUploadRetry){var i=g.status?0:b.chunkNetworkDownRetryTimeout;c.pause(e.file,c),b.log("X-Last-Known-Byte: "+a),a?e.end=a:(e.end=e.start-1,416==g.status&&(e.end=e.end-c.chunkSize)),setTimeout(function(){h._send(c,e)},i)}else h.end(g.status);else e.retry=0,e.end==e.size-1?h.end(g.status):(b.log("X-Last-Known-Byte: "+a),a&&(e.end=a),e.file.FileAPIReadPosition=e.end,setTimeout(function(){h._send(c,e)},0));g=null}},e.start=e.end+1,e.end=Math.max(Math.min(e.start+c.chunkSize,e.size)-1,e.start);var r=e.file,s=(r.slice||r.mozSlice||r.webkitSlice).call(r,e.start,e.end+1);e.size&&!s.size?setTimeout(function(){h.end(-1)}):(g.setRequestHeader("Content-Range","bytes "+e.start+"-"+e.end+"/"+e.size),g.setRequestHeader("Content-Disposition","attachment; filename="+encodeURIComponent(e.name)),g.setRequestHeader("Content-Type",e.type||"application/octet-stream"),g.send(s)),r=s=null}else if(g.upload&&g.upload.addEventListener("progress",b.throttle(function(a){c.progress(a,h,c)},100),!1),g.onreadystatechange=function(){if(h.status=g.status,h.statusText=g.statusText,h.readyState=g.readyState,4==g.readyState){for(var a in f)h["response"+a]=g["response"+a];if(g.onreadystatechange=null,!g.status||g.status>201)if(b.log("Error: "+g.status),(!g.status&&!g.aborted||500==g.status)&&(c.retry||0)=0?a+"px":a}function d(a){var b,c=f.createElement("canvas"),d=!1;try{b=c.getContext("2d"),b.drawImage(a,0,0,1,1),d=255!=b.getImageData(0,0,1,1).data[4]}catch(e){}return d}var e=a.URL||a.webkitURL,f=a.document,g=a.navigator,h=g.getUserMedia||g.webkitGetUserMedia||g.mozGetUserMedia||g.msGetUserMedia,i=!!h;b.support.media=i;var j=function(a){this.video=a};j.prototype={isActive:function(){return!!this._active},start:function(a){var b,c,f=this,i=f.video,j=function(d){f._active=!d,clearTimeout(c),clearTimeout(b),a&&a(d,f)};h.call(g,{video:!0},function(a){f.stream=a,i.src=e.createObjectURL(a),b=setInterval(function(){d(i)&&j(null)},1e3),c=setTimeout(function(){j("timeout")},5e3),i.play()},j)},stop:function(){try{this._active=!1,this.video.pause(),this.stream.stop()}catch(a){}},shot:function(){return new k(this.video)}},j.get=function(a){return new j(a.firstChild)},j.publish=function(d,e,g){"function"==typeof e&&(g=e,e={}),e=b.extend({},{width:"100%",height:"100%",start:!0},e),d.jquery&&(d=d[0]); 3 | var h=function(a){if(a)g(a);else{var b=j.get(d);e.start?b.start(g):g(null,b)}};if(d.style.width=c(e.width),d.style.height=c(e.height),b.html5&&i){var k=f.createElement("video");k.style.width=c(e.width),k.style.height=c(e.height),a.jQuery?jQuery(d).empty():d.innerHTML="",d.appendChild(k),h()}else j.fallback(d,e,h)},j.fallback=function(a,b,c){c("not_support_camera")};var k=function(a){var c=a.nodeName?b.Image.toCanvas(a):a,d=b.Image(c);return d.type="image/png",d.width=c.width,d.height=c.height,d.size=c.width*c.height*4,d};j.Shot=k,b.Camera=j}(window,FileAPI),function(a,b,c){"use strict";var d=a.document,e=a.location,f=a.navigator,g=c.each;c.support.flash=function(){var b=f.mimeTypes,d=!1;if(f.plugins&&"object"==typeof f.plugins["Shockwave Flash"])d=f.plugins["Shockwave Flash"].description&&!(b&&b["application/x-shockwave-flash"]&&!b["application/x-shockwave-flash"].enabledPlugin);else try{d=!(!a.ActiveXObject||!new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))}catch(g){c.log("Flash -- does not supported.")}return d&&/^file:/i.test(e)&&c.log("[warn] Flash does not work on `file:` protocol."),d}(),c.support.flash&&(!c.html5||!c.support.html5||c.cors&&!c.support.cors||c.media&&!c.support.media)&&function(){function h(a){return('').replace(/#(\w+)#/gi,function(b,c){return a[c]})}function i(a,b){if(a&&a.style){var c,d;for(c in b){d=b[c],"number"==typeof d&&(d+="px");try{a.style[c]=d}catch(e){}}}}function j(a,b){g(b,function(b,c){var d=a[c];a[c]=function(){return this.parent=d,b.apply(this,arguments)}})}function k(a){return a&&!a.flashId}function l(a){var b=a.wid=c.uid();return v._fn[b]=a,"FileAPI.Flash._fn."+b}function m(a){try{v._fn[a.wid]=null,delete v._fn[a.wid]}catch(b){}}function n(a,b){if(!u.test(a)){if(/^\.\//.test(a)||"/"!=a.charAt(0)){var c=e.pathname;c=c.substr(0,c.lastIndexOf("/")),a=(c+"/"+a).replace("/./","/")}"//"!=a.substr(0,2)&&(a="//"+e.host+a),u.test(a)||(a=e.protocol+a)}return b&&(a+=(/\?/.test(a)?"&":"?")+b),a}function o(a,b,e){function f(){try{var a=v.get(j);a.setImage(b)}catch(d){c.log('[err] FlashAPI.Preview.setImage -- can not set "base64":',d)}}var g,j=c.uid(),k=d.createElement("div"),o=10;for(g in a)k.setAttribute(g,a[g]),k[g]=a[g];i(k,a),a.width="100%",a.height="100%",k.innerHTML=h(c.extend({id:j,src:n(c.flashImageUrl,"r="+c.uid()),wmode:"opaque",flashvars:"scale="+a.scale+"&callback="+l(function p(){return m(p),--o>0&&f(),!0})},a)),e(!1,k),k=null}function p(a){return{id:a.id,name:a.name,matrix:a.matrix,flashId:a.flashId}}function q(b){var c=b.getBoundingClientRect(),e=d.body,f=(b&&b.ownerDocument).documentElement;return{top:c.top+(a.pageYOffset||f.scrollTop)-(f.clientTop||e.clientTop||0),left:c.left+(a.pageXOffset||f.scrollLeft)-(f.clientLeft||e.clientLeft||0),width:c.right-c.left,height:c.bottom-c.top}}var r=c.uid(),s=0,t={},u=/^https?:/i,v={_fn:{},init:function(){var a=d.body&&d.body.firstChild;if(a)do if(1==a.nodeType){c.log("FlashAPI.state: awaiting");var b=d.createElement("div");return b.id="_"+r,i(b,{top:1,right:1,width:5,height:5,position:"absolute",zIndex:"2147483647"}),a.parentNode.insertBefore(b,a),void v.publish(b,r)}while(a=a.nextSibling);10>s&&setTimeout(v.init,50*++s)},publish:function(a,b,d){d=d||{},a.innerHTML=h({id:b,src:n(c.flashUrl,"r="+c.version),wmode:d.camera?"":"transparent",flashvars:"callback="+(d.onEvent||"FileAPI.Flash.onEvent")+"&flashId="+b+"&storeKey="+f.userAgent.match(/\d/gi).join("")+"_"+c.version+(v.isReady||(c.pingUrl?"&ping="+c.pingUrl:""))+"&timeout="+c.flashAbortTimeout+(d.camera?"&useCamera="+n(c.flashWebcamUrl):"")+"&debug="+(c.debug?"1":"")},d)},ready:function(){c.log("FlashAPI.state: ready"),v.ready=c.F,v.isReady=!0,v.patch(),v.patchCamera&&v.patchCamera(),c.event.on(d,"mouseover",v.mouseover),c.event.on(d,"click",function(a){v.mouseover(a)&&(a.preventDefault?a.preventDefault():a.returnValue=!0)})},getEl:function(){return d.getElementById("_"+r)},getWrapper:function(a){do if(/js-fileapi-wrapper/.test(a.className))return a;while((a=a.parentNode)&&a!==d.body)},mouseover:function(a){var b=c.event.fix(a).target;if(/input/i.test(b.nodeName)&&"file"==b.type&&!b.disabled){var e=b.getAttribute(r),f=v.getWrapper(b);if(c.multiFlash){if("i"==e||"r"==e)return!1;if("p"!=e){b.setAttribute(r,"i");var g=d.createElement("div");if(!f)return void c.log("[err] FlashAPI.mouseover: js-fileapi-wrapper not found");i(g,{top:0,left:0,width:b.offsetWidth,height:b.offsetHeight,zIndex:"2147483647",position:"absolute"}),f.appendChild(g),v.publish(g,c.uid()),b.setAttribute(r,"p")}return!0}if(f){var h=q(f);i(v.getEl(),h),v.curInp=b}}else/object|embed/i.test(b.nodeName)||i(v.getEl(),{top:1,left:1,width:5,height:5})},onEvent:function(a){var b=a.type;if("ready"==b){try{v.getInput(a.flashId).setAttribute(r,"r")}catch(d){}return v.ready(),setTimeout(function(){v.mouseenter(a)},50),!0}"ping"===b?c.log("(flash -> js).ping:",[a.status,a.savedStatus],a.error):"log"===b?c.log("(flash -> js).log:",a.target):b in v&&setTimeout(function(){c.log("FlashAPI.event."+a.type+":",a),v[b](a)},1)},mouseenter:function(a){var b=v.getInput(a.flashId);if(b){v.cmd(a,"multiple",null!=b.getAttribute("multiple"));var d=[],e={};g((b.getAttribute("accept")||"").split(/,\s*/),function(a){c.accept[a]&&g(c.accept[a].split(" "),function(a){e[a]=1})}),g(e,function(a,b){d.push(b)}),v.cmd(a,"accept",d.length?d.join(",")+","+d.join(",").toUpperCase():"*")}},get:function(b){return d[b]||a[b]||d.embeds[b]},getInput:function(a){if(!c.multiFlash)return v.curInp;try{var b=v.getWrapper(v.get(a));if(b)return b.getElementsByTagName("input")[0]}catch(d){c.log('[err] Can not find "input" by flashId:',a,d)}},select:function(a){var e,f=v.getInput(a.flashId),h=c.uid(f),i=a.target.files;g(i,function(a){c.checkFileObj(a)}),t[h]=i,d.createEvent?(e=d.createEvent("Event"),e.files=i,e.initEvent("change",!0,!0),f.dispatchEvent(e)):b?b(f).trigger({type:"change",files:i}):(e=d.createEventObject(),e.files=i,f.fireEvent("onchange",e))},cmd:function(a,b,d,e){try{return c.log("(js -> flash)."+b+":",d),v.get(a.flashId||a).cmd(b,d)}catch(f){c.log("(js -> flash).onError:",f.toString()),e||setTimeout(function(){v.cmd(a,b,d,!0)},50)}},patch:function(){c.flashEngine=!0,j(c,{getFiles:function(a,b,d){if(d)return c.filterFiles(c.getFiles(a),b,d),null;var e=c.isArray(a)?a:t[c.uid(a.target||a.srcElement||a)];return e?(b&&(b=c.getFilesFilter(b),e=c.filter(e,function(a){return b.test(a.name)})),e):this.parent.apply(this,arguments)},getInfo:function(a,b){if(k(a))this.parent.apply(this,arguments);else if(a.isShot)b(null,a.info={width:a.width,height:a.height});else{if(!a.__info){var d=a.__info=c.defer();v.cmd(a,"getFileInfo",{id:a.id,callback:l(function e(b,c){m(e),d.resolve(b,a.info=c)})})}a.__info.then(b)}}}),c.support.transform=!0,c.Image&&j(c.Image.prototype,{get:function(a,b){return this.set({scaleMode:b||"noScale"}),this.parent(a)},_load:function(a,b){if(c.log("FlashAPI.Image._load:",a),k(a))this.parent.apply(this,arguments);else{var d=this;c.getInfo(a,function(c){b.call(d,c,a)})}},_apply:function(a,b){if(c.log("FlashAPI.Image._apply:",a),k(a))this.parent.apply(this,arguments);else{var d=this.getMatrix(a.info),e=b;v.cmd(a,"imageTransform",{id:a.id,matrix:d,callback:l(function f(g,h){c.log("FlashAPI.Image._apply.callback:",g),m(f),g?e(g):c.support.html5||c.support.dataURI&&!(h.length>3e4)?(d.filter&&(e=function(a,e){a?b(a):c.Image.applyFilter(e,d.filter,function(){b(a,this.canvas)})}),c.newImage("data:"+a.type+";base64,"+h,e)):o({width:d.deg%180?d.dh:d.dw,height:d.deg%180?d.dw:d.dh,scale:d.scaleMode},h,e)})})}},toData:function(a){var b=this.file,d=b.info,e=this.getMatrix(d);c.log("FlashAPI.Image.toData"),k(b)?this.parent.apply(this,arguments):("auto"==e.deg&&(e.deg=c.Image.exifOrientation[d&&d.exif&&d.exif.Orientation]||0),a.call(this,!b.info,{id:b.id,flashId:b.flashId,name:b.name,type:b.type,matrix:e}))}}),c.Image&&j(c.Image,{fromDataURL:function(a,b,d){!c.support.dataURI||a.length>3e4?o(c.extend({scale:"exactFit"},b),a.replace(/^data:[^,]+,/,""),function(a,b){d(b)}):this.parent(a,b,d)}}),j(c.Form.prototype,{toData:function(a){for(var b=this.items,d=b.length;d--;)if(b[d].file&&k(b[d].blob))return this.parent.apply(this,arguments);c.log("FlashAPI.Form.toData"),a(b)}}),j(c.XHR.prototype,{_send:function(a,b){if(b.nodeName||b.append&&c.support.html5||c.isArray(b)&&"string"==typeof b[0])return this.parent.apply(this,arguments);var d,e,f={},h={},i=this;if(g(b,function(a){a.file?(h[a.name]=a=p(a.blob),e=a.id,d=a.flashId):f[a.name]=a.blob}),e||(d=r),!d)return c.log("[err] FlashAPI._send: flashId -- undefined"),this.parent.apply(this,arguments);c.log("FlashAPI.XHR._send: "+d+" -> "+e),i.xhr={headers:{},abort:function(){v.cmd(d,"abort",{id:e})},getResponseHeader:function(a){return this.headers[a]},getAllResponseHeaders:function(){return this.headers}};var j=c.queue(function(){v.cmd(d,"upload",{url:n(a.url.replace(/([a-z]+)=(\?)&?/i,"")),data:f,files:e?h:null,headers:a.headers||{},callback:l(function b(d){var e=d.type,f=d.result;c.log("FlashAPI.upload."+e),"progress"==e?(d.loaded=Math.min(d.loaded,d.total),d.lengthComputable=!0,a.progress(d)):"complete"==e?(m(b),"string"==typeof f&&(i.responseText=f.replace(/%22/g,'"').replace(/%5c/g,"\\").replace(/%26/g,"&").replace(/%25/g,"%")),i.end(d.status||200)):("abort"==e||"error"==e)&&(i.end(d.status||0,d.message),m(b))})})});g(h,function(a){j.inc(),c.getInfo(a,j.next)}),j.check()}})}};c.Flash=v,c.newImage("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",function(a,b){c.support.dataURI=!(1!=b.width||1!=b.height),v.init()})}()}(window,window.jQuery,FileAPI),function(a,b,c){"use strict";var d=c.each,e=[];!c.support.flash||!c.media||c.support.media&&c.html5||!function(){function a(a){var b=a.wid=c.uid();return c.Flash._fn[b]=a,"FileAPI.Flash._fn."+b}function b(a){try{c.Flash._fn[a.wid]=null,delete c.Flash._fn[a.wid]}catch(b){}}var f=c.Flash;c.extend(c.Flash,{patchCamera:function(){c.Camera.fallback=function(d,e,g){var h=c.uid();c.log("FlashAPI.Camera.publish: "+h),f.publish(d,h,c.extend(e,{camera:!0,onEvent:a(function i(a){"camera"===a.type&&(b(i),a.error?(c.log("FlashAPI.Camera.publish.error: "+a.error),g(a.error)):(c.log("FlashAPI.Camera.publish.success: "+h),g(null)))})}))},d(e,function(a){c.Camera.fallback.apply(c.Camera,a)}),e=[],c.extend(c.Camera.prototype,{_id:function(){return this.video.id},start:function(d){var e=this;f.cmd(this._id(),"camera.on",{callback:a(function g(a){b(g),a.error?(c.log("FlashAPI.camera.on.error: "+a.error),d(a.error,e)):(c.log("FlashAPI.camera.on.success: "+e._id()),e._active=!0,d(null,e))})})},stop:function(){this._active=!1,f.cmd(this._id(),"camera.off")},shot:function(){c.log("FlashAPI.Camera.shot:",this._id());var a=c.Flash.cmd(this._id(),"shot",{});return a.type="image/png",a.flashId=this._id(),a.isShot=!0,new c.Camera.Shot(a)}})}}),c.Camera.fallback=function(){e.push(arguments)}}()}(window,window.jQuery,FileAPI),"function"==typeof define&&define.amd&&define("FileAPI",[],function(){return FileAPI}); --------------------------------------------------------------------------------