diff --git a/.gitignore b/.gitignore
index 3c3629e6..91832922 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
node_modules
+dist/FileAPI.html5ok.js
+dist/FileAPI.html5ok.min.js
+dist/FileAPI.ok.js
+dist/FileAPI.ok.min.js
+.idea
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..1fdfc355
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - 4.5
+before_script:
+ - npm install -g grunt-cli
diff --git a/Gruntfile.js b/Gruntfile.js
index 5d9b3f45..9276a4b6 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,6 +1,6 @@
'use strict';
-module.exports = function (grunt){
+module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
@@ -10,6 +10,7 @@ module.exports = function (grunt){
'Gruntfile.js'
, 'lib/**/*.js'
, 'plugins/jquery.fileapi.js'
+ , 'node/**/*.js'
],
options: {
@@ -25,7 +26,6 @@ module.exports = function (grunt){
, eqnull: true
, node: true
- , es5: true
, expr: true // - "Expected an assignment or function call and instead saw an expression."
, supernew: true // - "Missing '()' invoking a constructor."
, laxcomma: true
@@ -38,63 +38,121 @@ module.exports = function (grunt){
src: 'lib/FileAPI.core.js'
},
- qunit: {
- options: {
- files: {
- '1px.gif': ['tests/files/1px.gif']
- , 'hello.txt': ['tests/files/hello.txt']
- , 'image.jpg': ['tests/files/image.jpg']
- , 'dino.png': ['tests/files/dino.png']
- , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json']
+ connect: {
+ server: {
+ options: {
+ port: 9001,
+ base: '.'
}
},
- all: ['tests/*.html']
+ standalone: {
+ options: {
+ hostname: '*',
+ keepalive: true,
+ port: 9001,
+ base: '.'
+ }
+ }
+ },
+
+ curl: {
+ jpg: {
+ src: 'https://dl.dropboxusercontent.com/u/49592745/BigJPG.jpg',
+ dest: 'tests/files/big.jpg'
+ }
+ },
+
+ qunit: {
+ all: {
+ options: {
+ timeout: 5 * 60 * 1000, // 5min
+ files: {
+ '1px_gif': ['tests/files/1px.gif']
+ , 'big.jpg': ['tests/files/big.jpg']
+ , 'hello.txt': ['tests/files/hello.txt']
+ , 'image.jpg': ['tests/files/image.jpg']
+ , 'dino.png': ['tests/files/dino.png']
+ , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json']
+ },
+ urls: ['http://127.0.0.1:<%=connect.server.options.port%>/tests/index.html']
+ }
+ }
},
concat: {
options: {
- banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %>\n' +
+ banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %>\n' +
' * <%= pkg.description %>\n' +
' */\n\n',
- footer: 'if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }'
+ footer: 'if( typeof define === "function" && define.amd ){ define("<%= pkg.jam.name %>", [], function (){ return FileAPI; }); }'
},
all: {
src: [
- 'lib/FileAPI.core.js'
+ 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.core.js'
, 'lib/FileAPI.Image.js'
+ , 'lib/load-image-ios.js'
, 'lib/FileAPI.Form.js'
, 'lib/FileAPI.XHR.js'
, 'lib/FileAPI.Camera.js'
, 'lib/FileAPI.Flash.js'
- , 'lib/load-image-ios.js'
- , 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.Flash.Camera.js'
],
- dest: 'dist/<%= pkg.name %>.js'
+ dest: 'dist/<%= pkg.exportName %>.js'
},
html5: {
src: [
- 'lib/FileAPI.core.js'
+ 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.core.js'
, 'lib/FileAPI.Image.js'
+ , 'lib/load-image-ios.js'
, 'lib/FileAPI.Form.js'
, 'lib/FileAPI.XHR.js'
, 'lib/FileAPI.Camera.js'
- , 'lib/load-image-ios.js'
- , 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.Flash.Camera.js'
],
- dest: 'dist/<%= pkg.name %>.html5.js'
+ dest: 'dist/<%= pkg.exportName %>.html5.js'
}
},
uglify: {
- options: { banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' },
+ options: { banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' },
dist: {
files: {
- 'dist/<%= pkg.name %>.min.js': ['<%= concat.all.dest %>']
- , 'dist/<%= pkg.name %>.html5.min.js': ['<%= concat.html5.dest %>']
- , 'dist/jquery.fileapi.min.js': ['plugins/jquery.fileapi.js']
+ 'dist/<%= pkg.exportName %>.min.js': ['<%= concat.all.dest %>']
+ , 'dist/<%= pkg.exportName %>.html5.min.js': ['<%= concat.html5.dest %>']
+ }
+ }
+ },
+
+ mxmlc: {
+ core: {
+ options: {
+ rawConfig: '-target-player=10.1 -static-link-runtime-shared-libraries=true -compiler.debug=false' +
+ ' -library-path+=flash/core/lib/blooddy_crypto.swc -library-path+=flash/core/lib/EnginesLibrary.swc'
+ },
+ files: {
+ 'dist/<%= pkg.exportName %>.flash.swf': ['flash/core/src/FileAPI_flash.as']
+ }
+ },
+ image: {
+ options: {
+ rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' +
+ ' -library-path+=flash/image/lib/blooddy_crypto.swc'
+ },
+ files: {
+ 'dist/<%= pkg.exportName %>.flash.image.swf': ['flash/image/src/FileAPI_flash_image.as']
+ }
+ },
+ camera: {
+ options: {
+ rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false'
+ },
+ files: {
+ 'dist/<%= pkg.exportName %>.flash.camera.swf': ['flash/camera/src/FileAPI_flash_camera.as']
}
}
},
@@ -108,18 +166,41 @@ module.exports = function (grunt){
}
});
-
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-version');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-connect');
+ grunt.loadNpmTasks('grunt-contrib-compress');
+ grunt.loadNpmTasks('grunt-mxmlc');
+ grunt.loadNpmTasks('grunt-curl');
// Load custom QUnit task, based on grunt-contrib-qunit, but support "files" option.
grunt.loadTasks('./tests/grunt-task/');
+ grunt.loadTasks('./custom-tasks/');
// "npm build" runs these tasks
- grunt.registerTask('build', ['version', 'concat', 'uglify', 'qunit']);
- grunt.registerTask('default', ['jshint', 'build']);
+ grunt.registerTask('prepare-test-files', function (){
+ // big.jpg added to git
+ /*if (!grunt.file.exists('tests/files/big.jpg')) {
+ grunt.task.run('curl');
+ }*/
+ });
+
+ grunt.registerTask('express', 'Start a custom web server.', function() {
+ var done = this.async();
+
+ require('./node/server.js').createServer(8000, function () {
+ done();
+ });
+ });
+
+ grunt.registerTask('server', ['connect:server', 'express']);
+ grunt.registerTask('dev', ['concat', 'server', 'watch']);
+ grunt.registerTask('tests', ['jshint', 'concat', 'server', 'prepare-test-files', 'qunit']);
+ grunt.registerTask('build', ['version', 'concat', 'uglify']);
+ grunt.registerTask('build-all', ['build', 'mxmlc']);
+ grunt.registerTask('default', ['tests', 'build']);
};
diff --git a/README.md b/README.md
index cd67d5e3..ac75807d 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,18 @@
-## FileAPI
-A set of javascript tools for working with files.
+## FileAPI
+A set of JavaScript tools for working with files.
### Get started
+Download the files from the [dist](https://github.com/mailru/FileAPI/tree/master/dist) directory, and then:
+
```html
-
@@ -18,6 +20,7 @@ A set of javascript tools for working with files.
@@ -1189,7 +1220,7 @@ Submit Query
FileAPI::makeResponse(array(
'status' => FileAPI::OK
, 'statusText' => 'OK'
- , 'body' => array('count' => sizeof($files)
+ , 'body' => array('count' => sizeof($files))
), $jsonp);
exit;
}
@@ -1249,7 +1280,7 @@ Response headers:
X-Last-Known-Byte: int, library tries to resend chunk from the given offset. Applicable to response codes 200 and 416
-All the other codes - fatal error, user's involvement is recommend.
+All the other codes - fatal error, user's involvement is recommended.
---
@@ -1378,6 +1409,124 @@ Button like link.
## Changelog
+### 2.0.20
+
+
+
+### 2.0.19
+
+ #367: * [flash] allow gif and bmp to resize
+
+
+
+### 2.0.18
+
+ #364: * Camera#stop
+ #363: * support `Blob` in `FileAPI.getInfo`
+ #361: + upload zero-files
+
+
+
+### 2.0.16-2.0.17
+
+ #353: debug mode vs. IE
+ #352: correct filename via flash-uploading
+
+
+
+### 2.0.12-2.0.15 (!)
+
+ #346, #342, #344: fixes for XSS into Flash-transport
+
+
+
+### 2.0.11
+
+ #322, #308: dnd & safari + $.fn.dnd (store all dropped items)
+ #319: NodeJS tesing
+ #317, #313: fixed "malformed entry.name (OSX Unicode NFD)"
+ #311: fixed "Arithmetic result exceeded 32 bits"
+
+
+
+### 2.0.10
+
+ #289: * WebCam & html5 == false
+ #199, #265: flash fix 2015 error with BitmapData
+ #177: IE9, IE11 flash.camera remembered settigns
+ #254: check 'onLoadFnName' before call
+ #272: fixed `entry.createReader().readEntries`
+
+
+
+### 2.0.9
+
+ #253: fixed `proxyXHR.loaded`
+ #250: + check `disabled`-attr
+
+
+
+### 2.0.8
+
+ Two new resize strategies `width` and `height`
+
+
+
+### 2.0.7
+
+ #214: iframe transport under IE8
+ Fixed iframe-transport (remove `disabled`-attr for input)
+
+
+
+### 2.0.6
+
+ #240: Fixed `FileAPI.event.dnd.off`
+
+
+
+### 2.0.5
+
+ + #228: check callbacks with regexp
+ * Updated devDepending
+ + #207: support EXIF.Orientation == 4, 5 & 7
+
+
+
+### 2.0.4
+
+ + #176: Add params to the beginning of form
+ + #190: Add 204 as a successful response
+ + #192: many bugfixes; + `retry` & `multipass` options; + QUnit-tests for BigSizeImage
+
+
+### 2.0.3
+
+ + QUnit-tests for iframe-transport
+ + `postMessage` for iframe-transport
+ + `jsonp: "callback"` option
+ * resize: `imageTransform.type` rename to `imageTransform.strategy` (!!!)
+ + https://github.com/mailru/FileAPI/pull/165 (#140: fix)
+
+
+### 2.0.2
+
+ + test: upload headers
+ + test: upload + camanjs
+ + test: upload + autoOrientation
+ FileAPI.class.php: + HTTP header Content-Type: application/json
+ #143: + `FileAPI.flashWebcamUrl` option
+ * merge v1.2.7
+ + `FileAPI.formData: true` option
+
+
+### 2.0.1
+
+ + support 'filter' prop in imageTransform
+
+
### 2.0.0
+ FileAPI.Camera (HTML5 and Flash fallback)
diff --git a/README.ru.md b/README.ru.md
index 27162509..a00d8514 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -129,7 +129,7 @@ FileAPI.getInfo(file, function (err/**String*/, info/**Object*/){
### filterFiles(files`:Array`, filter`:Function`, callback`:Function`)`:void`
-Отфильтровать список файлов, используя дополнительную информацию о них.
+Отфильтровать список файлов, используя дополнительную информацию о них.
см. FileAPI.getInfo или FileAPI.addInfoReader.
* files — оригинальный список файлов
@@ -161,7 +161,7 @@ FileAPI.filterFiles(files, function (file/**Object*/, info/**Object*/){
Получить весь список файлов, включая директории.
* evt — `drop` event
-* callback — фнукция, принимает один аргумент — список файлов
+* callback — функция, принимает один аргумент — список файлов
```js
FileAPI.event.on(document, 'drop', function (evt/**Event*/){
@@ -182,7 +182,7 @@ FileAPI.event.on(document, 'drop', function (evt/**Event*/){
Помните, для корректной работы flash-транспорта, тело ответа сервера не должно быть пустым,
например можно ответить простым текстом "ok".
-* opts — объект настрое, см. раздел [Upload options](#options)
+* opts — объект настроек, см. раздел [Upload options](#options)
```js
var el = document.getElementById('my-input');
@@ -279,7 +279,7 @@ FileAPI.readAsBinaryString(file, function (evt/**Object*/){
---
-### readAsBinaryString(file`:Object`, callback`:Function`)`:void`
+### readAsArrayBuffer(file`:Object`, callback`:Function`)`:void`
Чтение содержимого указанного файла как `ArrayBuffer`.
* file — файл для чтения
@@ -383,7 +383,7 @@ var xhr = FileAPI.upload({
### chunkSize`:Number`
-Размер части файла в байта, только HTML5.
+Размер части файла в байтах, только HTML5.
```js
var xhr = FileAPI.upload({
@@ -523,7 +523,7 @@ var xhr = FileAPI.upload({
var xhr = FileAPI.upload({
url: '...',
files: { .. }
- uploadfile: function (file/**Object*/, xhr/**Object*/, options/**Object*/){
+ fileupload: function (file/**Object*/, xhr/**Object*/, options/**Object*/){
// ...
}
});
@@ -747,12 +747,12 @@ FileAPI.Image(imageFile)
---
-### resize(width`:Number`, height`:Number`[, type`:String`])`:FileAPI.Image`
+### resize(width`:Number`, height`:Number`[, strategy`:String`])`:FileAPI.Image`
Ресайз.
* width — новая ширина
* height — новая высота
-* type — enum: `min`, `max`, `preview`. По умолчанию `undefined`.
+* strategy — enum: `min`, `max`, `preview`, `width`, `height`. По умолчанию `undefined`.
```js
FileAPI.Image(imageFile)
@@ -769,6 +769,14 @@ FileAPI.Image(imageFile)
})
;
+
+// По заданной высоте.
+FileAPI.Image(imageFile)
+ .resize(240, 'height')
+ .get(function (err/**String*/, img/**HTMLElement*/){
+
+ })
+;
```
---
@@ -1137,6 +1145,14 @@ Submit Query
---
+
+### Security
+По умолчанию `FileAPI.flash.swf` разрешает доступ с любых доменов `Security.allowDomain("*")`.
+Это может привести к уязвимости same origin bypass, если flash лежит на том же домене, что и критичные данные.
+Чтобы этого избежать, нужно разрешить доступ только к своим доменам [здесь](https://github.com/mailru/FileAPI/blob/master/flash/core/src/ru/mail/communication/JSCallbackPresenter.as#L25) и пересобрать flash.
+
+---
+
## Server settings
@@ -1147,10 +1163,16 @@ Submit Query
@@ -1171,7 +1193,7 @@ Submit Query
FileAPI::makeResponse(array(
'status' => FileAPI::OK
, 'statusText' => 'OK'
- , 'body' => array('count' => sizeof($files)
+ , 'body' => array('count' => sizeof($files))
), $jsonp);
exit;
}
diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000..7143a66a
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,27 @@
+{
+ "name": "fileapi",
+ "main": [
+ "./dist/FileAPI.flash.camera.swf",
+ "./dist/FileAPI.flash.image.swf",
+ "./dist/FileAPI.flash.swf",
+ "./dist/FileAPI.html5.js",
+ "./dist/FileAPI.js",
+ "./dist/jquery.fileapi.min.js"
+ ],
+ "dependencies": {
+ "jquery":">=1.8.2"
+ },
+ "ignore": [
+ "custom-tasks/",
+ "flash/",
+ "lib/",
+ "plugins/",
+ "server/",
+ "statics/",
+ "tests/",
+ "package.json",
+ "bower.json",
+ "README.md",
+ ".*"
+ ]
+}
diff --git a/custom-tasks/Gruntfile-ok.js b/custom-tasks/Gruntfile-ok.js
new file mode 100644
index 00000000..1ff81290
--- /dev/null
+++ b/custom-tasks/Gruntfile-ok.js
@@ -0,0 +1,51 @@
+'use strict';
+
+module.exports = function (grunt) {
+ // Project configuration.
+ grunt.config.set('concat.ok', {
+ src: [
+ 'lib/FileAPI.header.js'
+ , 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.core.js'
+ , 'lib/FileAPI.Image.js'
+ , 'lib/load-image-ios.js'
+ , 'lib/FileAPI.Form.js'
+ , 'lib/FileAPI.XHR.js'
+ , 'lib/FileAPI.Flash.js'
+ , 'plugins/FileAPI.exif.js'
+ ],
+ dest: 'dist/<%= pkg.exportName %>.ok.js'
+ });
+ grunt.config.set('concat.html5ok', {
+ src: [
+ 'lib/FileAPI.header.js'
+ , 'lib/canvas-to-blob.js'
+ , 'lib/FileAPI.core.js'
+ , 'lib/FileAPI.Image.js'
+ , 'lib/load-image-ios.js'
+ , 'lib/FileAPI.Form.js'
+ , 'lib/FileAPI.XHR.js'
+ , 'plugins/FileAPI.exif.js'
+ ],
+ dest: 'dist/<%= pkg.exportName %>.html5ok.js'
+ });
+
+ grunt.config.set('uglify.distok', {
+ files: {
+ 'dist/<%= pkg.exportName %>.ok.min.js': ['<%= concat.ok.dest %>'], 'dist/<%= pkg.exportName %>.html5ok.min.js': ['<%= concat.html5ok.dest %>']
+ }
+ });
+
+ grunt.config.set('compress.main', {
+ options: {
+ archive: '<%= pkg.name %>-<%= pkg.version.replace(/\\./g,"-") %>.zip'
+ },
+ files: [
+ {cwd: 'dist/', expand: true, src: ['*'], dest: '<%= pkg.version.replace(/\\./g,"-") %>/'}
+ ]
+ });
+
+
+ grunt.registerTask('build-zip', ['build', 'compress']);
+
+};
diff --git a/dist/FileAPI.flash.camera.swf b/dist/FileAPI.flash.camera.swf
index 4be8104d..4c953e95 100644
Binary files a/dist/FileAPI.flash.camera.swf and b/dist/FileAPI.flash.camera.swf differ
diff --git a/dist/FileAPI.flash.image.swf b/dist/FileAPI.flash.image.swf
index 5e609b4e..001ff0f9 100644
Binary files a/dist/FileAPI.flash.image.swf and b/dist/FileAPI.flash.image.swf differ
diff --git a/dist/FileAPI.flash.swf b/dist/FileAPI.flash.swf
index ccdb136e..738f60d4 100644
Binary files a/dist/FileAPI.flash.swf and b/dist/FileAPI.flash.swf differ
diff --git a/dist/FileAPI.html5.js b/dist/FileAPI.html5.js
index d62a1895..c9f1cb61 100644
--- a/dist/FileAPI.html5.js
+++ b/dist/FileAPI.html5.js
@@ -1,7 +1,97 @@
-/*! fileapi 2.0.1 - BSD | git://github.com/mailru/FileAPI.git
+/*! FileAPI 2.0.25 - BSD | git://github.com/mailru/FileAPI.git
* FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
*/
+/*
+ * JavaScript Canvas to Blob 2.0.5
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on stackoverflow user Stoive's code snippet:
+ * http://stackoverflow.com/q/4998908
+ */
+
+/*jslint nomen: true, regexp: true */
+/*global window, atob, Blob, ArrayBuffer, Uint8Array */
+
+(function (window) {
+ 'use strict';
+ var CanvasPrototype = window.HTMLCanvasElement &&
+ window.HTMLCanvasElement.prototype,
+ hasBlobConstructor = window.Blob && (function () {
+ try {
+ return Boolean(new Blob());
+ } catch (e) {
+ return false;
+ }
+ }()),
+ hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
+ (function () {
+ try {
+ return new Blob([new Uint8Array(100)]).size === 100;
+ } catch (e) {
+ return false;
+ }
+ }()),
+ BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
+ window.MozBlobBuilder || window.MSBlobBuilder,
+ dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
+ window.ArrayBuffer && window.Uint8Array && function (dataURI) {
+ var byteString,
+ arrayBuffer,
+ intArray,
+ i,
+ mimeString,
+ bb;
+ if (dataURI.split(',')[0].indexOf('base64') >= 0) {
+ // Convert base64 to raw binary data held in a string:
+ byteString = atob(dataURI.split(',')[1]);
+ } else {
+ // Convert base64/URLEncoded data component to raw binary data:
+ byteString = decodeURIComponent(dataURI.split(',')[1]);
+ }
+ // Write the bytes of the string to an ArrayBuffer:
+ arrayBuffer = new ArrayBuffer(byteString.length);
+ intArray = new Uint8Array(arrayBuffer);
+ for (i = 0; i < byteString.length; i += 1) {
+ intArray[i] = byteString.charCodeAt(i);
+ }
+ // Separate out the mime component:
+ mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
+ // Write the ArrayBuffer (or ArrayBufferView) to a blob:
+ if (hasBlobConstructor) {
+ return new Blob(
+ [hasArrayBufferViewSupport ? intArray : arrayBuffer],
+ {type: mimeString}
+ );
+ }
+ bb = new BlobBuilder();
+ bb.append(arrayBuffer);
+ return bb.getBlob(mimeString);
+ };
+ if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
+ if (CanvasPrototype.mozGetAsFile) {
+ CanvasPrototype.toBlob = function (callback, type, quality) {
+ if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
+ } else {
+ callback(this.mozGetAsFile('blob', type));
+ }
+ };
+ } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
+ CanvasPrototype.toBlob = function (callback, type, quality) {
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
+ };
+ }
+ }
+ window.dataURLtoBlob = dataURLtoBlob;
+})(window);
+
/*jslint evil: true */
/*global window, URL, webkitURL, ActiveXObject */
@@ -15,6 +105,9 @@
document = window.document,
doctype = document.doctype || {},
userAgent = window.navigator.userAgent,
+ safari = /safari\//i.test(userAgent) && !/chrome\//i.test(userAgent),
+ iemobile = /iemobile\//i.test(userAgent),
+ insecureChrome = !safari && /chrome\//i.test(userAgent) && window.location.protocol === 'http:',
// https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48
apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL),
@@ -29,12 +122,14 @@
jQuery = window.jQuery,
html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary)))
- && !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
+ && !(safari && /windows/i.test(userAgent) && !iemobile), // BugFix: https://github.com/mailru/FileAPI/issues/25
cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
-
+
chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
+ normalize = ('' + ''.normalize).indexOf('[native code]') > 0,
+
// https://github.com/blueimp/JavaScript-Canvas-to-Blob
dataURLtoBlob = window.dataURLtoBlob,
@@ -45,6 +140,10 @@
_rinput = /input/i,
_rdata = /^data:[^,]+,/,
+ _toString = {}.toString,
+ _supportConsoleLog,
+ _supportConsoleLogApply,
+
Math = window.Math,
@@ -111,11 +210,11 @@
_elEvents[uid] = {};
}
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.add(el, type, fn);
- }
- else {
+ } else {
if( !_elEvents[uid][type] ){
_elEvents[uid][type] = [];
}
@@ -138,8 +237,9 @@
if( el ){
var uid = api.uid(el), events = _elEvents[uid] || {};
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.remove(el, type, fn);
}
else {
@@ -187,12 +287,14 @@
* FileAPI (core object)
*/
api = {
- version: '2.0.1',
+ version: '2.0.25',
cors: false,
html5: true,
media: false,
formData: true,
+ multiPassResize: true,
+ insecureChrome: insecureChrome,
debug: false,
pingUrl: false,
@@ -222,6 +324,9 @@
, 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
},
+ uploadRetry : 0,
+ networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
+
chunkSize : 0,
chunkUploadRetry : 0,
chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
@@ -231,6 +336,8 @@
GB: _SIZE_CONST(3),
TB: _SIZE_CONST(4),
+ EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
+
expando: 'fileapi' + (new Date).getTime(),
uid: function (obj){
@@ -241,8 +348,8 @@
},
log: function (){
- if( api.debug && window.console && console.log ){
- if( console.log.apply ){
+ if( api.debug && _supportConsoleLog ){
+ if( _supportConsoleLogApply ){
console.log.apply(console, arguments);
}
else {
@@ -459,13 +566,22 @@
/**
- * Is file instance
- *
+ * Is file?
* @param {File} file
* @return {Boolean}
*/
isFile: function (file){
- return html5 && file && (file instanceof File);
+ return _toString.call(file) === '[object File]';
+ },
+
+
+ /**
+ * Is blob?
+ * @param {Blob} blob
+ * @returns {Boolean}
+ */
+ isBlob: function (blob) {
+ return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
},
@@ -594,7 +710,7 @@
* @param {Boolean} [progress]
*/
readAsImage: function (file, fn, progress){
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
if( apiURL ){
/** @namespace apiURL.createObjectURL */
var data = apiURL.createObjectURL(file);
@@ -687,28 +803,84 @@
getDropFiles: function (evt, callback){
var
files = []
+ , all = []
+ , items
, dataTransfer = _getDataTransfer(evt)
- , entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
- , queue = api.queue(function (){ callback(files); })
+ , transFiles = dataTransfer.files
+ , transItems = dataTransfer.items
+ , entrySupport = _isArray(transItems) && transItems[0] && _getAsEntry(transItems[0])
+ , queue = api.queue(function (){ callback(files, all); })
;
- _each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
+ if( entrySupport ){
+ if( normalize && transFiles ){
+ var
+ i = transFiles.length
+ , file
+ , entry
+ ;
+
+ items = new Array(i);
+ while( i-- ){
+ file = transFiles[i];
+
+ try {
+ entry = _getAsEntry(transItems[i]);
+ }
+ catch( err ){
+ api.log('[err] getDropFiles: ', err);
+ entry = null;
+ }
+
+ if( _isEntry(entry) ){
+ // OSX filesystems use Unicode Normalization Form D (NFD),
+ // and entry.file(…) can't read the files with the same names
+ if( entry.isDirectory || (entry.isFile && file.name == file.name.normalize('NFC')) ){
+ items[i] = entry;
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ }
+ else {
+ items = transItems;
+ }
+ }
+ else {
+ items = transFiles;
+ }
+
+ _each(items || [], function (item){
queue.inc();
try {
- if( entrySupport ){
- _readEntryAsFiles(item, function (err, entryFiles){
+ if( entrySupport && _isEntry(item) ){
+ _readEntryAsFiles(item, function (err, entryFiles, allEntries){
if( err ){
api.log('[err] getDropFiles:', err);
} else {
files.push.apply(files, entryFiles);
}
+ all.push.apply(all, allEntries);
+
queue.next();
});
}
else {
- _isRegularFile(item, function (yes){
- yes && files.push(item);
+ _isRegularFile(item, function (yes, err){
+ if( yes ){
+ files.push(item);
+ }
+ else {
+ item.error = err;
+ }
+ all.push(item);
+
queue.next();
});
}
@@ -819,7 +991,7 @@
getInfo: function (file, fn){
var info = {}, readers = _infoReader.concat();
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
(function _next(){
var reader = readers.shift();
if( reader ){
@@ -917,7 +1089,8 @@
upload: function (options){
options = _extend({
- prepare: api.F
+ jsonp: 'callback'
+ , prepare: api.F
, beforeupload: api.F
, upload: api.F
, fileupload: api.F
@@ -928,7 +1101,8 @@
, pause: api.F
, imageOriginal: true
, chunkSize: api.chunkSize
- , chunkUpoloadRetry: api.chunkUploadRetry
+ , chunkUploadRetry: api.chunkUploadRetry
+ , uploadRetry: api.uploadRetry
}, options);
@@ -984,14 +1158,18 @@
}
if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
- // Mark active job
- _complete = false;
+ // Mark active job
+ _complete = false;
// Set current upload file
proxyXHR.currentFile = _file;
// Prepare file options
- _file && options.prepare(_file, _fileOptions);
+ if (_file && options.prepare(_file, _fileOptions) === false) {
+ _nextFile.call(_this);
+ return;
+ }
+ _fileOptions.file = _file;
_this._getFormData(_fileOptions, data, function (form){
if( !_loaded ){
@@ -1008,6 +1186,9 @@
progress: _file ? function (evt){
if( !_fileLoaded ){
+ // For ignore the double calls.
+ _fileLoaded = (evt.loaded === evt.total);
+
// emit "fileprogress" event
options.fileprogress({
type: 'progress'
@@ -1019,35 +1200,38 @@
options.progress({
type: 'progress'
, total: _total
- , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
+ , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total)) || 0
}, _file, xhr, _fileOptions);
}
} : noop,
complete: function (err){
- // fixed throttle event
- _fileLoaded = true;
-
_each(_xhrPropsExport, function (name){
proxyXHR[name] = xhr[name];
});
if( _file ){
+ data.total = (data.total || data.size);
data.loaded = data.total;
- // emulate 100% "progress"
- this.progress(data);
+ if( !err ) {
+ // emulate 100% "progress"
+ this.progress(data);
+
+ // fixed throttle event
+ _fileLoaded = true;
- // bytes loaded
- _loaded += data.size; // data.size != data.total, it's desirable fix this
- proxyXHR.loaded = _loaded;
+ // bytes loaded
+ _loaded += data.size; // data.size != data.total, it's desirable fix this
+ proxyXHR.loaded = _loaded;
+ }
// emit "filecomplete" event
options.filecomplete(err, xhr, _file, _fileOptions);
}
// upload next file
- _nextFile.call(_this);
+ setTimeout(function () {_nextFile.call(_this);}, 0);
}
})); // xhr
@@ -1064,7 +1248,8 @@
});
}
else {
- options.complete(proxyXHR.status == 200 || proxyXHR.status == 201 ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
+ var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
+ options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
// Mark done state
_complete = true;
}
@@ -1169,12 +1354,30 @@
, postNameConcat = api.postNameConcat
;
+ // Append data
+ _each(options.data, function add(val, name){
+ if( typeof val == 'object' ){
+ _each(val, function (v, i){
+ add(v, postNameConcat(name, i));
+ });
+ }
+ else {
+ Form.append(name, val);
+ }
+ });
+
(function _addFile(file/**Object*/){
if( file.image ){ // This is a FileAPI.Image
queue.inc();
file.toData(function (err, image){
- // @todo: error
+ // @todo: требует рефакторинга и обработки ошибки
+ if (file.file) {
+ image.type = file.file.type;
+ image.quality = file.matrix.quality;
+ filename = file.file && file.file.name;
+ }
+
filename = filename || (new Date).getTime()+'.png';
_addFile(image);
@@ -1228,19 +1431,6 @@
}
})(file);
-
- // Append data
- _each(options.data, function add(val, name){
- if( typeof val == 'object' ){
- _each(val, function (v, i){
- add(v, postNameConcat(name, i));
- });
- }
- else {
- Form.append(name, val);
- }
- });
-
queue.check();
},
@@ -1358,7 +1548,7 @@
} // api
;
-
+
function _emit(target, fn, name, res, ext){
var evt = {
@@ -1371,13 +1561,13 @@
}
- function _hasSupportReadAs(as){
- return FileReader && !!FileReader.prototype['readAs'+as];
+ function _hasSupportReadAs(method){
+ return FileReader && !!FileReader.prototype['readAs' + method];
}
- function _readAs(file, fn, as, encoding){
- if( api.isFile(file) && _hasSupportReadAs(as) ){
+ function _readAs(file, fn, method, encoding){
+ if( api.isBlob(file) && _hasSupportReadAs(method) ){
var Reader = new FileReader;
// Add event listener
@@ -1399,10 +1589,10 @@
try {
// ReadAs ...
if( encoding ){
- Reader['readAs'+as](file, encoding);
+ Reader['readAs' + method](file, encoding);
}
else {
- Reader['readAs'+as](file);
+ Reader['readAs' + method](file);
}
}
catch (err){
@@ -1410,33 +1600,38 @@
}
}
else {
- _emit(file, fn, 'error', undef, { error: 'filreader_not_support_'+as });
+ _emit(file, fn, 'error', undef, { error: 'filreader_not_support_' + method });
}
}
function _isRegularFile(file, callback){
// http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects
- if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){
+ if( !file.type && (safari || ((file.size % 4096) === 0 && (file.size <= 102400))) ){
if( FileReader ){
try {
- var Reader = new FileReader();
+ var reader = new FileReader();
- _one(Reader, _readerEvents, function (evt){
+ _one(reader, _readerEvents, function (evt){
var isFile = evt.type != 'error';
- callback(isFile);
if( isFile ){
- Reader.abort();
+ if ( reader.readyState == null || reader.readyState === reader.LOADING ) {
+ reader.abort();
+ }
+ callback(isFile);
+ }
+ else {
+ callback(false, reader.error);
}
});
- Reader.readAsDataURL(file);
+ reader.readAsDataURL(file);
} catch( err ){
- callback(false);
+ callback(false, err);
}
}
else {
- callback(null);
+ callback(null, new Error('FileReader is not supported'));
}
}
else {
@@ -1445,6 +1640,11 @@
}
+ function _isEntry(item){
+ return item && (item.isFile || item.isDirectory);
+ }
+
+
function _getAsEntry(item){
var entry;
if( item.getAsEntry ){ entry = item.getAsEntry(); }
@@ -1456,45 +1656,68 @@
function _readEntryAsFiles(entry, callback){
if( !entry ){
// error
- callback('invalid entry');
+ var err = new Error('invalid entry');
+ entry = new Object(entry);
+ entry.error = err;
+ callback(err.message, [], [entry]);
}
else if( entry.isFile ){
// Read as file
- entry.file(function(file){
+ entry.file(function (file){
// success
file.fullPath = entry.fullPath;
- callback(false, [file]);
+ callback(false, [file], [file]);
}, function (err){
// error
- callback('FileError.code: '+err.code);
+ entry.error = err;
+ callback('FileError.code: ' + err.code, [], [entry]);
});
}
else if( entry.isDirectory ){
- var reader = entry.createReader(), result = [];
+ var
+ reader = entry.createReader()
+ , firstAttempt = true
+ , files = []
+ , all = [entry]
+ ;
+
+ var onerror = function (err){
+ // error
+ entry.error = err;
+ callback('DirectoryError.code: ' + err.code, files, all);
+ };
+ var ondone = function ondone(entries){
+ if( firstAttempt ){
+ firstAttempt = false;
+ if( !entries.length ){
+ entry.error = new Error('directory is empty');
+ }
+ }
- reader.readEntries(function(entries){
// success
- api.afor(entries, function (next, entry){
- _readEntryAsFiles(entry, function (err, files){
- if( err ){
- api.log(err);
- }
- else {
- result = result.concat(files);
- }
+ if( entries.length ){
+ api.afor(entries, function (next, entry){
+ _readEntryAsFiles(entry, function (err, entryFiles, allEntries){
+ if( !err ){
+ files = files.concat(entryFiles);
+ }
+ all = all.concat(allEntries);
- if( next ){
- next();
- }
- else {
- callback(false, result);
- }
+ if( next ){
+ next();
+ }
+ else {
+ reader.readEntries(ondone, onerror);
+ }
+ });
});
- });
- }, function (err){
- // error
- callback('directory_reader: ' + err);
- });
+ }
+ else {
+ callback(false, files, all);
+ }
+ };
+
+ reader.readEntries(ondone, onerror);
}
else {
_readEntryAsFiles(_getAsEntry(entry), callback);
@@ -1548,6 +1771,7 @@
width: img.width
, height: img.height
});
+ img.src = api.EMPTY_PNG;
img = null;
});
}
@@ -1572,7 +1796,8 @@
}
if( FileReader ){
- _on(el, 'dragenter dragleave dragover', function (evt){
+ // Hover
+ _on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
var
types = _getDataTransfer(evt).types
, i = types && types.length
@@ -1605,15 +1830,18 @@
}
});
- _on(el, 'drop', function (evt){
+
+ // Drop
+ _on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){
evt[preventDefault]();
_type = 0;
- onHover.call(evt[currentTarget], false, evt);
- api.getDropFiles(evt, function (files){
- onDrop.call(evt[currentTarget], files, evt);
+ api.getDropFiles(evt, function (files, all){
+ onDrop.call(evt[currentTarget], files, all, evt);
});
+
+ onHover.call(evt[currentTarget], false, evt);
});
}
else {
@@ -1629,8 +1857,8 @@
* @param {Function} onDrop
*/
api.event.dnd.off = function (el, onHover, onDrop){
- _off(el, 'dragenter dragleave dragover', onHover);
- _off(el, 'drop', onDrop);
+ _off(el, 'dragenter dragleave dragover', onHover.ff);
+ _off(el, 'drop', onDrop.ff);
};
@@ -1667,25 +1895,35 @@
});
- // @configuration
+ // Configuration
+ try {
+ _supportConsoleLog = !!console.log;
+ _supportConsoleLogApply = !!console.log.apply;
+ }
+ catch (err) {}
+
if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
+ if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
})(window, void 0);
/*global window, FileAPI, document */
-(function (api, document, undef){
+(function (api, document, undef) {
'use strict';
var
min = Math.min,
round = Math.round,
- getCanvas = function (){ return document.createElement('canvas'); },
+ getCanvas = function () { return document.createElement('canvas'); },
support = false,
exifOrientation = {
8: 270
, 3: 180
, 6: 90
+ , 7: 270
+ , 4: 180
+ , 5: 90
}
;
@@ -1706,6 +1944,8 @@
}
this.file = file;
+ this.size = file.size || 100;
+
this.matrix = {
sx: 0,
sy: 0,
@@ -1741,13 +1981,13 @@
return this.set({ sx: x, sy: y, sw: w, sh: h || w });
},
- resize: function (w, h, type){
- if( typeof h == 'string' ){
- type = h;
+ resize: function (w, h, strategy){
+ if( /min|max|height|width/.test(h) ){
+ strategy = h;
h = w;
}
- return this.set({ dw: w, dh: h, resize: type });
+ return this.set({ dw: w, dh: h || w, resize: strategy });
},
preview: function (w, h){
@@ -1799,14 +2039,17 @@
, copy // canvas copy
, buffer = image
, overlay = m.overlay
- , queue = api.queue(function (){ fn(false, canvas); })
+ , queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
, renderImageToCanvas = api.renderImageToCanvas
;
+ // Normalize angle
+ deg = deg - Math.floor(deg/360)*360;
+
// For `renderImageToCanvas`
image._type = this.file.type;
- while( min(w/dw, h/dh) > 2 ){
+ while(m.multipass && min(w/dw, h/dh) > 2 ){
w = (w/2 + 0.5)|0;
h = (h/2 + 0.5)|0;
@@ -1833,7 +2076,7 @@
canvas.quality = m.quality;
ctx.rotate(deg * Math.PI / 180);
- renderImageToCanvas(canvas, buffer
+ renderImageToCanvas(ctx.canvas, buffer
, m.sx, m.sy
, m.sw || buffer.width
, m.sh || buffer.height
@@ -1841,7 +2084,6 @@
, (deg == 90 || deg == 180 ? -dh : 0)
, dw, dh
);
-
dw = canvas.width;
dh = canvas.height;
@@ -1899,10 +2141,10 @@
, dw = m.dw = m.dw || sw
, dh = m.dh = m.dh || sh
, sf = sw/sh, df = dw/dh
- , type = m.resize
+ , strategy = m.resize
;
- if( type == 'preview' ){
+ if( strategy == 'preview' ){
if( dw != sw || dh != sh ){
// Make preview
var w, h;
@@ -1923,12 +2165,18 @@
}
}
}
- else if( type ){
+ else if( strategy == 'height' ){
+ dw = dh * sf;
+ }
+ else if( strategy == 'width' ){
+ dh = dw / sf;
+ }
+ else if( strategy ){
if( !(sw > dw || sh > dh) ){
dw = sw;
dh = sh;
}
- else if( type == 'min' ){
+ else if( strategy == 'min' ){
dw = round(sf < df ? min(sw, dw) : dh*sf);
dh = round(sf < df ? dw/sf : min(sh, dh));
}
@@ -1942,7 +2190,7 @@
m.sh = sh;
m.dw = dw;
m.dh = dh;
-
+ m.multipass = api.multiPassResize;
return m;
},
@@ -1952,7 +2200,12 @@
fn(err);
}
else {
- this._apply(image, fn);
+ try {
+ this._apply(image, fn);
+ } catch (err){
+ api.log('[err] FileAPI.Image.fn._apply:', err);
+ fn(err);
+ }
}
});
},
@@ -1976,11 +2229,13 @@
else {
fn('not_support_transform');
}
+
+ return this;
},
toData: function (fn){
- this.get(fn);
+ return this.get(fn);
}
};
@@ -2002,13 +2257,13 @@
if( !err ){
api.each(transform, function (params, name){
if( !queue.isFail() ){
- var ImgTrans = new Image(img.nodeType ? img : file);
+ var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
- if( typeof params == 'function' ){
+ if( isFn ){
params(img, ImgTrans);
}
else if( params.width ){
- ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.type);
+ ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
}
else {
if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
@@ -2025,13 +2280,16 @@
params.rotate = 'auto';
}
- ImgTrans.set({
- deg: params.rotate
- , type: params.type || file.type || 'image/png'
- , quality: params.quality || 1
- , overlay: params.overlay
- , filter: params.filter
- });
+ ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
+
+ if( !isFn ){
+ ImgTrans.set({
+ deg: params.rotate
+ , overlay: params.overlay
+ , filter: params.filter
+ , quality: params.quality || 1
+ });
+ }
queue.inc();
ImgTrans.toData(function (err, image){
@@ -2130,8 +2388,12 @@
* For load-image-ios.js
*/
api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
- canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
- return canvas;
+ try {
+ return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
+ } catch (ex) {
+ api.log('renderImageToCanvas failed');
+ throw ex;
+ }
};
@@ -2140,24 +2402,200 @@
api.Image = Image;
})(FileAPI, document);
-/*global window, FileAPI */
+/*
+ * JavaScript Load Image iOS scaling fixes 1.0.3
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * iOS image scaling fixes based on
+ * https://github.com/stomita/ios-imagefile-megapixel
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
-(function (api, window){
- "use strict";
+/*jslint nomen: true, bitwise: true */
+/*global FileAPI, window, document */
- var
- document = window.document
- , FormData = window.FormData
- , Form = function (){ this.items = []; }
- , encodeURIComponent = window.encodeURIComponent
- ;
+(function (factory) {
+ 'use strict';
+ factory(FileAPI);
+}(function (loadImage) {
+ 'use strict';
+ // Only apply fixes on the iOS platform:
+ if (!window.navigator || !window.navigator.platform ||
+ !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
+ return;
+ }
- Form.prototype = {
+ var originalRenderMethod = loadImage.renderImageToCanvas;
- append: function (name, blob, file, type){
- this.items.push({
- name: name
+ // Detects subsampling in JPEG images:
+ loadImage.detectSubsampling = function (img) {
+ var canvas,
+ context;
+ if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
+ canvas = document.createElement('canvas');
+ canvas.width = canvas.height = 1;
+ context = canvas.getContext('2d');
+ context.drawImage(img, -img.width + 1, 0);
+ // subsampled image becomes half smaller in rendering size.
+ // check alpha channel value to confirm image is covering edge pixel or not.
+ // if alpha value is 0 image is not covering, hence subsampled.
+ return context.getImageData(0, 0, 1, 1).data[3] === 0;
+ }
+ return false;
+ };
+
+ // Detects vertical squash in JPEG images:
+ loadImage.detectVerticalSquash = function (img, subsampled) {
+ var naturalHeight = img.naturalHeight || img.height,
+ canvas = document.createElement('canvas'),
+ context = canvas.getContext('2d'),
+ data,
+ sy,
+ ey,
+ py,
+ alpha;
+ if (subsampled) {
+ naturalHeight /= 2;
+ }
+ canvas.width = 1;
+ canvas.height = naturalHeight;
+ context.drawImage(img, 0, 0);
+ data = context.getImageData(0, 0, 1, naturalHeight).data;
+ // search image edge pixel position in case it is squashed vertically:
+ sy = 0;
+ ey = naturalHeight;
+ py = naturalHeight;
+ while (py > sy) {
+ alpha = data[(py - 1) * 4 + 3];
+ if (alpha === 0) {
+ ey = py;
+ } else {
+ sy = py;
+ }
+ py = (ey + sy) >> 1;
+ }
+ return (py / naturalHeight) || 1;
+ };
+
+ // Renders image to canvas while working around iOS image scaling bugs:
+ // https://github.com/blueimp/JavaScript-Load-Image/issues/13
+ loadImage.renderImageToCanvas = function (
+ canvas,
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ ) {
+ if (img._type === 'image/jpeg') {
+ var context = canvas.getContext('2d'),
+ tmpCanvas = document.createElement('canvas'),
+ tileSize = 1024,
+ tmpContext = tmpCanvas.getContext('2d'),
+ subsampled,
+ vertSquashRatio,
+ tileX,
+ tileY;
+ tmpCanvas.width = tileSize;
+ tmpCanvas.height = tileSize;
+ context.save();
+ subsampled = loadImage.detectSubsampling(img);
+ if (subsampled) {
+ sourceX /= 2;
+ sourceY /= 2;
+ sourceWidth /= 2;
+ sourceHeight /= 2;
+ }
+ vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
+ if (subsampled || vertSquashRatio !== 1) {
+ sourceY *= vertSquashRatio;
+ destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
+ destHeight = Math.ceil(
+ tileSize * destHeight / sourceHeight / vertSquashRatio
+ );
+ destY = 0;
+ tileY = 0;
+ while (tileY < sourceHeight) {
+ destX = 0;
+ tileX = 0;
+ while (tileX < sourceWidth) {
+ tmpContext.clearRect(0, 0, tileSize, tileSize);
+ tmpContext.drawImage(
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ -tileX,
+ -tileY,
+ sourceWidth,
+ sourceHeight
+ );
+ context.drawImage(
+ tmpCanvas,
+ 0,
+ 0,
+ tileSize,
+ tileSize,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ );
+ tileX += tileSize;
+ destX += destWidth;
+ }
+ tileY += tileSize;
+ destY += destHeight;
+ }
+ context.restore();
+ return canvas;
+ }
+ }
+ return originalRenderMethod(
+ canvas,
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ );
+ };
+
+}));
+
+/*global window, FileAPI */
+
+(function (api, window){
+ "use strict";
+
+ var
+ document = window.document
+ , FormData = window.FormData
+ , Form = function (){ this.items = []; }
+ , encodeURIComponent = window.encodeURIComponent
+ ;
+
+
+ Form.prototype = {
+
+ append: function (name, blob, file, type){
+ this.items.push({
+ name: name
, blob: blob && blob.blob || (blob == void 0 ? '' : blob)
, file: blob && (file || blob.name)
, type: blob && (type || blob.type)
@@ -2200,7 +2638,13 @@
});
this.each(function (file){
- next(file, data, queue, arg);
+ try{
+ next(file, data, queue, arg);
+ }
+ catch( err ){
+ api.log('FileAPI.Form._to: ' + err.message);
+ complete(err);
+ }
});
queue.check();
@@ -2215,6 +2659,7 @@
api.reset(blob, true);
// set new name
blob.name = file.name;
+ blob.disabled = false;
data.appendChild(blob);
}
else {
@@ -2418,7 +2863,7 @@
this.end(0, 'abort');
if( this.xhr ){
- this.xhr.aborted = true;
+ this.xhr.aborted = true;
this.xhr.abort();
}
},
@@ -2427,14 +2872,19 @@
var _this = this, options = this.options;
FormData.toData(function (data){
- // Start uploading
- options.upload(options, _this);
- _this._send.call(_this, options, data);
+ if( data instanceof Error ){
+ _this.end(0, data.message);
+ }
+ else{
+ // Start uploading
+ options.upload(options, _this);
+ _this._send.call(_this, options, data);
+ }
}, options);
},
_send: function (options, data){
- var _this = this, xhr, uid = _this.uid, url = options.url;
+ var _this = this, xhr, uid = _this.uid, onLoadFnName = _this.uid + "Load", url = options.url;
api.log('XHR._send:', data);
@@ -2444,31 +2894,77 @@
}
if( data.nodeName ){
+ var jsonp = options.jsonp;
+
+ // prepare callback in GET
+ url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
+
// legacy
options.upload(options, _this);
- xhr = document.createElement('div');
- xhr.innerHTML = ' '
+ var
+ onPostMessage = function (evt){
+ if( ~url.indexOf(evt.origin) ){
+ try {
+ var result = api.parseJSON(evt.data);
+ if( result.id == uid ){
+ complete(result.status, result.statusText, result.response);
+ }
+ } catch( err ){
+ complete(0, err.message);
+ }
+ }
+ },
+
+ // jsonp-callack
+ complete = window[uid] = function (status, statusText, response){
+ _this.readyState = 4;
+ _this.responseText = response;
+ _this.end(status, statusText);
+
+ api.event.off(window, 'message', onPostMessage);
+ window[uid] = xhr = transport = window[onLoadFnName] = null;
+ }
;
_this.xhr.abort = function (){
- var transport = xhr.getElementsByTagName('iframe')[0];
- if( transport ){
- try {
- if( transport.stop ){ transport.stop(); }
- else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
- else { transport.contentWindow.document.execCommand('Stop'); }
- }
- catch (er) {}
+ try {
+ if( transport.stop ){ transport.stop(); }
+ else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
+ else { transport.contentWindow.document.execCommand('Stop'); }
+ }
+ catch (er) {}
+ complete(0, "abort");
+ };
+
+ api.event.on(window, 'message', onPostMessage);
+
+ window[onLoadFnName] = function (){
+ try {
+ var
+ win = transport.contentWindow
+ , doc = win.document
+ , result = win.result || api.parseJSON(doc.body.innerHTML)
+ ;
+ complete(result.status, result.statusText, result.response);
+ } catch (e){
+ api.log('[transport.onload]', e);
}
- xhr = null;
};
- // append form-data
- var form = xhr.getElementsByTagName('form')[0];
+ xhr = document.createElement('div');
+ xhr.innerHTML = '
'
+ ;
+
+ // get form-data & transport
+ var
+ form = xhr.getElementsByTagName('form')[0]
+ , transport = xhr.getElementsByTagName('iframe')[0]
+ ;
+
form.appendChild(data);
api.log(form.parentNode.innerHTML);
@@ -2479,20 +2975,19 @@
// keep a reference to node-transport
_this.xhr.node = xhr;
- // jsonp-callack
- window[uid] = function (status, statusText, response){
- _this.readyState = 4;
- _this.responseText = response;
- _this.end(status, statusText);
- xhr = null;
- };
-
// send
_this.readyState = 2; // loaded
- form.submit();
+ try {
+ form.submit();
+ } catch (err) {
+ api.log('iframe.error: ' + err);
+ }
form = null;
}
else {
+ // Clean url
+ url = url.replace(/([a-z]+)=(\?)&?/i, '');
+
// html5
if (this.xhr && this.xhr.aborted) {
api.log("Error: already aborted");
@@ -2501,7 +2996,7 @@
xhr = _this.xhr = api.getXHR();
if (data.params) {
- url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
+ url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
}
xhr.open('POST', url, true);
@@ -2518,13 +3013,13 @@
xhr.setRequestHeader(key, val);
});
-
+
if ( options._chunked ) {
// chunked upload
if( xhr.upload ){
- xhr.upload.addEventListener('progress', function (/**Event*/evt){
+ xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
if (!data.retry) {
- // show progress only for correct chunk uploads
+ // show progress only for correct chunk uploads
options.progress({
type: evt.type
, total: data.size
@@ -2532,7 +3027,7 @@
, totalSize: data.size
}, _this, options);
}
- }, false);
+ }, 100), false);
}
xhr.onreadystatechange = function (){
@@ -2547,9 +3042,9 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
-
+
if (!xhr.status || xhr.status - 201 > 0) {
- api.log("Error: " + xhr.status);
+ api.log("Error: " + xhr.status);
// some kind of error
// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
// up - server error
@@ -2558,7 +3053,7 @@
// only applicable for recoverable error codes 500 && 416
var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
- // inform about recoverable problems
+ // inform about recoverable problems
options.pause(data.file, options);
// smart restart if server reports about the last known byte
@@ -2567,10 +3062,13 @@
data.end = lkb;
} else {
data.end = data.start - 1;
+ if (416 == xhr.status) {
+ data.end = data.end - options.chunkSize;
+ }
}
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, delay);
} else {
// no mo retries
@@ -2594,28 +3092,37 @@
data.file.FileAPIReadPosition = data.end;
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, 0);
}
}
+
xhr = null;
}
};
data.start = data.end + 1;
- data.end = Math.max(Math.min(data.start + options.chunkSize, data.size ) - 1, data.start);
-
- var slice;
- (slice = 'slice') in data.file || (slice = 'mozSlice') in data.file || (slice = 'webkitSlice') in data.file;
-
- xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
- xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
- xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
-
- slice = data.file[slice](data.start, data.end + 1);
-
- xhr.send(slice);
- slice = null;
+ data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
+
+ // Retrieve a slice of file
+ var
+ file = data.file
+ , slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
+ ;
+
+ if( data.size && !slice.size ){
+ setTimeout(function (){
+ _this.end(-1);
+ });
+ } else {
+ xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
+ xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
+ xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
+
+ xhr.send(slice);
+ }
+
+ file = slice = null;
} else {
// single piece upload
if( xhr.upload ){
@@ -2624,7 +3131,7 @@
options.progress(evt, _this, options);
}, 100), false);
}
-
+
xhr.onreadystatechange = function (){
_this.status = xhr.status;
_this.statusText = xhr.statusText;
@@ -2635,7 +3142,28 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
- _this.end(xhr.status);
+
+ if (!xhr.status || xhr.status > 201) {
+ api.log("Error: " + xhr.status);
+ if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
+ options.retry = (options.retry || 0) + 1;
+ var delay = api.networkDownRetryTimeout;
+
+ // inform about recoverable problems
+ options.pause(options.file, options);
+
+ setTimeout(function () {
+ _this._send(options, data);
+ }, delay);
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+
xhr = null;
}
};
@@ -2643,19 +3171,19 @@
if( api.isArray(data) ){
// multipart
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
- data = data.join('') +'--_'+ api.expando +'--';
+ var rawData = data.join('') +'--_'+ api.expando +'--';
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
if( xhr.sendAsBinary ){
- xhr.sendAsBinary(data);
+ xhr.sendAsBinary(rawData);
}
else {
- var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
+ var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
xhr.send(new Uint8Array(bytes).buffer);
}
} else {
- // FormData
+ // FormData
xhr.send(data);
}
}
@@ -2734,7 +3262,11 @@
// });
// Set camera stream
- video.src = URL.createObjectURL(stream);
+ try {
+ video.src = URL.createObjectURL(stream);
+ } catch (err) {
+ video.srcObject = stream;
+ }
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
@@ -2761,8 +3293,19 @@
try {
this._active = false;
this.video.pause();
- this.stream.stop();
- } catch( err ){ }
+
+ try {
+ this.stream.stop();
+ } catch (err) {
+ api.each(this.stream.getTracks(), function (track) {
+ track.stop();
+ });
+ }
+
+ this.stream = null;
+ } catch( err ){
+ api.log('[FileAPI.Camera] stop:', err);
+ }
},
@@ -2837,7 +3380,7 @@
el.style.height = _px(options.height);
- if( api.html5 && html5 ){
+ if( api.html5 && html5 && !api.insecureChrome ){
// Create video element
var video = document.createElement('video');
@@ -2868,6 +3411,38 @@
callback('not_support_camera');
};
+ Camera.checkAlreadyCaptured = (function () {
+ var mediaDevices = navigator.mediaDevices,
+ MediaStreamTrack = window.MediaStreamTrack,
+ navigatorEnumerateDevices = navigator.enumerateDevices,
+ enumerateDevices;
+
+ if (mediaDevices && mediaDevices.enumerateDevices) {
+ enumerateDevices = function (callback) {
+ mediaDevices.enumerateDevices().then(callback);
+ };
+ } else if (MediaStreamTrack && MediaStreamTrack.getSources) {
+ enumerateDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
+ } else if (navigatorEnumerateDevices) {
+ enumerateDevices = navigatorEnumerateDevices.bind(navigator);
+ } else {
+ enumerateDevices = function (fn) {
+ fn([]);
+ };
+ }
+
+ return function (callback) {
+ enumerateDevices(function (devices) {
+ var deviceExists = devices.some(function (device) {
+ return (device.kind === 'videoinput' || device.kind === 'video') && device.label;
+ });
+
+ callback(deviceExists);
+ });
+ };
+
+ })();
+
/**
* @class FileAPI.Camera.Shot
@@ -2907,7 +3482,9 @@
ctx.drawImage(video, 0, 0, 1, 1);
res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
}
- catch( e ){}
+ catch( err ){
+ api.log('[FileAPI.Camera] detectVideoSignal:', err);
+ }
return res;
}
@@ -2917,269 +3494,115 @@
api.Camera = Camera;
})(window, FileAPI);
-/*
- * JavaScript Load Image iOS scaling fixes 1.0.3
- * https://github.com/blueimp/JavaScript-Load-Image
- *
- * Copyright 2013, Sebastian Tschan
- * https://blueimp.net
- *
- * iOS image scaling fixes based on
- * https://github.com/stomita/ios-imagefile-megapixel
+/**
+ * FileAPI fallback to Flash
*
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
+ * @flash-developer "Vladimir Demidov"
*/
-/*jslint nomen: true, bitwise: true */
-/*global FileAPI, window, document */
-
-(function (factory) {
- 'use strict';
- factory(FileAPI);
-}(function (loadImage) {
- 'use strict';
-
- // Only apply fixes on the iOS platform:
- if (!window.navigator || !window.navigator.platform ||
- !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
- return;
- }
-
- var originalRenderMethod = loadImage.renderImageToCanvas;
-
- // Detects subsampling in JPEG images:
- loadImage.detectSubsampling = function (img) {
- var canvas,
- context;
- if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
- canvas = document.createElement('canvas');
- canvas.width = canvas.height = 1;
- context = canvas.getContext('2d');
- context.drawImage(img, -img.width + 1, 0);
- // subsampled image becomes half smaller in rendering size.
- // check alpha channel value to confirm image is covering edge pixel or not.
- // if alpha value is 0 image is not covering, hence subsampled.
- return context.getImageData(0, 0, 1, 1).data[3] === 0;
- }
- return false;
- };
-
- // Detects vertical squash in JPEG images:
- loadImage.detectVerticalSquash = function (img, subsampled) {
- var naturalHeight = img.naturalHeight || img.height,
- canvas = document.createElement('canvas'),
- context = canvas.getContext('2d'),
- data,
- sy,
- ey,
- py,
- alpha;
- if (subsampled) {
- naturalHeight /= 2;
- }
- canvas.width = 1;
- canvas.height = naturalHeight;
- context.drawImage(img, 0, 0);
- data = context.getImageData(0, 0, 1, naturalHeight).data;
- // search image edge pixel position in case it is squashed vertically:
- sy = 0;
- ey = naturalHeight;
- py = naturalHeight;
- while (py > sy) {
- alpha = data[(py - 1) * 4 + 3];
- if (alpha === 0) {
- ey = py;
- } else {
- sy = py;
- }
- py = (ey + sy) >> 1;
- }
- return (py / naturalHeight) || 1;
- };
-
- // Renders image to canvas while working around iOS image scaling bugs:
- // https://github.com/blueimp/JavaScript-Load-Image/issues/13
- loadImage.renderImageToCanvas = function (
- canvas,
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- destX,
- destY,
- destWidth,
- destHeight
- ) {
- if (img._type === 'image/jpeg') {
- var context = canvas.getContext('2d'),
- tmpCanvas = document.createElement('canvas'),
- tileSize = 1024,
- tmpContext = tmpCanvas.getContext('2d'),
- subsampled,
- vertSquashRatio,
- tileX,
- tileY;
- tmpCanvas.width = tileSize;
- tmpCanvas.height = tileSize;
- context.save();
- subsampled = loadImage.detectSubsampling(img);
- if (subsampled) {
- sourceX /= 2;
- sourceY /= 2;
- sourceWidth /= 2;
- sourceHeight /= 2;
- }
- vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
- if (subsampled || vertSquashRatio !== 1) {
- sourceY *= vertSquashRatio;
- destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
- destHeight = Math.ceil(
- tileSize * destHeight / sourceHeight / vertSquashRatio
- );
- destY = 0;
- tileY = 0;
- while (tileY < sourceHeight) {
- destX = 0;
- tileX = 0;
- while (tileX < sourceWidth) {
- tmpContext.clearRect(0, 0, tileSize, tileSize);
- tmpContext.drawImage(
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- -tileX,
- -tileY,
- sourceWidth,
- sourceHeight
- );
- context.drawImage(
- tmpCanvas,
- 0,
- 0,
- tileSize,
- tileSize,
- destX,
- destY,
- destWidth,
- destHeight
- );
- tileX += tileSize;
- destX += destWidth;
- }
- tileY += tileSize;
- destY += destHeight;
- }
- context.restore();
- return canvas;
+/*global window, FileAPI */
+(function (window, jQuery, api) {
+ "use strict";
+
+ var _each = api.each,
+ _cameraQueue = [];
+
+ if (api.support.flash && (api.media && (!api.support.media || !api.html5 || api.insecureChrome))) {
+ (function () {
+ function _wrap(fn) {
+ var id = fn.wid = api.uid();
+ api.Flash._fn[id] = fn;
+ return 'FileAPI.Flash._fn.' + id;
}
- }
- return originalRenderMethod(
- canvas,
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- destX,
- destY,
- destWidth,
- destHeight
- );
- };
-
-}));
-
-/*
- * JavaScript Canvas to Blob 2.0.5
- * https://github.com/blueimp/JavaScript-Canvas-to-Blob
- *
- * Copyright 2012, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- *
- * Based on stackoverflow user Stoive's code snippet:
- * http://stackoverflow.com/q/4998908
- */
-/*jslint nomen: true, regexp: true */
-/*global window, atob, Blob, ArrayBuffer, Uint8Array */
-(function (window) {
- 'use strict';
- var CanvasPrototype = window.HTMLCanvasElement &&
- window.HTMLCanvasElement.prototype,
- hasBlobConstructor = window.Blob && (function () {
- try {
- return Boolean(new Blob());
- } catch (e) {
- return false;
- }
- }()),
- hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
- (function () {
+ function _unwrap(fn) {
try {
- return new Blob([new Uint8Array(100)]).size === 100;
+ api.Flash._fn[fn.wid] = null;
+ delete api.Flash._fn[fn.wid];
} catch (e) {
- return false;
- }
- }()),
- BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
- window.MozBlobBuilder || window.MSBlobBuilder,
- dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
- window.ArrayBuffer && window.Uint8Array && function (dataURI) {
- var byteString,
- arrayBuffer,
- intArray,
- i,
- mimeString,
- bb;
- if (dataURI.split(',')[0].indexOf('base64') >= 0) {
- // Convert base64 to raw binary data held in a string:
- byteString = atob(dataURI.split(',')[1]);
- } else {
- // Convert base64/URLEncoded data component to raw binary data:
- byteString = decodeURIComponent(dataURI.split(',')[1]);
- }
- // Write the bytes of the string to an ArrayBuffer:
- arrayBuffer = new ArrayBuffer(byteString.length);
- intArray = new Uint8Array(arrayBuffer);
- for (i = 0; i < byteString.length; i += 1) {
- intArray[i] = byteString.charCodeAt(i);
}
- // Separate out the mime component:
- mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
- // Write the ArrayBuffer (or ArrayBufferView) to a blob:
- if (hasBlobConstructor) {
- return new Blob(
- [hasArrayBufferViewSupport ? intArray : arrayBuffer],
- {type: mimeString}
- );
- }
- bb = new BlobBuilder();
- bb.append(arrayBuffer);
- return bb.getBlob(mimeString);
- };
- if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
- if (CanvasPrototype.mozGetAsFile) {
- CanvasPrototype.toBlob = function (callback, type, quality) {
- if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
- callback(dataURLtoBlob(this.toDataURL(type, quality)));
- } else {
- callback(this.mozGetAsFile('blob', type));
+ }
+
+ var flash = api.Flash;
+ api.extend(api.Flash, {
+
+ patchCamera: function () {
+ api.Camera.fallback = function (el, options, callback) {
+ var camId = api.uid();
+ api.log('FlashAPI.Camera.publish: ' + camId);
+ flash.publish(el, camId, api.extend(options, {
+ camera: true,
+ onEvent: _wrap(function _(evt) {
+ if (evt.type === 'camera') {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.Camera.publish.error: ' + evt.error);
+ callback(evt.error);
+ } else {
+ api.log('FlashAPI.Camera.publish.success: ' + camId);
+ callback(null);
+ }
+ }
+ })
+ }));
+ };
+ // Run
+ _each(_cameraQueue, function (args) {
+ api.Camera.fallback.apply(api.Camera, args);
+ });
+ _cameraQueue = [];
+
+
+ // FileAPI.Camera:proto
+ api.extend(api.Camera.prototype, {
+ _id: function () {
+ return this.video.id;
+ },
+
+ start: function (callback) {
+ var _this = this;
+ flash.cmd(this._id(), 'camera.on', {
+ callback: _wrap(function _(evt) {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.camera.on.error: ' + evt.error);
+ callback(evt.error, _this);
+ } else {
+ api.log('FlashAPI.camera.on.success: ' + _this._id());
+ _this._active = true;
+ callback(null, _this);
+ }
+ })
+ });
+ },
+
+ stop: function () {
+ this._active = false;
+ flash.cmd(this._id(), 'camera.off');
+ },
+
+ shot: function () {
+ api.log('FlashAPI.Camera.shot:', this._id());
+
+ var shot = api.Flash.cmd(this._id(), 'shot', {});
+ shot.type = 'image/png';
+ shot.flashId = this._id();
+ shot.isShot = true;
+
+ return new api.Camera.Shot(shot);
+ }
+ });
}
+ });
+
+ api.Camera.fallback = function () {
+ _cameraQueue.push(arguments);
};
- } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
- CanvasPrototype.toBlob = function (callback, type, quality) {
- callback(dataURLtoBlob(this.toDataURL(type, quality)));
- };
- }
+
+ }());
}
- window.dataURLtoBlob = dataURLtoBlob;
-})(window);
+}(window, window.jQuery, FileAPI));
if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }
\ No newline at end of file
diff --git a/dist/FileAPI.html5.min.js b/dist/FileAPI.html5.min.js
index fde45260..61cc07d3 100644
--- a/dist/FileAPI.html5.min.js
+++ b/dist/FileAPI.html5.min.js
@@ -1,2 +1,2 @@
-/*! fileapi 2.0.1 - BSD | git://github.com/mailru/FileAPI.git */
-!function(a,b){"use strict";function c(a,b,c,d,e){var f={type:c.type||c,target:a,result:d};R(f,e),b(f)}function d(a){return u&&!!u.prototype["readAs"+a]}function e(a,e,f,g){if(X.isFile(a)&&d(f)){var h=new u;S(h,L,function j(b){var d=b.type;"progress"==d?c(a,e,b,b.target.result,{loaded:b.loaded,total:b.total}):"loadend"==d?(T(h,L,j),h=null):c(a,e,b,b.target.result)});try{g?h["readAs"+f](a,g):h["readAs"+f](a)}catch(i){c(a,e,"error",b,{error:i.toString()})}}else c(a,e,"error",b,{error:"filreader_not_support_"+f})}function f(a,b){if(!a.type&&0===a.size%4096&&a.size<=102400)if(u)try{var c=new u;U(c,L,function(a){var d="error"!=a.type;b(d),d&&c.abort()}),c.readAsDataURL(a)}catch(d){b(!1)}else b(null);else b(!0)}function g(a){var b;return a.getAsEntry?b=a.getAsEntry():a.webkitGetAsEntry&&(b=a.webkitGetAsEntry()),b}function h(a,b){if(a)if(a.isFile)a.file(function(c){c.fullPath=a.fullPath,b(!1,[c])},function(a){b("FileError.code: "+a.code)});else if(a.isDirectory){var c=a.createReader(),d=[];c.readEntries(function(a){X.afor(a,function(a,c){h(c,function(c,e){c?X.log(c):d=d.concat(e),a?a():b(!1,d)})})},function(a){b("directory_reader: "+a)})}else h(g(a),b);else b("invalid entry")}function i(a){var b={};return Q(a,function(a,c){a&&"object"==typeof a&&void 0===a.nodeType&&(a=R({},a)),b[c]=a}),b}function j(a){return F.test(a&&a.tagName)}function k(a){return(a.originalEvent||a||"").dataTransfer||{}}function l(a){var b;for(b in a)if(a.hasOwnProperty(b)&&!(a[b]instanceof Object||"overlay"===b||"filter"===b))return!0;return!1}var m=1,n=function(){},o=a.document,p=o.doctype||{},q=a.navigator.userAgent,r=a.createObjectURL&&a||a.URL&&URL.revokeObjectURL&&URL||a.webkitURL&&webkitURL,s=a.Blob,t=a.File,u=a.FileReader,v=a.FormData,w=a.XMLHttpRequest,x=a.jQuery,y=!(!(t&&u&&(a.Uint8Array||v||w.prototype.sendAsBinary))||/safari\//i.test(q)&&!/chrome\//i.test(q)&&/windows/i.test(q)),z=y&&"withCredentials"in new w,A=y&&!!s&&!!(s.prototype.webkitSlice||s.prototype.mozSlice||s.prototype.slice),B=a.dataURLtoBlob,C=/img/i,D=/canvas/i,E=/img|canvas/i,F=/input/i,G=/^data:[^,]+,/,H=a.Math,I=function(b){return b=new a.Number(H.pow(1024,b)),b.from=function(a){return H.round(a*this)},b},J={},K=[],L="abort progress error load loadend",M="status statusText readyState response responseXML responseText responseBody".split(" "),N="currentTarget",O="preventDefault",P=function(a){return a&&"length"in a},Q=function(a,b,c){if(a)if(P(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)},R=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:Q,afor:function(a,b){var c=0,d=a.length;P(a)&&d--?!function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:R,isFile:function(a){return y&&a&&a instanceof t},isCanvas:function(a){return a&&D.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){X.isCanvas(a)?c(a,b,"load",X.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=X.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(X.toDataURL(b).replace(G,""))},readAsImage:function(a,d,e){if(X.isFile(a))if(r){var f=r.createObjectURL(a);f===b?c(a,d,"error"):X.readAsImage(f,d,e)}else X.readAsDataURL(a,function(b){"load"==b.type?X.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(X.isCanvas(a))c(a,d,"load",a);else if(C.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";U(a,g,function i(b){"load"==b.type&&r&&r.revokeObjectURL(a.src),T(a,g,i),c(a,d,b,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=X.newImage(a.dataURL||a);X.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=X.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),Q(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||X.ext2mime[b.type])&&(b.type=X.ext2mime[b.type]||c.split("/")[0]+"/"+b.type)}),b},getDropFiles:function(a,b){var c=[],d=k(a),e=P(d.items)&&d.items[0]&&g(d.items[0]),i=X.queue(function(){b(c)});Q((e?d.items:d.files)||[],function(a){i.inc();try{e?h(a,function(a,b){a?X.log("[err] getDropFiles:",a):c.push.apply(c,b),i.next()}):f(a,function(b){b&&c.push(a),i.next()})}catch(b){i.next(),X.log("[err] getDropFiles: ",b)}}),i.check()},getFiles:function(a,b,c){var d=[];return c?(X.filterFiles(X.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(X.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=X.getFilesFilter(b)),a.originalEvent?a=V(a.originalEvent):a.srcElement&&(a=V(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,y||(d[0].blob=a,d[0].iframe=!0)):!y&&j(a)?X.trim(a.value)&&(d=[X.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):P(a)&&(d=a),X.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=K.concat();X.isFile(a)?!function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(R(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},K.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(),X.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=R({prepare:X.F,beforeupload:X.F,upload:X.F,fileupload:X.F,fileprogress:X.F,filecomplete:X.F,progress:X.F,complete:X.F,pause:X.F,imageOriginal:!0,chunkSize:X.chunkSize,chunkUpoloadRetry:X.chunkUploadRetry},a),a.imageAutoOrientation&&!a.imageTransform&&(a.imageTransform={rotate:"auto"});var b,c=new X.XHR(a),d=this._getFilesDataArray(a.files),e=this,f=0,g=0,h=!1;return Q(d,function(a){f+=a.size}),c.files=[],Q(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 j=d.shift(),k=j&&j.file,l=!1,m=i(a);c.filesLeft=d.length,k&&k.name===X.expando&&(k=null,X.log("[warn] FileAPI.upload() — called without files")),("abort"!=c.statusText||c.current)&&j?(h=!1,c.currentFile=k,k&&a.prepare(k,m),e._getFormData(m,j,function(h){g||a.upload(c,a);var i=new X.XHR(R({},m,{upload:k?function(){a.fileupload(k,i,m)}:n,progress:k?function(b){l||(a.fileprogress({type:"progress",total:j.total=b.total,loaded:j.loaded=b.loaded},k,i,m),a.progress({type:"progress",total:f,loaded:c.loaded=0|g+j.size*(b.loaded/b.total)},k,i,m))}:n,complete:function(d){l=!0,Q(M,function(a){c[a]=i[a]}),k&&(j.loaded=j.total,this.progress(j),g+=j.size,c.loaded=g,a.filecomplete(d,i,k,m)),b.call(e)}}));c.abort=function(a){a||(d.length=0),this.current=a,i.abort()},i.send(h)})):(a.complete(200==c.status||201==c.status?!1:c.statusText||"error",c,a),h=!0)},setTimeout(b,0),c.append=function(a,g){a=X._getFilesDataArray([].concat(a)),Q(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(j(a)){var d=X.getFiles(a);c[a.name||"file"]=null!==a.getAttribute("multiple")?d:d[0]}else P(a)&&j(a[0])?Q(a,function(a){c[a.name||"file"]=X.getFiles(a)}):c=a;return Q(c,function e(a,c){P(a)?Q(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:X.expando}}),b},_getFormData:function(a,b,c){var d=b.file,e=b.name,f=d.name,g=d.type,h=X.support.transform&&a.imageTransform,i=new X.Form,j=X.queue(function(){c(i)}),k=h&&l(h),m=X.postNameConcat;!function n(b){b.image?(j.inc(),b.toData(function(a,b){f=f||(new Date).getTime()+".png",n(b),j.next()})):X.Image&&h&&(/^image/.test(b.type)||E.test(b.nodeName))?(j.inc(),k&&(h=[h]),X.Image.transform(b,h,a.imageAutoOrientation,function(c,d){if(k&&!c)B||X.flashEngine||(i.multipart=!0),i.append(e,d[0],f,h[0].type||g);else{var l=0;c||Q(d,function(a,b){B||X.flashEngine||(i.multipart=!0),h[b].postName||(l=1),i.append(h[b].postName||m(e,b),a,f,h[b].type||g)}),(c||a.imageOriginal)&&i.append(m(e,l?"original":null),b,f,g)}j.next()})):f!==X.expando&&i.append(e,b,f)}(d),Q(a.data,function o(a,b){"object"==typeof a?Q(a,function(a,c){o(a,m(b,c))}):i.append(b,a)}),j.check()},reset:function(a,b){var c,d;return x?(d=x(a).clone(!0).insertBefore(a).val("")[0],b||x(a).remove()):(c=a.parentNode,d=c.insertBefore(a.cloneNode(!0),a),d.value="",b||c.removeChild(a),Q(J[X.uid(a)],function(b,c){Q(b,function(b){T(a,c,b),S(d,c,b)})})),d},load:function(a,b){var c=X.getXHR();return c?(c.open("GET",a,!0),c.overrideMimeType&&c.overrideMimeType("text/plain; charset=x-user-defined"),S(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,"+X.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}};X.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=X.defer();X.readAsImage(a,function(a){var b=a.target;c.resolve("load"==a.type?!1:"error",{width:b.width,height:b.height}),b=null})}a.__dimensions.then(b)}),X.event.dnd=function(a,b,c){var d,e;c||(c=b,b=X.F),u?(S(a,"dragenter dragleave dragover",function(a){for(var c=k(a).types,f=c&&c.length,g=!1;f--;)if(~c[f].indexOf("File")){a[O](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[N],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[N],"dragleave"!=e,a)},50))}),S(a,"drop",function(a){a[O](),e=0,b.call(a[N],!1,a),X.getDropFiles(a,function(b){c.call(a[N],b,a)})})):X.log("Drag'n'Drop -- not supported")},X.event.dnd.off=function(a,b,c){T(a,"dragenter dragleave dragover",b),T(a,"drop",c)},x&&!x.fn.dnd&&(x.fn.dnd=function(a,b){return this.each(function(){X.event.dnd(this,a,b)})},x.fn.offdnd=function(a,b){return this.each(function(){X.event.dnd.off(this,a,b)})}),a.FileAPI=R(X,a.FileAPI),X.log("FileAPI: "+X.version),X.log("protocol: "+a.location.protocol),X.log("doctype: ["+p.name+"] "+p.publicId+" "+p.systemId),Q(o.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&X.log("meta.http-equiv: "+a.getAttribute("content"))}),X.flashUrl||(X.flashUrl=X.staticPath+"FileAPI.flash.swf"),X.flashImageUrl||(X.flashImageUrl=X.staticPath+"FileAPI.flash.image.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.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},void 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};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"string"==typeof b&&(c=b,b=a),this.set({dw:a,dh:b,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(){c(!1,h)}),v=a.renderImageToCanvas;for(b._type=this.file.type;e(p/n,q/o)>2;)p=0|p/2+.5,q=0|q/2+.5,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(h,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 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},_trans:function(a){this._load(this.file,function(b,c){b?a(b):this._apply(c,a)})},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")},toData:function(a){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);if("function"==typeof a?a(i,g):a.width?g[a.preview?"preview":"resize"](a.width,a.height,a.type):a.maxWidth&&(i.width>a.maxWidth||i.height>a.maxHeight)&&g.resize(a.maxWidth,a.maxHeight,"max"),a.crop){var h=a.crop;g.crop(0|h.x,0|h.y,h.w||h.width,h.h||h.height)}a.rotate===c&&f&&(a.rotate="auto"),g.set({deg:a.rotate,type:a.type||b.type||"image/png",quality:a.quality||1,overlay:a.overlay,filter:a.filter}),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(a,b,c,d,e,f,g,h,i,j){return a.getContext("2d").drawImage(b,c,d,e,f,g,h,i,j),a},a.support.canvas=a.support.transform=h,a.Image=d}(FileAPI,document),function(a,b){"use strict";function c(b,c,d){var e=b.blob,f=b.file;if(f){if(!e.toDataURL)return a.readAsBinaryString(e,function(a){"load"==a.type&&c(b,a.result)}),void 0;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(a){d(a,b,f,e)}),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,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){c.upload(c,b),b._send.call(b,c,a)},c)},_send:function(c,e){var g,h=this,i=h.uid,j=c.url;if(b.log("XHR._send:",e),c.cache||(j+=(~j.indexOf("?")?"&":"?")+b.uid()),e.nodeName){c.upload(c,h),g=d.createElement("div"),g.innerHTML='',h.xhr.abort=function(){var a=g.getElementsByTagName("iframe")[0];if(a)try{a.stop?a.stop():a.contentWindow.stop?a.contentWindow.stop():a.contentWindow.document.execCommand("Stop")}catch(b){}g=null};var k=g.getElementsByTagName("form")[0];k.appendChild(e),b.log(k.parentNode.innerHTML),d.body.appendChild(g),h.xhr.node=g,a[i]=function(a,b,c){h.readyState=4,h.responseText=c,h.end(a,b),g=null},h.readyState=2,k.submit(),k=null}else{if(this.xhr&&this.xhr.aborted)return b.log("Error: already aborted"),void 0;if(g=h.xhr=b.getXHR(),e.params&&(j+=(j.indexOf("?")<0?"?":"&")+e.params.join("&")),g.open("POST",j,!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",function(a){e.retry||c.progress({type:a.type,total:e.size,loaded:e.start+a.loaded,totalSize:e.size},h,c)},!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),e.end=a?a:e.start-1,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 l;(l="slice")in e.file||(l="mozSlice")in e.file||(l="webkitSlice")in e.file,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"),l=e.file[l](e.start,e.end+1),g.send(l),l=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];g.onreadystatechange=null,h.end(g.status),g=null}},b.isArray(e))if(g.setRequestHeader("Content-Type","multipart/form-data; boundary=_"+b.expando),e=e.join("")+"--_"+b.expando+"--",g.sendAsBinary)g.sendAsBinary(e);else{var m=Array.prototype.map.call(e,function(a){return 255&a.charCodeAt(0)});g.send(new Uint8Array(m).buffer)}else g.send(e)}}},b.XHR=e}(window,FileAPI),function(a,b){"use strict";function c(a){return a>=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]);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=4*c.width*c.height,d};j.Shot=k,b.Camera=j}(window,FileAPI),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){"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;h=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;h0,I=a.dataURLtoBlob,J=/img/i,K=/canvas/i,L=/img|canvas/i,M=/input/i,N=/^data:[^,]+,/,O={}.toString,P=a.Math,Q=function(b){return b=new a.Number(P.pow(1024,b)),b.from=function(a){return P.round(a*this)},b},R={},S=[],T="abort progress error load loadend",U="status statusText readyState response responseXML responseText responseBody".split(" "),V="currentTarget",W="preventDefault",X=function(a){return a&&"length"in a},Y=function(a,b,c){if(a)if(X(a))for(var d=0,e=a.length;d=c&&!d&&f.end()},isFail:function(){return d},fail:function(){!d&&a(d=!0)},end:function(){e||(e=!0,a())}};return f},each:Y,afor:function(a,b){var c=0,d=a.length;X(a)&&d--?function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:Z,isFile:function(a){return"[object File]"===O.call(a)},isBlob:function(a){return this.isFile(a)||"[object Blob]"===O.call(a)},isCanvas:function(a){return a&&K.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){da.isCanvas(a)?c(a,b,"load",da.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=da.toBinaryString(a.result)}catch(b){a.type="error",a.message=b.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(da.toDataURL(b).replace(N,""))},readAsImage:function(a,d,e){if(da.isBlob(a))if(x){var f=x.createObjectURL(a);f===b?c(a,d,"error"):da.readAsImage(f,d,e)}else da.readAsDataURL(a,function(b){"load"==b.type?da.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(da.isCanvas(a))c(a,d,"load",a);else if(J.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";aa(a,g,function b(e){"load"==e.type&&x&&x.revokeObjectURL(a.src),_(a,g,b),c(a,d,e,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=da.newImage(a.dataURL||a);da.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=da.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),Y(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||da.ext2mime[b.type])&&(b.type=da.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=X(m)&&m[0]&&h(m[0]),o=da.queue(function(){b(d,e)});if(n)if(H&&k){var p,q,r=k.length;for(c=new Array(r);r--;){p=k[r];try{q=h(m[r])}catch(a){da.log("[err] getDropFiles: ",a),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;Y(c||[],function(a){o.inc();try{n&&g(a)?i(a,function(a,b,c){a?da.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(a){o.next(),da.log("[err] getDropFiles: ",a)}}),o.check()},getFiles:function(a,b,c){var d=[];return c?(da.filterFiles(da.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(da.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=da.getFilesFilter(b)),a.originalEvent?a=ba(a.originalEvent):a.srcElement&&(a=ba(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,E||(d[0].blob=a,d[0].iframe=!0)):!E&&k(a)?da.trim(a.value)&&(d=[da.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):X(a)&&(d=a),da.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=S.concat();da.isBlob(a)?function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(Z(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},S.push(b)},filter:function(a,b){for(var c,d=[],e=0,f=a.length;e>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}};da.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=da.defer();da.readAsImage(a,function(a){var b=a.target;c.resolve("load"!=a.type&&"error",{width:b.width,height:b.height}),b.src=da.EMPTY_PNG,b=null})}a.__dimensions.then(b)}),da.event.dnd=function(a,b,c){var d,e;c||(c=b,b=da.F),A?($(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[W](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[V],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[V],"dragleave"!=e,a)},50))}),$(a,"drop",c.ff=c.ff||function(a){a[W](),e=0,da.getDropFiles(a,function(b,d){c.call(a[V],b,d,a)}),b.call(a[V],!1,a)})):da.log("Drag'n'Drop -- not supported")},da.event.dnd.off=function(a,b,c){_(a,"dragenter dragleave dragover",b.ff),_(a,"drop",c.ff)},D&&!D.fn.dnd&&(D.fn.dnd=function(a,b){return this.each(function(){da.event.dnd(this,a,b)})},D.fn.offdnd=function(a,b){return this.each(function(){da.event.dnd.off(this,a,b)})}),a.FileAPI=Z(da,a.FileAPI),da.log("FileAPI: "+da.version),da.log("protocol: "+a.location.protocol),da.log("doctype: ["+s.name+"] "+s.publicId+" "+s.systemId),Y(r.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&da.log("meta.http-equiv: "+a.getAttribute("content"))});try{n=!!console.log,o=!!console.log.apply}catch(a){}da.flashUrl||(da.flashUrl=da.staticPath+"FileAPI.flash.swf"),da.flashImageUrl||(da.flashImageUrl=da.staticPath+"FileAPI.flash.image.swf"),da.flashWebcamUrl||(da.flashWebcamUrl=da.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}if(!(this instanceof d))return new d(b);this.file=b,this.size=b.size||100,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}}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(a){}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(a){}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(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(b){throw a.log("renderImageToCanvas failed"),b}},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,1-a.width,0),0===c.getImageData(0,0,1,1).data[3])},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;p0&&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(b){a.log("FileAPI.Form._to: "+b.message),c(b)}}),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&&(f.statusText||"unknown"),f),f.xhr&&f.xhr.node&&setTimeout(function(){var b=f.xhr.node;try{b.parentNode.removeChild(b)}catch(a){}try{delete a[f.uid]}catch(a){}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(a){n(0,a.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(a){b.log("[transport.onload]",a)}},g=d.createElement("div"),g.innerHTML='";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(a){b.log("iframe.error: "+a)}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 q=e.file,r=(q.slice||q.mozSlice||q.webkitSlice).call(q,e.start,e.end+1);e.size&&!r.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(r)),q=r=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 c,d=f.createElement("canvas"),e=!1;try{c=d.getContext("2d"),c.drawImage(a,0,0,1,1),e=255!=c.getImageData(0,0,1,1).data[4]}catch(a){b.log("[FileAPI.Camera] detectVideoSignal:",a)}return e}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;try{i.src=e.createObjectURL(a)}catch(b){i.srcObject=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();try{this.stream.stop()}catch(a){b.each(this.stream.getTracks(),function(a){a.stop()})}this.stream=null}catch(a){b.log("[FileAPI.Camera] stop:",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]);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&&!b.insecureChrome){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")},j.checkAlreadyCaptured=function(){var b,c=g.mediaDevices,d=a.MediaStreamTrack,e=g.enumerateDevices;return b=c&&c.enumerateDevices?function(a){c.enumerateDevices().then(a)}:d&&d.getSources?d.getSources.bind(d):e?e.bind(g):function(a){a([])},function(a){b(function(b){var c=b.some(function(a){return("videoinput"===a.kind||"video"===a.kind)&&a.label});a(c)})}}();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=c.each,e=[];!c.support.flash||!c.media||c.support.media&&c.html5&&!c.insecureChrome||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(a){}}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 a(d){"camera"===d.type&&(b(a),d.error?(c.log("FlashAPI.Camera.publish.error: "+d.error),g(d.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 a(f){b(a),f.error?(c.log("FlashAPI.camera.on.error: "+f.error),d(f.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});
\ No newline at end of file
diff --git a/dist/FileAPI.js b/dist/FileAPI.js
index 325b3d25..932bb4eb 100644
--- a/dist/FileAPI.js
+++ b/dist/FileAPI.js
@@ -1,7 +1,97 @@
-/*! fileapi 2.0.1 - BSD | git://github.com/mailru/FileAPI.git
+/*! FileAPI 2.0.25 - BSD | git://github.com/mailru/FileAPI.git
* FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
*/
+/*
+ * JavaScript Canvas to Blob 2.0.5
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on stackoverflow user Stoive's code snippet:
+ * http://stackoverflow.com/q/4998908
+ */
+
+/*jslint nomen: true, regexp: true */
+/*global window, atob, Blob, ArrayBuffer, Uint8Array */
+
+(function (window) {
+ 'use strict';
+ var CanvasPrototype = window.HTMLCanvasElement &&
+ window.HTMLCanvasElement.prototype,
+ hasBlobConstructor = window.Blob && (function () {
+ try {
+ return Boolean(new Blob());
+ } catch (e) {
+ return false;
+ }
+ }()),
+ hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
+ (function () {
+ try {
+ return new Blob([new Uint8Array(100)]).size === 100;
+ } catch (e) {
+ return false;
+ }
+ }()),
+ BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
+ window.MozBlobBuilder || window.MSBlobBuilder,
+ dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
+ window.ArrayBuffer && window.Uint8Array && function (dataURI) {
+ var byteString,
+ arrayBuffer,
+ intArray,
+ i,
+ mimeString,
+ bb;
+ if (dataURI.split(',')[0].indexOf('base64') >= 0) {
+ // Convert base64 to raw binary data held in a string:
+ byteString = atob(dataURI.split(',')[1]);
+ } else {
+ // Convert base64/URLEncoded data component to raw binary data:
+ byteString = decodeURIComponent(dataURI.split(',')[1]);
+ }
+ // Write the bytes of the string to an ArrayBuffer:
+ arrayBuffer = new ArrayBuffer(byteString.length);
+ intArray = new Uint8Array(arrayBuffer);
+ for (i = 0; i < byteString.length; i += 1) {
+ intArray[i] = byteString.charCodeAt(i);
+ }
+ // Separate out the mime component:
+ mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
+ // Write the ArrayBuffer (or ArrayBufferView) to a blob:
+ if (hasBlobConstructor) {
+ return new Blob(
+ [hasArrayBufferViewSupport ? intArray : arrayBuffer],
+ {type: mimeString}
+ );
+ }
+ bb = new BlobBuilder();
+ bb.append(arrayBuffer);
+ return bb.getBlob(mimeString);
+ };
+ if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
+ if (CanvasPrototype.mozGetAsFile) {
+ CanvasPrototype.toBlob = function (callback, type, quality) {
+ if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
+ } else {
+ callback(this.mozGetAsFile('blob', type));
+ }
+ };
+ } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
+ CanvasPrototype.toBlob = function (callback, type, quality) {
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
+ };
+ }
+ }
+ window.dataURLtoBlob = dataURLtoBlob;
+})(window);
+
/*jslint evil: true */
/*global window, URL, webkitURL, ActiveXObject */
@@ -15,6 +105,9 @@
document = window.document,
doctype = document.doctype || {},
userAgent = window.navigator.userAgent,
+ safari = /safari\//i.test(userAgent) && !/chrome\//i.test(userAgent),
+ iemobile = /iemobile\//i.test(userAgent),
+ insecureChrome = !safari && /chrome\//i.test(userAgent) && window.location.protocol === 'http:',
// https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48
apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL),
@@ -29,12 +122,14 @@
jQuery = window.jQuery,
html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary)))
- && !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
+ && !(safari && /windows/i.test(userAgent) && !iemobile), // BugFix: https://github.com/mailru/FileAPI/issues/25
cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
-
+
chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
+ normalize = ('' + ''.normalize).indexOf('[native code]') > 0,
+
// https://github.com/blueimp/JavaScript-Canvas-to-Blob
dataURLtoBlob = window.dataURLtoBlob,
@@ -45,6 +140,10 @@
_rinput = /input/i,
_rdata = /^data:[^,]+,/,
+ _toString = {}.toString,
+ _supportConsoleLog,
+ _supportConsoleLogApply,
+
Math = window.Math,
@@ -111,11 +210,11 @@
_elEvents[uid] = {};
}
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.add(el, type, fn);
- }
- else {
+ } else {
if( !_elEvents[uid][type] ){
_elEvents[uid][type] = [];
}
@@ -138,8 +237,9 @@
if( el ){
var uid = api.uid(el), events = _elEvents[uid] || {};
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.remove(el, type, fn);
}
else {
@@ -187,12 +287,14 @@
* FileAPI (core object)
*/
api = {
- version: '2.0.1',
+ version: '2.0.25',
cors: false,
html5: true,
media: false,
formData: true,
+ multiPassResize: true,
+ insecureChrome: insecureChrome,
debug: false,
pingUrl: false,
@@ -222,6 +324,9 @@
, 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
},
+ uploadRetry : 0,
+ networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
+
chunkSize : 0,
chunkUploadRetry : 0,
chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
@@ -231,6 +336,8 @@
GB: _SIZE_CONST(3),
TB: _SIZE_CONST(4),
+ EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
+
expando: 'fileapi' + (new Date).getTime(),
uid: function (obj){
@@ -241,8 +348,8 @@
},
log: function (){
- if( api.debug && window.console && console.log ){
- if( console.log.apply ){
+ if( api.debug && _supportConsoleLog ){
+ if( _supportConsoleLogApply ){
console.log.apply(console, arguments);
}
else {
@@ -459,13 +566,22 @@
/**
- * Is file instance
- *
+ * Is file?
* @param {File} file
* @return {Boolean}
*/
isFile: function (file){
- return html5 && file && (file instanceof File);
+ return _toString.call(file) === '[object File]';
+ },
+
+
+ /**
+ * Is blob?
+ * @param {Blob} blob
+ * @returns {Boolean}
+ */
+ isBlob: function (blob) {
+ return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
},
@@ -594,7 +710,7 @@
* @param {Boolean} [progress]
*/
readAsImage: function (file, fn, progress){
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
if( apiURL ){
/** @namespace apiURL.createObjectURL */
var data = apiURL.createObjectURL(file);
@@ -687,28 +803,84 @@
getDropFiles: function (evt, callback){
var
files = []
+ , all = []
+ , items
, dataTransfer = _getDataTransfer(evt)
- , entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
- , queue = api.queue(function (){ callback(files); })
+ , transFiles = dataTransfer.files
+ , transItems = dataTransfer.items
+ , entrySupport = _isArray(transItems) && transItems[0] && _getAsEntry(transItems[0])
+ , queue = api.queue(function (){ callback(files, all); })
;
- _each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
+ if( entrySupport ){
+ if( normalize && transFiles ){
+ var
+ i = transFiles.length
+ , file
+ , entry
+ ;
+
+ items = new Array(i);
+ while( i-- ){
+ file = transFiles[i];
+
+ try {
+ entry = _getAsEntry(transItems[i]);
+ }
+ catch( err ){
+ api.log('[err] getDropFiles: ', err);
+ entry = null;
+ }
+
+ if( _isEntry(entry) ){
+ // OSX filesystems use Unicode Normalization Form D (NFD),
+ // and entry.file(…) can't read the files with the same names
+ if( entry.isDirectory || (entry.isFile && file.name == file.name.normalize('NFC')) ){
+ items[i] = entry;
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ }
+ else {
+ items = transItems;
+ }
+ }
+ else {
+ items = transFiles;
+ }
+
+ _each(items || [], function (item){
queue.inc();
try {
- if( entrySupport ){
- _readEntryAsFiles(item, function (err, entryFiles){
+ if( entrySupport && _isEntry(item) ){
+ _readEntryAsFiles(item, function (err, entryFiles, allEntries){
if( err ){
api.log('[err] getDropFiles:', err);
} else {
files.push.apply(files, entryFiles);
}
+ all.push.apply(all, allEntries);
+
queue.next();
});
}
else {
- _isRegularFile(item, function (yes){
- yes && files.push(item);
+ _isRegularFile(item, function (yes, err){
+ if( yes ){
+ files.push(item);
+ }
+ else {
+ item.error = err;
+ }
+ all.push(item);
+
queue.next();
});
}
@@ -819,7 +991,7 @@
getInfo: function (file, fn){
var info = {}, readers = _infoReader.concat();
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
(function _next(){
var reader = readers.shift();
if( reader ){
@@ -917,7 +1089,8 @@
upload: function (options){
options = _extend({
- prepare: api.F
+ jsonp: 'callback'
+ , prepare: api.F
, beforeupload: api.F
, upload: api.F
, fileupload: api.F
@@ -928,7 +1101,8 @@
, pause: api.F
, imageOriginal: true
, chunkSize: api.chunkSize
- , chunkUpoloadRetry: api.chunkUploadRetry
+ , chunkUploadRetry: api.chunkUploadRetry
+ , uploadRetry: api.uploadRetry
}, options);
@@ -984,14 +1158,18 @@
}
if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
- // Mark active job
- _complete = false;
+ // Mark active job
+ _complete = false;
// Set current upload file
proxyXHR.currentFile = _file;
// Prepare file options
- _file && options.prepare(_file, _fileOptions);
+ if (_file && options.prepare(_file, _fileOptions) === false) {
+ _nextFile.call(_this);
+ return;
+ }
+ _fileOptions.file = _file;
_this._getFormData(_fileOptions, data, function (form){
if( !_loaded ){
@@ -1008,6 +1186,9 @@
progress: _file ? function (evt){
if( !_fileLoaded ){
+ // For ignore the double calls.
+ _fileLoaded = (evt.loaded === evt.total);
+
// emit "fileprogress" event
options.fileprogress({
type: 'progress'
@@ -1019,35 +1200,38 @@
options.progress({
type: 'progress'
, total: _total
- , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
+ , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total)) || 0
}, _file, xhr, _fileOptions);
}
} : noop,
complete: function (err){
- // fixed throttle event
- _fileLoaded = true;
-
_each(_xhrPropsExport, function (name){
proxyXHR[name] = xhr[name];
});
if( _file ){
+ data.total = (data.total || data.size);
data.loaded = data.total;
- // emulate 100% "progress"
- this.progress(data);
+ if( !err ) {
+ // emulate 100% "progress"
+ this.progress(data);
+
+ // fixed throttle event
+ _fileLoaded = true;
- // bytes loaded
- _loaded += data.size; // data.size != data.total, it's desirable fix this
- proxyXHR.loaded = _loaded;
+ // bytes loaded
+ _loaded += data.size; // data.size != data.total, it's desirable fix this
+ proxyXHR.loaded = _loaded;
+ }
// emit "filecomplete" event
options.filecomplete(err, xhr, _file, _fileOptions);
}
// upload next file
- _nextFile.call(_this);
+ setTimeout(function () {_nextFile.call(_this);}, 0);
}
})); // xhr
@@ -1064,7 +1248,8 @@
});
}
else {
- options.complete(proxyXHR.status == 200 || proxyXHR.status == 201 ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
+ var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
+ options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
// Mark done state
_complete = true;
}
@@ -1169,12 +1354,30 @@
, postNameConcat = api.postNameConcat
;
+ // Append data
+ _each(options.data, function add(val, name){
+ if( typeof val == 'object' ){
+ _each(val, function (v, i){
+ add(v, postNameConcat(name, i));
+ });
+ }
+ else {
+ Form.append(name, val);
+ }
+ });
+
(function _addFile(file/**Object*/){
if( file.image ){ // This is a FileAPI.Image
queue.inc();
file.toData(function (err, image){
- // @todo: error
+ // @todo: требует рефакторинга и обработки ошибки
+ if (file.file) {
+ image.type = file.file.type;
+ image.quality = file.matrix.quality;
+ filename = file.file && file.file.name;
+ }
+
filename = filename || (new Date).getTime()+'.png';
_addFile(image);
@@ -1228,19 +1431,6 @@
}
})(file);
-
- // Append data
- _each(options.data, function add(val, name){
- if( typeof val == 'object' ){
- _each(val, function (v, i){
- add(v, postNameConcat(name, i));
- });
- }
- else {
- Form.append(name, val);
- }
- });
-
queue.check();
},
@@ -1358,7 +1548,7 @@
} // api
;
-
+
function _emit(target, fn, name, res, ext){
var evt = {
@@ -1371,13 +1561,13 @@
}
- function _hasSupportReadAs(as){
- return FileReader && !!FileReader.prototype['readAs'+as];
+ function _hasSupportReadAs(method){
+ return FileReader && !!FileReader.prototype['readAs' + method];
}
- function _readAs(file, fn, as, encoding){
- if( api.isFile(file) && _hasSupportReadAs(as) ){
+ function _readAs(file, fn, method, encoding){
+ if( api.isBlob(file) && _hasSupportReadAs(method) ){
var Reader = new FileReader;
// Add event listener
@@ -1399,10 +1589,10 @@
try {
// ReadAs ...
if( encoding ){
- Reader['readAs'+as](file, encoding);
+ Reader['readAs' + method](file, encoding);
}
else {
- Reader['readAs'+as](file);
+ Reader['readAs' + method](file);
}
}
catch (err){
@@ -1410,33 +1600,38 @@
}
}
else {
- _emit(file, fn, 'error', undef, { error: 'filreader_not_support_'+as });
+ _emit(file, fn, 'error', undef, { error: 'filreader_not_support_' + method });
}
}
function _isRegularFile(file, callback){
// http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects
- if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){
+ if( !file.type && (safari || ((file.size % 4096) === 0 && (file.size <= 102400))) ){
if( FileReader ){
try {
- var Reader = new FileReader();
+ var reader = new FileReader();
- _one(Reader, _readerEvents, function (evt){
+ _one(reader, _readerEvents, function (evt){
var isFile = evt.type != 'error';
- callback(isFile);
if( isFile ){
- Reader.abort();
+ if ( reader.readyState == null || reader.readyState === reader.LOADING ) {
+ reader.abort();
+ }
+ callback(isFile);
+ }
+ else {
+ callback(false, reader.error);
}
});
- Reader.readAsDataURL(file);
+ reader.readAsDataURL(file);
} catch( err ){
- callback(false);
+ callback(false, err);
}
}
else {
- callback(null);
+ callback(null, new Error('FileReader is not supported'));
}
}
else {
@@ -1445,6 +1640,11 @@
}
+ function _isEntry(item){
+ return item && (item.isFile || item.isDirectory);
+ }
+
+
function _getAsEntry(item){
var entry;
if( item.getAsEntry ){ entry = item.getAsEntry(); }
@@ -1456,45 +1656,68 @@
function _readEntryAsFiles(entry, callback){
if( !entry ){
// error
- callback('invalid entry');
+ var err = new Error('invalid entry');
+ entry = new Object(entry);
+ entry.error = err;
+ callback(err.message, [], [entry]);
}
else if( entry.isFile ){
// Read as file
- entry.file(function(file){
+ entry.file(function (file){
// success
file.fullPath = entry.fullPath;
- callback(false, [file]);
+ callback(false, [file], [file]);
}, function (err){
// error
- callback('FileError.code: '+err.code);
+ entry.error = err;
+ callback('FileError.code: ' + err.code, [], [entry]);
});
}
else if( entry.isDirectory ){
- var reader = entry.createReader(), result = [];
+ var
+ reader = entry.createReader()
+ , firstAttempt = true
+ , files = []
+ , all = [entry]
+ ;
+
+ var onerror = function (err){
+ // error
+ entry.error = err;
+ callback('DirectoryError.code: ' + err.code, files, all);
+ };
+ var ondone = function ondone(entries){
+ if( firstAttempt ){
+ firstAttempt = false;
+ if( !entries.length ){
+ entry.error = new Error('directory is empty');
+ }
+ }
- reader.readEntries(function(entries){
// success
- api.afor(entries, function (next, entry){
- _readEntryAsFiles(entry, function (err, files){
- if( err ){
- api.log(err);
- }
- else {
- result = result.concat(files);
- }
+ if( entries.length ){
+ api.afor(entries, function (next, entry){
+ _readEntryAsFiles(entry, function (err, entryFiles, allEntries){
+ if( !err ){
+ files = files.concat(entryFiles);
+ }
+ all = all.concat(allEntries);
- if( next ){
- next();
- }
- else {
- callback(false, result);
- }
+ if( next ){
+ next();
+ }
+ else {
+ reader.readEntries(ondone, onerror);
+ }
+ });
});
- });
- }, function (err){
- // error
- callback('directory_reader: ' + err);
- });
+ }
+ else {
+ callback(false, files, all);
+ }
+ };
+
+ reader.readEntries(ondone, onerror);
}
else {
_readEntryAsFiles(_getAsEntry(entry), callback);
@@ -1548,6 +1771,7 @@
width: img.width
, height: img.height
});
+ img.src = api.EMPTY_PNG;
img = null;
});
}
@@ -1572,7 +1796,8 @@
}
if( FileReader ){
- _on(el, 'dragenter dragleave dragover', function (evt){
+ // Hover
+ _on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
var
types = _getDataTransfer(evt).types
, i = types && types.length
@@ -1605,15 +1830,18 @@
}
});
- _on(el, 'drop', function (evt){
+
+ // Drop
+ _on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){
evt[preventDefault]();
_type = 0;
- onHover.call(evt[currentTarget], false, evt);
- api.getDropFiles(evt, function (files){
- onDrop.call(evt[currentTarget], files, evt);
+ api.getDropFiles(evt, function (files, all){
+ onDrop.call(evt[currentTarget], files, all, evt);
});
+
+ onHover.call(evt[currentTarget], false, evt);
});
}
else {
@@ -1629,8 +1857,8 @@
* @param {Function} onDrop
*/
api.event.dnd.off = function (el, onHover, onDrop){
- _off(el, 'dragenter dragleave dragover', onHover);
- _off(el, 'drop', onDrop);
+ _off(el, 'dragenter dragleave dragover', onHover.ff);
+ _off(el, 'drop', onDrop.ff);
};
@@ -1667,25 +1895,35 @@
});
- // @configuration
+ // Configuration
+ try {
+ _supportConsoleLog = !!console.log;
+ _supportConsoleLogApply = !!console.log.apply;
+ }
+ catch (err) {}
+
if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
+ if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
})(window, void 0);
/*global window, FileAPI, document */
-(function (api, document, undef){
+(function (api, document, undef) {
'use strict';
var
min = Math.min,
round = Math.round,
- getCanvas = function (){ return document.createElement('canvas'); },
+ getCanvas = function () { return document.createElement('canvas'); },
support = false,
exifOrientation = {
8: 270
, 3: 180
, 6: 90
+ , 7: 270
+ , 4: 180
+ , 5: 90
}
;
@@ -1706,6 +1944,8 @@
}
this.file = file;
+ this.size = file.size || 100;
+
this.matrix = {
sx: 0,
sy: 0,
@@ -1741,13 +1981,13 @@
return this.set({ sx: x, sy: y, sw: w, sh: h || w });
},
- resize: function (w, h, type){
- if( typeof h == 'string' ){
- type = h;
+ resize: function (w, h, strategy){
+ if( /min|max|height|width/.test(h) ){
+ strategy = h;
h = w;
}
- return this.set({ dw: w, dh: h, resize: type });
+ return this.set({ dw: w, dh: h || w, resize: strategy });
},
preview: function (w, h){
@@ -1799,14 +2039,17 @@
, copy // canvas copy
, buffer = image
, overlay = m.overlay
- , queue = api.queue(function (){ fn(false, canvas); })
+ , queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
, renderImageToCanvas = api.renderImageToCanvas
;
+ // Normalize angle
+ deg = deg - Math.floor(deg/360)*360;
+
// For `renderImageToCanvas`
image._type = this.file.type;
- while( min(w/dw, h/dh) > 2 ){
+ while(m.multipass && min(w/dw, h/dh) > 2 ){
w = (w/2 + 0.5)|0;
h = (h/2 + 0.5)|0;
@@ -1833,7 +2076,7 @@
canvas.quality = m.quality;
ctx.rotate(deg * Math.PI / 180);
- renderImageToCanvas(canvas, buffer
+ renderImageToCanvas(ctx.canvas, buffer
, m.sx, m.sy
, m.sw || buffer.width
, m.sh || buffer.height
@@ -1841,7 +2084,6 @@
, (deg == 90 || deg == 180 ? -dh : 0)
, dw, dh
);
-
dw = canvas.width;
dh = canvas.height;
@@ -1899,10 +2141,10 @@
, dw = m.dw = m.dw || sw
, dh = m.dh = m.dh || sh
, sf = sw/sh, df = dw/dh
- , type = m.resize
+ , strategy = m.resize
;
- if( type == 'preview' ){
+ if( strategy == 'preview' ){
if( dw != sw || dh != sh ){
// Make preview
var w, h;
@@ -1923,12 +2165,18 @@
}
}
}
- else if( type ){
+ else if( strategy == 'height' ){
+ dw = dh * sf;
+ }
+ else if( strategy == 'width' ){
+ dh = dw / sf;
+ }
+ else if( strategy ){
if( !(sw > dw || sh > dh) ){
dw = sw;
dh = sh;
}
- else if( type == 'min' ){
+ else if( strategy == 'min' ){
dw = round(sf < df ? min(sw, dw) : dh*sf);
dh = round(sf < df ? dw/sf : min(sh, dh));
}
@@ -1942,7 +2190,7 @@
m.sh = sh;
m.dw = dw;
m.dh = dh;
-
+ m.multipass = api.multiPassResize;
return m;
},
@@ -1952,7 +2200,12 @@
fn(err);
}
else {
- this._apply(image, fn);
+ try {
+ this._apply(image, fn);
+ } catch (err){
+ api.log('[err] FileAPI.Image.fn._apply:', err);
+ fn(err);
+ }
}
});
},
@@ -1976,11 +2229,13 @@
else {
fn('not_support_transform');
}
+
+ return this;
},
toData: function (fn){
- this.get(fn);
+ return this.get(fn);
}
};
@@ -2002,13 +2257,13 @@
if( !err ){
api.each(transform, function (params, name){
if( !queue.isFail() ){
- var ImgTrans = new Image(img.nodeType ? img : file);
+ var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
- if( typeof params == 'function' ){
+ if( isFn ){
params(img, ImgTrans);
}
else if( params.width ){
- ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.type);
+ ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
}
else {
if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
@@ -2025,13 +2280,16 @@
params.rotate = 'auto';
}
- ImgTrans.set({
- deg: params.rotate
- , type: params.type || file.type || 'image/png'
- , quality: params.quality || 1
- , overlay: params.overlay
- , filter: params.filter
- });
+ ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
+
+ if( !isFn ){
+ ImgTrans.set({
+ deg: params.rotate
+ , overlay: params.overlay
+ , filter: params.filter
+ , quality: params.quality || 1
+ });
+ }
queue.inc();
ImgTrans.toData(function (err, image){
@@ -2130,8 +2388,12 @@
* For load-image-ios.js
*/
api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
- canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
- return canvas;
+ try {
+ return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
+ } catch (ex) {
+ api.log('renderImageToCanvas failed');
+ throw ex;
+ }
};
@@ -2140,20 +2402,196 @@
api.Image = Image;
})(FileAPI, document);
-/*global window, FileAPI */
+/*
+ * JavaScript Load Image iOS scaling fixes 1.0.3
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * iOS image scaling fixes based on
+ * https://github.com/stomita/ios-imagefile-megapixel
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
-(function (api, window){
- "use strict";
+/*jslint nomen: true, bitwise: true */
+/*global FileAPI, window, document */
- var
- document = window.document
- , FormData = window.FormData
- , Form = function (){ this.items = []; }
- , encodeURIComponent = window.encodeURIComponent
- ;
+(function (factory) {
+ 'use strict';
+ factory(FileAPI);
+}(function (loadImage) {
+ 'use strict';
+ // Only apply fixes on the iOS platform:
+ if (!window.navigator || !window.navigator.platform ||
+ !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
+ return;
+ }
- Form.prototype = {
+ var originalRenderMethod = loadImage.renderImageToCanvas;
+
+ // Detects subsampling in JPEG images:
+ loadImage.detectSubsampling = function (img) {
+ var canvas,
+ context;
+ if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
+ canvas = document.createElement('canvas');
+ canvas.width = canvas.height = 1;
+ context = canvas.getContext('2d');
+ context.drawImage(img, -img.width + 1, 0);
+ // subsampled image becomes half smaller in rendering size.
+ // check alpha channel value to confirm image is covering edge pixel or not.
+ // if alpha value is 0 image is not covering, hence subsampled.
+ return context.getImageData(0, 0, 1, 1).data[3] === 0;
+ }
+ return false;
+ };
+
+ // Detects vertical squash in JPEG images:
+ loadImage.detectVerticalSquash = function (img, subsampled) {
+ var naturalHeight = img.naturalHeight || img.height,
+ canvas = document.createElement('canvas'),
+ context = canvas.getContext('2d'),
+ data,
+ sy,
+ ey,
+ py,
+ alpha;
+ if (subsampled) {
+ naturalHeight /= 2;
+ }
+ canvas.width = 1;
+ canvas.height = naturalHeight;
+ context.drawImage(img, 0, 0);
+ data = context.getImageData(0, 0, 1, naturalHeight).data;
+ // search image edge pixel position in case it is squashed vertically:
+ sy = 0;
+ ey = naturalHeight;
+ py = naturalHeight;
+ while (py > sy) {
+ alpha = data[(py - 1) * 4 + 3];
+ if (alpha === 0) {
+ ey = py;
+ } else {
+ sy = py;
+ }
+ py = (ey + sy) >> 1;
+ }
+ return (py / naturalHeight) || 1;
+ };
+
+ // Renders image to canvas while working around iOS image scaling bugs:
+ // https://github.com/blueimp/JavaScript-Load-Image/issues/13
+ loadImage.renderImageToCanvas = function (
+ canvas,
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ ) {
+ if (img._type === 'image/jpeg') {
+ var context = canvas.getContext('2d'),
+ tmpCanvas = document.createElement('canvas'),
+ tileSize = 1024,
+ tmpContext = tmpCanvas.getContext('2d'),
+ subsampled,
+ vertSquashRatio,
+ tileX,
+ tileY;
+ tmpCanvas.width = tileSize;
+ tmpCanvas.height = tileSize;
+ context.save();
+ subsampled = loadImage.detectSubsampling(img);
+ if (subsampled) {
+ sourceX /= 2;
+ sourceY /= 2;
+ sourceWidth /= 2;
+ sourceHeight /= 2;
+ }
+ vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
+ if (subsampled || vertSquashRatio !== 1) {
+ sourceY *= vertSquashRatio;
+ destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
+ destHeight = Math.ceil(
+ tileSize * destHeight / sourceHeight / vertSquashRatio
+ );
+ destY = 0;
+ tileY = 0;
+ while (tileY < sourceHeight) {
+ destX = 0;
+ tileX = 0;
+ while (tileX < sourceWidth) {
+ tmpContext.clearRect(0, 0, tileSize, tileSize);
+ tmpContext.drawImage(
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ -tileX,
+ -tileY,
+ sourceWidth,
+ sourceHeight
+ );
+ context.drawImage(
+ tmpCanvas,
+ 0,
+ 0,
+ tileSize,
+ tileSize,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ );
+ tileX += tileSize;
+ destX += destWidth;
+ }
+ tileY += tileSize;
+ destY += destHeight;
+ }
+ context.restore();
+ return canvas;
+ }
+ }
+ return originalRenderMethod(
+ canvas,
+ img,
+ sourceX,
+ sourceY,
+ sourceWidth,
+ sourceHeight,
+ destX,
+ destY,
+ destWidth,
+ destHeight
+ );
+ };
+
+}));
+
+/*global window, FileAPI */
+
+(function (api, window){
+ "use strict";
+
+ var
+ document = window.document
+ , FormData = window.FormData
+ , Form = function (){ this.items = []; }
+ , encodeURIComponent = window.encodeURIComponent
+ ;
+
+
+ Form.prototype = {
append: function (name, blob, file, type){
this.items.push({
@@ -2200,7 +2638,13 @@
});
this.each(function (file){
- next(file, data, queue, arg);
+ try{
+ next(file, data, queue, arg);
+ }
+ catch( err ){
+ api.log('FileAPI.Form._to: ' + err.message);
+ complete(err);
+ }
});
queue.check();
@@ -2215,6 +2659,7 @@
api.reset(blob, true);
// set new name
blob.name = file.name;
+ blob.disabled = false;
data.appendChild(blob);
}
else {
@@ -2418,7 +2863,7 @@
this.end(0, 'abort');
if( this.xhr ){
- this.xhr.aborted = true;
+ this.xhr.aborted = true;
this.xhr.abort();
}
},
@@ -2427,14 +2872,19 @@
var _this = this, options = this.options;
FormData.toData(function (data){
- // Start uploading
- options.upload(options, _this);
- _this._send.call(_this, options, data);
+ if( data instanceof Error ){
+ _this.end(0, data.message);
+ }
+ else{
+ // Start uploading
+ options.upload(options, _this);
+ _this._send.call(_this, options, data);
+ }
}, options);
},
_send: function (options, data){
- var _this = this, xhr, uid = _this.uid, url = options.url;
+ var _this = this, xhr, uid = _this.uid, onLoadFnName = _this.uid + "Load", url = options.url;
api.log('XHR._send:', data);
@@ -2444,31 +2894,77 @@
}
if( data.nodeName ){
+ var jsonp = options.jsonp;
+
+ // prepare callback in GET
+ url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
+
// legacy
options.upload(options, _this);
- xhr = document.createElement('div');
- xhr.innerHTML = ''
+ var
+ onPostMessage = function (evt){
+ if( ~url.indexOf(evt.origin) ){
+ try {
+ var result = api.parseJSON(evt.data);
+ if( result.id == uid ){
+ complete(result.status, result.statusText, result.response);
+ }
+ } catch( err ){
+ complete(0, err.message);
+ }
+ }
+ },
+
+ // jsonp-callack
+ complete = window[uid] = function (status, statusText, response){
+ _this.readyState = 4;
+ _this.responseText = response;
+ _this.end(status, statusText);
+
+ api.event.off(window, 'message', onPostMessage);
+ window[uid] = xhr = transport = window[onLoadFnName] = null;
+ }
;
_this.xhr.abort = function (){
- var transport = xhr.getElementsByTagName('iframe')[0];
- if( transport ){
- try {
- if( transport.stop ){ transport.stop(); }
- else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
- else { transport.contentWindow.document.execCommand('Stop'); }
- }
- catch (er) {}
+ try {
+ if( transport.stop ){ transport.stop(); }
+ else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
+ else { transport.contentWindow.document.execCommand('Stop'); }
+ }
+ catch (er) {}
+ complete(0, "abort");
+ };
+
+ api.event.on(window, 'message', onPostMessage);
+
+ window[onLoadFnName] = function (){
+ try {
+ var
+ win = transport.contentWindow
+ , doc = win.document
+ , result = win.result || api.parseJSON(doc.body.innerHTML)
+ ;
+ complete(result.status, result.statusText, result.response);
+ } catch (e){
+ api.log('[transport.onload]', e);
}
- xhr = null;
};
- // append form-data
- var form = xhr.getElementsByTagName('form')[0];
+ xhr = document.createElement('div');
+ xhr.innerHTML = ''
+ ;
+
+ // get form-data & transport
+ var
+ form = xhr.getElementsByTagName('form')[0]
+ , transport = xhr.getElementsByTagName('iframe')[0]
+ ;
+
form.appendChild(data);
api.log(form.parentNode.innerHTML);
@@ -2479,20 +2975,19 @@
// keep a reference to node-transport
_this.xhr.node = xhr;
- // jsonp-callack
- window[uid] = function (status, statusText, response){
- _this.readyState = 4;
- _this.responseText = response;
- _this.end(status, statusText);
- xhr = null;
- };
-
// send
_this.readyState = 2; // loaded
- form.submit();
+ try {
+ form.submit();
+ } catch (err) {
+ api.log('iframe.error: ' + err);
+ }
form = null;
}
else {
+ // Clean url
+ url = url.replace(/([a-z]+)=(\?)&?/i, '');
+
// html5
if (this.xhr && this.xhr.aborted) {
api.log("Error: already aborted");
@@ -2501,7 +2996,7 @@
xhr = _this.xhr = api.getXHR();
if (data.params) {
- url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
+ url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
}
xhr.open('POST', url, true);
@@ -2518,13 +3013,13 @@
xhr.setRequestHeader(key, val);
});
-
+
if ( options._chunked ) {
// chunked upload
if( xhr.upload ){
- xhr.upload.addEventListener('progress', function (/**Event*/evt){
+ xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
if (!data.retry) {
- // show progress only for correct chunk uploads
+ // show progress only for correct chunk uploads
options.progress({
type: evt.type
, total: data.size
@@ -2532,7 +3027,7 @@
, totalSize: data.size
}, _this, options);
}
- }, false);
+ }, 100), false);
}
xhr.onreadystatechange = function (){
@@ -2547,9 +3042,9 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
-
+
if (!xhr.status || xhr.status - 201 > 0) {
- api.log("Error: " + xhr.status);
+ api.log("Error: " + xhr.status);
// some kind of error
// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
// up - server error
@@ -2558,7 +3053,7 @@
// only applicable for recoverable error codes 500 && 416
var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
- // inform about recoverable problems
+ // inform about recoverable problems
options.pause(data.file, options);
// smart restart if server reports about the last known byte
@@ -2567,10 +3062,13 @@
data.end = lkb;
} else {
data.end = data.start - 1;
+ if (416 == xhr.status) {
+ data.end = data.end - options.chunkSize;
+ }
}
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, delay);
} else {
// no mo retries
@@ -2594,28 +3092,37 @@
data.file.FileAPIReadPosition = data.end;
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, 0);
}
}
+
xhr = null;
}
};
data.start = data.end + 1;
- data.end = Math.max(Math.min(data.start + options.chunkSize, data.size ) - 1, data.start);
-
- var slice;
- (slice = 'slice') in data.file || (slice = 'mozSlice') in data.file || (slice = 'webkitSlice') in data.file;
-
- xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
- xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
- xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
-
- slice = data.file[slice](data.start, data.end + 1);
-
- xhr.send(slice);
- slice = null;
+ data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
+
+ // Retrieve a slice of file
+ var
+ file = data.file
+ , slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
+ ;
+
+ if( data.size && !slice.size ){
+ setTimeout(function (){
+ _this.end(-1);
+ });
+ } else {
+ xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
+ xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
+ xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
+
+ xhr.send(slice);
+ }
+
+ file = slice = null;
} else {
// single piece upload
if( xhr.upload ){
@@ -2624,7 +3131,7 @@
options.progress(evt, _this, options);
}, 100), false);
}
-
+
xhr.onreadystatechange = function (){
_this.status = xhr.status;
_this.statusText = xhr.statusText;
@@ -2635,7 +3142,28 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
- _this.end(xhr.status);
+
+ if (!xhr.status || xhr.status > 201) {
+ api.log("Error: " + xhr.status);
+ if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
+ options.retry = (options.retry || 0) + 1;
+ var delay = api.networkDownRetryTimeout;
+
+ // inform about recoverable problems
+ options.pause(options.file, options);
+
+ setTimeout(function () {
+ _this._send(options, data);
+ }, delay);
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+
xhr = null;
}
};
@@ -2643,19 +3171,19 @@
if( api.isArray(data) ){
// multipart
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
- data = data.join('') +'--_'+ api.expando +'--';
+ var rawData = data.join('') +'--_'+ api.expando +'--';
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
if( xhr.sendAsBinary ){
- xhr.sendAsBinary(data);
+ xhr.sendAsBinary(rawData);
}
else {
- var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
+ var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
xhr.send(new Uint8Array(bytes).buffer);
}
} else {
- // FormData
+ // FormData
xhr.send(data);
}
}
@@ -2734,7 +3262,11 @@
// });
// Set camera stream
- video.src = URL.createObjectURL(stream);
+ try {
+ video.src = URL.createObjectURL(stream);
+ } catch (err) {
+ video.srcObject = stream;
+ }
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
@@ -2761,8 +3293,19 @@
try {
this._active = false;
this.video.pause();
- this.stream.stop();
- } catch( err ){ }
+
+ try {
+ this.stream.stop();
+ } catch (err) {
+ api.each(this.stream.getTracks(), function (track) {
+ track.stop();
+ });
+ }
+
+ this.stream = null;
+ } catch( err ){
+ api.log('[FileAPI.Camera] stop:', err);
+ }
},
@@ -2837,7 +3380,7 @@
el.style.height = _px(options.height);
- if( api.html5 && html5 ){
+ if( api.html5 && html5 && !api.insecureChrome ){
// Create video element
var video = document.createElement('video');
@@ -2868,6 +3411,38 @@
callback('not_support_camera');
};
+ Camera.checkAlreadyCaptured = (function () {
+ var mediaDevices = navigator.mediaDevices,
+ MediaStreamTrack = window.MediaStreamTrack,
+ navigatorEnumerateDevices = navigator.enumerateDevices,
+ enumerateDevices;
+
+ if (mediaDevices && mediaDevices.enumerateDevices) {
+ enumerateDevices = function (callback) {
+ mediaDevices.enumerateDevices().then(callback);
+ };
+ } else if (MediaStreamTrack && MediaStreamTrack.getSources) {
+ enumerateDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
+ } else if (navigatorEnumerateDevices) {
+ enumerateDevices = navigatorEnumerateDevices.bind(navigator);
+ } else {
+ enumerateDevices = function (fn) {
+ fn([]);
+ };
+ }
+
+ return function (callback) {
+ enumerateDevices(function (devices) {
+ var deviceExists = devices.some(function (device) {
+ return (device.kind === 'videoinput' || device.kind === 'video') && device.label;
+ });
+
+ callback(deviceExists);
+ });
+ };
+
+ })();
+
/**
* @class FileAPI.Camera.Shot
@@ -2907,7 +3482,9 @@
ctx.drawImage(video, 0, 0, 1, 1);
res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
}
- catch( e ){}
+ catch( err ){
+ api.log('[FileAPI.Camera] detectVideoSignal:', err);
+ }
return res;
}
@@ -2924,16 +3501,14 @@
*/
/*global window, ActiveXObject, FileAPI */
-(function (window, jQuery, api){
+(function (window, jQuery, api) {
"use strict";
var
document = window.document
, location = window.location
, navigator = window.navigator
-
, _each = api.each
- , _cameraQueue = []
;
@@ -2965,6 +3540,7 @@
|| !api.html5 || !api.support.html5
|| (api.cors && !api.support.cors)
|| (api.media && !api.support.media)
+ || api.insecureChrome
)
&& (function (){
var
@@ -2998,7 +3574,7 @@
, width: 5
, height: 5
, position: 'absolute'
- , zIndex: 1e6+'' // set max zIndex
+ , zIndex: 2147483647+'' // set max zIndex
});
child.parentNode.insertBefore(dummy, child);
@@ -3035,8 +3611,8 @@
+ '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
+ (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
+ '&timeout='+api.flashAbortTimeout
- + (opts.camera ? '&useCamera=1' : '')
-// + '&debug=1'
+ + (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
+ + '&debug='+(api.debug?"1":"")
}, opts);
},
@@ -3047,7 +3623,7 @@
flash.ready = api.F;
flash.isReady = true;
flash.patch();
-
+ flash.patchCamera && flash.patchCamera();
api.event.on(document, 'mouseover', flash.mouseover);
api.event.on(document, 'click', function (evt){
if( flash.mouseover(evt) ){
@@ -3078,7 +3654,7 @@
mouseover: function (evt){
var target = api.event.fix(evt).target;
- if( /input/i.test(target.nodeName) && target.type == 'file' ){
+ if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
var
state = target.getAttribute(_attr)
, wrapper = flash.getWrapper(target)
@@ -3086,7 +3662,7 @@
if( api.multiFlash ){
// check state:
- // i — published
+ // p — published
// i — initialization
// r — ready
if( state == 'i' || state == 'r' ){
@@ -3107,9 +3683,9 @@
_css(dummy, {
top: 0
, left: 0
- , width: target.offsetWidth + 100
- , height: target.offsetHeight + 100
- , zIndex: 1e6+'' // set max zIndex
+ , width: target.offsetWidth
+ , height: target.offsetHeight
+ , zIndex: 2147483647+'' // set max zIndex
, position: 'absolute'
});
@@ -3248,8 +3824,8 @@
try {
api.log('(js -> flash).'+name+':', data);
return flash.get(id.flashId || id).cmd(name, data);
- } catch (e){
- api.log('(js -> flash).onError:', e);
+ } catch (err){
+ api.log('(js -> flash).onError:', err.toString());
if( !last ){
// try again
setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
@@ -3259,8 +3835,7 @@
patch: function (){
- api.flashEngine =
- api.support.transform = true;
+ api.flashEngine = true;
// FileAPI
_inherit(api, {
@@ -3322,7 +3897,7 @@
api.Image && _inherit(api.Image.prototype, {
get: function (fn, scaleMode){
this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
- this.parent(fn);
+ return this.parent(fn);
},
_load: function (file, fn){
@@ -3392,6 +3967,7 @@
, info = file.info
, matrix = this.getMatrix(info)
;
+ api.log('FlashAPI.Image.toData');
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
@@ -3428,81 +4004,6 @@
}
});
-
-
- // FileAPI.Camera:statics
- api.Camera.fallback = function (el, options, callback){
- var camId = api.uid();
- api.log('FlashAPI.Camera.publish: ' + camId);
- flash.publish(el, camId, api.extend(options, {
- camera: true,
- onEvent: _wrap(function _(evt){
- if( evt.type == 'camera' ){
- _unwrap(_);
-
- if( evt.error ){
- api.log('FlashAPI.Camera.publish.error: ' + evt.error);
- callback(evt.error);
- }
- else {
- api.log('FlashAPI.Camera.publish.success: ' + camId);
- callback(null);
- }
- }
- })
- }));
- };
-
- // Run
- _each(_cameraQueue, function (args){
- api.Camera.fallback.apply(api.Camera, args);
- });
- _cameraQueue = [];
-
-
- // FileAPI.Camera:proto
- _inherit(api.Camera.prototype, {
- _id: function (){
- return this.video.id;
- },
-
- start: function (callback){
- var _this = this;
- flash.cmd(this._id(), 'camera.on', {
- callback: _wrap(function _(evt){
- _unwrap(_);
-
- if( evt.error ){
- api.log('FlashAPI.camera.on.error: ' + evt.error);
- callback(evt.error, _this);
- }
- else {
- api.log('FlashAPI.camera.on.success: ' + _this._id());
- _this._active = true;
- callback(null, _this);
- }
- })
- });
- },
-
- stop: function (){
- this._active = false;
- flash.cmd(this._id(), 'camera.off');
- },
-
- shot: function (){
- api.log('FlashAPI.Camera.shot:', this._id());
-
- var shot = flash.cmd(this._id(), 'shot', {});
- shot.type = 'image/png';
- shot.flashId = this._id();
- shot.isShot = true;
-
- return new api.Camera.Shot(shot);
- }
- });
-
-
// FileAPI.Form
_inherit(api.Form.prototype, {
toData: function (fn){
@@ -3561,7 +4062,7 @@
return this.parent.apply(this, arguments);
}
else {
- api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId, files);
+ api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
}
_this.xhr = {
@@ -3573,14 +4074,14 @@
var queue = api.queue(function (){
flash.cmd(flashId, 'upload', {
- url: _getUrl(options.url)
+ url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
, data: data
, files: fileId ? files : null
, headers: options.headers || {}
, callback: _wrap(function upload(evt){
var type = evt.type, result = evt.result;
- api.log('FlashAPI.upload.'+type+':', evt);
+ api.log('FlashAPI.upload.'+type);
if( type == 'progress' ){
evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
@@ -3774,12 +4275,6 @@
};
}
-
- api.Camera.fallback = function (){
- _cameraQueue.push(arguments);
- };
-
-
// @export
api.Flash = flash;
@@ -3792,269 +4287,115 @@
})();
})(window, window.jQuery, FileAPI);
-/*
- * JavaScript Load Image iOS scaling fixes 1.0.3
- * https://github.com/blueimp/JavaScript-Load-Image
- *
- * Copyright 2013, Sebastian Tschan
- * https://blueimp.net
- *
- * iOS image scaling fixes based on
- * https://github.com/stomita/ios-imagefile-megapixel
+/**
+ * FileAPI fallback to Flash
*
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
+ * @flash-developer "Vladimir Demidov"
*/
-/*jslint nomen: true, bitwise: true */
-/*global FileAPI, window, document */
-
-(function (factory) {
- 'use strict';
- factory(FileAPI);
-}(function (loadImage) {
- 'use strict';
-
- // Only apply fixes on the iOS platform:
- if (!window.navigator || !window.navigator.platform ||
- !(/iP(hone|od|ad)/).test(window.navigator.platform)) {
- return;
- }
-
- var originalRenderMethod = loadImage.renderImageToCanvas;
-
- // Detects subsampling in JPEG images:
- loadImage.detectSubsampling = function (img) {
- var canvas,
- context;
- if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
- canvas = document.createElement('canvas');
- canvas.width = canvas.height = 1;
- context = canvas.getContext('2d');
- context.drawImage(img, -img.width + 1, 0);
- // subsampled image becomes half smaller in rendering size.
- // check alpha channel value to confirm image is covering edge pixel or not.
- // if alpha value is 0 image is not covering, hence subsampled.
- return context.getImageData(0, 0, 1, 1).data[3] === 0;
- }
- return false;
- };
-
- // Detects vertical squash in JPEG images:
- loadImage.detectVerticalSquash = function (img, subsampled) {
- var naturalHeight = img.naturalHeight || img.height,
- canvas = document.createElement('canvas'),
- context = canvas.getContext('2d'),
- data,
- sy,
- ey,
- py,
- alpha;
- if (subsampled) {
- naturalHeight /= 2;
- }
- canvas.width = 1;
- canvas.height = naturalHeight;
- context.drawImage(img, 0, 0);
- data = context.getImageData(0, 0, 1, naturalHeight).data;
- // search image edge pixel position in case it is squashed vertically:
- sy = 0;
- ey = naturalHeight;
- py = naturalHeight;
- while (py > sy) {
- alpha = data[(py - 1) * 4 + 3];
- if (alpha === 0) {
- ey = py;
- } else {
- sy = py;
- }
- py = (ey + sy) >> 1;
- }
- return (py / naturalHeight) || 1;
- };
-
- // Renders image to canvas while working around iOS image scaling bugs:
- // https://github.com/blueimp/JavaScript-Load-Image/issues/13
- loadImage.renderImageToCanvas = function (
- canvas,
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- destX,
- destY,
- destWidth,
- destHeight
- ) {
- if (img._type === 'image/jpeg') {
- var context = canvas.getContext('2d'),
- tmpCanvas = document.createElement('canvas'),
- tileSize = 1024,
- tmpContext = tmpCanvas.getContext('2d'),
- subsampled,
- vertSquashRatio,
- tileX,
- tileY;
- tmpCanvas.width = tileSize;
- tmpCanvas.height = tileSize;
- context.save();
- subsampled = loadImage.detectSubsampling(img);
- if (subsampled) {
- sourceX /= 2;
- sourceY /= 2;
- sourceWidth /= 2;
- sourceHeight /= 2;
- }
- vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
- if (subsampled || vertSquashRatio !== 1) {
- sourceY *= vertSquashRatio;
- destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
- destHeight = Math.ceil(
- tileSize * destHeight / sourceHeight / vertSquashRatio
- );
- destY = 0;
- tileY = 0;
- while (tileY < sourceHeight) {
- destX = 0;
- tileX = 0;
- while (tileX < sourceWidth) {
- tmpContext.clearRect(0, 0, tileSize, tileSize);
- tmpContext.drawImage(
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- -tileX,
- -tileY,
- sourceWidth,
- sourceHeight
- );
- context.drawImage(
- tmpCanvas,
- 0,
- 0,
- tileSize,
- tileSize,
- destX,
- destY,
- destWidth,
- destHeight
- );
- tileX += tileSize;
- destX += destWidth;
- }
- tileY += tileSize;
- destY += destHeight;
- }
- context.restore();
- return canvas;
+/*global window, FileAPI */
+(function (window, jQuery, api) {
+ "use strict";
+
+ var _each = api.each,
+ _cameraQueue = [];
+
+ if (api.support.flash && (api.media && (!api.support.media || !api.html5 || api.insecureChrome))) {
+ (function () {
+ function _wrap(fn) {
+ var id = fn.wid = api.uid();
+ api.Flash._fn[id] = fn;
+ return 'FileAPI.Flash._fn.' + id;
}
- }
- return originalRenderMethod(
- canvas,
- img,
- sourceX,
- sourceY,
- sourceWidth,
- sourceHeight,
- destX,
- destY,
- destWidth,
- destHeight
- );
- };
-
-}));
-
-/*
- * JavaScript Canvas to Blob 2.0.5
- * https://github.com/blueimp/JavaScript-Canvas-to-Blob
- *
- * Copyright 2012, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- *
- * Based on stackoverflow user Stoive's code snippet:
- * http://stackoverflow.com/q/4998908
- */
-/*jslint nomen: true, regexp: true */
-/*global window, atob, Blob, ArrayBuffer, Uint8Array */
-(function (window) {
- 'use strict';
- var CanvasPrototype = window.HTMLCanvasElement &&
- window.HTMLCanvasElement.prototype,
- hasBlobConstructor = window.Blob && (function () {
- try {
- return Boolean(new Blob());
- } catch (e) {
- return false;
- }
- }()),
- hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
- (function () {
+ function _unwrap(fn) {
try {
- return new Blob([new Uint8Array(100)]).size === 100;
+ api.Flash._fn[fn.wid] = null;
+ delete api.Flash._fn[fn.wid];
} catch (e) {
- return false;
- }
- }()),
- BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
- window.MozBlobBuilder || window.MSBlobBuilder,
- dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
- window.ArrayBuffer && window.Uint8Array && function (dataURI) {
- var byteString,
- arrayBuffer,
- intArray,
- i,
- mimeString,
- bb;
- if (dataURI.split(',')[0].indexOf('base64') >= 0) {
- // Convert base64 to raw binary data held in a string:
- byteString = atob(dataURI.split(',')[1]);
- } else {
- // Convert base64/URLEncoded data component to raw binary data:
- byteString = decodeURIComponent(dataURI.split(',')[1]);
}
- // Write the bytes of the string to an ArrayBuffer:
- arrayBuffer = new ArrayBuffer(byteString.length);
- intArray = new Uint8Array(arrayBuffer);
- for (i = 0; i < byteString.length; i += 1) {
- intArray[i] = byteString.charCodeAt(i);
- }
- // Separate out the mime component:
- mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
- // Write the ArrayBuffer (or ArrayBufferView) to a blob:
- if (hasBlobConstructor) {
- return new Blob(
- [hasArrayBufferViewSupport ? intArray : arrayBuffer],
- {type: mimeString}
- );
- }
- bb = new BlobBuilder();
- bb.append(arrayBuffer);
- return bb.getBlob(mimeString);
- };
- if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
- if (CanvasPrototype.mozGetAsFile) {
- CanvasPrototype.toBlob = function (callback, type, quality) {
- if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
- callback(dataURLtoBlob(this.toDataURL(type, quality)));
- } else {
- callback(this.mozGetAsFile('blob', type));
+ }
+
+ var flash = api.Flash;
+ api.extend(api.Flash, {
+
+ patchCamera: function () {
+ api.Camera.fallback = function (el, options, callback) {
+ var camId = api.uid();
+ api.log('FlashAPI.Camera.publish: ' + camId);
+ flash.publish(el, camId, api.extend(options, {
+ camera: true,
+ onEvent: _wrap(function _(evt) {
+ if (evt.type === 'camera') {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.Camera.publish.error: ' + evt.error);
+ callback(evt.error);
+ } else {
+ api.log('FlashAPI.Camera.publish.success: ' + camId);
+ callback(null);
+ }
+ }
+ })
+ }));
+ };
+ // Run
+ _each(_cameraQueue, function (args) {
+ api.Camera.fallback.apply(api.Camera, args);
+ });
+ _cameraQueue = [];
+
+
+ // FileAPI.Camera:proto
+ api.extend(api.Camera.prototype, {
+ _id: function () {
+ return this.video.id;
+ },
+
+ start: function (callback) {
+ var _this = this;
+ flash.cmd(this._id(), 'camera.on', {
+ callback: _wrap(function _(evt) {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.camera.on.error: ' + evt.error);
+ callback(evt.error, _this);
+ } else {
+ api.log('FlashAPI.camera.on.success: ' + _this._id());
+ _this._active = true;
+ callback(null, _this);
+ }
+ })
+ });
+ },
+
+ stop: function () {
+ this._active = false;
+ flash.cmd(this._id(), 'camera.off');
+ },
+
+ shot: function () {
+ api.log('FlashAPI.Camera.shot:', this._id());
+
+ var shot = api.Flash.cmd(this._id(), 'shot', {});
+ shot.type = 'image/png';
+ shot.flashId = this._id();
+ shot.isShot = true;
+
+ return new api.Camera.Shot(shot);
+ }
+ });
}
+ });
+
+ api.Camera.fallback = function () {
+ _cameraQueue.push(arguments);
};
- } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
- CanvasPrototype.toBlob = function (callback, type, quality) {
- callback(dataURLtoBlob(this.toDataURL(type, quality)));
- };
- }
+
+ }());
}
- window.dataURLtoBlob = dataURLtoBlob;
-})(window);
+}(window, window.jQuery, FileAPI));
if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }
\ No newline at end of file
diff --git a/dist/FileAPI.min.js b/dist/FileAPI.min.js
index e5e99938..bf21d7a2 100644
--- a/dist/FileAPI.min.js
+++ b/dist/FileAPI.min.js
@@ -1,3 +1,2 @@
-/*! fileapi 2.0.1 - BSD | git://github.com/mailru/FileAPI.git */
-!function(a,b){"use strict";function c(a,b,c,d,e){var f={type:c.type||c,target:a,result:d};R(f,e),b(f)}function d(a){return u&&!!u.prototype["readAs"+a]}function e(a,e,f,g){if(X.isFile(a)&&d(f)){var h=new u;S(h,L,function j(b){var d=b.type;"progress"==d?c(a,e,b,b.target.result,{loaded:b.loaded,total:b.total}):"loadend"==d?(T(h,L,j),h=null):c(a,e,b,b.target.result)});try{g?h["readAs"+f](a,g):h["readAs"+f](a)}catch(i){c(a,e,"error",b,{error:i.toString()})}}else c(a,e,"error",b,{error:"filreader_not_support_"+f})}function f(a,b){if(!a.type&&0===a.size%4096&&a.size<=102400)if(u)try{var c=new u;U(c,L,function(a){var d="error"!=a.type;b(d),d&&c.abort()}),c.readAsDataURL(a)}catch(d){b(!1)}else b(null);else b(!0)}function g(a){var b;return a.getAsEntry?b=a.getAsEntry():a.webkitGetAsEntry&&(b=a.webkitGetAsEntry()),b}function h(a,b){if(a)if(a.isFile)a.file(function(c){c.fullPath=a.fullPath,b(!1,[c])},function(a){b("FileError.code: "+a.code)});else if(a.isDirectory){var c=a.createReader(),d=[];c.readEntries(function(a){X.afor(a,function(a,c){h(c,function(c,e){c?X.log(c):d=d.concat(e),a?a():b(!1,d)})})},function(a){b("directory_reader: "+a)})}else h(g(a),b);else b("invalid entry")}function i(a){var b={};return Q(a,function(a,c){a&&"object"==typeof a&&void 0===a.nodeType&&(a=R({},a)),b[c]=a}),b}function j(a){return F.test(a&&a.tagName)}function k(a){return(a.originalEvent||a||"").dataTransfer||{}}function l(a){var b;for(b in a)if(a.hasOwnProperty(b)&&!(a[b]instanceof Object||"overlay"===b||"filter"===b))return!0;return!1}var m=1,n=function(){},o=a.document,p=o.doctype||{},q=a.navigator.userAgent,r=a.createObjectURL&&a||a.URL&&URL.revokeObjectURL&&URL||a.webkitURL&&webkitURL,s=a.Blob,t=a.File,u=a.FileReader,v=a.FormData,w=a.XMLHttpRequest,x=a.jQuery,y=!(!(t&&u&&(a.Uint8Array||v||w.prototype.sendAsBinary))||/safari\//i.test(q)&&!/chrome\//i.test(q)&&/windows/i.test(q)),z=y&&"withCredentials"in new w,A=y&&!!s&&!!(s.prototype.webkitSlice||s.prototype.mozSlice||s.prototype.slice),B=a.dataURLtoBlob,C=/img/i,D=/canvas/i,E=/img|canvas/i,F=/input/i,G=/^data:[^,]+,/,H=a.Math,I=function(b){return b=new a.Number(H.pow(1024,b)),b.from=function(a){return H.round(a*this)},b},J={},K=[],L="abort progress error load loadend",M="status statusText readyState response responseXML responseText responseBody".split(" "),N="currentTarget",O="preventDefault",P=function(a){return a&&"length"in a},Q=function(a,b,c){if(a)if(P(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)},R=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:Q,afor:function(a,b){var c=0,d=a.length;P(a)&&d--?!function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:R,isFile:function(a){return y&&a&&a instanceof t},isCanvas:function(a){return a&&D.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){X.isCanvas(a)?c(a,b,"load",X.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=X.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(X.toDataURL(b).replace(G,""))},readAsImage:function(a,d,e){if(X.isFile(a))if(r){var f=r.createObjectURL(a);f===b?c(a,d,"error"):X.readAsImage(f,d,e)}else X.readAsDataURL(a,function(b){"load"==b.type?X.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(X.isCanvas(a))c(a,d,"load",a);else if(C.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";U(a,g,function i(b){"load"==b.type&&r&&r.revokeObjectURL(a.src),T(a,g,i),c(a,d,b,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=X.newImage(a.dataURL||a);X.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=X.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),Q(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||X.ext2mime[b.type])&&(b.type=X.ext2mime[b.type]||c.split("/")[0]+"/"+b.type)}),b},getDropFiles:function(a,b){var c=[],d=k(a),e=P(d.items)&&d.items[0]&&g(d.items[0]),i=X.queue(function(){b(c)});Q((e?d.items:d.files)||[],function(a){i.inc();try{e?h(a,function(a,b){a?X.log("[err] getDropFiles:",a):c.push.apply(c,b),i.next()}):f(a,function(b){b&&c.push(a),i.next()})}catch(b){i.next(),X.log("[err] getDropFiles: ",b)}}),i.check()},getFiles:function(a,b,c){var d=[];return c?(X.filterFiles(X.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(X.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=X.getFilesFilter(b)),a.originalEvent?a=V(a.originalEvent):a.srcElement&&(a=V(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,y||(d[0].blob=a,d[0].iframe=!0)):!y&&j(a)?X.trim(a.value)&&(d=[X.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):P(a)&&(d=a),X.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=K.concat();X.isFile(a)?!function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(R(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},K.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(),X.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=R({prepare:X.F,beforeupload:X.F,upload:X.F,fileupload:X.F,fileprogress:X.F,filecomplete:X.F,progress:X.F,complete:X.F,pause:X.F,imageOriginal:!0,chunkSize:X.chunkSize,chunkUpoloadRetry:X.chunkUploadRetry},a),a.imageAutoOrientation&&!a.imageTransform&&(a.imageTransform={rotate:"auto"});var b,c=new X.XHR(a),d=this._getFilesDataArray(a.files),e=this,f=0,g=0,h=!1;return Q(d,function(a){f+=a.size}),c.files=[],Q(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 j=d.shift(),k=j&&j.file,l=!1,m=i(a);c.filesLeft=d.length,k&&k.name===X.expando&&(k=null,X.log("[warn] FileAPI.upload() — called without files")),("abort"!=c.statusText||c.current)&&j?(h=!1,c.currentFile=k,k&&a.prepare(k,m),e._getFormData(m,j,function(h){g||a.upload(c,a);var i=new X.XHR(R({},m,{upload:k?function(){a.fileupload(k,i,m)}:n,progress:k?function(b){l||(a.fileprogress({type:"progress",total:j.total=b.total,loaded:j.loaded=b.loaded},k,i,m),a.progress({type:"progress",total:f,loaded:c.loaded=0|g+j.size*(b.loaded/b.total)},k,i,m))}:n,complete:function(d){l=!0,Q(M,function(a){c[a]=i[a]}),k&&(j.loaded=j.total,this.progress(j),g+=j.size,c.loaded=g,a.filecomplete(d,i,k,m)),b.call(e)}}));c.abort=function(a){a||(d.length=0),this.current=a,i.abort()},i.send(h)})):(a.complete(200==c.status||201==c.status?!1:c.statusText||"error",c,a),h=!0)},setTimeout(b,0),c.append=function(a,g){a=X._getFilesDataArray([].concat(a)),Q(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(j(a)){var d=X.getFiles(a);c[a.name||"file"]=null!==a.getAttribute("multiple")?d:d[0]}else P(a)&&j(a[0])?Q(a,function(a){c[a.name||"file"]=X.getFiles(a)}):c=a;return Q(c,function e(a,c){P(a)?Q(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:X.expando}}),b},_getFormData:function(a,b,c){var d=b.file,e=b.name,f=d.name,g=d.type,h=X.support.transform&&a.imageTransform,i=new X.Form,j=X.queue(function(){c(i)}),k=h&&l(h),m=X.postNameConcat;!function n(b){b.image?(j.inc(),b.toData(function(a,b){f=f||(new Date).getTime()+".png",n(b),j.next()})):X.Image&&h&&(/^image/.test(b.type)||E.test(b.nodeName))?(j.inc(),k&&(h=[h]),X.Image.transform(b,h,a.imageAutoOrientation,function(c,d){if(k&&!c)B||X.flashEngine||(i.multipart=!0),i.append(e,d[0],f,h[0].type||g);else{var l=0;c||Q(d,function(a,b){B||X.flashEngine||(i.multipart=!0),h[b].postName||(l=1),i.append(h[b].postName||m(e,b),a,f,h[b].type||g)}),(c||a.imageOriginal)&&i.append(m(e,l?"original":null),b,f,g)}j.next()})):f!==X.expando&&i.append(e,b,f)}(d),Q(a.data,function o(a,b){"object"==typeof a?Q(a,function(a,c){o(a,m(b,c))}):i.append(b,a)}),j.check()},reset:function(a,b){var c,d;return x?(d=x(a).clone(!0).insertBefore(a).val("")[0],b||x(a).remove()):(c=a.parentNode,d=c.insertBefore(a.cloneNode(!0),a),d.value="",b||c.removeChild(a),Q(J[X.uid(a)],function(b,c){Q(b,function(b){T(a,c,b),S(d,c,b)})})),d},load:function(a,b){var c=X.getXHR();return c?(c.open("GET",a,!0),c.overrideMimeType&&c.overrideMimeType("text/plain; charset=x-user-defined"),S(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,"+X.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}};X.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=X.defer();X.readAsImage(a,function(a){var b=a.target;c.resolve("load"==a.type?!1:"error",{width:b.width,height:b.height}),b=null})}a.__dimensions.then(b)}),X.event.dnd=function(a,b,c){var d,e;c||(c=b,b=X.F),u?(S(a,"dragenter dragleave dragover",function(a){for(var c=k(a).types,f=c&&c.length,g=!1;f--;)if(~c[f].indexOf("File")){a[O](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[N],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[N],"dragleave"!=e,a)},50))}),S(a,"drop",function(a){a[O](),e=0,b.call(a[N],!1,a),X.getDropFiles(a,function(b){c.call(a[N],b,a)})})):X.log("Drag'n'Drop -- not supported")},X.event.dnd.off=function(a,b,c){T(a,"dragenter dragleave dragover",b),T(a,"drop",c)},x&&!x.fn.dnd&&(x.fn.dnd=function(a,b){return this.each(function(){X.event.dnd(this,a,b)})},x.fn.offdnd=function(a,b){return this.each(function(){X.event.dnd.off(this,a,b)})}),a.FileAPI=R(X,a.FileAPI),X.log("FileAPI: "+X.version),X.log("protocol: "+a.location.protocol),X.log("doctype: ["+p.name+"] "+p.publicId+" "+p.systemId),Q(o.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&X.log("meta.http-equiv: "+a.getAttribute("content"))}),X.flashUrl||(X.flashUrl=X.staticPath+"FileAPI.flash.swf"),X.flashImageUrl||(X.flashImageUrl=X.staticPath+"FileAPI.flash.image.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.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},void 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};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"string"==typeof b&&(c=b,b=a),this.set({dw:a,dh:b,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(){c(!1,h)}),v=a.renderImageToCanvas;for(b._type=this.file.type;e(p/n,q/o)>2;)p=0|p/2+.5,q=0|q/2+.5,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(h,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 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},_trans:function(a){this._load(this.file,function(b,c){b?a(b):this._apply(c,a)})},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")},toData:function(a){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);if("function"==typeof a?a(i,g):a.width?g[a.preview?"preview":"resize"](a.width,a.height,a.type):a.maxWidth&&(i.width>a.maxWidth||i.height>a.maxHeight)&&g.resize(a.maxWidth,a.maxHeight,"max"),a.crop){var h=a.crop;g.crop(0|h.x,0|h.y,h.w||h.width,h.h||h.height)}a.rotate===c&&f&&(a.rotate="auto"),g.set({deg:a.rotate,type:a.type||b.type||"image/png",quality:a.quality||1,overlay:a.overlay,filter:a.filter}),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(a,b,c,d,e,f,g,h,i,j){return a.getContext("2d").drawImage(b,c,d,e,f,g,h,i,j),a},a.support.canvas=a.support.transform=h,a.Image=d}(FileAPI,document),function(a,b){"use strict";function c(b,c,d){var e=b.blob,f=b.file;if(f){if(!e.toDataURL)return a.readAsBinaryString(e,function(a){"load"==a.type&&c(b,a.result)}),void 0;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(a){d(a,b,f,e)}),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,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){c.upload(c,b),b._send.call(b,c,a)},c)},_send:function(c,e){var g,h=this,i=h.uid,j=c.url;if(b.log("XHR._send:",e),c.cache||(j+=(~j.indexOf("?")?"&":"?")+b.uid()),e.nodeName){c.upload(c,h),g=d.createElement("div"),g.innerHTML='',h.xhr.abort=function(){var a=g.getElementsByTagName("iframe")[0];if(a)try{a.stop?a.stop():a.contentWindow.stop?a.contentWindow.stop():a.contentWindow.document.execCommand("Stop")}catch(b){}g=null};var k=g.getElementsByTagName("form")[0];k.appendChild(e),b.log(k.parentNode.innerHTML),d.body.appendChild(g),h.xhr.node=g,a[i]=function(a,b,c){h.readyState=4,h.responseText=c,h.end(a,b),g=null},h.readyState=2,k.submit(),k=null}else{if(this.xhr&&this.xhr.aborted)return b.log("Error: already aborted"),void 0;if(g=h.xhr=b.getXHR(),e.params&&(j+=(j.indexOf("?")<0?"?":"&")+e.params.join("&")),g.open("POST",j,!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",function(a){e.retry||c.progress({type:a.type,total:e.size,loaded:e.start+a.loaded,totalSize:e.size},h,c)},!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),e.end=a?a:e.start-1,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 l;(l="slice")in e.file||(l="mozSlice")in e.file||(l="webkitSlice")in e.file,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"),l=e.file[l](e.start,e.end+1),g.send(l),l=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];g.onreadystatechange=null,h.end(g.status),g=null}},b.isArray(e))if(g.setRequestHeader("Content-Type","multipart/form-data; boundary=_"+b.expando),e=e.join("")+"--_"+b.expando+"--",g.sendAsBinary)g.sendAsBinary(e);else{var m=Array.prototype.map.call(e,function(a){return 255&a.charCodeAt(0)});g.send(new Uint8Array(m).buffer)}else g.send(e)}}},b.XHR=e}(window,FileAPI),function(a,b){"use strict";function c(a){return a>=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]);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=4*c.width*c.height,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,h=[];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&&(0||!c.html5||!c.support.html5||c.cors&&!c.support.cors||c.media&&!c.support.media)&&function(){function i(a){return(' ').replace(/#(\w+)#/gi,function(b,c){return a[c]})}function j(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 k(a,b){g(b,function(b,c){var d=a[c];a[c]=function(){return this.parent=d,b.apply(this,arguments)}})}function l(a){return a&&!a.flashId}function m(a){var b=a.wid=c.uid();return w._fn[b]=a,"FileAPI.Flash._fn."+b}function n(a){try{w._fn[a.wid]=null,delete w._fn[a.wid]}catch(b){}}function o(a,b){if(!v.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),v.test(a)||(a=e.protocol+a)}return b&&(a+=(/\?/.test(a)?"&":"?")+b),a}function p(a,b,e){function f(){try{var a=w.get(h);a.setImage(b)}catch(d){c.log('[err] FlashAPI.Preview.setImage -- can not set "base64":',d)}}var g,h=c.uid(),k=d.createElement("div"),l=10;for(g in a)k.setAttribute(g,a[g]),k[g]=a[g];j(k,a),a.width="100%",a.height="100%",k.innerHTML=i(c.extend({id:h,src:o(c.flashImageUrl,"r="+c.uid()),wmode:"opaque",flashvars:"scale="+a.scale+"&callback="+m(function p(){return n(p),--l>0&&f(),!0})},a)),e(!1,k),k=null}function q(a){return{id:a.id,name:a.name,matrix:a.matrix,flashId:a.flashId}}function r(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 s=c.uid(),t=0,u={},v=/^https?:/i,w={_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="_"+s,j(b,{top:1,right:1,width:5,height:5,position:"absolute",zIndex:1e6+""}),a.parentNode.insertBefore(b,a),w.publish(b,s),void 0}while(a=a.nextSibling);10>t&&setTimeout(w.init,50*++t)},publish:function(a,b,d){d=d||{},a.innerHTML=i({id:b,src:o(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+(w.isReady||(c.pingUrl?"&ping="+c.pingUrl:""))+"&timeout="+c.flashAbortTimeout+(d.camera?"&useCamera=1":"")},d)},ready:function(){c.log("FlashAPI.state: ready"),w.ready=c.F,w.isReady=!0,w.patch(),c.event.on(d,"mouseover",w.mouseover),c.event.on(d,"click",function(a){w.mouseover(a)&&(a.preventDefault?a.preventDefault():a.returnValue=!0)})},getEl:function(){return d.getElementById("_"+s)},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){var e=b.getAttribute(s),f=w.getWrapper(b);if(c.multiFlash){if("i"==e||"r"==e)return!1;if("p"!=e){b.setAttribute(s,"i");var g=d.createElement("div");if(!f)return c.log("[err] FlashAPI.mouseover: js-fileapi-wrapper not found"),void 0;
-j(g,{top:0,left:0,width:b.offsetWidth+100,height:b.offsetHeight+100,zIndex:1e6+"",position:"absolute"}),f.appendChild(g),w.publish(g,c.uid()),b.setAttribute(s,"p")}return!0}if(f){var h=r(f);j(w.getEl(),h),w.curInp=b}}else/object|embed/i.test(b.nodeName)||j(w.getEl(),{top:1,left:1,width:5,height:5})},onEvent:function(a){var b=a.type;if("ready"==b){try{w.getInput(a.flashId).setAttribute(s,"r")}catch(d){}return w.ready(),setTimeout(function(){w.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 w&&setTimeout(function(){c.log("FlashAPI.event."+a.type+":",a),w[b](a)},1)},mouseenter:function(a){var b=w.getInput(a.flashId);if(b){w.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)}),w.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 w.curInp;try{var b=w.getWrapper(w.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=w.getInput(a.flashId),h=c.uid(f),i=a.target.files;g(i,function(a){c.checkFileObj(a)}),u[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),w.get(a.flashId||a).cmd(b,d)}catch(f){c.log("(js -> flash).onError:",f),e||setTimeout(function(){w.cmd(a,b,d,!0)},50)}},patch:function(){c.flashEngine=c.support.transform=!0,k(c,{getFiles:function(a,b,d){if(d)return c.filterFiles(c.getFiles(a),b,d),null;var e=c.isArray(a)?a:u[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(l(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();w.cmd(a,"getFileInfo",{id:a.id,callback:m(function e(b,c){n(e),d.resolve(b,a.info=c)})})}a.__info.then(b)}}}),c.support.transform=!0,c.Image&&k(c.Image.prototype,{get:function(a,b){this.set({scaleMode:b||"noScale"}),this.parent(a)},_load:function(a,b){if(c.log("FlashAPI.Image._load:",a),l(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),l(a))this.parent.apply(this,arguments);else{var d=this.getMatrix(a.info),e=b;w.cmd(a,"imageTransform",{id:a.id,matrix:d,callback:m(function f(g,h){c.log("FlashAPI.Image._apply.callback:",g),n(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)):p({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);l(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&&k(c.Image,{fromDataURL:function(a,b,d){!c.support.dataURI||a.length>3e4?p(c.extend({scale:"exactFit"},b),a.replace(/^data:[^,]+,/,""),function(a,b){d(b)}):this.parent(a,b,d)}}),c.Camera.fallback=function(a,b,d){var e=c.uid();c.log("FlashAPI.Camera.publish: "+e),w.publish(a,e,c.extend(b,{camera:!0,onEvent:m(function f(a){"camera"==a.type&&(n(f),a.error?(c.log("FlashAPI.Camera.publish.error: "+a.error),d(a.error)):(c.log("FlashAPI.Camera.publish.success: "+e),d(null)))})}))},g(h,function(a){c.Camera.fallback.apply(c.Camera,a)}),h=[],k(c.Camera.prototype,{_id:function(){return this.video.id},start:function(a){var b=this;w.cmd(this._id(),"camera.on",{callback:m(function d(e){n(d),e.error?(c.log("FlashAPI.camera.on.error: "+e.error),a(e.error,b)):(c.log("FlashAPI.camera.on.success: "+b._id()),b._active=!0,a(null,b))})})},stop:function(){this._active=!1,w.cmd(this._id(),"camera.off")},shot:function(){c.log("FlashAPI.Camera.shot:",this._id());var a=w.cmd(this._id(),"shot",{});return a.type="image/png",a.flashId=this._id(),a.isShot=!0,new c.Camera.Shot(a)}}),k(c.Form.prototype,{toData:function(a){for(var b=this.items,d=b.length;d--;)if(b[d].file&&l(b[d].blob))return this.parent.apply(this,arguments);c.log("FlashAPI.Form.toData"),a(b)}}),k(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=q(a.blob),e=a.id,d=a.flashId):f[a.name]=a.blob}),e||(d=s),!d)return c.log("[err] FlashAPI._send: flashId -- undefined"),this.parent.apply(this,arguments);c.log("FlashAPI.XHR._send: "+d+" -> "+e,h),i.xhr={headers:{},abort:function(){w.cmd(d,"abort",{id:e})},getResponseHeader:function(a){return this.headers[a]},getAllResponseHeaders:function(){return this.headers}};var j=c.queue(function(){w.cmd(d,"upload",{url:o(a.url),data:f,files:e?h:null,headers:a.headers||{},callback:m(function b(d){var e=d.type,f=d.result;c.log("FlashAPI.upload."+e+":",d),"progress"==e?(d.loaded=Math.min(d.loaded,d.total),d.lengthComputable=!0,a.progress(d)):"complete"==e?(n(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),n(b))})})});g(h,function(a){j.inc(),c.getInfo(a,j.next)}),j.check()}})}};c.Camera.fallback=function(){h.push(arguments)},c.Flash=w,c.newImage("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",function(a,b){c.support.dataURI=!(1!=b.width||1!=b.height),w.init()})}()}(window,window.jQuery,FileAPI),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){"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;h=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;h0,I=a.dataURLtoBlob,J=/img/i,K=/canvas/i,L=/img|canvas/i,M=/input/i,N=/^data:[^,]+,/,O={}.toString,P=a.Math,Q=function(b){return b=new a.Number(P.pow(1024,b)),b.from=function(a){return P.round(a*this)},b},R={},S=[],T="abort progress error load loadend",U="status statusText readyState response responseXML responseText responseBody".split(" "),V="currentTarget",W="preventDefault",X=function(a){return a&&"length"in a},Y=function(a,b,c){if(a)if(X(a))for(var d=0,e=a.length;d=c&&!d&&f.end()},isFail:function(){return d},fail:function(){!d&&a(d=!0)},end:function(){e||(e=!0,a())}};return f},each:Y,afor:function(a,b){var c=0,d=a.length;X(a)&&d--?function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:Z,isFile:function(a){return"[object File]"===O.call(a)},isBlob:function(a){return this.isFile(a)||"[object Blob]"===O.call(a)},isCanvas:function(a){return a&&K.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){da.isCanvas(a)?c(a,b,"load",da.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=da.toBinaryString(a.result)}catch(b){a.type="error",a.message=b.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(da.toDataURL(b).replace(N,""))},readAsImage:function(a,d,e){if(da.isBlob(a))if(x){var f=x.createObjectURL(a);f===b?c(a,d,"error"):da.readAsImage(f,d,e)}else da.readAsDataURL(a,function(b){"load"==b.type?da.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(da.isCanvas(a))c(a,d,"load",a);else if(J.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";aa(a,g,function b(e){"load"==e.type&&x&&x.revokeObjectURL(a.src),_(a,g,b),c(a,d,e,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=da.newImage(a.dataURL||a);da.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=da.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),Y(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||da.ext2mime[b.type])&&(b.type=da.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=X(m)&&m[0]&&h(m[0]),o=da.queue(function(){b(d,e)});if(n)if(H&&k){var p,q,r=k.length;for(c=new Array(r);r--;){p=k[r];try{q=h(m[r])}catch(a){da.log("[err] getDropFiles: ",a),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;Y(c||[],function(a){o.inc();try{n&&g(a)?i(a,function(a,b,c){a?da.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(a){o.next(),da.log("[err] getDropFiles: ",a)}}),o.check()},getFiles:function(a,b,c){var d=[];return c?(da.filterFiles(da.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(da.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=da.getFilesFilter(b)),a.originalEvent?a=ba(a.originalEvent):a.srcElement&&(a=ba(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,E||(d[0].blob=a,d[0].iframe=!0)):!E&&k(a)?da.trim(a.value)&&(d=[da.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):X(a)&&(d=a),da.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=S.concat();da.isBlob(a)?function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(Z(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},S.push(b)},filter:function(a,b){for(var c,d=[],e=0,f=a.length;e>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}};da.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=da.defer();da.readAsImage(a,function(a){var b=a.target;c.resolve("load"!=a.type&&"error",{width:b.width,height:b.height}),b.src=da.EMPTY_PNG,b=null})}a.__dimensions.then(b)}),da.event.dnd=function(a,b,c){var d,e;c||(c=b,b=da.F),A?($(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[W](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[V],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[V],"dragleave"!=e,a)},50))}),$(a,"drop",c.ff=c.ff||function(a){a[W](),e=0,da.getDropFiles(a,function(b,d){c.call(a[V],b,d,a)}),b.call(a[V],!1,a)})):da.log("Drag'n'Drop -- not supported")},da.event.dnd.off=function(a,b,c){_(a,"dragenter dragleave dragover",b.ff),_(a,"drop",c.ff)},D&&!D.fn.dnd&&(D.fn.dnd=function(a,b){return this.each(function(){da.event.dnd(this,a,b)})},D.fn.offdnd=function(a,b){return this.each(function(){da.event.dnd.off(this,a,b)})}),a.FileAPI=Z(da,a.FileAPI),da.log("FileAPI: "+da.version),da.log("protocol: "+a.location.protocol),da.log("doctype: ["+s.name+"] "+s.publicId+" "+s.systemId),Y(r.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&da.log("meta.http-equiv: "+a.getAttribute("content"))});try{n=!!console.log,o=!!console.log.apply}catch(a){}da.flashUrl||(da.flashUrl=da.staticPath+"FileAPI.flash.swf"),da.flashImageUrl||(da.flashImageUrl=da.staticPath+"FileAPI.flash.image.swf"),da.flashWebcamUrl||(da.flashWebcamUrl=da.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}if(!(this instanceof d))return new d(b);this.file=b,this.size=b.size||100,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}}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(a){}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(a){}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(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(b){throw a.log("renderImageToCanvas failed"),b}},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,1-a.width,0),0===c.getImageData(0,0,1,1).data[3])},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;p0&&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(b){a.log("FileAPI.Form._to: "+b.message),c(b)}}),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&&(f.statusText||"unknown"),f),f.xhr&&f.xhr.node&&setTimeout(function(){var b=f.xhr.node;try{b.parentNode.removeChild(b)}catch(a){}try{delete a[f.uid]}catch(a){}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(a){n(0,a.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(a){b.log("[transport.onload]",a)}},g=d.createElement("div"),g.innerHTML='";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(a){b.log("iframe.error: "+a)}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 q=e.file,r=(q.slice||q.mozSlice||q.webkitSlice).call(q,e.start,e.end+1);e.size&&!r.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(r)),q=r=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 c,d=f.createElement("canvas"),e=!1;try{c=d.getContext("2d"),c.drawImage(a,0,0,1,1),e=255!=c.getImageData(0,0,1,1).data[4]}catch(a){b.log("[FileAPI.Camera] detectVideoSignal:",a)}return e}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;try{i.src=e.createObjectURL(a)}catch(b){i.srcObject=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();try{this.stream.stop()}catch(a){b.each(this.stream.getTracks(),function(a){a.stop()})}this.stream=null}catch(a){b.log("[FileAPI.Camera] stop:",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]);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&&!b.insecureChrome){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")},j.checkAlreadyCaptured=function(){var b,c=g.mediaDevices,d=a.MediaStreamTrack,e=g.enumerateDevices;return b=c&&c.enumerateDevices?function(a){c.enumerateDevices().then(a)}:d&&d.getSources?d.getSources.bind(d):e?e.bind(g):function(a){a([])},function(a){b(function(b){var c=b.some(function(a){return("videoinput"===a.kind||"video"===a.kind)&&a.label});a(c)})}}();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(a){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||c.insecureChrome)&&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(a){}}}}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(a){}}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{v.get(j).setImage(b)}catch(a){c.log('[err] FlashAPI.Preview.setImage -- can not set "base64":',a)}}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 a(){return m(a),--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);s<10&&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(a){}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(b){c.log('[err] Can not find "input" by flashId:',a,b)}},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 b(c,e){m(b),d.resolve(c,a.info=e)})})}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&&!c.insecureChrome||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(a){}}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 a(d){"camera"===d.type&&(b(a),d.error?(c.log("FlashAPI.Camera.publish.error: "+d.error),g(d.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 a(f){b(a),f.error?(c.log("FlashAPI.camera.on.error: "+f.error),d(f.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});
\ No newline at end of file
diff --git a/examples/demo.html b/examples/demo.html
index 1b098107..93570b8c 100644
--- a/examples/demo.html
+++ b/examples/demo.html
@@ -486,7 +486,7 @@
_upload: function (file){
if( file ){
file.xhr = FileAPI.upload({
- url: 'http://www.rubaxa.org/index.php',
+ url: '/upload',
files: { file: file },
upload: function (){
FU._getEl(file).addClass('b-file_upload');
diff --git a/examples/thumbnails.html b/examples/thumbnails.html
index 03c94ba2..5f296054 100644
--- a/examples/thumbnails.html
+++ b/examples/thumbnails.html
@@ -57,6 +57,25 @@
right: 0;
position: absolute;
}
+<<<<<<< HEAD
+
+ .js-fileapi-wrapper { position: relative }
+=======
+ /* IE9/IE8 required */
+ .js-fileapi-wrapper { position: relative; }
+>>>>>>> master
+ .js-fileapi-wrapper input {
+ top: -10px;
+ right: -40px;
+ z-index: 2;
+ position: absolute;
+ cursor: pointer;
+ opacity: 0;
+ width: auto;
+ height: auto;
+ filter: alpha(opacity=0);
+ font-size: 50px;
+ }
@@ -78,8 +97,8 @@ Thumbnails
- Select photo (min 480x320)
+ Select photo (min 480x320)
@@ -110,11 +129,17 @@
Thumbnails
}
image.get(function (err, img){
- var el = document.createElement('div');
- el.innerHTML = '
'+label+' ';
- el.className = 'thumb';
- el.appendChild(img);
- thumbnails.appendChild(el);
+ if (err) {
+ alert(width + 'x' + height + ' — fail');
+ }
+ else {
+ var el = document.createElement('div');
+ el.innerHTML = '
'+label+' ';
+ el.className = 'thumb';
+ el.appendChild(img);
+ thumbnails.appendChild(el);
+ }
+
callback && callback();
});
diff --git a/examples/toolkit.css b/examples/toolkit.css
index 8cba3b1f..df05ba43 100644
--- a/examples/toolkit.css
+++ b/examples/toolkit.css
@@ -47,7 +47,6 @@
width: 0;
height: 0;
opacity: 0;
- overflow: 0;
position: absolute;
}
diff --git a/examples/userpic.html b/examples/userpic.html
index e2383f25..e72a55d4 100644
--- a/examples/userpic.html
+++ b/examples/userpic.html
@@ -154,7 +154,7 @@
// Параметры загрузки
var uploadOpts = {
- url: 'http://www.rubaxa.org/index.php' // куда грузить
+ url: '/upload' // куда грузить
, data: {} // дополнительный POST-параметры
, name: 'userpic' // название POST-параметра загружаемого файла
, activeClassName: 'upload_active' // класс, который будем добавлять общему контейнеру при загрузке
diff --git a/examples/watermark.html b/examples/watermark.html
index d6172282..4403d000 100644
--- a/examples/watermark.html
+++ b/examples/watermark.html
@@ -182,7 +182,7 @@
EXIF
// Upload
FileAPI.upload({
- url: '//rubaxa.org/FileAPI/server/ctrl.php'
+ url: '/upload'
, files: elms.file
, imageTransform: {
width: elms.width.value|0
diff --git a/examples/webcam.html b/examples/webcam.html
index 12281d92..0a995eee 100644
--- a/examples/webcam.html
+++ b/examples/webcam.html
@@ -114,7 +114,7 @@
Server
;
FileAPI.upload({
- url: 'http://rubaxa.org/FileAPI/server/ctrl.php'
+ url: '/upload'
, files: { shot: file }
, complete: function (err, xhr){
var res = JSON.parse(xhr.responseText);
diff --git a/flash/camera/src/FileAPI_flash_camera.as b/flash/camera/src/FileAPI_flash_camera.as
index 7d6be4b4..736ab4f2 100644
--- a/flash/camera/src/FileAPI_flash_camera.as
+++ b/flash/camera/src/FileAPI_flash_camera.as
@@ -1,14 +1,14 @@
package
{
- import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
- import flash.events.*;
+ import flash.events.Event;
+ import flash.events.StatusEvent;
import flash.media.Camera;
import flash.media.Video;
- import flash.text.TextField;
+ import flash.utils.setTimeout;
public class FileAPI_flash_camera extends Sprite
{
@@ -39,38 +39,20 @@ package
// we need to show settings dialog, so we attach camera to a video
video = new Video();
video.attachCamera(camera);
+ if (!camera.muted) {
+ onCameraStatus(new StatusEvent(StatusEvent.STATUS, false, false, 'Camera.Unmuted'));
+ }
+ else {
+ setTimeout(function ():void {
+ if (securityPanelIsClosed()) {
+ onCameraStatus(new StatusEvent(StatusEvent.STATUS, false, false, 'Camera.Muted'));
+ }
+ }, 1000);
+ }
+
} else {
// callback with error
}
-
-
- // test
- /*var tf:TextField = new TextField();
- tf.text = 'on';
- tf.x = 10;
- tf.width = 30;
- tf.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void {
- toggleCamera(true);
- });
- addChild(tf);
-
- tf = new TextField();
- tf.text = 'off';
- tf.x = 50;
- tf.width = 30;
- tf.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void {
- toggleCamera(false);
- });
- addChild(tf);
-
- tf = new TextField();
- tf.text = 'photo';
- tf.x = 100;
- tf.width = 30;
- tf.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void {
- shot();
- });
- addChild(tf);*/
}
public function toggleCamera(on:Boolean):void {
@@ -121,5 +103,31 @@ package
// redispatch
dispatchEvent(event.clone());
}
+
+ /**
+ * This code checks one time if the security panel is closed.
+ * When you open the security panel, you should run this test
+ * repeatedly with a timer (every 500ms seems to work well).
+ * If the security panel is closed, you can then clean up your timers
+ */
+ private function securityPanelIsClosed():Boolean
+ {
+ // Why not just wait for an event from the SettingsPanel to know that it's closed? Because there isn't one.
+ // See http://bugs.adobe.com/jira/browse/FP-41
+ var closed:Boolean = true;
+ var hack:BitmapData = new BitmapData(1,1);
+ try
+ {
+ // Trying to capture the stage triggers a Security error when the settings dialog box is open.
+ hack.draw(stage);
+ }
+ catch (error:Error)
+ {
+ closed = false;
+ }
+ hack.dispose();
+ hack = null;
+ return (closed);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/flash/core/html-template/index.template.html b/flash/core/html-template/index.template.html
index d327dade..8d458fd7 100644
--- a/flash/core/html-template/index.template.html
+++ b/flash/core/html-template/index.template.html
@@ -1,108 +1,108 @@
-
-
-
-
-
-
${title}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- To view this page ensure that Adobe Flash Player version
- ${version_major}.${version_minor}.${version_revision} or greater is installed.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Either scripts and active content are not permitted to run or Adobe Flash Player version
- ${version_major}.${version_minor}.${version_revision} or greater is not installed.
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
${title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To view this page ensure that Adobe Flash Player version
+ ${version_major}.${version_minor}.${version_revision} or greater is installed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Either scripts and active content are not permitted to run or Adobe Flash Player version
+ ${version_major}.${version_minor}.${version_revision} or greater is not installed.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/flash/core/src/FileAPI_flash.as b/flash/core/src/FileAPI_flash.as
index b4d8767e..c869a068 100644
--- a/flash/core/src/FileAPI_flash.as
+++ b/flash/core/src/FileAPI_flash.as
@@ -7,11 +7,11 @@ package
import flash.events.Event;
import flash.events.UncaughtErrorEvent;
- import ru.mail.controller.AppController;
+ import ru.mail.controller.AppController;
/**
*
- * @author v.demidov
+ * @author v.demidov
https://github.com/im-saxo
*
*/
public class FileAPI_flash extends Sprite
@@ -36,7 +36,6 @@ package
*/
protected function init(event:Event = null):void
{
- trace ("{FlashFileAPI} - init");
removeEventListener(Event.ADDED_TO_STAGE, init);
// config stage
@@ -48,22 +47,10 @@ package
addChild(_graphicContext);
// initiate controller
- _controller = new AppController(_graphicContext, parseFlashVars());
+ _controller = new AppController(_graphicContext, loaderInfo.parameters);
// add some global listeners
stage.addEventListener(Event.RESIZE, _controller.onStageResize);
loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, _controller.onUncaughtError);
}
-
- /**
- * parse all flashvars into object
- */
- private function parseFlashVars():Object
- {
- var options:Object = new Object();
- for (var s:String in loaderInfo.parameters) {
- options[s] = loaderInfo.parameters[s];
- }
- return options;
- }
}
-}
\ No newline at end of file
+}
diff --git a/flash/core/src/net/inspirit/MultipartURLLoader.as b/flash/core/src/net/inspirit/MultipartURLLoader.as
index 3ea716f4..05216616 100644
--- a/flash/core/src/net/inspirit/MultipartURLLoader.as
+++ b/flash/core/src/net/inspirit/MultipartURLLoader.as
@@ -481,6 +481,7 @@
private function removeListener(): void
{
+ if (!_loader) return;
_loader.removeEventListener( Event.COMPLETE, onComplete );
_loader.removeEventListener( ProgressEvent.PROGRESS, onProgress );
_loader.removeEventListener( IOErrorEvent.IO_ERROR, onIOError );
@@ -562,16 +563,17 @@
}
}
+import flash.utils.ByteArray;
internal class FilePart
{
- public var fileContent:flash.utils.ByteArray;
+ public var fileContent:ByteArray;
public var fileName:String;
public var dataField:String;
public var contentType:String;
- public function FilePart(fileContent:flash.utils.ByteArray, fileName:String, dataField:String = 'Filedata', contentType:String = 'application/octet-stream')
+ public function FilePart(fileContent:ByteArray, fileName:String, dataField:String = 'Filedata', contentType:String = 'application/octet-stream')
{
this.fileContent = fileContent;
this.fileName = fileName;
diff --git a/flash/core/src/ru/mail/commands/ResizeFileCommand.as b/flash/core/src/ru/mail/commands/ResizeFileCommand.as
index d4efb4b1..0a9d6136 100644
--- a/flash/core/src/ru/mail/commands/ResizeFileCommand.as
+++ b/flash/core/src/ru/mail/commands/ResizeFileCommand.as
@@ -22,8 +22,8 @@ package ru.mail.commands
*
* file must be loaded before transforming.
*
- * Only JPG or PNG images.
- * Performing transform on gif or bmp will result in returning the original image data.
+ * JPG and PNG will keep their extensions after transform.
+ * GIF and BMP will be encoded as PNG, so uploaded filename will be .png
*
* The possible solution is to transform them but save as PNG, but we also have to change the uploaded file extension to png.
*
@@ -47,18 +47,16 @@ package ru.mail.commands
{
if( !file.imageData ) {
complete(false, null, new ErrorVO("ResizeImageCommand - cannot resize file because it has not been succesfully loaded") );
+ return;
}
if (!needResize()) {
LoggerJS.log('ResizeImageCommand no need to resize');
complete(true, file.fileData);
+ return;
}
var fileType:String = file.fileType;
- if (fileType == "gif" || fileType == "bmp") {
- // TODO: scale but save jpg
- complete(true, file.fileData)
- }
checkTransform();
@@ -131,6 +129,7 @@ package ru.mail.commands
else
{
currentImageMap = fullImageMap.clone();
+// currentImageMap = fullImageMap; // #199 #265 вернул обратно clone(), т.к. ниже есть currentImageMap.dispose()
}
// ==============
@@ -140,9 +139,7 @@ package ru.mail.commands
var angle:Number = imageTransform.deg*Math.PI/180;
var maxScale:Number = Math.max(scaleX, scaleY);
- if (maxScale < 0.5) {
-
- trace ("multi-step ");
+ if (imageTransform.multiPassResize && maxScale < 0.5) {
var curWidth:Number = currentImageMap.width;
var curHeight:Number = currentImageMap.height;
@@ -150,7 +147,6 @@ package ru.mail.commands
// multi-step
while(maxScale < 0.5)
{
- trace ("step ", maxScale);
// series if x2 scalings
// temp bitmapdata
diff --git a/flash/core/src/ru/mail/commands/UploadCommand.as b/flash/core/src/ru/mail/commands/UploadCommand.as
index f1fe8d4a..db522f7b 100644
--- a/flash/core/src/ru/mail/commands/UploadCommand.as
+++ b/flash/core/src/ru/mail/commands/UploadCommand.as
@@ -137,7 +137,9 @@ package ru.mail.commands
checkFilesPool();
});
- imageFactory.createImage( trans? new ImageTransformVO(trans.sx, trans.sy, trans.sw, trans.sh, trans.dw, trans.dh, trans.deg, trans.type, trans.quality, (trans.overlay is Array)? trans.overlay : [trans.overlay]) : null );
+ imageFactory.createImage( trans
+ ? new ImageTransformVO(trans.sx, trans.sy, trans.sw, trans.sh, trans.dw, trans.dh, trans.deg, trans.type, trans.quality, (trans.overlay is Array)? trans.overlay : [trans.overlay], trans.multipass)
+ : null );
}
}
diff --git a/flash/core/src/ru/mail/communication/JSCaller.as b/flash/core/src/ru/mail/communication/JSCaller.as
index 4eb36cdb..675105cc 100644
--- a/flash/core/src/ru/mail/communication/JSCaller.as
+++ b/flash/core/src/ru/mail/communication/JSCaller.as
@@ -1,6 +1,7 @@
package ru.mail.communication
{
import flash.external.ExternalInterface;
+ import flash.utils.ByteArray;
import ru.mail.data.vo.ErrorVO;
import ru.mail.data.vo.FileVO;
@@ -64,15 +65,9 @@ package ru.mail.communication
}
// pass data to given callback
- if (data2) {
- ExternalInterface.call(_callback, data, data2);
- }
- else {
- ExternalInterface.call(_callback, data);
- }
+ _call(_callback, data, data2);
}
catch (e:Error) {
- trace ("callJS caused an exception", e);
}
}
@@ -86,13 +81,11 @@ package ru.mail.communication
{
var isReady:Boolean = false;
try {
- var r:* = ExternalInterface.call(callback, {type:"ready", flashId:flashId});
- trace( "JSCaller.notifyJSAboutAppReady() ", triesCount );
+ var r:* = _call(callback, {type:"ready", flashId:flashId});
isReady = ( r != null );
}
catch ( e:Error ) {
- trace ("notifyJSAboutAppReady error", e);
}
return isReady;
@@ -113,10 +106,9 @@ package ru.mail.communication
}
try {
- ExternalInterface.call(callback, { type:eventType, flashId:flashId });
+ _call(callback, { type:eventType, flashId:flashId });
}
catch (e:Error) {
- trace ("notifyJSMouseEvents error", e);
}
}
@@ -131,8 +123,6 @@ package ru.mail.communication
*/
public function notifyJSFilesEvents(eventType:String, filesVector:Vector. = null):void
{
- trace ("{JSCaller} - notifyJSFilesEvents, eventType", eventType)
-
var details:Object = new Object();
details.type = eventType;
@@ -166,10 +156,9 @@ package ru.mail.communication
try
{
- ExternalInterface.call(callback, details);
+ _call(callback, details);
}
catch (e:Error) {
- trace ("notifyJSFilesEvents error",e);
}
}
@@ -191,10 +180,9 @@ package ru.mail.communication
try
{
- ExternalInterface.call(callback, details);
+ _call(callback, details);
}
catch (e:Error) {
- trace ("notifyJSErrors error",e);
}
}
@@ -206,10 +194,9 @@ package ru.mail.communication
public function notifyCameraStatus(error:String):void
{
try {
- ExternalInterface.call(callback, { type:'camera', error:error, flashId:flashId });
+ _call(callback, { type:'camera', error:error, flashId:flashId });
}
catch (e:Error) {
- trace ("notifyCameraStatus error", e);
}
}
@@ -224,11 +211,46 @@ package ru.mail.communication
{
try
{
- ExternalInterface.call(callback, {type:"error", message:errorVO.getError(), flashId:flashId});
+ _call(callback, {type:"error", message:errorVO.getError(), flashId:flashId});
}
catch (e:Error) {
- trace ("notifyJSErrors error",e);
+ }
+ }
+
+ private function clone(source:Object):* {
+ var myBA:ByteArray = new ByteArray();
+ myBA.writeObject(source);
+ myBA.position = 0;
+ return(myBA.readObject());
+ }
+
+ private function _escape(data:*):* {
+ if (typeof data === 'string') {
+ return data.replace(/\\/g, '\\\\');
+ } else if (typeof data === 'object') {
+ var ret:* = clone(data);
+ for (var i:String in data) {
+ ret[i] = _escape(data[i]);
+ }
+ return ret;
+ }
+ return data;
+ }
+
+ private function _call(callback:String, data:Object, data2:Object = null):* {
+ data = _escape(data);
+ if ( callback.match(/^FileAPI\.Flash\.(onEvent|_fn\.fileapi\d+)$/) ) {
+ if (data2) {
+ data2 = _escape(data2);
+ return ExternalInterface.call(callback, data, data2);
+ }
+ else {
+ return ExternalInterface.call(callback, data);
+ }
+ }
+ else {
+ return null;
}
}
}
-}
\ No newline at end of file
+}
diff --git a/flash/core/src/ru/mail/controller/AppController.as b/flash/core/src/ru/mail/controller/AppController.as
index 8d24425c..2c5e38d9 100644
--- a/flash/core/src/ru/mail/controller/AppController.as
+++ b/flash/core/src/ru/mail/controller/AppController.as
@@ -107,9 +107,10 @@ package ru.mail.controller
JSCaller.flashId = options["flashId"];
}
- // use camera
- _model.useCamera = !!options["useCamera"];
- if (_model.useCamera) {
+ // use camera
+ // options["useCamera"], if not false, contains url to camera swf
+ _model.useCamera = options["useCamera"];
+ if (_model.useCamera && _model.useCamera !== 'false') {
setupCamera();
}
@@ -513,11 +514,9 @@ package ru.mail.controller
*/
public function getFileInfo(fileID:String, callback:String):void
{
- trace ("getFileInfo");
LoggerJS.log('getFileInfo, fileID: '+fileID+', callback: '+callback );
var file:BaseFileVO = _model.filesBuilder.getFileByID(fileID);
if (!file) {
- trace ("file with id "+ fileID +" doen't exist");
LoggerJS.log("getFileInfo, file with id "+ fileID +" doen't exist" );
return;
}
@@ -525,7 +524,6 @@ package ru.mail.controller
var imageFactory:IImageFactory = file.imageFactory;
(imageFactory as EventDispatcher).addEventListener(ImageTransformCompleteEvent.TYPE,function (event:ImageTransformCompleteEvent):void {
event.currentTarget.removeEventListener(event.type, arguments.callee);
- trace("getfileinfo complete", event.isSuccess);
LoggerJS.log("getFileInfo complete, success = "+ event.isSuccess );
if (event.isSuccess && (file as IFileVO).imageData)
{
@@ -581,7 +579,9 @@ package ru.mail.controller
}
});
- imageFactory.createImage( trans? new ImageTransformVO(trans.sx, trans.sy, trans.sw, trans.sh, trans.dw, trans.dh, trans.deg, trans.type, trans.quality, (trans.overlay is Array)? trans.overlay : [trans.overlay]) : null );
+ imageFactory.createImage( trans
+ ? new ImageTransformVO(trans.sx, trans.sy, trans.sw, trans.sh, trans.dw, trans.dh, trans.deg, trans.type, trans.quality, (trans.overlay is Array)? trans.overlay : [trans.overlay], trans.multipass)
+ : null);
}
catch (e:Error){
LoggerJS.log('imageFactory createImage error: '+e.toString());
diff --git a/flash/core/src/ru/mail/controller/CameraController.as b/flash/core/src/ru/mail/controller/CameraController.as
index 0822aab1..aa34ae56 100644
--- a/flash/core/src/ru/mail/controller/CameraController.as
+++ b/flash/core/src/ru/mail/controller/CameraController.as
@@ -97,8 +97,8 @@ package ru.mail.controller
initComplete(false, evt.error.getError());
}
});
- // todo: parametrize swf path
- loader.loadGraphic(new URLRequest('../dist/FileAPI.flash.camera.swf'));
+
+ loader.loadGraphic(new URLRequest(_model.useCamera || 'FileAPI.flash.camera.swf'));
}
private function onCameraStatus(event:StatusEvent):void
diff --git a/flash/core/src/ru/mail/data/AttachmentsModel.as b/flash/core/src/ru/mail/data/AttachmentsModel.as
index 68bdd3e7..462711a4 100644
--- a/flash/core/src/ru/mail/data/AttachmentsModel.as
+++ b/flash/core/src/ru/mail/data/AttachmentsModel.as
@@ -51,7 +51,7 @@ package ru.mail.data
return _filesBuilder;
}
- public var useCamera:Boolean = false;
+ public var useCamera:String = null;
/**
* if true, user can select multiple files
diff --git a/flash/core/src/ru/mail/data/vo/FakeFileVO.as b/flash/core/src/ru/mail/data/vo/FakeFileVO.as
index df7a2017..f9ef422d 100644
--- a/flash/core/src/ru/mail/data/vo/FakeFileVO.as
+++ b/flash/core/src/ru/mail/data/vo/FakeFileVO.as
@@ -45,12 +45,12 @@ package ru.mail.data.vo
* @return
*
*/
- public function get imageData():BitmapData
+ public function get imageData():BitmapData
{
return _imageData;
}
- public function set imageData(bd:BitmapData):void
+ public function set imageData(bd:BitmapData):void
{
_imageData = bd;
}
diff --git a/flash/core/src/ru/mail/data/vo/FileVO.as b/flash/core/src/ru/mail/data/vo/FileVO.as
index eda1ccf7..3834a579 100644
--- a/flash/core/src/ru/mail/data/vo/FileVO.as
+++ b/flash/core/src/ru/mail/data/vo/FileVO.as
@@ -75,7 +75,7 @@ package ru.mail.data.vo
if ( fileNameParts.length < 2 )
return fileName;
- return fileNameParts[0] + 'png';
+ return fileNameParts[0] + '.png';
}
}
@@ -84,4 +84,4 @@ package ru.mail.data.vo
super();
}
}
-}
\ No newline at end of file
+}
diff --git a/flash/core/src/ru/mail/data/vo/ImageTransformVO.as b/flash/core/src/ru/mail/data/vo/ImageTransformVO.as
index 74e95b62..159d00da 100644
--- a/flash/core/src/ru/mail/data/vo/ImageTransformVO.as
+++ b/flash/core/src/ru/mail/data/vo/ImageTransformVO.as
@@ -21,9 +21,10 @@ package ru.mail.data.vo
public var type:String = 'image/png'; // encoded image type. If type value is unknown, png is used
public var quality:Number = 1; // encode quality (jpeg only)
public var overlay:Array = []; // array of OverlayVO instances
+ public var multiPassResize: Boolean = true;
public function ImageTransformVO(sx:Number = 0, sy:Number = 0, sw:Number = 0, sh:Number = 0, dw:Number = 0, dh:Number = 0, deg:Number = 0
- , type:String = null, quality:Number = 1, overlay:Array = null)
+ , type:String = null, quality:Number = 1, overlay:Array = null, multiPassResize:Boolean = true)
{
super();
@@ -45,6 +46,9 @@ package ru.mail.data.vo
this.type = type;
if ( !isNaN(quality) )
this.quality = quality;
+
+ this.multiPassResize = multiPassResize;
+
if ( overlay )
setOverlay( overlay );
}
diff --git a/flash/image/src/FileAPI_flash_image.as b/flash/image/src/FileAPI_flash_image.as
index e3b5f1ff..3912d1d9 100644
--- a/flash/image/src/FileAPI_flash_image.as
+++ b/flash/image/src/FileAPI_flash_image.as
@@ -56,8 +56,10 @@ package{
private function callReady():Boolean{
var isReady:Boolean = false;
try {
- var r:* = ExternalInterface.call(callback);
- isReady = ( r != null );
+ if ( callback.match(/^FileAPI\.Flash\.(onEvent|_fn\.fileapi\d+)$/) ) {
+ var r:* = ExternalInterface.call(callback);
+ isReady = ( r != null );
+ }
}
catch ( e:Error ) {}
return isReady;
diff --git a/index.html b/index.html
index 330e3350..a7439a45 100644
--- a/index.html
+++ b/index.html
@@ -77,11 +77,6 @@
-
-
diff --git a/lib/FileAPI.Camera.js b/lib/FileAPI.Camera.js
index 42df39aa..d97eb983 100644
--- a/lib/FileAPI.Camera.js
+++ b/lib/FileAPI.Camera.js
@@ -64,7 +64,11 @@
// });
// Set camera stream
- video.src = URL.createObjectURL(stream);
+ try {
+ video.src = URL.createObjectURL(stream);
+ } catch (err) {
+ video.srcObject = stream;
+ }
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
@@ -91,8 +95,19 @@
try {
this._active = false;
this.video.pause();
- this.stream.stop();
- } catch( err ){ }
+
+ try {
+ this.stream.stop();
+ } catch (err) {
+ api.each(this.stream.getTracks(), function (track) {
+ track.stop();
+ });
+ }
+
+ this.stream = null;
+ } catch( err ){
+ api.log('[FileAPI.Camera] stop:', err);
+ }
},
@@ -167,7 +182,7 @@
el.style.height = _px(options.height);
- if( api.html5 && html5 ){
+ if( api.html5 && html5 && !api.insecureChrome ){
// Create video element
var video = document.createElement('video');
@@ -198,6 +213,38 @@
callback('not_support_camera');
};
+ Camera.checkAlreadyCaptured = (function () {
+ var mediaDevices = navigator.mediaDevices,
+ MediaStreamTrack = window.MediaStreamTrack,
+ navigatorEnumerateDevices = navigator.enumerateDevices,
+ enumerateDevices;
+
+ if (mediaDevices && mediaDevices.enumerateDevices) {
+ enumerateDevices = function (callback) {
+ mediaDevices.enumerateDevices().then(callback);
+ };
+ } else if (MediaStreamTrack && MediaStreamTrack.getSources) {
+ enumerateDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack);
+ } else if (navigatorEnumerateDevices) {
+ enumerateDevices = navigatorEnumerateDevices.bind(navigator);
+ } else {
+ enumerateDevices = function (fn) {
+ fn([]);
+ };
+ }
+
+ return function (callback) {
+ enumerateDevices(function (devices) {
+ var deviceExists = devices.some(function (device) {
+ return (device.kind === 'videoinput' || device.kind === 'video') && device.label;
+ });
+
+ callback(deviceExists);
+ });
+ };
+
+ })();
+
/**
* @class FileAPI.Camera.Shot
@@ -237,7 +284,9 @@
ctx.drawImage(video, 0, 0, 1, 1);
res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
}
- catch( e ){}
+ catch( err ){
+ api.log('[FileAPI.Camera] detectVideoSignal:', err);
+ }
return res;
}
diff --git a/lib/FileAPI.Flash.Camera.js b/lib/FileAPI.Flash.Camera.js
new file mode 100644
index 00000000..ddf97e65
--- /dev/null
+++ b/lib/FileAPI.Flash.Camera.js
@@ -0,0 +1,111 @@
+/**
+ * FileAPI fallback to Flash
+ *
+ * @flash-developer "Vladimir Demidov"
+ */
+
+/*global window, FileAPI */
+(function (window, jQuery, api) {
+ "use strict";
+
+ var _each = api.each,
+ _cameraQueue = [];
+
+ if (api.support.flash && (api.media && (!api.support.media || !api.html5 || api.insecureChrome))) {
+ (function () {
+ function _wrap(fn) {
+ var id = fn.wid = api.uid();
+ api.Flash._fn[id] = fn;
+ return 'FileAPI.Flash._fn.' + id;
+ }
+
+
+ function _unwrap(fn) {
+ try {
+ api.Flash._fn[fn.wid] = null;
+ delete api.Flash._fn[fn.wid];
+ } catch (e) {
+ }
+ }
+
+ var flash = api.Flash;
+ api.extend(api.Flash, {
+
+ patchCamera: function () {
+ api.Camera.fallback = function (el, options, callback) {
+ var camId = api.uid();
+ api.log('FlashAPI.Camera.publish: ' + camId);
+ flash.publish(el, camId, api.extend(options, {
+ camera: true,
+ onEvent: _wrap(function _(evt) {
+ if (evt.type === 'camera') {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.Camera.publish.error: ' + evt.error);
+ callback(evt.error);
+ } else {
+ api.log('FlashAPI.Camera.publish.success: ' + camId);
+ callback(null);
+ }
+ }
+ })
+ }));
+ };
+ // Run
+ _each(_cameraQueue, function (args) {
+ api.Camera.fallback.apply(api.Camera, args);
+ });
+ _cameraQueue = [];
+
+
+ // FileAPI.Camera:proto
+ api.extend(api.Camera.prototype, {
+ _id: function () {
+ return this.video.id;
+ },
+
+ start: function (callback) {
+ var _this = this;
+ flash.cmd(this._id(), 'camera.on', {
+ callback: _wrap(function _(evt) {
+ _unwrap(_);
+
+ if (evt.error) {
+ api.log('FlashAPI.camera.on.error: ' + evt.error);
+ callback(evt.error, _this);
+ } else {
+ api.log('FlashAPI.camera.on.success: ' + _this._id());
+ _this._active = true;
+ callback(null, _this);
+ }
+ })
+ });
+ },
+
+ stop: function () {
+ this._active = false;
+ flash.cmd(this._id(), 'camera.off');
+ },
+
+ shot: function () {
+ api.log('FlashAPI.Camera.shot:', this._id());
+
+ var shot = api.Flash.cmd(this._id(), 'shot', {});
+ shot.type = 'image/png';
+ shot.flashId = this._id();
+ shot.isShot = true;
+
+ return new api.Camera.Shot(shot);
+ }
+ });
+ }
+ });
+
+ api.Camera.fallback = function () {
+ _cameraQueue.push(arguments);
+ };
+
+ }());
+ }
+}(window, window.jQuery, FileAPI));
diff --git a/lib/FileAPI.Flash.js b/lib/FileAPI.Flash.js
index 4385f790..6ec95736 100644
--- a/lib/FileAPI.Flash.js
+++ b/lib/FileAPI.Flash.js
@@ -5,16 +5,14 @@
*/
/*global window, ActiveXObject, FileAPI */
-(function (window, jQuery, api){
+(function (window, jQuery, api) {
"use strict";
var
document = window.document
, location = window.location
, navigator = window.navigator
-
, _each = api.each
- , _cameraQueue = []
;
@@ -46,6 +44,7 @@
|| !api.html5 || !api.support.html5
|| (api.cors && !api.support.cors)
|| (api.media && !api.support.media)
+ || api.insecureChrome
)
&& (function (){
var
@@ -79,7 +78,7 @@
, width: 5
, height: 5
, position: 'absolute'
- , zIndex: 1e6+'' // set max zIndex
+ , zIndex: 2147483647+'' // set max zIndex
});
child.parentNode.insertBefore(dummy, child);
@@ -116,8 +115,8 @@
+ '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
+ (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
+ '&timeout='+api.flashAbortTimeout
- + (opts.camera ? '&useCamera=1' : '')
-// + '&debug=1'
+ + (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
+ + '&debug='+(api.debug?"1":"")
}, opts);
},
@@ -128,7 +127,7 @@
flash.ready = api.F;
flash.isReady = true;
flash.patch();
-
+ flash.patchCamera && flash.patchCamera();
api.event.on(document, 'mouseover', flash.mouseover);
api.event.on(document, 'click', function (evt){
if( flash.mouseover(evt) ){
@@ -159,7 +158,7 @@
mouseover: function (evt){
var target = api.event.fix(evt).target;
- if( /input/i.test(target.nodeName) && target.type == 'file' ){
+ if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
var
state = target.getAttribute(_attr)
, wrapper = flash.getWrapper(target)
@@ -167,7 +166,7 @@
if( api.multiFlash ){
// check state:
- // i — published
+ // p — published
// i — initialization
// r — ready
if( state == 'i' || state == 'r' ){
@@ -188,9 +187,9 @@
_css(dummy, {
top: 0
, left: 0
- , width: target.offsetWidth + 100
- , height: target.offsetHeight + 100
- , zIndex: 1e6+'' // set max zIndex
+ , width: target.offsetWidth
+ , height: target.offsetHeight
+ , zIndex: 2147483647+'' // set max zIndex
, position: 'absolute'
});
@@ -329,8 +328,8 @@
try {
api.log('(js -> flash).'+name+':', data);
return flash.get(id.flashId || id).cmd(name, data);
- } catch (e){
- api.log('(js -> flash).onError:', e);
+ } catch (err){
+ api.log('(js -> flash).onError:', err.toString());
if( !last ){
// try again
setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
@@ -340,8 +339,7 @@
patch: function (){
- api.flashEngine =
- api.support.transform = true;
+ api.flashEngine = true;
// FileAPI
_inherit(api, {
@@ -403,7 +401,7 @@
api.Image && _inherit(api.Image.prototype, {
get: function (fn, scaleMode){
this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
- this.parent(fn);
+ return this.parent(fn);
},
_load: function (file, fn){
@@ -473,6 +471,7 @@
, info = file.info
, matrix = this.getMatrix(info)
;
+ api.log('FlashAPI.Image.toData');
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
@@ -509,81 +508,6 @@
}
});
-
-
- // FileAPI.Camera:statics
- api.Camera.fallback = function (el, options, callback){
- var camId = api.uid();
- api.log('FlashAPI.Camera.publish: ' + camId);
- flash.publish(el, camId, api.extend(options, {
- camera: true,
- onEvent: _wrap(function _(evt){
- if( evt.type == 'camera' ){
- _unwrap(_);
-
- if( evt.error ){
- api.log('FlashAPI.Camera.publish.error: ' + evt.error);
- callback(evt.error);
- }
- else {
- api.log('FlashAPI.Camera.publish.success: ' + camId);
- callback(null);
- }
- }
- })
- }));
- };
-
- // Run
- _each(_cameraQueue, function (args){
- api.Camera.fallback.apply(api.Camera, args);
- });
- _cameraQueue = [];
-
-
- // FileAPI.Camera:proto
- _inherit(api.Camera.prototype, {
- _id: function (){
- return this.video.id;
- },
-
- start: function (callback){
- var _this = this;
- flash.cmd(this._id(), 'camera.on', {
- callback: _wrap(function _(evt){
- _unwrap(_);
-
- if( evt.error ){
- api.log('FlashAPI.camera.on.error: ' + evt.error);
- callback(evt.error, _this);
- }
- else {
- api.log('FlashAPI.camera.on.success: ' + _this._id());
- _this._active = true;
- callback(null, _this);
- }
- })
- });
- },
-
- stop: function (){
- this._active = false;
- flash.cmd(this._id(), 'camera.off');
- },
-
- shot: function (){
- api.log('FlashAPI.Camera.shot:', this._id());
-
- var shot = flash.cmd(this._id(), 'shot', {});
- shot.type = 'image/png';
- shot.flashId = this._id();
- shot.isShot = true;
-
- return new api.Camera.Shot(shot);
- }
- });
-
-
// FileAPI.Form
_inherit(api.Form.prototype, {
toData: function (fn){
@@ -642,7 +566,7 @@
return this.parent.apply(this, arguments);
}
else {
- api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId, files);
+ api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
}
_this.xhr = {
@@ -654,14 +578,14 @@
var queue = api.queue(function (){
flash.cmd(flashId, 'upload', {
- url: _getUrl(options.url)
+ url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
, data: data
, files: fileId ? files : null
, headers: options.headers || {}
, callback: _wrap(function upload(evt){
var type = evt.type, result = evt.result;
- api.log('FlashAPI.upload.'+type+':', evt);
+ api.log('FlashAPI.upload.'+type);
if( type == 'progress' ){
evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
@@ -855,12 +779,6 @@
};
}
-
- api.Camera.fallback = function (){
- _cameraQueue.push(arguments);
- };
-
-
// @export
api.Flash = flash;
diff --git a/lib/FileAPI.Form.js b/lib/FileAPI.Form.js
index ed6efdbb..b89bac37 100644
--- a/lib/FileAPI.Form.js
+++ b/lib/FileAPI.Form.js
@@ -58,7 +58,13 @@
});
this.each(function (file){
- next(file, data, queue, arg);
+ try{
+ next(file, data, queue, arg);
+ }
+ catch( err ){
+ api.log('FileAPI.Form._to: ' + err.message);
+ complete(err);
+ }
});
queue.check();
@@ -73,6 +79,7 @@
api.reset(blob, true);
// set new name
blob.name = file.name;
+ blob.disabled = false;
data.appendChild(blob);
}
else {
diff --git a/lib/FileAPI.Image.js b/lib/FileAPI.Image.js
index ab4f5f12..87b7e500 100644
--- a/lib/FileAPI.Image.js
+++ b/lib/FileAPI.Image.js
@@ -1,17 +1,20 @@
/*global window, FileAPI, document */
-(function (api, document, undef){
+(function (api, document, undef) {
'use strict';
var
min = Math.min,
round = Math.round,
- getCanvas = function (){ return document.createElement('canvas'); },
+ getCanvas = function () { return document.createElement('canvas'); },
support = false,
exifOrientation = {
8: 270
, 3: 180
, 6: 90
+ , 7: 270
+ , 4: 180
+ , 5: 90
}
;
@@ -32,6 +35,8 @@
}
this.file = file;
+ this.size = file.size || 100;
+
this.matrix = {
sx: 0,
sy: 0,
@@ -67,13 +72,13 @@
return this.set({ sx: x, sy: y, sw: w, sh: h || w });
},
- resize: function (w, h, type){
- if( typeof h == 'string' ){
- type = h;
+ resize: function (w, h, strategy){
+ if( /min|max|height|width/.test(h) ){
+ strategy = h;
h = w;
}
- return this.set({ dw: w, dh: h, resize: type });
+ return this.set({ dw: w, dh: h || w, resize: strategy });
},
preview: function (w, h){
@@ -125,14 +130,17 @@
, copy // canvas copy
, buffer = image
, overlay = m.overlay
- , queue = api.queue(function (){ fn(false, canvas); })
+ , queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
, renderImageToCanvas = api.renderImageToCanvas
;
+ // Normalize angle
+ deg = deg - Math.floor(deg/360)*360;
+
// For `renderImageToCanvas`
image._type = this.file.type;
- while( min(w/dw, h/dh) > 2 ){
+ while(m.multipass && min(w/dw, h/dh) > 2 ){
w = (w/2 + 0.5)|0;
h = (h/2 + 0.5)|0;
@@ -159,7 +167,7 @@
canvas.quality = m.quality;
ctx.rotate(deg * Math.PI / 180);
- renderImageToCanvas(canvas, buffer
+ renderImageToCanvas(ctx.canvas, buffer
, m.sx, m.sy
, m.sw || buffer.width
, m.sh || buffer.height
@@ -167,7 +175,6 @@
, (deg == 90 || deg == 180 ? -dh : 0)
, dw, dh
);
-
dw = canvas.width;
dh = canvas.height;
@@ -225,10 +232,10 @@
, dw = m.dw = m.dw || sw
, dh = m.dh = m.dh || sh
, sf = sw/sh, df = dw/dh
- , type = m.resize
+ , strategy = m.resize
;
- if( type == 'preview' ){
+ if( strategy == 'preview' ){
if( dw != sw || dh != sh ){
// Make preview
var w, h;
@@ -249,12 +256,18 @@
}
}
}
- else if( type ){
+ else if( strategy == 'height' ){
+ dw = dh * sf;
+ }
+ else if( strategy == 'width' ){
+ dh = dw / sf;
+ }
+ else if( strategy ){
if( !(sw > dw || sh > dh) ){
dw = sw;
dh = sh;
}
- else if( type == 'min' ){
+ else if( strategy == 'min' ){
dw = round(sf < df ? min(sw, dw) : dh*sf);
dh = round(sf < df ? dw/sf : min(sh, dh));
}
@@ -268,7 +281,7 @@
m.sh = sh;
m.dw = dw;
m.dh = dh;
-
+ m.multipass = api.multiPassResize;
return m;
},
@@ -278,7 +291,12 @@
fn(err);
}
else {
- this._apply(image, fn);
+ try {
+ this._apply(image, fn);
+ } catch (err){
+ api.log('[err] FileAPI.Image.fn._apply:', err);
+ fn(err);
+ }
}
});
},
@@ -302,11 +320,13 @@
else {
fn('not_support_transform');
}
+
+ return this;
},
toData: function (fn){
- this.get(fn);
+ return this.get(fn);
}
};
@@ -328,13 +348,13 @@
if( !err ){
api.each(transform, function (params, name){
if( !queue.isFail() ){
- var ImgTrans = new Image(img.nodeType ? img : file);
+ var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
- if( typeof params == 'function' ){
+ if( isFn ){
params(img, ImgTrans);
}
else if( params.width ){
- ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.type);
+ ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
}
else {
if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
@@ -351,13 +371,16 @@
params.rotate = 'auto';
}
- ImgTrans.set({
- deg: params.rotate
- , type: params.type || file.type || 'image/png'
- , quality: params.quality || 1
- , overlay: params.overlay
- , filter: params.filter
- });
+ ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' });
+
+ if( !isFn ){
+ ImgTrans.set({
+ deg: params.rotate
+ , overlay: params.overlay
+ , filter: params.filter
+ , quality: params.quality || 1
+ });
+ }
queue.inc();
ImgTrans.toData(function (err, image){
@@ -456,8 +479,12 @@
* For load-image-ios.js
*/
api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
- canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
- return canvas;
+ try {
+ return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
+ } catch (ex) {
+ api.log('renderImageToCanvas failed');
+ throw ex;
+ }
};
diff --git a/lib/FileAPI.XHR.js b/lib/FileAPI.XHR.js
index 24eaba61..ba4a158d 100644
--- a/lib/FileAPI.XHR.js
+++ b/lib/FileAPI.XHR.js
@@ -62,7 +62,7 @@
this.end(0, 'abort');
if( this.xhr ){
- this.xhr.aborted = true;
+ this.xhr.aborted = true;
this.xhr.abort();
}
},
@@ -71,14 +71,19 @@
var _this = this, options = this.options;
FormData.toData(function (data){
- // Start uploading
- options.upload(options, _this);
- _this._send.call(_this, options, data);
+ if( data instanceof Error ){
+ _this.end(0, data.message);
+ }
+ else{
+ // Start uploading
+ options.upload(options, _this);
+ _this._send.call(_this, options, data);
+ }
}, options);
},
_send: function (options, data){
- var _this = this, xhr, uid = _this.uid, url = options.url;
+ var _this = this, xhr, uid = _this.uid, onLoadFnName = _this.uid + "Load", url = options.url;
api.log('XHR._send:', data);
@@ -88,31 +93,77 @@
}
if( data.nodeName ){
+ var jsonp = options.jsonp;
+
+ // prepare callback in GET
+ url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
+
// legacy
options.upload(options, _this);
- xhr = document.createElement('div');
- xhr.innerHTML = ''
+ var
+ onPostMessage = function (evt){
+ if( ~url.indexOf(evt.origin) ){
+ try {
+ var result = api.parseJSON(evt.data);
+ if( result.id == uid ){
+ complete(result.status, result.statusText, result.response);
+ }
+ } catch( err ){
+ complete(0, err.message);
+ }
+ }
+ },
+
+ // jsonp-callack
+ complete = window[uid] = function (status, statusText, response){
+ _this.readyState = 4;
+ _this.responseText = response;
+ _this.end(status, statusText);
+
+ api.event.off(window, 'message', onPostMessage);
+ window[uid] = xhr = transport = window[onLoadFnName] = null;
+ }
;
_this.xhr.abort = function (){
- var transport = xhr.getElementsByTagName('iframe')[0];
- if( transport ){
- try {
- if( transport.stop ){ transport.stop(); }
- else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
- else { transport.contentWindow.document.execCommand('Stop'); }
- }
- catch (er) {}
+ try {
+ if( transport.stop ){ transport.stop(); }
+ else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
+ else { transport.contentWindow.document.execCommand('Stop'); }
+ }
+ catch (er) {}
+ complete(0, "abort");
+ };
+
+ api.event.on(window, 'message', onPostMessage);
+
+ window[onLoadFnName] = function (){
+ try {
+ var
+ win = transport.contentWindow
+ , doc = win.document
+ , result = win.result || api.parseJSON(doc.body.innerHTML)
+ ;
+ complete(result.status, result.statusText, result.response);
+ } catch (e){
+ api.log('[transport.onload]', e);
}
- xhr = null;
};
- // append form-data
- var form = xhr.getElementsByTagName('form')[0];
+ xhr = document.createElement('div');
+ xhr.innerHTML = ''
+ ;
+
+ // get form-data & transport
+ var
+ form = xhr.getElementsByTagName('form')[0]
+ , transport = xhr.getElementsByTagName('iframe')[0]
+ ;
+
form.appendChild(data);
api.log(form.parentNode.innerHTML);
@@ -123,20 +174,19 @@
// keep a reference to node-transport
_this.xhr.node = xhr;
- // jsonp-callack
- window[uid] = function (status, statusText, response){
- _this.readyState = 4;
- _this.responseText = response;
- _this.end(status, statusText);
- xhr = null;
- };
-
// send
_this.readyState = 2; // loaded
- form.submit();
+ try {
+ form.submit();
+ } catch (err) {
+ api.log('iframe.error: ' + err);
+ }
form = null;
}
else {
+ // Clean url
+ url = url.replace(/([a-z]+)=(\?)&?/i, '');
+
// html5
if (this.xhr && this.xhr.aborted) {
api.log("Error: already aborted");
@@ -145,7 +195,7 @@
xhr = _this.xhr = api.getXHR();
if (data.params) {
- url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
+ url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
}
xhr.open('POST', url, true);
@@ -162,13 +212,13 @@
xhr.setRequestHeader(key, val);
});
-
+
if ( options._chunked ) {
// chunked upload
if( xhr.upload ){
- xhr.upload.addEventListener('progress', function (/**Event*/evt){
+ xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
if (!data.retry) {
- // show progress only for correct chunk uploads
+ // show progress only for correct chunk uploads
options.progress({
type: evt.type
, total: data.size
@@ -176,7 +226,7 @@
, totalSize: data.size
}, _this, options);
}
- }, false);
+ }, 100), false);
}
xhr.onreadystatechange = function (){
@@ -191,9 +241,9 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
-
+
if (!xhr.status || xhr.status - 201 > 0) {
- api.log("Error: " + xhr.status);
+ api.log("Error: " + xhr.status);
// some kind of error
// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
// up - server error
@@ -202,7 +252,7 @@
// only applicable for recoverable error codes 500 && 416
var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
- // inform about recoverable problems
+ // inform about recoverable problems
options.pause(data.file, options);
// smart restart if server reports about the last known byte
@@ -211,10 +261,13 @@
data.end = lkb;
} else {
data.end = data.start - 1;
+ if (416 == xhr.status) {
+ data.end = data.end - options.chunkSize;
+ }
}
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, delay);
} else {
// no mo retries
@@ -238,28 +291,37 @@
data.file.FileAPIReadPosition = data.end;
setTimeout(function () {
- _this._send(options, data);
+ _this._send(options, data);
}, 0);
}
}
+
xhr = null;
}
};
data.start = data.end + 1;
- data.end = Math.max(Math.min(data.start + options.chunkSize, data.size ) - 1, data.start);
-
- var slice;
- (slice = 'slice') in data.file || (slice = 'mozSlice') in data.file || (slice = 'webkitSlice') in data.file;
-
- xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
- xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
- xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
-
- slice = data.file[slice](data.start, data.end + 1);
-
- xhr.send(slice);
- slice = null;
+ data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
+
+ // Retrieve a slice of file
+ var
+ file = data.file
+ , slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
+ ;
+
+ if( data.size && !slice.size ){
+ setTimeout(function (){
+ _this.end(-1);
+ });
+ } else {
+ xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
+ xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
+ xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
+
+ xhr.send(slice);
+ }
+
+ file = slice = null;
} else {
// single piece upload
if( xhr.upload ){
@@ -268,7 +330,7 @@
options.progress(evt, _this, options);
}, 100), false);
}
-
+
xhr.onreadystatechange = function (){
_this.status = xhr.status;
_this.statusText = xhr.statusText;
@@ -279,7 +341,28 @@
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
- _this.end(xhr.status);
+
+ if (!xhr.status || xhr.status > 201) {
+ api.log("Error: " + xhr.status);
+ if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
+ options.retry = (options.retry || 0) + 1;
+ var delay = api.networkDownRetryTimeout;
+
+ // inform about recoverable problems
+ options.pause(options.file, options);
+
+ setTimeout(function () {
+ _this._send(options, data);
+ }, delay);
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+ } else {
+ //success
+ _this.end(xhr.status);
+ }
+
xhr = null;
}
};
@@ -287,19 +370,19 @@
if( api.isArray(data) ){
// multipart
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
- data = data.join('') +'--_'+ api.expando +'--';
+ var rawData = data.join('') +'--_'+ api.expando +'--';
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
if( xhr.sendAsBinary ){
- xhr.sendAsBinary(data);
+ xhr.sendAsBinary(rawData);
}
else {
- var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
+ var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
xhr.send(new Uint8Array(bytes).buffer);
}
} else {
- // FormData
+ // FormData
xhr.send(data);
}
}
diff --git a/lib/FileAPI.core.js b/lib/FileAPI.core.js
index 7ac0b0a1..f7bfbd08 100644
--- a/lib/FileAPI.core.js
+++ b/lib/FileAPI.core.js
@@ -11,6 +11,9 @@
document = window.document,
doctype = document.doctype || {},
userAgent = window.navigator.userAgent,
+ safari = /safari\//i.test(userAgent) && !/chrome\//i.test(userAgent),
+ iemobile = /iemobile\//i.test(userAgent),
+ insecureChrome = !safari && /chrome\//i.test(userAgent) && window.location.protocol === 'http:',
// https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48
apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL),
@@ -25,12 +28,14 @@
jQuery = window.jQuery,
html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary)))
- && !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
+ && !(safari && /windows/i.test(userAgent) && !iemobile), // BugFix: https://github.com/mailru/FileAPI/issues/25
cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
-
+
chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
+ normalize = ('' + ''.normalize).indexOf('[native code]') > 0,
+
// https://github.com/blueimp/JavaScript-Canvas-to-Blob
dataURLtoBlob = window.dataURLtoBlob,
@@ -41,6 +46,10 @@
_rinput = /input/i,
_rdata = /^data:[^,]+,/,
+ _toString = {}.toString,
+ _supportConsoleLog,
+ _supportConsoleLogApply,
+
Math = window.Math,
@@ -107,11 +116,11 @@
_elEvents[uid] = {};
}
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.add(el, type, fn);
- }
- else {
+ } else {
if( !_elEvents[uid][type] ){
_elEvents[uid][type] = [];
}
@@ -134,8 +143,9 @@
if( el ){
var uid = api.uid(el), events = _elEvents[uid] || {};
+ var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
- if( jQuery ){
+ if( jQuery && !isFileReader){
jQuery.event.remove(el, type, fn);
}
else {
@@ -183,12 +193,14 @@
* FileAPI (core object)
*/
api = {
- version: '2.0.1',
+ version: '2.0.25',
cors: false,
html5: true,
media: false,
formData: true,
+ multiPassResize: true,
+ insecureChrome: insecureChrome,
debug: false,
pingUrl: false,
@@ -218,6 +230,9 @@
, 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
},
+ uploadRetry : 0,
+ networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
+
chunkSize : 0,
chunkUploadRetry : 0,
chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
@@ -227,6 +242,8 @@
GB: _SIZE_CONST(3),
TB: _SIZE_CONST(4),
+ EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
+
expando: 'fileapi' + (new Date).getTime(),
uid: function (obj){
@@ -237,8 +254,8 @@
},
log: function (){
- if( api.debug && window.console && console.log ){
- if( console.log.apply ){
+ if( api.debug && _supportConsoleLog ){
+ if( _supportConsoleLogApply ){
console.log.apply(console, arguments);
}
else {
@@ -455,13 +472,22 @@
/**
- * Is file instance
- *
+ * Is file?
* @param {File} file
* @return {Boolean}
*/
isFile: function (file){
- return html5 && file && (file instanceof File);
+ return _toString.call(file) === '[object File]';
+ },
+
+
+ /**
+ * Is blob?
+ * @param {Blob} blob
+ * @returns {Boolean}
+ */
+ isBlob: function (blob) {
+ return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
},
@@ -590,7 +616,7 @@
* @param {Boolean} [progress]
*/
readAsImage: function (file, fn, progress){
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
if( apiURL ){
/** @namespace apiURL.createObjectURL */
var data = apiURL.createObjectURL(file);
@@ -683,28 +709,84 @@
getDropFiles: function (evt, callback){
var
files = []
+ , all = []
+ , items
, dataTransfer = _getDataTransfer(evt)
- , entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
- , queue = api.queue(function (){ callback(files); })
+ , transFiles = dataTransfer.files
+ , transItems = dataTransfer.items
+ , entrySupport = _isArray(transItems) && transItems[0] && _getAsEntry(transItems[0])
+ , queue = api.queue(function (){ callback(files, all); })
;
- _each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
+ if( entrySupport ){
+ if( normalize && transFiles ){
+ var
+ i = transFiles.length
+ , file
+ , entry
+ ;
+
+ items = new Array(i);
+ while( i-- ){
+ file = transFiles[i];
+
+ try {
+ entry = _getAsEntry(transItems[i]);
+ }
+ catch( err ){
+ api.log('[err] getDropFiles: ', err);
+ entry = null;
+ }
+
+ if( _isEntry(entry) ){
+ // OSX filesystems use Unicode Normalization Form D (NFD),
+ // and entry.file(…) can't read the files with the same names
+ if( entry.isDirectory || (entry.isFile && file.name == file.name.normalize('NFC')) ){
+ items[i] = entry;
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ else {
+ items[i] = file;
+ }
+ }
+ }
+ else {
+ items = transItems;
+ }
+ }
+ else {
+ items = transFiles;
+ }
+
+ _each(items || [], function (item){
queue.inc();
try {
- if( entrySupport ){
- _readEntryAsFiles(item, function (err, entryFiles){
+ if( entrySupport && _isEntry(item) ){
+ _readEntryAsFiles(item, function (err, entryFiles, allEntries){
if( err ){
api.log('[err] getDropFiles:', err);
} else {
files.push.apply(files, entryFiles);
}
+ all.push.apply(all, allEntries);
+
queue.next();
});
}
else {
- _isRegularFile(item, function (yes){
- yes && files.push(item);
+ _isRegularFile(item, function (yes, err){
+ if( yes ){
+ files.push(item);
+ }
+ else {
+ item.error = err;
+ }
+ all.push(item);
+
queue.next();
});
}
@@ -815,7 +897,7 @@
getInfo: function (file, fn){
var info = {}, readers = _infoReader.concat();
- if( api.isFile(file) ){
+ if( api.isBlob(file) ){
(function _next(){
var reader = readers.shift();
if( reader ){
@@ -913,7 +995,8 @@
upload: function (options){
options = _extend({
- prepare: api.F
+ jsonp: 'callback'
+ , prepare: api.F
, beforeupload: api.F
, upload: api.F
, fileupload: api.F
@@ -924,7 +1007,8 @@
, pause: api.F
, imageOriginal: true
, chunkSize: api.chunkSize
- , chunkUpoloadRetry: api.chunkUploadRetry
+ , chunkUploadRetry: api.chunkUploadRetry
+ , uploadRetry: api.uploadRetry
}, options);
@@ -980,14 +1064,18 @@
}
if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
- // Mark active job
- _complete = false;
+ // Mark active job
+ _complete = false;
// Set current upload file
proxyXHR.currentFile = _file;
// Prepare file options
- _file && options.prepare(_file, _fileOptions);
+ if (_file && options.prepare(_file, _fileOptions) === false) {
+ _nextFile.call(_this);
+ return;
+ }
+ _fileOptions.file = _file;
_this._getFormData(_fileOptions, data, function (form){
if( !_loaded ){
@@ -1004,6 +1092,9 @@
progress: _file ? function (evt){
if( !_fileLoaded ){
+ // For ignore the double calls.
+ _fileLoaded = (evt.loaded === evt.total);
+
// emit "fileprogress" event
options.fileprogress({
type: 'progress'
@@ -1015,35 +1106,38 @@
options.progress({
type: 'progress'
, total: _total
- , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
+ , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total)) || 0
}, _file, xhr, _fileOptions);
}
} : noop,
complete: function (err){
- // fixed throttle event
- _fileLoaded = true;
-
_each(_xhrPropsExport, function (name){
proxyXHR[name] = xhr[name];
});
if( _file ){
+ data.total = (data.total || data.size);
data.loaded = data.total;
- // emulate 100% "progress"
- this.progress(data);
+ if( !err ) {
+ // emulate 100% "progress"
+ this.progress(data);
+
+ // fixed throttle event
+ _fileLoaded = true;
- // bytes loaded
- _loaded += data.size; // data.size != data.total, it's desirable fix this
- proxyXHR.loaded = _loaded;
+ // bytes loaded
+ _loaded += data.size; // data.size != data.total, it's desirable fix this
+ proxyXHR.loaded = _loaded;
+ }
// emit "filecomplete" event
options.filecomplete(err, xhr, _file, _fileOptions);
}
// upload next file
- _nextFile.call(_this);
+ setTimeout(function () {_nextFile.call(_this);}, 0);
}
})); // xhr
@@ -1060,7 +1154,8 @@
});
}
else {
- options.complete(proxyXHR.status == 200 || proxyXHR.status == 201 ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
+ var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
+ options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
// Mark done state
_complete = true;
}
@@ -1165,12 +1260,30 @@
, postNameConcat = api.postNameConcat
;
+ // Append data
+ _each(options.data, function add(val, name){
+ if( typeof val == 'object' ){
+ _each(val, function (v, i){
+ add(v, postNameConcat(name, i));
+ });
+ }
+ else {
+ Form.append(name, val);
+ }
+ });
+
(function _addFile(file/**Object*/){
if( file.image ){ // This is a FileAPI.Image
queue.inc();
file.toData(function (err, image){
- // @todo: error
+ // @todo: требует рефакторинга и обработки ошибки
+ if (file.file) {
+ image.type = file.file.type;
+ image.quality = file.matrix.quality;
+ filename = file.file && file.file.name;
+ }
+
filename = filename || (new Date).getTime()+'.png';
_addFile(image);
@@ -1224,19 +1337,6 @@
}
})(file);
-
- // Append data
- _each(options.data, function add(val, name){
- if( typeof val == 'object' ){
- _each(val, function (v, i){
- add(v, postNameConcat(name, i));
- });
- }
- else {
- Form.append(name, val);
- }
- });
-
queue.check();
},
@@ -1354,7 +1454,7 @@
} // api
;
-
+
function _emit(target, fn, name, res, ext){
var evt = {
@@ -1367,13 +1467,13 @@
}
- function _hasSupportReadAs(as){
- return FileReader && !!FileReader.prototype['readAs'+as];
+ function _hasSupportReadAs(method){
+ return FileReader && !!FileReader.prototype['readAs' + method];
}
- function _readAs(file, fn, as, encoding){
- if( api.isFile(file) && _hasSupportReadAs(as) ){
+ function _readAs(file, fn, method, encoding){
+ if( api.isBlob(file) && _hasSupportReadAs(method) ){
var Reader = new FileReader;
// Add event listener
@@ -1395,10 +1495,10 @@
try {
// ReadAs ...
if( encoding ){
- Reader['readAs'+as](file, encoding);
+ Reader['readAs' + method](file, encoding);
}
else {
- Reader['readAs'+as](file);
+ Reader['readAs' + method](file);
}
}
catch (err){
@@ -1406,33 +1506,38 @@
}
}
else {
- _emit(file, fn, 'error', undef, { error: 'filreader_not_support_'+as });
+ _emit(file, fn, 'error', undef, { error: 'filreader_not_support_' + method });
}
}
function _isRegularFile(file, callback){
// http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects
- if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){
+ if( !file.type && (safari || ((file.size % 4096) === 0 && (file.size <= 102400))) ){
if( FileReader ){
try {
- var Reader = new FileReader();
+ var reader = new FileReader();
- _one(Reader, _readerEvents, function (evt){
+ _one(reader, _readerEvents, function (evt){
var isFile = evt.type != 'error';
- callback(isFile);
if( isFile ){
- Reader.abort();
+ if ( reader.readyState == null || reader.readyState === reader.LOADING ) {
+ reader.abort();
+ }
+ callback(isFile);
+ }
+ else {
+ callback(false, reader.error);
}
});
- Reader.readAsDataURL(file);
+ reader.readAsDataURL(file);
} catch( err ){
- callback(false);
+ callback(false, err);
}
}
else {
- callback(null);
+ callback(null, new Error('FileReader is not supported'));
}
}
else {
@@ -1441,6 +1546,11 @@
}
+ function _isEntry(item){
+ return item && (item.isFile || item.isDirectory);
+ }
+
+
function _getAsEntry(item){
var entry;
if( item.getAsEntry ){ entry = item.getAsEntry(); }
@@ -1452,45 +1562,68 @@
function _readEntryAsFiles(entry, callback){
if( !entry ){
// error
- callback('invalid entry');
+ var err = new Error('invalid entry');
+ entry = new Object(entry);
+ entry.error = err;
+ callback(err.message, [], [entry]);
}
else if( entry.isFile ){
// Read as file
- entry.file(function(file){
+ entry.file(function (file){
// success
file.fullPath = entry.fullPath;
- callback(false, [file]);
+ callback(false, [file], [file]);
}, function (err){
// error
- callback('FileError.code: '+err.code);
+ entry.error = err;
+ callback('FileError.code: ' + err.code, [], [entry]);
});
}
else if( entry.isDirectory ){
- var reader = entry.createReader(), result = [];
+ var
+ reader = entry.createReader()
+ , firstAttempt = true
+ , files = []
+ , all = [entry]
+ ;
+
+ var onerror = function (err){
+ // error
+ entry.error = err;
+ callback('DirectoryError.code: ' + err.code, files, all);
+ };
+ var ondone = function ondone(entries){
+ if( firstAttempt ){
+ firstAttempt = false;
+ if( !entries.length ){
+ entry.error = new Error('directory is empty');
+ }
+ }
- reader.readEntries(function(entries){
// success
- api.afor(entries, function (next, entry){
- _readEntryAsFiles(entry, function (err, files){
- if( err ){
- api.log(err);
- }
- else {
- result = result.concat(files);
- }
+ if( entries.length ){
+ api.afor(entries, function (next, entry){
+ _readEntryAsFiles(entry, function (err, entryFiles, allEntries){
+ if( !err ){
+ files = files.concat(entryFiles);
+ }
+ all = all.concat(allEntries);
- if( next ){
- next();
- }
- else {
- callback(false, result);
- }
+ if( next ){
+ next();
+ }
+ else {
+ reader.readEntries(ondone, onerror);
+ }
+ });
});
- });
- }, function (err){
- // error
- callback('directory_reader: ' + err);
- });
+ }
+ else {
+ callback(false, files, all);
+ }
+ };
+
+ reader.readEntries(ondone, onerror);
}
else {
_readEntryAsFiles(_getAsEntry(entry), callback);
@@ -1544,6 +1677,7 @@
width: img.width
, height: img.height
});
+ img.src = api.EMPTY_PNG;
img = null;
});
}
@@ -1568,7 +1702,8 @@
}
if( FileReader ){
- _on(el, 'dragenter dragleave dragover', function (evt){
+ // Hover
+ _on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
var
types = _getDataTransfer(evt).types
, i = types && types.length
@@ -1601,15 +1736,18 @@
}
});
- _on(el, 'drop', function (evt){
+
+ // Drop
+ _on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){
evt[preventDefault]();
_type = 0;
- onHover.call(evt[currentTarget], false, evt);
- api.getDropFiles(evt, function (files){
- onDrop.call(evt[currentTarget], files, evt);
+ api.getDropFiles(evt, function (files, all){
+ onDrop.call(evt[currentTarget], files, all, evt);
});
+
+ onHover.call(evt[currentTarget], false, evt);
});
}
else {
@@ -1625,8 +1763,8 @@
* @param {Function} onDrop
*/
api.event.dnd.off = function (el, onHover, onDrop){
- _off(el, 'dragenter dragleave dragover', onHover);
- _off(el, 'drop', onDrop);
+ _off(el, 'dragenter dragleave dragover', onHover.ff);
+ _off(el, 'drop', onDrop.ff);
};
@@ -1663,7 +1801,14 @@
});
- // @configuration
+ // Configuration
+ try {
+ _supportConsoleLog = !!console.log;
+ _supportConsoleLogApply = !!console.log.apply;
+ }
+ catch (err) {}
+
if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
+ if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
})(window, void 0);
diff --git a/node/file-api.js b/node/file-api.js
new file mode 100644
index 00000000..5d9fd6c5
--- /dev/null
+++ b/node/file-api.js
@@ -0,0 +1,62 @@
+var fs = require('fs');
+var qs = require('qs');
+var imageSize = require('image-size');
+
+function convertToBase64(buffer, mimetype) {
+ return 'data:' + mimetype + ';base64,' + buffer.toString('base64');
+}
+
+function fileApi() {
+ return function (req, res, next) {
+ var queryString = '';
+
+ req.files = {};
+ req.images = {};
+
+ req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
+ var buffersArray = [];
+
+ file.on('data', function (data) {
+ buffersArray.push(data);
+ });
+
+ file.on('end', function () {
+ var bufferResult = Buffer.concat(buffersArray);
+ var fileObj = {
+ name: filename,
+ type: mimetype,
+ mime: mimetype,
+ size: bufferResult.length,
+ dataURL: convertToBase64(bufferResult, mimetype)
+ };
+
+ req.files[fieldname] = fileObj;
+
+ if (mimetype.indexOf('image/') === 0) {
+ fs.writeFileSync(filename, bufferResult);
+
+ var size = imageSize(filename);
+
+ fileObj.width = size.width;
+ fileObj.height = size.height;
+
+ req.images[fieldname] = fileObj;
+
+ fs.unlinkSync(filename);
+ }
+ });
+ });
+
+ req.busboy.on('field', function (key, value) {
+ queryString += encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
+ });
+
+ req.busboy.on('finish', function () {
+ req.body = qs.parse(queryString);
+
+ next();
+ });
+ };
+}
+
+module.exports = fileApi;
diff --git a/node/server.js b/node/server.js
new file mode 100644
index 00000000..119dddae
--- /dev/null
+++ b/node/server.js
@@ -0,0 +1,59 @@
+var express = require('express');
+var busboy = require('connect-busboy');
+var fileApi = require('./file-api');
+var app = express();
+
+app.use(express.static('.', {index: 'index.html'}));
+
+app.use(function (req, res, next) {
+ // Enable CORS for non static files
+ var origin = req.get('Origin');
+
+ if (origin) {
+ res.set({
+ 'Access-Control-Allow-Origin': origin,
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, X-Foo, X-Rnd',
+ 'Access-Control-Allow-Credentials': 'true'
+ });
+ }
+ next();
+});
+
+var uploadPath = '/upload';
+
+app.options(uploadPath, function (req, res) {
+ res.end();
+});
+
+app.post(
+ uploadPath,
+ busboy({immediate: true}), // parse post data
+ fileApi(), // prepare req.body, req.files and req.images
+ function (req, res) {
+ var jsonp = req.query.callback || null;
+
+ res[jsonp ? 'jsonp' : 'json']({
+ status: 200,
+ statusText: 'OK',
+ images: req.images,
+ data: {
+ HEADERS: req.headers,
+ _REQUEST: req.body,
+ _FILES: req.files
+ }
+ });
+ }
+);
+
+// Export
+module.exports.createServer = function (port, callback) {
+ var server = app.listen(port, function () {
+ var host = server.address().address;
+ var port = server.address().port;
+
+ console.log('Test server listening at http://%s:%s', host, port);
+
+ callback(server);
+ });
+};
diff --git a/package.json b/package.json
index 39f63cf8..f296715b 100644
--- a/package.json
+++ b/package.json
@@ -1,39 +1,61 @@
{
- "name": "fileapi",
- "version": "2.0.1",
- "devDependencies": {
- "grunt": "~0.4.0",
- "grunt-version": "*",
- "grunt-contrib-jshint": "~0.2.0",
- "grunt-contrib-concat": "*",
- "grunt-contrib-uglify": "*",
- "grunt-contrib-watch": "*",
-
- "eventemitter2": "~0.4.9",
- "semver": "~1.0.14",
- "temporary": "~0.0.4",
- "phantomjs": "latest"
- },
- "description": "FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.",
- "main": "dist/FileAPI.js",
- "scripts": {
- "test": "grunt test"
- },
- "repository": {
- "type": "git",
- "url": "git://github.com/mailru/FileAPI.git"
- },
- "keywords": [
- "FileAPI",
- "upload",
- "file",
- "html5",
- "chunked"
- ],
- "author": "Konstantin Lebedev ",
- "contributors": [
- "Vladimir Demidov ",
- "Ilya Lebedev "
- ],
- "license": "BSD"
+ "name": "fileapi",
+ "exportName": "FileAPI",
+ "version": "2.0.25",
+ "devDependencies": {
+ "connect-busboy": "~0.0.2",
+ "eventemitter2": "~0.4.13",
+ "express": "~4.12.3",
+ "grunt": "^0.4.5",
+ "grunt-contrib-compress": "~0.9.1",
+ "grunt-contrib-concat": "~0.4.0",
+ "grunt-contrib-connect": "~0.8.0",
+ "grunt-contrib-jshint": "~0.10.0",
+ "grunt-contrib-uglify": "~0.5.0",
+ "grunt-contrib-watch": "~0.6.1",
+ "grunt-curl": "~2.0.2",
+ "grunt-mxmlc": "~0.5.2",
+ "grunt-version": "~0.3.0",
+ "image-size": "~0.3.5",
+ "phantomjs": "~1.9.7-9",
+ "qs": "~2.4.1",
+ "semver": "~5.0.0",
+ "temporary": "~0.0.8"
+ },
+ "peerDependencies": {},
+ "description": "FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.",
+ "main": "dist/FileAPI.js",
+ "files": [
+ "dist",
+ "plugins",
+ "*.*"
+ ],
+ "jam": {
+ "name": "FileAPI"
+ },
+ "scripts": {
+ "test": "grunt tests --verbose",
+ "build": "grunt build"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mailru/FileAPI.git"
+ },
+ "keywords": [
+ "FileAPI",
+ "upload",
+ "file",
+ "html5",
+ "chunked"
+ ],
+ "author": "Konstantin Lebedev ",
+ "contributors": [
+ "Vladimir Demidov ",
+ "Ilya Lebedev ",
+ "Mikhail Bezoyan "
+ ],
+ "license": "BSD",
+ "dependencies": {
+ "grunt-cli": "^1.3.2"
+ }
}
diff --git a/plugins/FileAPI.exif.js b/plugins/FileAPI.exif.js
index 7ae3f28c..0dd92d00 100644
--- a/plugins/FileAPI.exif.js
+++ b/plugins/FileAPI.exif.js
@@ -40,10 +40,20 @@ FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){
if( !file.__exif ){
var defer = file.__exif = FileAPI.defer();
- FileAPI.readAsBinaryString(file, function (evt){
+ var blob = file;
+ if (blob instanceof Blob && blob.size > 128*1024) {
+ try {
+ var size = Math.min(blob.size, 128 * 1024);
+ blob = (blob.slice || blob.mozSlice || blob.webkitSlice).call(blob, 0, size);
+ } catch (e) {
+ FileAPI.log("exception "+ e);
+ }
+ }
+
+ FileAPI.readAsBinaryString(blob, function (evt){
if( evt.type == 'load' ){
var binaryString = evt.result;
- var oFile = new BinaryFile(binaryString, 0, file.size);
+ var oFile = new BinaryFile(binaryString, 0, blob.size);
var exif = EXIF.readFromBinaryFile(oFile);
defer.resolve(false, { 'exif': exif || {} });
diff --git a/plugins/jquery.fileapi.js b/plugins/jquery.fileapi.js
deleted file mode 100644
index cb74ca3e..00000000
--- a/plugins/jquery.fileapi.js
+++ /dev/null
@@ -1,966 +0,0 @@
-/**
- * jQuery plugin for FileAPI v2+
- * @auhtor RubaXa
- */
-
-/*jshint evil: true*/
-/*global jQuery, FileAPI*/
-(function ($, api){
- "use strict";
-
- var
- noop = $.noop
- , oldJQ = !$.fn.prop
- , propFn = oldJQ ? 'attr' : 'prop'
-
- , _dataAttr = 'data-fileapi'
- , _dataFileId = 'data-fileapi-id'
- ;
-
-
-
- var Plugin = function (el, options){
- this.$el = el = $(el).on('change.fileapi', $.proxy(this, '_onSelect'));
- this.el = el[0];
-
- this._options = {}; // previous options
- this.options = options = $.extend({
- url: 0,
- data: {}, // additional POST-data
- accept: 0, // accept mime types, "*" — unlimited
- multiple: false, // single or multiple mode upload mode
- paramName: 0, // POST-parameter name
- dataType: 'json',
- duplicate: false, // ignore duplicate
- chunkSize: 0, // or chunk size in bytes, eg: .5 * FileAPI.MB
- chunkUploadRetry: 3, // number of retries during upload chunks (html5)
-
- maxSize: 0, // max file size, 0 — unlimited
- maxFiles: 0, // @todo: max uploaded files
- imageSize: 0, // { minWidth: 320, minHeight: 240, maxWidth: 3840, maxHeight: 2160 }
-
- sortFn: 0,
- filterFn: 0,
- autoUpload: false,
-
- lang: {
- B: 'bytes'
- , KB: 'KB'
- , MB: 'MB'
- , GB: 'GB'
- , TB: 'TB'
- },
- sizeFormat: '0.00',
-
- imageTransform: 0,
-
- elements: {
- ctrl: {
- upload: '[data-fileapi="ctrl.upload"]',
- reset: '[data-fileapi="ctrl.reset"]',
- abort: '[data-fileapi="ctrl.abort"]'
- },
- empty: {
- show: '[data-fileapi="empty.show"]',
- hide: '[data-fileapi="empty.hide"]'
- },
- emptyQueue: {
- show: '[data-fileapi="emptyQueue.show"]',
- hide: '[data-fileapi="emptyQueue.hide"]'
- },
- active: {
- show: '[data-fileapi="active.show"]',
- hide: '[data-fileapi="active.hide"]'
- },
- size: '[data-fileapi="size"]',
- name: '[data-fileapi="name"]',
- progress: '[data-fileapi="progress"]',
- file: {
- tpl: '[data-fileapi="file.tpl"]',
- progress: '[data-fileapi="file.progress"]',
- active: {
- show: '[data-fileapi="active.show"]',
- hide: '[data-fileapi="active.hide"]'
- },
- preview: {
- el: 0,
- get: 0,
- width: 0,
- height: 0,
- processing: 0
- }
- },
- dnd: {
- el: '[data-fileapi="dnd"]',
- hover: 'dnd_hover'
- }
- },
-
- onDrop: noop,
- onDropHover: noop,
-
- onSelect: noop,
-
- onUpload: noop,
- onProgress: noop,
- onComplete: noop,
-
- onFileUpload: noop,
- onFileProgress: noop,
- onFileComplete: noop
- }, options);
-
-
- if( !options.url ){
- var url = this.$el.attr('action') || this.$el.find('form').attr('action');
- if( url ){
- options.url = url;
- } else {
- this._throw('url — is not defined');
- }
- }
-
-
- this.$files = this.elem('list');
- this.itemTplFn = $.fn.fileapi.tpl( $('
').append( this.elem('file.tpl')).html() );
-
-
- api.each(options, function (value, option){
- this._setOption(option, value);
- }, this);
-
-
- this.$el
- .on('reset.fileapi', $.proxy(this, '_onReset'))
- .on('submit.fileapi', $.proxy(this, '_onSubmit'))
- .on('upload.fileapi progress.fileapi complete.fileapi', $.proxy(this, '_onUploadEvent'))
- .on('fileupload.fileapi fileprogress.fileapi filecomplete.fileapi', $.proxy(this, '_onFileUploadEvent'))
- .on('click', '['+_dataAttr+']', $.proxy(this, '_onActionClick'))
- ;
-
-
- // Controls
- var ctrl = options.elements.ctrl;
- if( ctrl ){
- if( ctrl.reset ){
- this.$el.on('click.fileapi', ctrl.reset, $.proxy(this, '_onReset'));
- }
- if( ctrl.upload ){
- this.$el.on('click.fileapi', ctrl.upload, $.proxy(this, '_onSubmit'));
- }
- }
-
- this.elem('dnd.el', true).dnd($.proxy(this, '_onDropHover'), $.proxy(this, '_onDrop'));
- this.$progress = this.elem('progress');
-
- this._crop = {};
- this._rotate = {}; // rotate deg
-
- this.files = []; // all files
- this.uploaded = []; // uploaded files
-
- this.clear();
- };
-
-
- Plugin.prototype = {
- constructor: Plugin,
-
- _throw: function (msg){
- throw "jquery.fileapi: " + msg;
- },
-
- _getFiles: function (evt, fn){
- var
- opts = this.options
- , maxSize = opts.maxSize
- , filterFn = opts.filterFn
- , files = api.getFiles(evt)
- , data = {
- all: files
- , files: []
- , other: []
- , duplicate: opts.duplicate ? [] : this._extractDuplicateFiles(files)
- }
- , imageSize = opts.imageSize
- , _this = this
- ;
-
- if( imageSize || filterFn ){
- api.filterFiles(files, function (file, info){
- var ok = true;
- if( info && imageSize ){
- ok = (!imageSize.minWidth || info.width >= imageSize.minWidth)
- && (!imageSize.minHeight || info.height >= imageSize.minHeight)
- && (!imageSize.maxWidth || info.height <= imageSize.maxWidth)
- && (!imageSize.maxHeight || info.height <= imageSize.maxHeight)
- ;
- }
- return ok && (!maxSize || file.size <= maxSize) && (!filterFn || filterFn(file, info));
- }, function (succes, other){
- data.files = succes;
- data.other = other;
-
- fn.call(_this, data);
- });
- } else {
- api.each(files, function (file){
- data[!maxSize || file.size <= maxSize ? 'files' : 'other'].push(file);
- });
-
- fn.call(_this, data);
- }
- },
-
- _extractDuplicateFiles: function (list/**Array*/){
- var duplicates = [], i = list.length, files = this.files, j;
-
- while( i-- ){
- j = files.length;
- while( j-- ){
- if( this._fileCompare(list[i], files[j]) ){
- duplicates.push( list.splice(i, 1) );
- break;
- }
- }
- }
-
- return duplicates;
- },
-
- _fileCompare: function (A/**File*/, B/**File*/){
- return (A.size == B.size) && (A.name == B.name);
- },
-
- _getFormatedSize: function (size){
- var opts = this.options, postfix = 'B';
-
- if( size >= api.TB ){
- size /= api[postfix = 'TB'];
- }
- else if( size >= api.GB ){
- size /= api[postfix = 'GB'];
- }
- else if( size >= api.MB ){
- size /= api[postfix = 'MB'];
- }
- else if( size >= api.KB ){
- size /= api[postfix = 'KB'];
- }
-
- return opts.sizeFormat.replace(/^\d+([^\d]+)(\d*)/, function (_, separator, fix){
- size = size.toFixed(fix.length);
- return (size + '').replace('.', separator) +' '+ opts.lang[postfix];
- });
- },
-
- _onSelect: function (evt){
- this._getFiles(evt, $.proxy(function (data){
- if( data.all.length && this.emit('select', data) !== false ){
- this.add(data.files);
- }
- }, this));
- },
-
- _onActionClick: function (evt){
- var
- el = evt.currentTarget
- , act = $.attr(el, _dataAttr)
- , $item = $(el).closest('['+_dataFileId+']', this.$el)
- , uid = $item.attr(_dataFileId)
- , prevent = true
- ;
-
- if( 'remove' == act ){
- $item.remove();
- this.queue = api.filter(this.queue, function (file){ return api.uid(file) != uid; });
- this.files = api.filter(this.files, function (file){ return api.uid(file) != uid; });
- this._redraw();
- }
- else if( /^rotate/.test(act) ){
- this.rotate(uid, (/ccw/.test(act) ? '-=90' : '+=90'));
- }
- else {
- prevent = false;
- }
-
- if( prevent ){
- evt.preventDefault();
- }
- },
-
- _onSubmit: function (evt){
- this.upload();
- evt.preventDefault();
- },
-
- _onReset: function (evt){
- this.clear();
- evt.preventDefault();
- },
-
- _onDrop: function (files){
- this._getFiles(files, function (data){
- if( this.emit('drop', data) !== false ){
- this.add(data.files);
- }
- });
- },
-
- _onDropHover: function (state, evt){
- if( this.emit('dropHover', { state: state, event: evt }) !== false ){
- var hover = this.option('elements.dnd.hover');
- if( hover ){
- $(evt.currentTarget).toggleClass(hover, state);
- }
- }
- },
-
- _getUploadEvent: function (extra){
- var xhr = this.xhr, evt = {
- xhr: xhr
- , file: xhr.currentFile
- , files: xhr.files
- , widget: this
- };
- return $.extend(evt, extra);
- },
-
- _emitUploadEvent: function (prefix){
- var evt = this._getUploadEvent();
- this.emit(prefix+'upload', evt);
- },
-
- _emitProgressEvent: function (prefix, event){
- var evt = this._getUploadEvent(event);
- this.emit(prefix+'progress', evt);
- },
-
- _emitCompleteEvent: function (prefix, err){
- var
- xhr = this.xhr
- , evt = this._getUploadEvent({
- error: err
- , status: xhr.status
- , statusText: xhr.statusText
- , result: xhr.responseText
- })
- ;
-
- if( this.options.dataType == 'json' ){
- evt.result = $.parseJSON(evt.result);
- }
-
- this.emit(prefix+'complete', evt);
- },
-
- _onUploadEvent: function (evt, ui){
- var _this = this, $progress = this.$progress, type = evt.type;
-
- if( type == 'progress' ){
- $progress.stop().animate({ width: ui.loaded/ui.total*100 + '%' }, 300);
- }
- else if( type == 'upload' ){
- $progress.width(0);
- }
- else {
- var fn = function (){
- $progress.dequeue();
- _this.clear();
- };
-
- this.xhr = null;
- this.active = false;
-
- if( $progress.length ){
- $progress.queue(fn);
- } else {
- fn();
- }
- }
- },
-
- _onFileUploadPrepare: function (file, opts){
- var
- uid = api.uid(file)
- , deg = this._rotate[uid]
- , crop = this._crop[uid]
- ;
-
- if( deg || crop ){
- var trans = opts.imageTransform = opts.imageTransform || {};
- if( $.isEmptyObject(trans) || parseInt(trans.maxWidth || trans.minWidth || trans.width, 10) > 0 || trans.type || trans.quality ){
- trans.crop = crop;
- trans.rotate = deg;
- }
- else {
- api.each(trans, function (opts){
- opts.crop = crop;
- opts.rotate = deg;
- });
- }
- }
- },
-
- _onFileUploadEvent: function (evt, ui){
- var
- _this = this
- , type = evt.type.substr(4)
- , uid = api.uid(ui.file)
- , $file = this.fileElem(uid)
- , $progress = this._$fileprogress
- ;
-
- if( this.__fileId !== uid ){
- this.__fileId = uid;
- this._$fileprogress = $progress = $file.find(this.option('elements.file.progress'));
- }
-
- if( type == 'progress' ){
- $progress.stop().animate({ width: ui.loaded/ui.total*100 + '%' }, 300);
- }
- else if( type == 'upload' || type == 'complete' ){
- var fn = function (){
- var elem = 'elements.file.'+ type;
-
- if( type == 'upload' ){
- $file.find('['+_dataAttr+'="remove"]').hide();
- $progress.width(0);
- }
-
- $progress.dequeue();
-
- $file.find(_this.option(elem + '.show')).show();
- $file.find(_this.option(elem + '.hide')).hide();
- };
-
- if( $progress.length ){
- $progress.queue(fn);
- } else {
- fn();
- }
-
- if( type == 'complete' ){
- this.uploaded.push(ui.file);
- delete this._rotate[uid];
- }
- }
- },
-
- _redraw: function (){
- var
- active = !!this.active
- , files = this.files
- , empty = !files.length && !active
- , emptyQueue = !this.queue.length && !active
- , name = []
- , size = 0
- , $files = this.$files
- , offset = $files.children().length
- , preview = this.option('elements.file.preview')
- ;
-
-
- api.each(files, function (file, i){
- var uid = api.uid(file);
-
- name.push(file.name);
- size += file.size;
-
- if( $files.length && !this.fileElem(uid).length ){
- var html = this.itemTplFn({
- $idx: offset + i
- , uid: file.uid
- , name: file.name
- , type: file.type
- , size: file.size
- , sizeText: this._getFormatedSize(file.size)
- });
-
- $files.append( $(html).attr(_dataFileId, uid) );
-
- if( preview.el ){
- this._makeFilePreview(uid, file, preview);
- }
- }
- }, this);
-
-
- this.elem('name').text( name.join(', ') );
- this.elem('size').text( this._getFormatedSize(size) );
-
-
- if( this.__empty !== empty ){
- this.__empty = empty;
-
- this.elem('empty.show').toggle( empty );
- this.elem('empty.hide').toggle( !empty );
- }
-
-
- if( this.__emptyQueue !== emptyQueue ){
- this.__emptyQueue = empty;
-
- this.elem('emptyQueue.show').toggle( emptyQueue );
- this.elem('emptyQueue.hide').toggle( !emptyQueue );
- }
-
-
- if( this.__active !== active ){
- this.__active = active;
-
- this.elem('active.show').toggle( active );
- this.elem('active.hide').toggle( !active );
-
- this.$('.js-fileapi-wrapper,:file')
- [active ? 'attr' : 'removeAttr']('aria-disabled', active)
- [propFn]('disabled', active)
- ;
- }
-
-
- // Upload & reset control
- this.elem('ctrl.upload')
- .add( this.elem('ctrl.reset') )
- [empty || active ? 'attr' : 'removeAttr']('aria-disabled', empty || active)
- [propFn]('disabled', empty || active)
- ;
- },
-
- _makeFilePreview: function (uid, file, opts, global){
- var
- _this = this
- , $el = global ? _this.$(opts.el) : _this.fileElem(uid).find(opts.el)
- ;
-
- if( /^image/.test(file.type) ){
- api.log('_makeFilePreview:', uid, file);
-
- var
- image = api.Image(file)
- , doneFn = function (){
- image.get(function (err, img){
- if( !_this._crop[uid] ){
- if( err ){
- opts.get && opts.get($el, file);
- _this.emit('filePreviewError', { error: err, file: file });
- } else {
- $el.html(img);
- }
- }
- });
- }
- ;
-
- if( opts.width ){
- image.preview(opts.width, opts.height);
- }
-
- if( opts.rotate ){
- image.rotate(opts.rotate);
- }
-
- if( opts.processing ){
- opts.processing(file, image, doneFn);
- } else {
- doneFn();
- }
- }
- else {
- opts.get && opts.get($el, file);
- }
- },
-
- emit: function (name, arg){
- var opts = this.options, evt = $.Event(name), res;
- evt.widget = this;
- name = $.camelCase('on-'+name.replace(/(file)(upload)/, '$1-$2'));
- if( $.isFunction(opts[name]) ){
- res = opts[name].call(this.el, evt, arg);
- }
- return (res !== false) && this.$el.triggerHandler(evt, arg);
- },
-
- /**
- * Add files to queue
- * @param {Array} files
- */
- add: function (files){
- if( files.length ){
- var
- opts = this.options
- , sortFn = opts.sortFn
- , preview = opts.elements.preview
- ;
-
- if( sortFn ){
- files.sort(sortFn);
- }
-
- if( preview && preview.el ){
- api.each(files, function (file){
- this._makeFilePreview(api.uid(file), file, preview, true);
- }, this);
- }
-
- if( this.xhr ){
- this.xhr.append(files);
- }
-
- this.queue = this.queue.concat(files);
- this.files = this.files.concat(files);
-
- if( this.options.autoUpload ){
- this.upload();
- } else {
- this._redraw();
- }
- }
- },
-
- /**
- * Find element
- * @param {String} sel
- * @param {jQuery} [ctx]
- * @return {jQuery}
- */
- $: function (sel, ctx){
- if( typeof sel === 'string' ){
- sel = /^#/.test(sel) ? sel : (ctx ? $(ctx) : this.$el).find(sel);
- }
- return $(sel);
- },
-
- /**
- * @param {String} name
- * @param {Boolean} [up]
- * @return {jQuery}
- */
- elem: function (name, up){
- var sel = this.option('elements.'+name);
- if( sel === void 0 && up ){
- sel = this.option('elements.'+name.substr(0, name.lastIndexOf('.')));
- }
- return this.$($.type(sel) != 'string' && $.isEmptyObject(sel) ? [] : sel);
- },
-
- /**
- * @param {String} uid
- * @return {jQuery}
- */
- fileElem: function (uid){
- return this.$('['+_dataFileId+'="'+uid+'"]');
- },
-
- /**
- * Get/set options
- * @param {String} name
- * @param {*} [value]
- * @return {*}
- */
- option: function (name, value){
- if( value !== void 0 && $.isPlainObject(value) ){
- api.each(value, function (val, key){
- this.option(name+'.'+key, val);
- }, this);
-
- return this;
- }
-
- var opts = this.options, val = opts[name], i = 0, len, part;
-
- if( name.indexOf('.') != -1 ){
- val = opts;
- name = name.split('.');
- len = name.length;
-
- for( ; i < len; i++ ){
- part = name[i];
-
- if( (value !== void 0) && (len - i === 1) ){
- val[part] = value;
- break;
- }
- else if( val[part] === void 0 ){
- val[part] = {};
- }
-
- val = val[part];
- }
- }
- else if( value !== void 0 ){
- opts[name] = value;
- }
-
- if( value !== void 0 ){
- this._setOption(name, value, this._options[name]);
- this._options[name] = value;
- }
-
- return value !== void 0 ? value : val;
- },
-
- _setOption: function (name, nVal){
- switch( name ){
- case 'accept':
- case 'multiple':
- case 'paramName':
- if( name == 'paramName' ){ name = 'name'; }
- if( nVal ){
- this.$(':file')[propFn](name, nVal);
- }
- break;
- }
- },
-
- serialize: function (){
- var obj = {}, val;
-
- this.$el.find(':input').each(function(name, node){
- if(
- (name = node.name) && !node.disabled
- && (node.checked || /select|textarea|input/i.test(node.nodeName) && /checkbox|radio/i.test(node.type))
- ){
- val = $(node).val();
- if( obj[name] !== void 0 ){
- if( !obj[name].push ){
- obj[name] = [obj[name]];
- }
-
- obj[name].push(val);
- } else {
- obj[name] = val;
- }
- }
- });
-
- return obj;
- },
-
- upload: function (){
- if( !this.active ){
- this.active = true;
-
- var
- $el = this.$el
- , opts = this.options
- , files = {}
- , uploadOpts = {
- url: opts.url
- , data: $.extend({}, this.serialize(), opts.data)
- , files: files
- , chunkSize: opts.chunkSize|0
- , chunkUploadRetry: opts.chunkUploadRetry|0
- , prepare: $.proxy(this, '_onFileUploadPrepare')
- , imageTransform: opts.imageTransform
- }
- ;
-
- // Set files
- files[$el.find(':file').attr('name') || 'files[]'] = this.queue;
-
- // Add event listeners
- api.each(['upload', 'progress', 'complete'], function (name){
- uploadOpts[name] = $.proxy(this, $.camelCase('_emit-'+name+'Event'), '');
- uploadOpts['file'+name] = $.proxy(this, $.camelCase('_emit-'+name+'Event'), 'file');
- }, this);
-
- // Start uploading
- this.xhr = api.upload(uploadOpts);
- this._redraw();
- }
- },
-
- crop: function (file, coords){
- var
- uid = api.uid(file)
- , opts = this.options
- , preview = opts.multiple ? this.option('elements.file.preview') : opts.elements.preview
- , $el = (opts.multiple ? this.fileElem(uid) : this.$el).find(preview && preview.el)
- ;
-
- if( $el.length ){
- api.getInfo(file, $.proxy(function (err, info){
- if( !err ){
- // @todo error emit
- if( !$el.find('div>div').length ){
- $el.html(
- $('')
- .css(preview)
- .css('overflow', 'hidden')
- );
- }
-
- if( this.__cropFile !== file ){
- this.__cropFile = file;
- api.Image(file).get(function (err, img){
- $el.find('>div>div').html($(img).width('100%').height('100%'));
- }, 'exactFit');
- }
-
-
- var rx = preview.width/coords.w, ry = preview.height/coords.h;
-
- $el.find('>div>div').css({
- width: Math.round(rx * info.width)
- , height: Math.round(ry * info.height)
- , marginLeft: -Math.round(rx * coords.x)
- , marginTop: -Math.round(ry * coords.y)
- });
- }
- }, this));
- }
-
- this._crop[uid] = coords;
- },
-
- rotate: function (file, deg){
- var
- uid = typeof file == 'object' ? api.uid(file) : file
- , opts = this.options
- , preview = opts.multiple ? this.option('elements.file.preview') : opts.elements.preview
- , $el = (opts.multiple ? this.fileElem(uid) : this.$el).find(preview && preview.el)
- , _rotate = this._rotate
- ;
-
- if( /([+-])=/.test(deg) ){
- deg = _rotate[uid] = (_rotate[uid] || 0) + (RegExp.$1 == '+' ? 1 : -1) * deg.substr(2);
- } else {
- _rotate[uid] = deg;
- }
-
- $el.css({
- '-webkit-transform': 'rotate('+deg+'deg)'
- , '-moz-transform': 'rotate('+deg+'deg)'
- , 'transform': 'rotate('+deg+'deg)'
- });
- },
-
- clear: function (){
- this.queue = [];
- this._redraw();
- },
-
- widget: function (){
- return this;
- },
-
- destroy: function (){
- this.$el
- .off('.fileapi')
- .removeData('fileapi')
- ;
- }
- };
-
-
-
-
-
- /**
- * @export
- * @param {Object} options
- * @param {String} [value]
- */
- $.fn.fileapi = function (options, value){
- var plugin = this.data('fileapi');
-
- if( plugin ){
- if( options === 'widget' ){
- return plugin;
- }
-
- if( typeof options == 'string' ){
- var fn = plugin[options], res;
- if( $.isFunction(fn) ){
- res = fn.call(plugin, value, arguments[2]);
- }
- else if( fn === void 0 ){
- res = this.option(options, value);
- }
- return res === void 0 ? this : res;
- }
- } else {
- this.data('fileapi', new Plugin(this, options));
- }
-
- return this;
- };
-
-
- $.fn.fileapi.version = '0.1.0';
- $.fn.fileapi.tpl = function (text){
- var index = 0;
- var source = "__b+='";
-
- text.replace(/(?:<|<)%([-=])?([\s\S]+?)%(?:>|>)|$/g, function (match, mode, expr, offset){
- source += text.slice(index, offset).replace(/[\r\n"']/g, function (match){ return '\\'+match; });
-
- if( expr ){
- if( mode ){
- source += "'+\n((__x=("+ expr +"))==null?'':" + (mode == "-" ? "__esc(__x)" : "__x")+")\n+'";
- } else {
- source += "';\n"+ expr +"\n__b+='";
- }
- }
-
- index = offset + match.length;
- return match;
- });
-
- return new Function("ctx", "var __x,__b=''," +
- "__esc=function(val){return typeof val=='string'?val.replace(/ $arFileDescriptions ){
- foreach( $arFileDescriptions as $fileDescriptionParam => $mixedValue ){
- self::rRestructuringFilesArray(self::$_files, $firstNameKey, $_FILES[$firstNameKey][$fileDescriptionParam], $fileDescriptionParam);
- }
- }
- }
- }
-
-
private static function rRestructuringFilesArray(&$arrayForFill, $currentKey, $currentMixedValue, $fileDescriptionParam){
if( is_array($currentMixedValue) ){
foreach( $currentMixedValue as $nameKey => $mixedValue ){
@@ -34,6 +18,60 @@ private static function rRestructuringFilesArray(&$arrayForFill, $currentKey, $c
}
+ private static function determineMimeType(&$file){
+ if( function_exists('mime_content_type') ){
+ if( isset($file['tmp_name']) && is_string($file['tmp_name']) ){
+ if( $file['type'] == 'application/octet-stream' ){
+ $mime = mime_content_type($file['tmp_name']);
+ if( !empty($mime) ){
+ $file['type'] = $mime;
+ }
+ }
+ }
+ else if( is_array($file) ){
+ foreach( $file as &$entry ){
+ self::determineMimeType($entry);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Enable CORS -- http://enable-cors.org/
+ * @param array [$options]
+ */
+ public static function enableCORS($options = null){
+ if( is_null($options) ){
+ $options = array();
+ }
+
+ if( !isset($options['origin']) ){
+ $options['origin'] = $_SERVER['HTTP_ORIGIN'];
+ }
+
+ if( !isset($options['methods']) ){
+ $options['methods'] = 'POST, GET';
+ }
+
+ if( !isset($options['headers']) ){
+ $options['headers'] = array();
+ }
+
+ header('Access-Control-Allow-Origin: ' . $options['origin']);
+ header('Access-Control-Allow-Methods: ' . $options['methods']);
+ header('Access-Control-Allow-Headers: ' . implode(', ', array_merge($options['headers'], array('X-Requested-With', 'Content-Range', 'Content-Disposition'))));
+
+ if( !isset($options['cookie']) || $options['cookie'] ){
+ header('Access-Control-Allow-Credentials: true');
+ }
+ }
+
+
+ /**
+ * Request header
+ * @return array
+ */
public static function getRequestHeaders(){
$headers = array();
@@ -48,36 +86,66 @@ public static function getRequestHeaders(){
}
+ /**
+ * Retrieve File List
+ * @return array
+ */
public static function getFiles(){
- self::init();
- return self::$_files;
+ $files = array();
+
+ // http://www.php.net/manual/ru/reserved.variables.files.php#106558
+ foreach( $_FILES as $firstNameKey => $arFileDescriptions ){
+ foreach( $arFileDescriptions as $fileDescriptionParam => $mixedValue ){
+ self::rRestructuringFilesArray($files, $firstNameKey, $_FILES[$firstNameKey][$fileDescriptionParam], $fileDescriptionParam);
+ }
+ }
+
+ self::determineMimeType($files);
+
+ return $files;
}
+ /**
+ * Make server response
+ * @param array $res
+ * @param string [$jsonp]
+ */
public static function makeResponse(array $res, $jsonp = null){
$body = $res['body'];
- $json = json_encode($body);
+ $json = is_array($body) ? json_encode($body) : $body;
$httpStatus = isset($res['status']) ? $res['status'] : self::OK;
- $httpStatusText = isset($res['statusText']) ? $res['statusText'] : 'OK';
+ $httpStatusText = addslashes(isset($res['statusText']) ? $res['statusText'] : 'OK');
$httpHeaders = isset($res['headers']) ? $res['headers'] : array();
if( empty($jsonp) ){
header("HTTP/1.1 $httpStatus $httpStatusText");
+ $httpHeaders['Content-Type'] = 'application/json';
foreach( $httpHeaders as $header => $value ){
header("$header: $value");
}
echo $json;
}
else {
- echo "";
+ $json = addslashes($json);
+
+ echo <<
+ (function (ctx, jsonp){
+ 'use strict';
+ var status = $httpStatus, statusText = "$httpStatusText", response = "$json";
+ try {
+ ctx[jsonp](status, statusText, response);
+ } catch (e){
+ var data = "{\"id\":\"$jsonp\",\"status\":"+status+",\"statusText\":\""+statusText+"\",\"response\":\""+response.replace(/\"/g, '\\\\\"')+"\"}";
+ try {
+ ctx.postMessage(data, document.referrer);
+ } catch (e){}
+ }
+ })(window.parent, '$jsonp');
+
+END;
}
}
diff --git a/server/ctrl.php b/server/ctrl.php
index 12e97f93..cba9facf 100644
--- a/server/ctrl.php
+++ b/server/ctrl.php
@@ -12,11 +12,12 @@
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type');
+ header('Access-Control-Allow-Credentials: true');
}
if( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ){
- exit;
+ exit();
}
diff --git a/statics/docs.json b/statics/docs.json
index 91ea8c8f..3bdc9a26 100644
--- a/statics/docs.json
+++ b/statics/docs.json
@@ -1 +1 @@
-{"FileAPI":{"label":"FileAPI","class":"FileAPI","descr":{"en":"A set of javascript tools for working with files.","ru":"Набор JavaScript инструментов для работы с файлами."},"props":{"Get started":{"name":"Get started","type":-1,"label":"started","descr":{"en":"","ru":""},"code":{"type":"html","source":{"en":" \n\n \n \n ","ru":" \n\n \n \n "}}},"Setup options":{"name":"Setup options","type":-1,"label":"FileAPI.setup","descr":{"en":"Edit the file `crossdomain.xml` and place it to the root of the domain to which files will be uploaded.","ru":"Отредактируйте файл `crossdomain.xml` и разместите его в корне домена, на который будут загружаться файлы."},"code":{"type":"html","source":{"en":" \n \n\n \n\n ","ru":" \n \n\n \n\n "}}}},"fn":{"getFiles":{"name":"getFiles","label":"FileAPI.getFiles","args":{"input":{"en":"`HTMLInputElement`, `change` and `drop` event, `jQuery` collection or `jQuery.Event`","ru":"`HTMLInputElement`, `change` и `drop` события, `jQuery` коллекция или `jQuery.Event`"}},"variants":[{"args":[{"name":"input","type":"HTMLInputElement|Event|$.Event","optional":false}],"descr":{"en":"Retrieve file list from `input` element or `event` object, also support `jQuery`.","ru":"Получить список файлов из `input` элемента, или `event`, также поддерживается `jQuery`."}}],"returns":"Array","code":{"type":"js","source":{"en":"var el = document.getElement('my-input');\nFileAPI.event.on(el, function (evt/**Event*/){\n // Retrieve file list\n var files = FileAPI.getFiles(el);\n\n // or event\n var files = FileAPI.getFiles(evt);\n});","ru":"var el = document.getElement('my-input');\nFileAPI.event.on(el, function (evt/**Event*/){\n // Получить список файлов из `input`\n var files = FileAPI.getFiles(el);\n\n // или события\n var files = FileAPI.getFiles(evt);\n});"}}},"getInfo":{"name":"getInfo","label":"FileAPI.getInfo","args":{"file":{"en":"file object (https://developer.mozilla.org/en-US/docs/DOM/File)","ru":"объект файла (https://developer.mozilla.org/en-US/docs/DOM/File)"},"callback":{"en":"function, called after collected info of file","ru":"функция, вызывается по завершению сбора информации"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Get info of file (see also: FileAPI.addInfoReader).","ru":"Получить информацию о файле (см. FileAPI.addInfoReader)."}}],"returns":"void","code":{"type":"js","source":{"en":"// Get info of image file (FileAPI.exif.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { width: 800, height: 600, exif: {..} }\n }\n});\n\n// Get info of mp3 file (FileAPI.id3.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { title: \"...\", album: \"...\", artists: \"...\", ... }\n }\n});","ru":"// Получить информацию о изображении (FileAPI.exif.js подключен)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { width: 800, height: 600, exif: {..} }\n }\n});\n\n// Получить информацию о mp3 файле (FileAPI.id3.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { title: \"...\", album: \"...\", artists: \"...\", ... }\n }\n});"}}},"filterFiles":{"name":"filterFiles","label":"FileAPI.filterFiles","args":{"files":{"en":"original list of files","ru":"оригинальный список файлов"},"filter":{"en":"function, takes two arguments: `file` — the file itself, `info` — additional information.","ru":"функция, принимает два аргумента: `file` — сам файл, `info` — дополнительная информация"},"callback":{"en":"function: `list` — files that match the condition, `other` — all the rest.","ru":"функция: `list` — список файлов, подошедшие под условия, `other` — все остальные."}},"variants":[{"args":[{"name":"files","type":"Array","optional":false},{"name":"filter","type":"Function","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Filtering the list of files, with additional information about files.\nSee also: FileAPI.getInfo and FileAPI.addInfoReader.","ru":"Отфильтровать список файлов, используя дополнительную информацию о них.\nсм. FileAPI.getInfo или FileAPI.addInfoReader."}}],"returns":"void","code":{"type":"js","source":{"en":"// Get list of file\nvar files = FileAPI.getFiles(input);\n\n// Filter the List\nFileAPI.filterFiles(files, function (file/**Object*/, info/**Object*/){\n if( /^image/.test(file.type) && info ){\n return info.width > 320 && info.height > 240;\n } else {\n return file.size < 20 * FileAPI.MB;\n }\n}, function (list/**Array*/, other/**Array*/){\n if( list.length ){\n // ..\n }\n});","ru":"// Получаем список файлов\nvar files = FileAPI.getFiles(input);\n\n// Фильтруем список\nFileAPI.filterFiles(files, function (file/**Object*/, info/**Object*/){\n if( /^image/.test(file.type) && info ){\n return info.width > 320 && info.height > 240;\n } else {\n return file.size < 20 * FileAPI.MB;\n }\n}, function (list/**Array*/, other/**Array*/){\n if( list.length ){\n // ..\n }\n});"}}},"getDropFiles":{"name":"getDropFiles","label":"FileAPI.getDropFiles","args":{"evt":{"en":"`drop` event","ru":"`drop` event"},"callback":{"en":"function, takes one argument, a list of files","ru":"фнукция, принимает один аргумент — список файлов"}},"variants":[{"args":[{"name":"evt","type":"Event|$.Event","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Get a list of files, including directories.","ru":"Получить весь список файлов, включая директории."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.event.on(document, 'drop', function (evt/**Event*/){\n evt.preventDefault();\n\n // Get a list of files\n FileAPI.getDropFiles(evt, function (files/**Array*/){\n // ...\n });\n});","ru":"FileAPI.event.on(document, 'drop', function (evt/**Event*/){\n evt.preventDefault();\n\n // Получаем все файлы\n FileAPI.getDropFiles(evt, function (files/**Array*/){\n // ...\n });\n});"}}},"upload":{"name":"upload","label":"FileAPI.upload","args":{"opts":{"en":"options object, see [Upload options](#options)","ru":"объект настрое, см. раздел [Upload options](#options)"}},"variants":[{"args":[{"name":"opts","type":"Object","optional":false}],"descr":{"en":"Uploading files to the server (successively). Returns XHR-like object.\nIt is important to remember to correctly worked flash-transport server response body must not be empty,\nfor example, you can pass, just text \"ok\".","ru":"Загрузка файлов на сервер (последовательно). Возвращает XHR-подобный объект.\nПомните, для корректной работы flash-транспорта, тело ответа сервера не должно быть пустым,\nнапример можно ответить простым текстом \"ok\"."}}],"returns":"XmlHttpRequest","code":{"type":"js","source":{"en":"var el = document.getElementById('my-input');\nFileAPI.event.on(el, 'change', function (evt/**Event*/){\n var files = FileAPI.getFiles(evt);\n var xhr = FileAPI.upload({\n url: 'http://rubaxa.org/FileAPI/server/ctrl.php',\n files: { file: files[0] },\n complete: function (err, xhr){\n if( !err ){\n var result = xhr.responseText;\n // ...\n }\n }\n });\n});","ru":"var el = document.getElementById('my-input');\nFileAPI.event.on(el, 'change', function (evt/**Event*/){\n var files = FileAPI.getFiles(evt);\n var xhr = FileAPI.upload({\n url: 'http://rubaxa.org/FileAPI/server/ctrl.php',\n files: { file: files[0] },\n complete: function (err, xhr){\n if( !err ){\n var result = xhr.responseText;\n // ...\n }\n }\n });\n});"}}},"addInfoReader":{"name":"addInfoReader","label":"FileAPI.addInfoReader","args":{"mime":{"en":"pattern of mime-type","ru":"маска mime-type"},"handler":{"en":"takes two arguments: `file` object and `complete` function callback","ru":"функция, принимает два аргумента: `file` объект и `complete` функция обратного вызова"}},"variants":[{"args":[{"name":"mime","type":"RegExp","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Adds a handler for the collection of information about a file.\nSee also: FileAPI.getInfo and FileAPI.filterFiles.","ru":"Добавить обработчик, для сбора информации о файле.\nсм. также: FileAPI.getInfo и FileAPI.filterFiles."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){\n // http://www.nihilogic.dk/labs/exif/exif.js\n // http://www.nihilogic.dk/labs/binaryajax/binaryajax.js\n FileAPI.readAsBinaryString(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n var binaryString = evt.result;\n var oFile = new BinaryFile(binaryString, 0, file.size);\n var exif = EXIF.readFromBinaryFile(oFile);\n callback(false, { 'exif': exif || {} });\n }\n else if( evt.type == 'error' ){\n callback('read_as_binary_string');\n }\n else if( evt.type == 'progress' ){\n // ...\n }\n });\n});","ru":"FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){\n // http://www.nihilogic.dk/labs/exif/exif.js\n // http://www.nihilogic.dk/labs/binaryajax/binaryajax.js\n FileAPI.readAsBinaryString(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n var binaryString = evt.result;\n var oFile = new BinaryFile(binaryString, 0, file.size);\n var exif = EXIF.readFromBinaryFile(oFile);\n callback(false, { 'exif': exif || {} });\n }\n else if( evt.type == 'error' ){\n callback('read_as_binary_string');\n }\n else if( evt.type == 'progress' ){\n // ...\n }\n });\n});"}}},"readAsDataURL":{"name":"readAsDataURL","label":"FileAPI.readAsDataURL","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `dataURL`.","ru":"Чтение содержимого указанного файла как dataURL."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsDataURL(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var dataURL = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsDataURL(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var dataURL = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}},"readAsBinaryString":{"name":"readAsBinaryString","label":"FileAPI.readAsBinaryString","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `BinaryString`.","ru":"Чтение содержимого указанного файла как `BinaryString`."}},{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `ArrayBuffer`.","ru":"Чтение содержимого указанного файла как `ArrayBuffer`."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsArrayBuffer(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var arrayBuffer = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsArrayBuffer(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var arrayBuffer = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}},"readAsText":{"name":"readAsText","label":"FileAPI.readAsText","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"},"encoding":{"en":"a string indicating the encoding to use for the returned data. By default, UTF-8.","ru":"строкой с указанием кодировки. По умолчанию UTF-8."}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `text`.","ru":"Чтение содержимого указанного файла как `text`."}},{"args":[{"name":"file","type":"Object","optional":false},{"name":"encoding","type":"String","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `text`.","ru":"Чтение содержимого указанного файла как `text` в нужной кодировке."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsText(file, \"utf-8\", function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var text = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsText(file, \"utf-8\", function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var text = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}}}},"Upload options":{"label":"options","class":"Upload options","descr":{"en":"","ru":""},"props":{"url":{"name":"url","type":"String","label":"options.url","descr":{"en":"A string containing the URL to which the request is sent.","ru":"Строка, содержащая адрес, на который отправляется запрос."}},"data":{"name":"data","type":"Object","label":"options.data","descr":{"en":"Additional post data to be sent along with the file uploads.","ru":"Дополнительные данные, которые должны быть отправлены вместе с файлом."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n data: { 'session-id': 123 },\n files: { ... },\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n data: { 'session-id': 123 },\n files: { ... },\n});"}}},"headers":{"name":"headers","type":"Object","label":"options.headers","descr":{"en":"Additional request headers, HTML5 only.","ru":"Дополнительные заголовки запроса, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n headers: { 'x-upload': 'fileapi' },\n files: { .. },\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n headers: { 'x-upload': 'fileapi' },\n files: { .. },\n});"}}},"files":{"name":"files","type":"Object","label":"options.files","descr":{"en":"Key-value object, `key` — post name, `value` — File or FileAPI.Image object."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: {\n audio: files\n }\n});"}}},"chunkSize":{"name":"chunkSize","type":"Number","label":"options.chunkSize","descr":{"en":"Chunk size in bytes, HTML5 only.","ru":"Размер части файла в байта, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB\n});"}}},"chunkUploadRetry":{"name":"chunkUploadRetry","type":"Number","label":"options.chunkUploadRetry","descr":{"en":"Number of retries during upload chunks, HTML5 only.","ru":"Количество попыток загрузки одной части, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB,\n chunkUploadRetry: 3\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB,\n chunkUploadRetry: 3\n});"}}},"imageTransform":{"name":"imageTransform","type":"Object","label":"options.imageTransform","descr":{"en":"Convert all images to jpeg or png.","ru":"Конвертация всех изображений в jpeg или png."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { image: imageFiles },\n imageTransform: {\n type: 'image/jpeg',\n quality: 0.86 // jpeg quality\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { image: imageFiles },\n imageTransform: {\n type: 'image/jpeg',\n quality: 0.86 // качество jpeg\n }\n});"}}},"imageOriginal":{"name":"imageOriginal","type":"Boolean","label":"options.imageOriginal","descr":{"en":"Sent to the server the original image or not, if defined imageTransform option.","ru":"Отправлять исходное изображение на сервер или нет, если определен `imageTransform` вариант."}},"imageAutoOrientation":{"name":"imageAutoOrientation","type":"Boolean","label":"options.imageAutoOrientation","descr":{"en":"Auto-rotate images on the basis of EXIF.","ru":"Автоматический поворот изображения на основе EXIF."}},"prepare":{"name":"prepare","type":"Function","label":"options.prepare","descr":{"en":"Prepare options upload for a particular file.","ru":"Подготовка опций загрузки для конкретного файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n prepare: function (file/**Object*/, options/**Object*/){\n options.data.secret = utils.getSecretKey(file.name);\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n prepare: function (file/**Object*/, options/**Object*/){\n options.data.secret = utils.getSecretKey(file.name);\n }\n});"}}},"upload":{"name":"upload","type":"Function","label":"options.upload","descr":{"en":"Start uploading.","ru":"Начало загрузки"},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n upload: function (xhr/**Object*/, options/**Object*/){\n // ...\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n upload: function (xhr/**Object*/, options/**Object*/){\n // ...\n }\n});"}}},"fileupload":{"name":"fileupload","type":"Function","label":"options.fileupload","descr":{"en":"Start file uploading.","ru":"Начало загрузки файла"},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n uploadfile: function (file/**Object*/, xhr/**Object*/, options/**Object*/){\n // ...\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n uploadfile: function (file/**Object*/, xhr/**Object*/, options/**Object*/){\n // ...\n }\n});"}}},"progress":{"name":"progress","type":"Function","label":"options.progress","descr":{"en":"Callback for upload progress events.","ru":"Общий прогресс загрузки файлов."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n progress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n progress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});"}}},"fileprogress":{"name":"fileprogress","type":"Function","label":"options.fileprogress","descr":{"en":"Callback for upload file progress events.","ru":"Прогресс загрузки файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileprogress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileprogress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});"}}},"complete":{"name":"complete","type":"Function","label":"options.complete","descr":{"en":"Callback for end upload requests.","ru":"Завершение загрузки всех файлов."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n complete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // All files successfully uploaded.\n }\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n complete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // Все файлы загружены успешно\n }\n }\n});"}}},"filecomplete":{"name":"filecomplete","type":"Function","label":"options.filecomplete","descr":{"en":"Callback for end upload requests.","ru":"Конец загрузки файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n filecomplete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // File successfully uploaded\n var result = xhr.responseText;\n }\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n filecomplete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // Файл загружен успешно\n var result = xhr.responseText;\n }\n }\n});"}}}},"fn":{}},"File object":{"label":"File","class":"File object","descr":{"en":"","ru":""},"props":{"name":{"name":"name","type":-1,"label":"File.name","descr":{"en":"The name of the file referenced by the File object.","ru":"Имя файла."}},"type":{"name":"type","type":-1,"label":"File.type","descr":{"en":"The type (MIME type) of the file referenced by the File object.","ru":"MIME type"}},"size":{"name":"size","type":-1,"label":"File.size","descr":{"en":"The size (in bytes) of the file referenced by the File object.","ru":"Размер файла в байтах."}}},"fn":{}},"FileAPI.event":{"label":"FileAPI.event","class":"FileAPI.event","descr":{"en":"","ru":""},"props":{},"fn":{"on":{"name":"on","label":"FileAPI.event.on","args":{"el":{"en":"DOM element","ru":"DOM элемент."},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"A function to execute when the event is triggered.","ru":"функция обработчик события."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an event handler function.","ru":"Добавить функцию обработки события."}}],"returns":"void"},"off":{"name":"off","label":"FileAPI.event.off","args":{"el":{"en":"DOM element","ru":"DOM элемент"},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"a handler function previously attached for the event(s).","ru":"функции обработчика ранее назначения на `event`."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Remove an event handler.","ru":"Удалить обработчик события."}}],"returns":"void"},"one":{"name":"one","label":"FileAPI.event.one","args":{"el":{"en":"DOM element","ru":"DOM элемент."},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"a function to execute when the event is triggered.","ru":"функция обработчик события."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an event handler function. The handler is executed at most once.","ru":"Добавить функцию обработки события. Обработчик выполняется не более одного раза."}}],"returns":"void"},"dnd":{"name":"dnd","label":"FileAPI.event.dnd","args":{"el":{"en":"drop zone","ru":"DOM элемент"},"hover":{"en":"`dragenter` and `dragleave` listener","ru":"`dragenter` и `dragleave` слушатель"},"handler":{"en":"`drop` event handler.","ru":"обработчик события `drop`"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"hover","type":"Function","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an drag and drop event handler function.","ru":"Добавить функцию обработки событий `drag` и `drop`."}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('dropzone');\nFileAPI.event.dnd(el, function (over){\n el.style.backgroundColor = over ? '#f60': '';\n}, function (files){\n if( files.length ){\n // Upload their.\n }\n});\n\n// or jQuery\n$('#dropzone').dnd(hoverFn, dropFn);","ru":"var el = document.getElementById('dropzone');\nFileAPI.event.dnd(el, function (over){\n el.style.backgroundColor = over ? '#f60': '';\n}, function (files){\n if( files.length ){\n // Загружаем их.\n }\n});\n\n// или jQuery\n$('#dropzone').dnd(hoverFn, dropFn);"}}},"dnd.off":{"name":"dnd.off","label":"FileAPI.event.dnd.off","args":{"el":{"en":"drop zone","ru":"DOM элемент"},"hover":{"en":"`dragenter` and `dragleave` listener","ru":"`dragenter` и `dragleave` слушатель"},"handler":{"en":"`drop` event handler.","ru":"обработчик события `drop`"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"hover","type":"Function","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Remove an drag and drop event handler function.","ru":"Удалить функцию обработки событий `drag` и `drop`."}}],"returns":"void","code":{"type":"js","source":{"en":"// Native\nFileAPI.event.dnd.off(el, hoverFn, dropFn);\n\n// jQuery\n$('#dropzone').dndoff(hoverFn, dropFn);","ru":"// Native\nFileAPI.event.dnd.off(el, hoverFn, dropFn);\n\n// jQuery\n$('#dropzone').dndoff(hoverFn, dropFn);"}}}}},"FileAPI.Image":{"label":"FileAPI.Image","class":"FileAPI.Image","descr":{"en":"Class for working with images","ru":"Класс для работы с изображениями"},"props":{},"fn":{"constructor":{"name":"constructor","label":"FileAPI.Image","args":{"file":{"en":"the `File` object","ru":"файл изображения"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false}],"descr":{"en":"The constructor takes a single argument, the `File` object.","ru":"Конструктор получает только один параметр, файл."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile).get(function (err/**String*/, img/**HTMLElement*/){\n if( !err ){\n document.body.appendChild( img );\n }\n});","ru":"FileAPI.Image(imageFile).get(function (err/**String*/, img/**HTMLElement*/){\n if( !err ){\n document.body.appendChild( img );\n }\n});"}}},"crop":{"name":"crop","label":"FileAPI.Image.crop","args":{"width":{"en":"new image width","ru":"новая ширина изображения"},"height":{"en":"new image height","ru":"новая высота изображения"},"x":{"en":"offset from the top corner","ru":"смещение относительно по x левого угла"},"y":{"en":"offset from the left corner","ru":"смещение относительно по y левого угла"}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false}],"descr":{"en":"Crop image by width and height.","ru":"Кроп изображения по ширине и высоте."}},{"args":[{"name":"x","type":"Number","optional":false},{"name":"y","type":"Number","optional":false},{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false}],"descr":{"en":"Crop image by x, y, width and height.","ru":"Кроп изображения по ширине и высоте, а также смещению по x и y."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .crop(100, 50, 320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .crop(100, 50, 320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"resize":{"name":"resize","label":"FileAPI.Image.resize","args":{"width":{"en":"new image width","ru":"новая ширина"},"height":{"en":"new image height","ru":"новая высота"},"type":{"en":"enum: `min`, `max`, `preview`. By default `undefined`.","ru":"enum: `min`, `max`, `preview`. По умолчанию `undefined`."}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false},{"name":"type","type":"String","optional":true}],"descr":{"en":"Resize image.","ru":"Ресайз."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .resize(320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;\n\n// Resize image on by max side.\nFileAPI.Image(imageFile)\n .resize(320, 240, 'max')\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .resize(320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;\n\n// По большей стороне\nFileAPI.Image(imageFile)\n .resize(320, 240, 'max')\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"preview":{"name":"preview","label":"FileAPI.Image.preview","args":{"width":{"en":"new image width","ru":"новая ширина"},"height":{"en":"new image height","ru":"новая высота"}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":true}],"descr":{"en":"Crop and resize image.","ru":"Кроп и ресайз изображения."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .preview(100, 100)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .preview(100, 100)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"rotate":{"name":"rotate","label":"FileAPI.Image.rotate","args":{"deg":{"en":"rotation angle in degrees","ru":"угол поворота в градусах"}},"variants":[{"args":[{"name":"deg","type":"Number","optional":false}],"descr":{"en":"Rotate image.","ru":"Поворот."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .rotate(90)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .rotate(90)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"filter":{"name":"filter","label":"FileAPI.Image.filter","args":{"callback":{"en":"takes two arguments, `canvas` element and `done` method.","ru":"принимает два рагумента, `canvas` элемент и метод `done`."},"name":{"en":"CamanJS filter name (custom or preset)","ru":"название CamanJS фильтра (произвольный, либо предустановленный)"}},"variants":[{"args":[{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Apply filter function. Only `HTML5`.","ru":"Применить фильтр функцию. Только `HTML5`."}},{"args":[{"name":"name","type":"String","optional":false}],"descr":{"en":"Uses [CamanJS](http://camanjs.com/), include it before FileAPI library.","ru":"Используется [CamanJS](http://camanjs.com/), подключите его перед библиотекой FileAPI."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"Caman.Filter.register(\"my-funky-filter\", function () {\n // http://camanjs.com/guides/#Extending\n});\n\nFileAPI.Image(imageFile)\n .filter(\"my-funky-filter\") // or .filter(\"vintage\")\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"Caman.Filter.register(\"my-funky-filter\", function () {\n // http://camanjs.com/guides/#Extending\n});\n\nFileAPI.Image(imageFile)\n .filter(\"my-funky-filter\") // или .filter(\"vintage\")\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"overlay":{"name":"overlay","label":"FileAPI.Image.overlay","args":{"images":{"en":"array of overlays","ru":"массив наложений"}},"variants":[{"args":[{"name":"images","type":"Array","optional":false}],"descr":{"en":"Add overlay images, eg: watermark.","ru":"Добавить наложение, например: водяной знак."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .overlay([\n // Left corner.\n { x: 10, y: 10, w: 100, h: 10, src: '/i/watermark.png' },\n\n // Right bottom corner.\n { x: 10, y: 10, src: '/i/watermark.png', rel: FileAPI.Image.RIGHT_BOTTOM }\n ])\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .overlay([\n // Левый угл.\n { x: 10, y: 10, w: 100, h: 10, src: '/i/watermark.png' },\n\n // Правый нижний угл.\n { x: 10, y: 10, src: '/i/watermark.png', rel: FileAPI.Image.RIGHT_BOTTOM }\n ])\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"get":{"name":"get","label":"FileAPI.Image.get","args":{"fn":{"en":"complete callback","ru":"функция обратного вызова"}},"variants":[{"args":[{"name":"fn","type":"Function","optional":false}],"descr":{"en":"Get the final image.","ru":"Получить итоговое изображение."}}],"returns":"FileAPI.Image"}}},"FileAPI.Camera":{"label":"FileAPI.Camera","class":"FileAPI.Camera","descr":{"en":"To work with a webcam, be sure to set `FileAPI.media: true`.","ru":"Для работы с веб-камерой, обязательно установить параметр `FileAPI.media: true`."},"props":{},"fn":{"publish":{"name":"publish","label":"FileAPI.Camera.publish","args":{"el":{"en":"target","ru":"куда публикуем"},"options":{"en":"{ `width: 100%`, `height: 100%`, `start: true` }","ru":"{ `width: 100%`, `height: 100%`, `start: true` }"},"callback":{"en":"the first parameter is a possible error, the second instance of FileAPI.Camera","ru":"первый параметр возможная ошибка, второй экземпляр FileAPI.Camera"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"options","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Publication of the camera.","ru":"Публикация камеры."}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { width: 320, height: 240 }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // The webcam is ready, you can use it.\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { width: 320, height: 240 }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Камера готова, можно использовать\n }\n});"}}},"start":{"name":"start","label":"FileAPI.Camera.start","args":{"callback":{"en":"will be called when the camera ready","ru":"будет вызван в момент готовности камеры"}},"variants":[{"args":[{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Turn on the camera.","ru":"Включить камеру"}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { start: false }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Turn on\n cam.start(function (err){\n if( !err ){\n // The camera is ready for use.\n }\n });\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { start: false }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Включаем камеру\n cam.start(function (err){\n if( !err ){\n // камера готова к использованию\n }\n });\n }\n});"}}},"stop":{"name":"stop","label":"FileAPI.Camera.stop","args":{},"variants":[{"args":0,"descr":{"en":"Turn off the camera.","ru":"Выключить камеру"}}],"returns":"void"},"shot":{"name":"shot","label":"FileAPI.Camera.shot","args":{},"variants":[{"args":0,"descr":{"en":"Take a picture with the camera.","ru":"Сделать снимок с камеры"}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n var shot = cam.shot(); // take a picture\n\n // create thumbnail 100x100\n shot.preview(100).get(function (err, img){\n previews.appendChild(img);\n });\n\n // and/or\n FileAPI.upload({\n url: '...',\n files: { cam: shot\n });\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n var shot = cam.shot(); // делаем снимок\n\n // создаем предпросмотр 100x100\n shot.preview(100).get(function (err, img){\n previews.appendChild(img);\n });\n\n // и/или загружаем\n FileAPI.upload({\n url: '...',\n files: { cam: shot\n });\n }\n});"}}}}},"Сonst":{"label":"const","class":"Сonst","descr":{"en":"","ru":""},"props":{"FileAPI.KB":{"name":"FileAPI.KB","type":"Number","label":"FileAPI.KB","descr":{"en":"1024 bytes","ru":"1024 байт"}},"FileAPI.MB":{"name":"FileAPI.MB","type":"Number","label":"FileAPI.MB","descr":{"en":"1048576 bytes","ru":"1048576 байт"}},"FileAPI.GB":{"name":"FileAPI.GB","type":"Number","label":"FileAPI.GB","descr":{"en":"1073741824 bytes","ru":"1073741824 байт"}},"FileAPI.TB":{"name":"FileAPI.TB","type":"Number","label":"FileAPI.TB","descr":{"en":"1.0995116e+12 bytes","ru":"1.0995116e+12 байт"}}},"fn":{}},"Utils":{"label":"FileAPI.utils","class":"Utils","descr":{"en":"","ru":""},"props":{},"fn":{"FileAPI.each":{"name":"FileAPI.each","label":"FileAPI.each","args":{"obj":{"en":"array or object","ru":"массив или объект"},"callback":{"en":"a function to execute for each element.","ru":"функция, выполняется для каждого элемента."},"thisObject":{"en":"object to use as `this` when executing `callback`.","ru":"объект для использования в качестве `this` при выполнении `callback`."}},"variants":[{"args":[{"name":"obj","type":"Object|Array","optional":false},{"name":"callback","type":"Function","optional":false},{"name":"thisObject","type":"Mixed","optional":true}],"descr":{"en":"Iterate over a object or array, executing a function for each matched element.","ru":"Перебор объект или массив, выполняя функцию для каждого элемента."}}],"returns":"void"},"FileAPI.extend":{"name":"FileAPI.extend","label":"FileAPI.extend","args":{"dst":{"en":"an object that will receive the new properties","ru":"объект, который получит новые свойства"},"src":{"en":"an object containing additional properties to merge in.","ru":"объект, содержащий дополнительные свойства для объединения"}},"variants":[{"args":[{"name":"dst","type":"Object","optional":false},{"name":"src","type":"Object","optional":false}],"descr":{"en":"Merge the contents of two objects together into the first object.","ru":"Объединить содержимое двух объектов вместе."}}],"returns":"Object"},"FileAPI.filter":{"name":"FileAPI.filter","label":"FileAPI.filter","args":{"array":{"en":"original Array","ru":"оригинальный массив"},"callback":{"en":"Function to test each element of the array.","ru":"функция для проверки каждого элемента массива."},"thisObject":{"en":"object to use as `this` when executing `callback`.","ru":"объект для использования в качестве `this` при выполнении `callback`."}},"variants":[{"args":[{"name":"array","type":"Array","optional":false},{"name":"callback","type":"Function","optional":false},{"name":"thisObject","type":"Mixed","optional":false}],"descr":{"en":"Creates a new array with all elements that pass the test implemented by the provided function.","ru":"Создает новый массив со всеми элементами, которые соответствуют условиям."}}],"returns":"Object"}}},"Support":{"label":"support","class":"Support","descr":{"en":"\n\tMultiupload: all browsers that support HTML5 or Flash \n\tDrag'n'Drop upload: files (HTML5) & directories (Chrome 21+) \n\tChunked file upload (HTML5) \n\tUpload one file: all browsers \n\t\n\t\tWorking with Images: IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+\n\t\t\n\t\t\tcrop, resize, preview & rotate (HTML5 or Flash) \n\t\t\tauto orientation by exif (HTML5, if include FileAPI.exif.js or Flash) \n\t\t \n\t \n ","ru":"\n\tMultiupload: все браузеры поддерживающие HTML5 или Flash \n\tDrag'n'Drop загрузка: файлы (HTML5) и директории (Chrome 21+) \n\tЗагрузка файлов по частям, только HTML5 \n\tЗагрузка одно файла: все браузеры, даже очень старые \n\t\n\t\tРабота с изображениями: IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+\n\t\t\n\t\t\tcrop, resize, preview & rotate (HTML5 или Flash) \n\t\t\tавто ориентация на основе EXIF (HTML5, если подключен FileAPI.exif.js или Flash) \n\t\t \n\t \n "},"props":{"FileAPI.support.html5":{"name":"FileAPI.support.html5","type":"Boolean","label":"FileAPI.support.html5","descr":{"en":"HTML5 borwser support","ru":"Поддержка HTML5."}},"FileAPI.support.cors":{"name":"FileAPI.support.cors","type":"Boolean","label":"FileAPI.support.cors","descr":{"en":"This cross-origin resource sharing is used to enable cross-site HTTP requests.","ru":"Поддержка кроссдоменных запросов."}},"FileAPI.support.dnd":{"name":"FileAPI.support.dnd","type":"Boolean","label":"FileAPI.support.dnd","descr":{"en":"Drag'n'drop events support.","ru":"Поддержка Drag'n'drop событий."}},"FileAPI.support.flash":{"name":"FileAPI.support.flash","type":"Boolean","label":"FileAPI.support.flash","descr":{"en":"Availability Flash plugin.","ru":"Наличие Flash плагина."}},"FileAPI.support.canvas":{"name":"FileAPI.support.canvas","type":"Boolean","label":"FileAPI.support.canvas","descr":{"en":"Canvas support.","ru":"Поддержка canvas."}},"FileAPI.support.dataURI":{"name":"FileAPI.support.dataURI","type":"Boolean","label":"FileAPI.support.dataURI","descr":{"en":"Support dataURI as src for image.","ru":"Поддержка dataURI в качестве src для изображений."}},"FileAPI.support.chunked":{"name":"FileAPI.support.chunked","type":"Boolean","label":"FileAPI.support.chunked","descr":{"en":"Support chuncked upload.","ru":"Возможность загрузки по частям."}}},"fn":{}},"Flash":{"label":"flash","class":"Flash","descr":{"en":"Flash is very \"buggy\" thing :]\nThe server response can not be empty.\nTherefore, in the event of a successful uploading `http status` should be only `200 OK`.","ru":"Флеш очень \"глючная\" штука :]\nПоэтому в случае успешной загрузки http status должен быть только `200 OK`."},"props":{"Settings":{"name":"Settings","type":-1,"label":"flash.settings","descr":{"en":"Flash settings.\nIt is advisable to place flash on the same server where the files will be uploaded.","ru":"Настройки для flash части.\nЖелательно, разместить flash на том же сервере, куда будут загружаться файлы."},"code":{"type":"html","source":{"en":"\n","ru":"\n"}}},"crossdomain.xml":{"name":"crossdomain.xml","type":-1,"label":"crossdomain.xml","descr":{"en":"Necessarily make this file on the server.\nDo not forget to replace `youdomain.com` on the name of your domain.","ru":"Обязательно создайте этот файл на сервере, куда будут загружаться файлы.\nНе забудьте заменить `youdomain.com` на имя вашего домена."},"code":{"type":"xml","source":{"en":"\n\n\n \n \n \n \n ","ru":"\n\n\n \n \n \n \n "}}},"request":{"name":"request","type":-1,"label":"flash.request","descr":{"en":"The following sample HTTP POST request is sent from Flash Player to a server-side script if no parameters are specified:","ru":"Пример запроса, который отправляет flash player."},"code":{"type":"xml","source":{"en":"POST /server/ctrl.php HTTP/1.1\nAccept: text/*\nContent-Type: multipart/form-data;\nboundary=----------Ij5ae0ae0KM7GI3KM7\nUser-Agent: Shockwave Flash\nHost: www.youdomain.com\nContent-Length: 421\nConnection: Keep-Alive\nCache-Control: no-cache\n\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filename\"\n\nMyFile.jpg\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"MyFile.jpg\"\nContent-Type: application/octet-stream\n\n[[..FILE_DATA_HERE..]]\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Upload\"\n\nSubmit Query\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7--","ru":"POST /server/ctrl.php HTTP/1.1\nAccept: text/*\nContent-Type: multipart/form-data;\nboundary=----------Ij5ae0ae0KM7GI3KM7\nUser-Agent: Shockwave Flash\nHost: www.youdomain.com\nContent-Length: 421\nConnection: Keep-Alive\nCache-Control: no-cache\n\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filename\"\n\nMyFile.jpg\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"MyFile.jpg\"\nContent-Type: application/octet-stream\n\n[[..FILE_DATA_HERE..]]\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Upload\"\n\nSubmit Query\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7--"}}}},"fn":{}},"Server settings":{"label":"server","class":"Server settings","descr":{"en":"","ru":""},"props":{"IFrame/JSONP":{"name":"IFrame/JSONP","type":-1,"label":"server.iframe","descr":{"en":"","ru":""},"code":{"type":"php","source":{"en":"\n\n\n\n FileAPI::OK\n , 'statusText' => 'OK'\n , 'body' => array('count' => sizeof($files)\n ), $jsonp);\n exit;\n }\n?>","ru":"\n\n\n\n FileAPI::OK\n , 'statusText' => 'OK'\n , 'body' => array('count' => sizeof($files)\n ), $jsonp);\n exit;\n }\n?>"}}},"CORS":{"name":"CORS","type":-1,"label":"server.CORS","descr":{"en":"Enable CORS.","ru":"Включение CORS."},"code":{"type":"php","source":{"en":"","ru":"\nClient explicitly sets the following headers: \n\n\tContent-Range: bytes <start-offset>-<end-offset>/<total> \n\tContent-Disposition: attachment; filename=<file-name> \n \nAny other headers are set by a target browser and are not used by client. Library does not provide any facilities to track a file uniqueness across requests, it's left on developer's consideration. \nResponse codes:\n\n\t200 - last chunk is uploaded \n\t201 - chunk is successfully saved \n\t416 - range is not acceptable error, recoverable \n\t500 - server error, recoverable \n \nFor recoverable errors server tries to resend chunk `chunkUploadRetry` times then fails. \n\tX-Last-Known-Byte: int, library tries to resend chunk from the given offset. Applicable to response codes 200 and 416 \n\nAll the other codes - fatal error, user's involvement is recommend.","ru":"Всё общение между клиентом и сервером ведётся на уровне HTTP заголовков. \nДля передачи отдельного chunk'а клиент устанавливает заголовки: \n\n\tContent-Range: bytes <start-offset>-<end-offset>/<total> \n\tContent-Disposition: attachment; filename=<file-name> \n \nДругие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика. \nВ ответ на передаваемый chunk сервер может отвечать следующими кодами: \n\n\t200, 201 — chunk сохранён успешно \n\t416, 500 — восстановимая ошибка \n \nОстальные коды — фатальная ошибка, требуется вмешательство пользователя."}}},"fn":{}},"Buttons examples":{"label":"buttons.examples","class":"Buttons examples","descr":{"en":"","ru":""},"props":{"Base":{"name":"Base","type":-1,"label":"buttons.examples.base","descr":{"en":"Simple input[type=\"file\"]","ru":"Простой input[type=\"file\"]"},"code":{"type":"html","source":{"en":"\n \n ","ru":"\n \n "}}},"Button":{"name":"Button","type":-1,"label":"buttons.examples.button","descr":{"en":"Stylized button.","ru":"Стилизованная кнопка."},"code":{"type":"html","source":{"en":"\n","ru":"\n"}}},"Link":{"name":"Link","type":-1,"label":"buttons.examples.link","descr":{"en":"Button like link.","ru":"Кнопка в виде ссылки"},"code":{"type":"html","source":{"en":"\n\n Upload photo \n \n ","ru":"\n\n Upload photo \n \n "}}}},"fn":{}},"Installation":{"label":"install","class":"Installation","descr":{"en":"`npm install fileapi` \n`cd fileapi` \n`npm install` \n`grunt`"},"props":{},"fn":{}},"Changelog":{"label":"Changelog","class":"Changelog","descr":{"en":""},"props":{"2.0.0":{"name":"2.0.0","type":-1,"label":"Changelog","descr":{"en":"\n\t+ FileAPI.Camera (HTML5 and Flash fallback) \n\t+ jquery.fileapi.js, see demo \n\t+ npm support \n\t+ grunt support \n\t+ requirejs support \n\t+ [#80](https://https://github.com/mailru/FileAPI/issues/80): FileAPI.Image.fn.overlay \n \t`imageTransform` — now supports: `crop`, `type`, `quality` and `overlay` properties. \n\tImproved the documentation \n\t+iOS fix (https://github.com/blueimp/JavaScript-Load-Image) \n\t[#121](https://github.com/mailru/FileAPI/issues/121): + FileAPI.`postNameConcat:Function(name, idx)` \n\t[#116](https://github.com/mailru/FileAPI/issues/116): + `cache:false` option for FileAPI.upload \n "}},"1.2.6":{"name":"1.2.6","type":-1,"label":"Changelog","descr":{"en":"\n\t[#91](https://github.com/mailru/FileAPI/issues/91): replace `new Image` to `FileAPI.newImage` \n\t+ FileAPI.withCredentials: true \n\t[#90](https://github.com/mailru/FileAPI/issues/90): Fixed `progress` event \n\t[#105](https://github.com/mailru/FileAPI/issues/105): Fixed `image/jpg` -> `image/jpeg` \n\t[#108](https://github.com/mailru/FileAPI/issues/108): Check width/height before resize by type(min/max) \n "}},"1.2.5":{"name":"1.2.5","type":-1,"label":"Changelog","descr":{"en":"\n\t[#86](https://github.com/mailru/FileAPI/issues/86): Smarter upload recovery \n\t[#87](https://github.com/mailru/FileAPI/issues/87): Fixed upload files into browsers that do not support FormData \n\tFixed support \"accept\" attribute for Flash. \n\tFixed detection of HTML5 support for FireFox 3.6 \n\t + FileAPI.html5 option, default \"true\" \n "}},"1.2.4":{"name":"1.2.4","type":-1,"label":"Changelog","descr":{"en":"\n\tFixed auto orientation image by EXIF (Flash) \n\tFixed image dimensions after rotate (Flash) \n\t[#82](https://github.com/mailru/FileAPI/issues/82): \"undefined\" data-fields cause exceptions \n\t[#83](https://github.com/mailru/FileAPI/issues/83): Allow requests without files \n\t[#84](https://github.com/mailru/FileAPI/pull/84): Fixed connection abort when waiting for connection recovery \n "}},"1.2.3":{"name":"1.2.3","type":-1,"label":"Changelog","descr":{"en":"\n\t[#77](https://github.com/mailru/FileAPI/pull/77): Fixed flash.abort(), [#75](https://github.com/mailru/FileAPI/issues/75) \n\t- `FileAPI.addMime` \n\t+ `FileAPI.accept` — fallback for flash. \n "}},"1.2.2":{"name":"1.2.2","type":-1,"label":"Changelog","descr":{"en":"\n\t[#67](https://github.com/mailru/FileAPI/pull/67): Added correct httpStatus for upload fail, [#62](https://github.com/mailru/FileAPI/pull/68) \n\t[#68](https://github.com/mailru/FileAPI/pull/68) Added \"Content-Type\" for chunked upload, [#65](https://github.com/mailru/FileAPI/pull/65) \n\t[#69](https://github.com/mailru/FileAPI/issues/69): Fixed network down recovery \n\tFixed progress event, [#66](https://github.com/mailru/FileAPI/issues/66) \n\tIncrease flash stage size, [#73](https://github.com/mailru/FileAPI/pull/73) \n\t- array index from POST-param \"name\", [#72](https://github.com/mailru/FileAPI/issues/72) \n\t- dependency on FileAPI.Image for FileAPI.Flash \n "}},"1.2.1":{"name":"1.2.1","type":-1,"label":"Changelog","descr":{"en":"\n\t[#64](https://github.com/mailru/FileAPI/issues/64): Bufixed for [#63](https://github.com/mailru/FileAPI/issues/63) \n "}},"1.2.0":{"name":"1.2.0","type":-1,"label":"Changelog","descr":{"en":"\n\t[#57](https://github.com/mailru/FileAPI/issues/57): Chunked file upload \n "}},"1.1.0":{"name":"1.1.0","type":-1,"label":"Changelog","descr":{"en":"\n\t[#54](https://github.com/mailru/FileAPI/issues/54): added `FileAPI.flashUrl` and `FileAPI.flashImageUrl` \n "}},"1.0.1":{"name":"1.0.1","type":-1,"label":"Changelog","descr":{"en":"\n\t[#51](https://github.com/mailru/FileAPI/issues/51): remove circular references from `file-objects` (Flash transport) \n\tadded `changelog` \n "}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":""}}},"fn":{}}}
\ No newline at end of file
+{"FileAPI":{"label":"FileAPI","class":"FileAPI","descr":{"en":"A set of javascript tools for working with files.","ru":"Набор JavaScript инструментов для работы с файлами."},"props":{"Get started":{"name":"Get started","type":-1,"label":"started","descr":{"en":"","ru":""},"code":{"type":"html","source":{"en":" \n\n \n \n ","ru":" \n\n \n \n "}}},"Setup options":{"name":"Setup options","type":-1,"label":"FileAPI.setup","descr":{"en":"Edit the file `crossdomain.xml` and place it to the root of the domain to which files will be uploaded.","ru":"Отредактируйте файл `crossdomain.xml` и разместите его в корне домена, на который будут загружаться файлы."},"code":{"type":"html","source":{"en":" \n \n\n \n\n ","ru":" \n \n\n \n\n "}}}},"fn":{"getFiles":{"name":"getFiles","label":"FileAPI.getFiles","args":{"input":{"en":"`HTMLInputElement`, `change` and `drop` event, `jQuery` collection or `jQuery.Event`","ru":"`HTMLInputElement`, `change` и `drop` события, `jQuery` коллекция или `jQuery.Event`"}},"variants":[{"args":[{"name":"input","type":"HTMLInputElement|Event|$.Event","optional":false}],"descr":{"en":"Retrieve file list from `input` element or `event` object, also support `jQuery`.","ru":"Получить список файлов из `input` элемента, или `event`, также поддерживается `jQuery`."}}],"returns":"Array","code":{"type":"js","source":{"en":"var el = document.getElement('my-input');\nFileAPI.event.on(el, function (evt/**Event*/){\n // Retrieve file list\n var files = FileAPI.getFiles(el);\n\n // or event\n var files = FileAPI.getFiles(evt);\n});","ru":"var el = document.getElement('my-input');\nFileAPI.event.on(el, function (evt/**Event*/){\n // Получить список файлов из `input`\n var files = FileAPI.getFiles(el);\n\n // или события\n var files = FileAPI.getFiles(evt);\n});"}}},"getInfo":{"name":"getInfo","label":"FileAPI.getInfo","args":{"file":{"en":"file object (https://developer.mozilla.org/en-US/docs/DOM/File)","ru":"объект файла (https://developer.mozilla.org/en-US/docs/DOM/File)"},"callback":{"en":"function, called after collected info of file","ru":"функция, вызывается по завершению сбора информации"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Get info of file (see also: FileAPI.addInfoReader).","ru":"Получить информацию о файле (см. FileAPI.addInfoReader)."}}],"returns":"void","code":{"type":"js","source":{"en":"// Get info of image file (FileAPI.exif.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { width: 800, height: 600, exif: {..} }\n }\n});\n\n// Get info of mp3 file (FileAPI.id3.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { title: \"...\", album: \"...\", artists: \"...\", ... }\n }\n});","ru":"// Получить информацию о изображении (FileAPI.exif.js подключен)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { width: 800, height: 600, exif: {..} }\n }\n});\n\n// Получить информацию о mp3 файле (FileAPI.id3.js included)\nFileAPI.getInfo(file, function (err/**String*/, info/**Object*/){\n if( !err ){\n console.log(info); // { title: \"...\", album: \"...\", artists: \"...\", ... }\n }\n});"}}},"filterFiles":{"name":"filterFiles","label":"FileAPI.filterFiles","args":{"files":{"en":"original list of files","ru":"оригинальный список файлов"},"filter":{"en":"function, takes two arguments: `file` — the file itself, `info` — additional information.","ru":"функция, принимает два аргумента: `file` — сам файл, `info` — дополнительная информация"},"callback":{"en":"function: `list` — files that match the condition, `other` — all the rest.","ru":"функция: `list` — список файлов, подошедшие под условия, `other` — все остальные."}},"variants":[{"args":[{"name":"files","type":"Array","optional":false},{"name":"filter","type":"Function","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Filtering the list of files, with additional information about files.\nSee also: FileAPI.getInfo and FileAPI.addInfoReader.","ru":"Отфильтровать список файлов, используя дополнительную информацию о них.\nсм. FileAPI.getInfo или FileAPI.addInfoReader."}}],"returns":"void","code":{"type":"js","source":{"en":"// Get list of file\nvar files = FileAPI.getFiles(input);\n\n// Filter the List\nFileAPI.filterFiles(files, function (file/**Object*/, info/**Object*/){\n if( /^image/.test(file.type) && info ){\n return info.width > 320 && info.height > 240;\n } else {\n return file.size < 20 * FileAPI.MB;\n }\n}, function (list/**Array*/, other/**Array*/){\n if( list.length ){\n // ..\n }\n});","ru":"// Получаем список файлов\nvar files = FileAPI.getFiles(input);\n\n// Фильтруем список\nFileAPI.filterFiles(files, function (file/**Object*/, info/**Object*/){\n if( /^image/.test(file.type) && info ){\n return info.width > 320 && info.height > 240;\n } else {\n return file.size < 20 * FileAPI.MB;\n }\n}, function (list/**Array*/, other/**Array*/){\n if( list.length ){\n // ..\n }\n});"}}},"getDropFiles":{"name":"getDropFiles","label":"FileAPI.getDropFiles","args":{"evt":{"en":"`drop` event","ru":"`drop` event"},"callback":{"en":"function, takes one argument, a list of files","ru":"фнукция, принимает один аргумент — список файлов"}},"variants":[{"args":[{"name":"evt","type":"Event|$.Event","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Get a list of files, including directories.","ru":"Получить весь список файлов, включая директории."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.event.on(document, 'drop', function (evt/**Event*/){\n evt.preventDefault();\n\n // Get a list of files\n FileAPI.getDropFiles(evt, function (files/**Array*/){\n // ...\n });\n});","ru":"FileAPI.event.on(document, 'drop', function (evt/**Event*/){\n evt.preventDefault();\n\n // Получаем все файлы\n FileAPI.getDropFiles(evt, function (files/**Array*/){\n // ...\n });\n});"}}},"upload":{"name":"upload","label":"FileAPI.upload","args":{"opts":{"en":"options object, see [Upload options](#options)","ru":"объект настрое, см. раздел [Upload options](#options)"}},"variants":[{"args":[{"name":"opts","type":"Object","optional":false}],"descr":{"en":"Uploading files to the server (successively). Returns XHR-like object.\nIt is important to remember to correctly worked flash-transport server response body must not be empty,\nfor example, you can pass, just text \"ok\".","ru":"Загрузка файлов на сервер (последовательно). Возвращает XHR-подобный объект.\nПомните, для корректной работы flash-транспорта, тело ответа сервера не должно быть пустым,\nнапример можно ответить простым текстом \"ok\"."}}],"returns":"XmlHttpRequest","code":{"type":"js","source":{"en":"var el = document.getElementById('my-input');\nFileAPI.event.on(el, 'change', function (evt/**Event*/){\n var files = FileAPI.getFiles(evt);\n var xhr = FileAPI.upload({\n url: 'http://rubaxa.org/FileAPI/server/ctrl.php',\n files: { file: files[0] },\n complete: function (err, xhr){\n if( !err ){\n var result = xhr.responseText;\n // ...\n }\n }\n });\n});","ru":"var el = document.getElementById('my-input');\nFileAPI.event.on(el, 'change', function (evt/**Event*/){\n var files = FileAPI.getFiles(evt);\n var xhr = FileAPI.upload({\n url: 'http://rubaxa.org/FileAPI/server/ctrl.php',\n files: { file: files[0] },\n complete: function (err, xhr){\n if( !err ){\n var result = xhr.responseText;\n // ...\n }\n }\n });\n});"}}},"addInfoReader":{"name":"addInfoReader","label":"FileAPI.addInfoReader","args":{"mime":{"en":"pattern of mime-type","ru":"маска mime-type"},"handler":{"en":"takes two arguments: `file` object and `complete` function callback","ru":"функция, принимает два аргумента: `file` объект и `complete` функция обратного вызова"}},"variants":[{"args":[{"name":"mime","type":"RegExp","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Adds a handler for the collection of information about a file.\nSee also: FileAPI.getInfo and FileAPI.filterFiles.","ru":"Добавить обработчик, для сбора информации о файле.\nсм. также: FileAPI.getInfo и FileAPI.filterFiles."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){\n // http://www.nihilogic.dk/labs/exif/exif.js\n // http://www.nihilogic.dk/labs/binaryajax/binaryajax.js\n FileAPI.readAsBinaryString(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n var binaryString = evt.result;\n var oFile = new BinaryFile(binaryString, 0, file.size);\n var exif = EXIF.readFromBinaryFile(oFile);\n callback(false, { 'exif': exif || {} });\n }\n else if( evt.type == 'error' ){\n callback('read_as_binary_string');\n }\n else if( evt.type == 'progress' ){\n // ...\n }\n });\n});","ru":"FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){\n // http://www.nihilogic.dk/labs/exif/exif.js\n // http://www.nihilogic.dk/labs/binaryajax/binaryajax.js\n FileAPI.readAsBinaryString(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n var binaryString = evt.result;\n var oFile = new BinaryFile(binaryString, 0, file.size);\n var exif = EXIF.readFromBinaryFile(oFile);\n callback(false, { 'exif': exif || {} });\n }\n else if( evt.type == 'error' ){\n callback('read_as_binary_string');\n }\n else if( evt.type == 'progress' ){\n // ...\n }\n });\n});"}}},"readAsDataURL":{"name":"readAsDataURL","label":"FileAPI.readAsDataURL","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `dataURL`.","ru":"Чтение содержимого указанного файла как dataURL."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsDataURL(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var dataURL = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsDataURL(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var dataURL = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}},"readAsBinaryString":{"name":"readAsBinaryString","label":"FileAPI.readAsBinaryString","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `BinaryString`.","ru":"Чтение содержимого указанного файла как `BinaryString`."}},{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `ArrayBuffer`.","ru":"Чтение содержимого указанного файла как `ArrayBuffer`."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsArrayBuffer(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var arrayBuffer = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsArrayBuffer(file, function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var arrayBuffer = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}},"readAsText":{"name":"readAsText","label":"FileAPI.readAsText","args":{"file":{"en":"file object","ru":"файл для чтения"},"callback":{"en":"function, receives a result","ru":"функция обработчик"},"encoding":{"en":"a string indicating the encoding to use for the returned data. By default, UTF-8.","ru":"строкой с указанием кодировки. По умолчанию UTF-8."}},"variants":[{"args":[{"name":"file","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `text`.","ru":"Чтение содержимого указанного файла как `text`."}},{"args":[{"name":"file","type":"Object","optional":false},{"name":"encoding","type":"String","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Reading the contents of the specified `File` as `text`.","ru":"Чтение содержимого указанного файла как `text` в нужной кодировке."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.readAsText(file, \"utf-8\", function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Success\n var text = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Error\n }\n})","ru":"FileAPI.readAsText(file, \"utf-8\", function (evt/**Object*/){\n if( evt.type == 'load' ){\n // Всё хорошо\n var text = evt.result;\n } else if( evt.type =='progress' ){\n var pr = evt.loaded/evt.total * 100;\n } else {\n // Ошибка\n }\n})"}}}}},"Upload options":{"label":"options","class":"Upload options","descr":{"en":"","ru":""},"props":{"url":{"name":"url","type":"String","label":"options.url","descr":{"en":"A string containing the URL to which the request is sent.","ru":"Строка, содержащая адрес, на который отправляется запрос."}},"data":{"name":"data","type":"Object","label":"options.data","descr":{"en":"Additional post data to be sent along with the file uploads.","ru":"Дополнительные данные, которые должны быть отправлены вместе с файлом."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n data: { 'session-id': 123 },\n files: { ... },\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n data: { 'session-id': 123 },\n files: { ... },\n});"}}},"headers":{"name":"headers","type":"Object","label":"options.headers","descr":{"en":"Additional request headers, HTML5 only.","ru":"Дополнительные заголовки запроса, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n headers: { 'x-upload': 'fileapi' },\n files: { .. },\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n headers: { 'x-upload': 'fileapi' },\n files: { .. },\n});"}}},"files":{"name":"files","type":"Object","label":"options.files","descr":{"en":"Key-value object, `key` — post name, `value` — File or FileAPI.Image object."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: {\n audio: files\n }\n});"}}},"chunkSize":{"name":"chunkSize","type":"Number","label":"options.chunkSize","descr":{"en":"Chunk size in bytes, HTML5 only.","ru":"Размер части файла в байта, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB\n});"}}},"chunkUploadRetry":{"name":"chunkUploadRetry","type":"Number","label":"options.chunkUploadRetry","descr":{"en":"Number of retries during upload chunks, HTML5 only.","ru":"Количество попыток загрузки одной части, только HTML5."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB,\n chunkUploadRetry: 3\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { images: fileList },\n chunkSize: 0.5 * FileAPI.MB,\n chunkUploadRetry: 3\n});"}}},"imageTransform":{"name":"imageTransform","type":"Object","label":"options.imageTransform","descr":{"en":"Convert all images to jpeg or png.","ru":"Конвертация всех изображений в jpeg или png."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { image: imageFiles },\n imageTransform: {\n type: 'image/jpeg',\n quality: 0.86 // jpeg quality\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { image: imageFiles },\n imageTransform: {\n type: 'image/jpeg',\n quality: 0.86 // качество jpeg\n }\n});"}}},"imageOriginal":{"name":"imageOriginal","type":"Boolean","label":"options.imageOriginal","descr":{"en":"Sent to the server the original image or not, if defined imageTransform option.","ru":"Отправлять исходное изображение на сервер или нет, если определен `imageTransform` вариант."}},"imageAutoOrientation":{"name":"imageAutoOrientation","type":"Boolean","label":"options.imageAutoOrientation","descr":{"en":"Auto-rotate images on the basis of EXIF.","ru":"Автоматический поворот изображения на основе EXIF."}},"prepare":{"name":"prepare","type":"Function","label":"options.prepare","descr":{"en":"Prepare options upload for a particular file.","ru":"Подготовка опций загрузки для конкретного файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n prepare: function (file/**Object*/, options/**Object*/){\n options.data.secret = utils.getSecretKey(file.name);\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n prepare: function (file/**Object*/, options/**Object*/){\n options.data.secret = utils.getSecretKey(file.name);\n }\n});"}}},"upload":{"name":"upload","type":"Function","label":"options.upload","descr":{"en":"Start uploading.","ru":"Начало загрузки"},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n upload: function (xhr/**Object*/, options/**Object*/){\n // ...\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n upload: function (xhr/**Object*/, options/**Object*/){\n // ...\n }\n});"}}},"fileupload":{"name":"fileupload","type":"Function","label":"options.fileupload","descr":{"en":"Start file uploading.","ru":"Начало загрузки файла"},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileupload: function (file/**Object*/, xhr/**Object*/, options/**Object*/){\n // ...\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileupload: function (file/**Object*/, xhr/**Object*/, options/**Object*/){\n // ...\n }\n});"}}},"progress":{"name":"progress","type":"Function","label":"options.progress","descr":{"en":"Callback for upload progress events.","ru":"Общий прогресс загрузки файлов."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n progress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n progress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});"}}},"fileprogress":{"name":"fileprogress","type":"Function","label":"options.fileprogress","descr":{"en":"Callback for upload file progress events.","ru":"Прогресс загрузки файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileprogress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n fileprogress: function (evt/**Object*/, file/**Object*/, xhr/**Object*/, options/**Object*/){\n var pr = evt.loaded/evt.total * 100;\n }\n});"}}},"complete":{"name":"complete","type":"Function","label":"options.complete","descr":{"en":"Callback for end upload requests.","ru":"Завершение загрузки всех файлов."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n complete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // All files successfully uploaded.\n }\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n complete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // Все файлы загружены успешно\n }\n }\n});"}}},"filecomplete":{"name":"filecomplete","type":"Function","label":"options.filecomplete","descr":{"en":"Callback for end upload requests.","ru":"Конец загрузки файла."},"code":{"type":"js","source":{"en":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n filecomplete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // File successfully uploaded\n var result = xhr.responseText;\n }\n }\n});","ru":"var xhr = FileAPI.upload({\n url: '...',\n files: { .. }\n filecomplete: function (err/**String*/, xhr/**Object*/, file/**Object/, options/**Object*/){\n if( !err ){\n // Файл загружен успешно\n var result = xhr.responseText;\n }\n }\n});"}}}},"fn":{}},"File object":{"label":"File","class":"File object","descr":{"en":"","ru":""},"props":{"name":{"name":"name","type":-1,"label":"File.name","descr":{"en":"The name of the file referenced by the File object.","ru":"Имя файла."}},"type":{"name":"type","type":-1,"label":"File.type","descr":{"en":"The type (MIME type) of the file referenced by the File object.","ru":"MIME type"}},"size":{"name":"size","type":-1,"label":"File.size","descr":{"en":"The size (in bytes) of the file referenced by the File object.","ru":"Размер файла в байтах."}}},"fn":{}},"FileAPI.event":{"label":"FileAPI.event","class":"FileAPI.event","descr":{"en":"","ru":""},"props":{},"fn":{"on":{"name":"on","label":"FileAPI.event.on","args":{"el":{"en":"DOM element","ru":"DOM элемент."},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"A function to execute when the event is triggered.","ru":"функция обработчик события."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an event handler function.","ru":"Добавить функцию обработки события."}}],"returns":"void"},"off":{"name":"off","label":"FileAPI.event.off","args":{"el":{"en":"DOM element","ru":"DOM элемент"},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"a handler function previously attached for the event(s).","ru":"функции обработчика ранее назначения на `event`."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Remove an event handler.","ru":"Удалить обработчик события."}}],"returns":"void"},"one":{"name":"one","label":"FileAPI.event.one","args":{"el":{"en":"DOM element","ru":"DOM элемент."},"events":{"en":"one or more space-separated event types.","ru":"одно или нескольких разделенных пробелами типов событий."},"handler":{"en":"a function to execute when the event is triggered.","ru":"функция обработчик события."}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"events","type":"String","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an event handler function. The handler is executed at most once.","ru":"Добавить функцию обработки события. Обработчик выполняется не более одного раза."}}],"returns":"void"},"dnd":{"name":"dnd","label":"FileAPI.event.dnd","args":{"el":{"en":"drop zone","ru":"DOM элемент"},"hover":{"en":"`dragenter` and `dragleave` listener","ru":"`dragenter` и `dragleave` слушатель"},"handler":{"en":"`drop` event handler.","ru":"обработчик события `drop`"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"hover","type":"Function","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Attach an drag and drop event handler function.","ru":"Добавить функцию обработки событий `drag` и `drop`."}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('dropzone');\nFileAPI.event.dnd(el, function (over){\n el.style.backgroundColor = over ? '#f60': '';\n}, function (files){\n if( files.length ){\n // Upload their.\n }\n});\n\n// or jQuery\n$('#dropzone').dnd(hoverFn, dropFn);","ru":"var el = document.getElementById('dropzone');\nFileAPI.event.dnd(el, function (over){\n el.style.backgroundColor = over ? '#f60': '';\n}, function (files){\n if( files.length ){\n // Загружаем их.\n }\n});\n\n// или jQuery\n$('#dropzone').dnd(hoverFn, dropFn);"}}},"dnd.off":{"name":"dnd.off","label":"FileAPI.event.dnd.off","args":{"el":{"en":"drop zone","ru":"DOM элемент"},"hover":{"en":"`dragenter` and `dragleave` listener","ru":"`dragenter` и `dragleave` слушатель"},"handler":{"en":"`drop` event handler.","ru":"обработчик события `drop`"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"hover","type":"Function","optional":false},{"name":"handler","type":"Function","optional":false}],"descr":{"en":"Remove an drag and drop event handler function.","ru":"Удалить функцию обработки событий `drag` и `drop`."}}],"returns":"void","code":{"type":"js","source":{"en":"// Native\nFileAPI.event.dnd.off(el, hoverFn, dropFn);\n\n// jQuery\n$('#dropzone').dndoff(hoverFn, dropFn);","ru":"// Native\nFileAPI.event.dnd.off(el, hoverFn, dropFn);\n\n// jQuery\n$('#dropzone').dndoff(hoverFn, dropFn);"}}}}},"FileAPI.Image":{"label":"FileAPI.Image","class":"FileAPI.Image","descr":{"en":"Class for working with images","ru":"Класс для работы с изображениями"},"props":{},"fn":{"constructor":{"name":"constructor","label":"FileAPI.Image","args":{"file":{"en":"the `File` object","ru":"файл изображения"}},"variants":[{"args":[{"name":"file","type":"Object","optional":false}],"descr":{"en":"The constructor takes a single argument, the `File` object.","ru":"Конструктор получает только один параметр, файл."}}],"returns":"void","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile).get(function (err/**String*/, img/**HTMLElement*/){\n if( !err ){\n document.body.appendChild( img );\n }\n});","ru":"FileAPI.Image(imageFile).get(function (err/**String*/, img/**HTMLElement*/){\n if( !err ){\n document.body.appendChild( img );\n }\n});"}}},"crop":{"name":"crop","label":"FileAPI.Image.crop","args":{"width":{"en":"new image width","ru":"новая ширина изображения"},"height":{"en":"new image height","ru":"новая высота изображения"},"x":{"en":"offset from the top corner","ru":"смещение относительно по x левого угла"},"y":{"en":"offset from the left corner","ru":"смещение относительно по y левого угла"}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false}],"descr":{"en":"Crop image by width and height.","ru":"Кроп изображения по ширине и высоте."}},{"args":[{"name":"x","type":"Number","optional":false},{"name":"y","type":"Number","optional":false},{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false}],"descr":{"en":"Crop image by x, y, width and height.","ru":"Кроп изображения по ширине и высоте, а также смещению по x и y."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .crop(100, 50, 320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .crop(100, 50, 320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"resize":{"name":"resize","label":"FileAPI.Image.resize","args":{"width":{"en":"new image width","ru":"новая ширина"},"height":{"en":"new image height","ru":"новая высота"},"type":{"en":"enum: `min`, `max`, `preview`. By default `undefined`.","ru":"enum: `min`, `max`, `preview`. По умолчанию `undefined`."}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":false},{"name":"type","type":"String","optional":true}],"descr":{"en":"Resize image.","ru":"Ресайз."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .resize(320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;\n\n// Resize image on by max side.\nFileAPI.Image(imageFile)\n .resize(320, 240, 'max')\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .resize(320, 240)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;\n\n// По большей стороне\nFileAPI.Image(imageFile)\n .resize(320, 240, 'max')\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"preview":{"name":"preview","label":"FileAPI.Image.preview","args":{"width":{"en":"new image width","ru":"новая ширина"},"height":{"en":"new image height","ru":"новая высота"}},"variants":[{"args":[{"name":"width","type":"Number","optional":false},{"name":"height","type":"Number","optional":true}],"descr":{"en":"Crop and resize image.","ru":"Кроп и ресайз изображения."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .preview(100, 100)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .preview(100, 100)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"rotate":{"name":"rotate","label":"FileAPI.Image.rotate","args":{"deg":{"en":"rotation angle in degrees","ru":"угол поворота в градусах"}},"variants":[{"args":[{"name":"deg","type":"Number","optional":false}],"descr":{"en":"Rotate image.","ru":"Поворот."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .rotate(90)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .rotate(90)\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"filter":{"name":"filter","label":"FileAPI.Image.filter","args":{"callback":{"en":"takes two arguments, `canvas` element and `done` method.","ru":"принимает два рагумента, `canvas` элемент и метод `done`."},"name":{"en":"CamanJS filter name (custom or preset)","ru":"название CamanJS фильтра (произвольный, либо предустановленный)"}},"variants":[{"args":[{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Apply filter function. Only `HTML5`.","ru":"Применить фильтр функцию. Только `HTML5`."}},{"args":[{"name":"name","type":"String","optional":false}],"descr":{"en":"Uses [CamanJS](http://camanjs.com/), include it before FileAPI library.","ru":"Используется [CamanJS](http://camanjs.com/), подключите его перед библиотекой FileAPI."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"Caman.Filter.register(\"my-funky-filter\", function () {\n // http://camanjs.com/guides/#Extending\n});\n\nFileAPI.Image(imageFile)\n .filter(\"my-funky-filter\") // or .filter(\"vintage\")\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"Caman.Filter.register(\"my-funky-filter\", function () {\n // http://camanjs.com/guides/#Extending\n});\n\nFileAPI.Image(imageFile)\n .filter(\"my-funky-filter\") // или .filter(\"vintage\")\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"overlay":{"name":"overlay","label":"FileAPI.Image.overlay","args":{"images":{"en":"array of overlays","ru":"массив наложений"}},"variants":[{"args":[{"name":"images","type":"Array","optional":false}],"descr":{"en":"Add overlay images, eg: watermark.","ru":"Добавить наложение, например: водяной знак."}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"FileAPI.Image(imageFile)\n .overlay([\n // Left corner.\n { x: 10, y: 10, w: 100, h: 10, src: '/i/watermark.png' },\n\n // Right bottom corner.\n { x: 10, y: 10, src: '/i/watermark.png', rel: FileAPI.Image.RIGHT_BOTTOM }\n ])\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;","ru":"FileAPI.Image(imageFile)\n .overlay([\n // Левый угл.\n { x: 10, y: 10, w: 100, h: 10, src: '/i/watermark.png' },\n\n // Правый нижний угл.\n { x: 10, y: 10, src: '/i/watermark.png', rel: FileAPI.Image.RIGHT_BOTTOM }\n ])\n .get(function (err/**String*/, img/**HTMLElement*/){\n\n })\n;"}}},"get":{"name":"get","label":"FileAPI.Image.get","args":{"fn":{"en":"complete callback","ru":"функция обратного вызова"}},"variants":[{"args":[{"name":"fn","type":"Function","optional":false}],"descr":{"en":"Get the final image.","ru":"Получить итоговое изображение."}}],"returns":"FileAPI.Image"}}},"FileAPI.Camera":{"label":"FileAPI.Camera","class":"FileAPI.Camera","descr":{"en":"To work with a webcam, be sure to set `FileAPI.media: true`.","ru":"Для работы с веб-камерой, обязательно установить параметр `FileAPI.media: true`."},"props":{},"fn":{"publish":{"name":"publish","label":"FileAPI.Camera.publish","args":{"el":{"en":"target","ru":"куда публикуем"},"options":{"en":"{ `width: 100%`, `height: 100%`, `start: true` }","ru":"{ `width: 100%`, `height: 100%`, `start: true` }"},"callback":{"en":"the first parameter is a possible error, the second instance of FileAPI.Camera","ru":"первый параметр возможная ошибка, второй экземпляр FileAPI.Camera"}},"variants":[{"args":[{"name":"el","type":"HTMLElement","optional":false},{"name":"options","type":"Object","optional":false},{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Publication of the camera.","ru":"Публикация камеры."}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { width: 320, height: 240 }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // The webcam is ready, you can use it.\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { width: 320, height: 240 }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Камера готова, можно использовать\n }\n});"}}},"start":{"name":"start","label":"FileAPI.Camera.start","args":{"callback":{"en":"will be called when the camera ready","ru":"будет вызван в момент готовности камеры"}},"variants":[{"args":[{"name":"callback","type":"Function","optional":false}],"descr":{"en":"Turn on the camera.","ru":"Включить камеру"}}],"returns":"void","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { start: false }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Turn on\n cam.start(function (err){\n if( !err ){\n // The camera is ready for use.\n }\n });\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, { start: false }, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n // Включаем камеру\n cam.start(function (err){\n if( !err ){\n // камера готова к использованию\n }\n });\n }\n});"}}},"stop":{"name":"stop","label":"FileAPI.Camera.stop","args":{},"variants":[{"args":0,"descr":{"en":"Turn off the camera.","ru":"Выключить камеру"}}],"returns":"void"},"shot":{"name":"shot","label":"FileAPI.Camera.shot","args":{},"variants":[{"args":0,"descr":{"en":"Take a picture with the camera.","ru":"Сделать снимок с камеры"}}],"returns":"FileAPI.Image","code":{"type":"js","source":{"en":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n var shot = cam.shot(); // take a picture\n\n // create thumbnail 100x100\n shot.preview(100).get(function (err, img){\n previews.appendChild(img);\n });\n\n // and/or\n FileAPI.upload({\n url: '...',\n files: { cam: shot\n });\n }\n});","ru":"var el = document.getElementById('cam');\nFileAPI.Camera.publish(el, function (err, cam/**FileAPI.Camera*/){\n if( !err ){\n var shot = cam.shot(); // делаем снимок\n\n // создаем предпросмотр 100x100\n shot.preview(100).get(function (err, img){\n previews.appendChild(img);\n });\n\n // и/или загружаем\n FileAPI.upload({\n url: '...',\n files: { cam: shot\n });\n }\n});"}}}}},"Сonst":{"label":"const","class":"Сonst","descr":{"en":"","ru":""},"props":{"FileAPI.KB":{"name":"FileAPI.KB","type":"Number","label":"FileAPI.KB","descr":{"en":"1024 bytes","ru":"1024 байт"}},"FileAPI.MB":{"name":"FileAPI.MB","type":"Number","label":"FileAPI.MB","descr":{"en":"1048576 bytes","ru":"1048576 байт"}},"FileAPI.GB":{"name":"FileAPI.GB","type":"Number","label":"FileAPI.GB","descr":{"en":"1073741824 bytes","ru":"1073741824 байт"}},"FileAPI.TB":{"name":"FileAPI.TB","type":"Number","label":"FileAPI.TB","descr":{"en":"1.0995116e+12 bytes","ru":"1.0995116e+12 байт"}}},"fn":{}},"Utils":{"label":"FileAPI.utils","class":"Utils","descr":{"en":"","ru":""},"props":{},"fn":{"FileAPI.each":{"name":"FileAPI.each","label":"FileAPI.each","args":{"obj":{"en":"array or object","ru":"массив или объект"},"callback":{"en":"a function to execute for each element.","ru":"функция, выполняется для каждого элемента."},"thisObject":{"en":"object to use as `this` when executing `callback`.","ru":"объект для использования в качестве `this` при выполнении `callback`."}},"variants":[{"args":[{"name":"obj","type":"Object|Array","optional":false},{"name":"callback","type":"Function","optional":false},{"name":"thisObject","type":"Mixed","optional":true}],"descr":{"en":"Iterate over a object or array, executing a function for each matched element.","ru":"Перебор объект или массив, выполняя функцию для каждого элемента."}}],"returns":"void"},"FileAPI.extend":{"name":"FileAPI.extend","label":"FileAPI.extend","args":{"dst":{"en":"an object that will receive the new properties","ru":"объект, который получит новые свойства"},"src":{"en":"an object containing additional properties to merge in.","ru":"объект, содержащий дополнительные свойства для объединения"}},"variants":[{"args":[{"name":"dst","type":"Object","optional":false},{"name":"src","type":"Object","optional":false}],"descr":{"en":"Merge the contents of two objects together into the first object.","ru":"Объединить содержимое двух объектов вместе."}}],"returns":"Object"},"FileAPI.filter":{"name":"FileAPI.filter","label":"FileAPI.filter","args":{"array":{"en":"original Array","ru":"оригинальный массив"},"callback":{"en":"Function to test each element of the array.","ru":"функция для проверки каждого элемента массива."},"thisObject":{"en":"object to use as `this` when executing `callback`.","ru":"объект для использования в качестве `this` при выполнении `callback`."}},"variants":[{"args":[{"name":"array","type":"Array","optional":false},{"name":"callback","type":"Function","optional":false},{"name":"thisObject","type":"Mixed","optional":false}],"descr":{"en":"Creates a new array with all elements that pass the test implemented by the provided function.","ru":"Создает новый массив со всеми элементами, которые соответствуют условиям."}}],"returns":"Object"}}},"Support":{"label":"support","class":"Support","descr":{"en":"\n\tMultiupload: all browsers that support HTML5 or Flash \n\tDrag'n'Drop upload: files (HTML5) & directories (Chrome 21+) \n\tChunked file upload (HTML5) \n\tUpload one file: all browsers \n\t\n\t\tWorking with Images: IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+\n\t\t\n\t\t\tcrop, resize, preview & rotate (HTML5 or Flash) \n\t\t\tauto orientation by exif (HTML5, if include FileAPI.exif.js or Flash) \n\t\t \n\t \n ","ru":"\n\tMultiupload: все браузеры поддерживающие HTML5 или Flash \n\tDrag'n'Drop загрузка: файлы (HTML5) и директории (Chrome 21+) \n\tЗагрузка файлов по частям, только HTML5 \n\tЗагрузка одно файла: все браузеры, даже очень старые \n\t\n\t\tРабота с изображениями: IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+\n\t\t\n\t\t\tcrop, resize, preview & rotate (HTML5 или Flash) \n\t\t\tавто ориентация на основе EXIF (HTML5, если подключен FileAPI.exif.js или Flash) \n\t\t \n\t \n "},"props":{"FileAPI.support.html5":{"name":"FileAPI.support.html5","type":"Boolean","label":"FileAPI.support.html5","descr":{"en":"HTML5 borwser support","ru":"Поддержка HTML5."}},"FileAPI.support.cors":{"name":"FileAPI.support.cors","type":"Boolean","label":"FileAPI.support.cors","descr":{"en":"This cross-origin resource sharing is used to enable cross-site HTTP requests.","ru":"Поддержка кроссдоменных запросов."}},"FileAPI.support.dnd":{"name":"FileAPI.support.dnd","type":"Boolean","label":"FileAPI.support.dnd","descr":{"en":"Drag'n'drop events support.","ru":"Поддержка Drag'n'drop событий."}},"FileAPI.support.flash":{"name":"FileAPI.support.flash","type":"Boolean","label":"FileAPI.support.flash","descr":{"en":"Availability Flash plugin.","ru":"Наличие Flash плагина."}},"FileAPI.support.canvas":{"name":"FileAPI.support.canvas","type":"Boolean","label":"FileAPI.support.canvas","descr":{"en":"Canvas support.","ru":"Поддержка canvas."}},"FileAPI.support.dataURI":{"name":"FileAPI.support.dataURI","type":"Boolean","label":"FileAPI.support.dataURI","descr":{"en":"Support dataURI as src for image.","ru":"Поддержка dataURI в качестве src для изображений."}},"FileAPI.support.chunked":{"name":"FileAPI.support.chunked","type":"Boolean","label":"FileAPI.support.chunked","descr":{"en":"Support chuncked upload.","ru":"Возможность загрузки по частям."}}},"fn":{}},"Flash":{"label":"flash","class":"Flash","descr":{"en":"Flash is very \"buggy\" thing :]\nThe server response can not be empty.\nTherefore, in the event of a successful uploading `http status` should be only `200 OK`.","ru":"Флеш очень \"глючная\" штука :]\nПоэтому в случае успешной загрузки http status должен быть только `200 OK`."},"props":{"Settings":{"name":"Settings","type":-1,"label":"flash.settings","descr":{"en":"Flash settings.\nIt is advisable to place flash on the same server where the files will be uploaded.","ru":"Настройки для flash части.\nЖелательно, разместить flash на том же сервере, куда будут загружаться файлы."},"code":{"type":"html","source":{"en":"\n","ru":"\n"}}},"crossdomain.xml":{"name":"crossdomain.xml","type":-1,"label":"crossdomain.xml","descr":{"en":"Necessarily make this file on the server.\nDo not forget to replace `youdomain.com` on the name of your domain.","ru":"Обязательно создайте этот файл на сервере, куда будут загружаться файлы.\nНе забудьте заменить `youdomain.com` на имя вашего домена."},"code":{"type":"xml","source":{"en":"\n\n\n \n \n \n \n ","ru":"\n\n\n \n \n \n \n "}}},"request":{"name":"request","type":-1,"label":"flash.request","descr":{"en":"The following sample HTTP POST request is sent from Flash Player to a server-side script if no parameters are specified:","ru":"Пример запроса, который отправляет flash player."},"code":{"type":"xml","source":{"en":"POST /server/ctrl.php HTTP/1.1\nAccept: text/*\nContent-Type: multipart/form-data;\nboundary=----------Ij5ae0ae0KM7GI3KM7\nUser-Agent: Shockwave Flash\nHost: www.youdomain.com\nContent-Length: 421\nConnection: Keep-Alive\nCache-Control: no-cache\n\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filename\"\n\nMyFile.jpg\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"MyFile.jpg\"\nContent-Type: application/octet-stream\n\n[[..FILE_DATA_HERE..]]\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Upload\"\n\nSubmit Query\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7--","ru":"POST /server/ctrl.php HTTP/1.1\nAccept: text/*\nContent-Type: multipart/form-data;\nboundary=----------Ij5ae0ae0KM7GI3KM7\nUser-Agent: Shockwave Flash\nHost: www.youdomain.com\nContent-Length: 421\nConnection: Keep-Alive\nCache-Control: no-cache\n\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filename\"\n\nMyFile.jpg\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"MyFile.jpg\"\nContent-Type: application/octet-stream\n\n[[..FILE_DATA_HERE..]]\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7\nContent-Disposition: form-data; name=\"Upload\"\n\nSubmit Query\n------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7--"}}}},"fn":{}},"Server settings":{"label":"server","class":"Server settings","descr":{"en":"","ru":""},"props":{"IFrame/JSONP":{"name":"IFrame/JSONP","type":-1,"label":"server.iframe","descr":{"en":"","ru":""},"code":{"type":"php","source":{"en":"\n\n\n\n FileAPI::OK\n , 'statusText' => 'OK'\n , 'body' => array('count' => sizeof($files)\n ), $jsonp);\n exit;\n }\n?>","ru":"\n\n\n\n FileAPI::OK\n , 'statusText' => 'OK'\n , 'body' => array('count' => sizeof($files)\n ), $jsonp);\n exit;\n }\n?>"}}},"CORS":{"name":"CORS","type":-1,"label":"server.CORS","descr":{"en":"Enable CORS.","ru":"Включение CORS."},"code":{"type":"php","source":{"en":"","ru":"\nClient explicitly sets the following headers: \n\n\tContent-Range: bytes <start-offset>-<end-offset>/<total> \n\tContent-Disposition: attachment; filename=<file-name> \n \nAny other headers are set by a target browser and are not used by client. Library does not provide any facilities to track a file uniqueness across requests, it's left on developer's consideration. \nResponse codes:\n\n\t200 - last chunk is uploaded \n\t201 - chunk is successfully saved \n\t416 - range is not acceptable error, recoverable \n\t500 - server error, recoverable \n \nFor recoverable errors server tries to resend chunk `chunkUploadRetry` times then fails. \n\tX-Last-Known-Byte: int, library tries to resend chunk from the given offset. Applicable to response codes 200 and 416 \n\nAll the other codes - fatal error, user's involvement is recommend.","ru":"Всё общение между клиентом и сервером ведётся на уровне HTTP заголовков. \nДля передачи отдельного chunk'а клиент устанавливает заголовки: \n\n\tContent-Range: bytes <start-offset>-<end-offset>/<total> \n\tContent-Disposition: attachment; filename=<file-name> \n \nДругие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика. \nВ ответ на передаваемый chunk сервер может отвечать следующими кодами: \n\n\t200, 201 — chunk сохранён успешно \n\t416, 500 — восстановимая ошибка \n \nОстальные коды — фатальная ошибка, требуется вмешательство пользователя."}}},"fn":{}},"Buttons examples":{"label":"buttons.examples","class":"Buttons examples","descr":{"en":"","ru":""},"props":{"Base":{"name":"Base","type":-1,"label":"buttons.examples.base","descr":{"en":"Simple input[type=\"file\"]","ru":"Простой input[type=\"file\"]"},"code":{"type":"html","source":{"en":"\n \n ","ru":"\n \n "}}},"Button":{"name":"Button","type":-1,"label":"buttons.examples.button","descr":{"en":"Stylized button.","ru":"Стилизованная кнопка."},"code":{"type":"html","source":{"en":"\n","ru":"\n"}}},"Link":{"name":"Link","type":-1,"label":"buttons.examples.link","descr":{"en":"Button like link.","ru":"Кнопка в виде ссылки"},"code":{"type":"html","source":{"en":"\n\n Upload photo \n \n ","ru":"\n\n Upload photo \n \n "}}}},"fn":{}},"Installation":{"label":"install","class":"Installation","descr":{"en":"`npm install fileapi` \n`cd fileapi` \n`npm install` \n`grunt`"},"props":{},"fn":{}},"Changelog":{"label":"Changelog","class":"Changelog","descr":{"en":""},"props":{"2.0.0":{"name":"2.0.0","type":-1,"label":"Changelog","descr":{"en":"\n\t+ FileAPI.Camera (HTML5 and Flash fallback) \n\t+ jquery.fileapi.js, see demo \n\t+ npm support \n\t+ grunt support \n\t+ requirejs support \n\t+ [#80](https://https://github.com/mailru/FileAPI/issues/80): FileAPI.Image.fn.overlay \n \t`imageTransform` — now supports: `crop`, `type`, `quality` and `overlay` properties. \n\tImproved the documentation \n\t+iOS fix (https://github.com/blueimp/JavaScript-Load-Image) \n\t[#121](https://github.com/mailru/FileAPI/issues/121): + FileAPI.`postNameConcat:Function(name, idx)` \n\t[#116](https://github.com/mailru/FileAPI/issues/116): + `cache:false` option for FileAPI.upload \n "}},"1.2.6":{"name":"1.2.6","type":-1,"label":"Changelog","descr":{"en":"\n\t[#91](https://github.com/mailru/FileAPI/issues/91): replace `new Image` to `FileAPI.newImage` \n\t+ FileAPI.withCredentials: true \n\t[#90](https://github.com/mailru/FileAPI/issues/90): Fixed `progress` event \n\t[#105](https://github.com/mailru/FileAPI/issues/105): Fixed `image/jpg` -> `image/jpeg` \n\t[#108](https://github.com/mailru/FileAPI/issues/108): Check width/height before resize by type(min/max) \n "}},"1.2.5":{"name":"1.2.5","type":-1,"label":"Changelog","descr":{"en":"\n\t[#86](https://github.com/mailru/FileAPI/issues/86): Smarter upload recovery \n\t[#87](https://github.com/mailru/FileAPI/issues/87): Fixed upload files into browsers that do not support FormData \n\tFixed support \"accept\" attribute for Flash. \n\tFixed detection of HTML5 support for FireFox 3.6 \n\t + FileAPI.html5 option, default \"true\" \n "}},"1.2.4":{"name":"1.2.4","type":-1,"label":"Changelog","descr":{"en":"\n\tFixed auto orientation image by EXIF (Flash) \n\tFixed image dimensions after rotate (Flash) \n\t[#82](https://github.com/mailru/FileAPI/issues/82): \"undefined\" data-fields cause exceptions \n\t[#83](https://github.com/mailru/FileAPI/issues/83): Allow requests without files \n\t[#84](https://github.com/mailru/FileAPI/pull/84): Fixed connection abort when waiting for connection recovery \n "}},"1.2.3":{"name":"1.2.3","type":-1,"label":"Changelog","descr":{"en":"\n\t[#77](https://github.com/mailru/FileAPI/pull/77): Fixed flash.abort(), [#75](https://github.com/mailru/FileAPI/issues/75) \n\t- `FileAPI.addMime` \n\t+ `FileAPI.accept` — fallback for flash. \n "}},"1.2.2":{"name":"1.2.2","type":-1,"label":"Changelog","descr":{"en":"\n\t[#67](https://github.com/mailru/FileAPI/pull/67): Added correct httpStatus for upload fail, [#62](https://github.com/mailru/FileAPI/pull/68) \n\t[#68](https://github.com/mailru/FileAPI/pull/68) Added \"Content-Type\" for chunked upload, [#65](https://github.com/mailru/FileAPI/pull/65) \n\t[#69](https://github.com/mailru/FileAPI/issues/69): Fixed network down recovery \n\tFixed progress event, [#66](https://github.com/mailru/FileAPI/issues/66) \n\tIncrease flash stage size, [#73](https://github.com/mailru/FileAPI/pull/73) \n\t- array index from POST-param \"name\", [#72](https://github.com/mailru/FileAPI/issues/72) \n\t- dependency on FileAPI.Image for FileAPI.Flash \n "}},"1.2.1":{"name":"1.2.1","type":-1,"label":"Changelog","descr":{"en":"\n\t[#64](https://github.com/mailru/FileAPI/issues/64): Bufixed for [#63](https://github.com/mailru/FileAPI/issues/63) \n "}},"1.2.0":{"name":"1.2.0","type":-1,"label":"Changelog","descr":{"en":"\n\t[#57](https://github.com/mailru/FileAPI/issues/57): Chunked file upload \n "}},"1.1.0":{"name":"1.1.0","type":-1,"label":"Changelog","descr":{"en":"\n\t[#54](https://github.com/mailru/FileAPI/issues/54): added `FileAPI.flashUrl` and `FileAPI.flashImageUrl` \n "}},"1.0.1":{"name":"1.0.1","type":-1,"label":"Changelog","descr":{"en":"\n\t[#51](https://github.com/mailru/FileAPI/issues/51): remove circular references from `file-objects` (Flash transport) \n\tadded `changelog` \n "}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":""}}},"fn":{}}}
diff --git a/tests/.DS_Store b/tests/.DS_Store
deleted file mode 100644
index 2ba8b680..00000000
Binary files a/tests/.DS_Store and /dev/null differ
diff --git a/tests/files/.DS_Store b/tests/files/.DS_Store
deleted file mode 100644
index 87622715..00000000
Binary files a/tests/files/.DS_Store and /dev/null differ
diff --git a/tests/files/big.jpg b/tests/files/big.jpg
new file mode 100644
index 00000000..d0dc3b17
Binary files /dev/null and b/tests/files/big.jpg differ
diff --git a/tests/files/samples/firefox-vintage.png b/tests/files/samples/firefox-vintage.png
new file mode 100644
index 00000000..ac85e950
Binary files /dev/null and b/tests/files/samples/firefox-vintage.png differ
diff --git a/tests/files/samples/phantomjs-vintage.png b/tests/files/samples/phantomjs-vintage.png
new file mode 100644
index 00000000..ac85e950
Binary files /dev/null and b/tests/files/samples/phantomjs-vintage.png differ
diff --git a/tests/flash.html b/tests/flash.html
new file mode 100644
index 00000000..057ec35b
--- /dev/null
+++ b/tests/flash.html
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+ FileAPI :: Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+