diff --git a/.gitignore b/.gitignore index 9225f8c8..feefd2f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ node_modules -tests/files/BigJPG.jpg -tests/files/big.jpg +dist/FileAPI.html5ok.js +dist/FileAPI.html5ok.min.js +dist/FileAPI.ok.js +dist/FileAPI.ok.min.js +.idea diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..191381ee --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +.git \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 98f49b4b..1fdfc355 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - 0.10 + - 4.5 before_script: - npm install -g grunt-cli diff --git a/Gruntfile.js b/Gruntfile.js index b7c9f352..9276a4b6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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 @@ -67,7 +67,7 @@ module.exports = function (grunt) { options: { timeout: 5 * 60 * 1000, // 5min files: { - '1px.gif': ['tests/files/1px.gif'] + '1px_gif': ['tests/files/1px.gif'] , 'big.jpg': ['tests/files/big.jpg'] , 'hello.txt': ['tests/files/hello.txt'] , 'image.jpg': ['tests/files/image.jpg'] @@ -85,7 +85,7 @@ module.exports = function (grunt) { ' * <%= 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: { @@ -131,7 +131,7 @@ module.exports = function (grunt) { mxmlc: { core: { options: { - rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=true' + + 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: { @@ -140,7 +140,7 @@ module.exports = function (grunt) { }, image: { options: { - rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=true' + + rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' + ' -library-path+=flash/image/lib/blooddy_crypto.swc' }, files: { @@ -149,7 +149,7 @@ module.exports = function (grunt) { }, camera: { options: { - rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=true' + rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' }, files: { 'dist/<%= pkg.exportName %>.flash.camera.swf': ['flash/camera/src/FileAPI_flash_camera.as'] @@ -176,17 +176,31 @@ module.exports = function (grunt) { 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('prepare-test-files', function (){ - if (!grunt.file.exists('tests/files/big.jpg')) { + // 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('tests', ['jshint', 'concat', 'connect:server','prepare-test-files', 'qunit']); - grunt.registerTask('build', ['version', 'concat', 'uglify', 'mxmlc']); + + 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 23d505d2..b5037a77 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@  ## FileAPI -A set of javascript tools for working with files. +A set of JavaScript tools for working with files. ### Get started @@ -10,9 +10,9 @@ Download the files from the [dist](https://github.com/mailru/FileAPI/tree/master ```html
-
+
Choose files
- +
@@ -20,6 +20,7 @@ Download the files from the [dist](https://github.com/mailru/FileAPI/tree/master - - - - -
-

- To view this page ensure that Adobe Flash Player version - ${version_major}.${version_minor}.${version_revision} or greater is installed. -

- -
- - - - + + + + + + ${title} + + + + + + + + + + + + + +
+

+ To view this page ensure that Adobe Flash Player version + ${version_major}.${version_minor}.${version_revision} or greater is 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 b37dd45e..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(); @@ -130,8 +128,8 @@ package ru.mail.commands } else { -// currentImageMap = fullImageMap.clone(); - currentImageMap = fullImageMap; + currentImageMap = fullImageMap.clone(); +// currentImageMap = fullImageMap; // #199 #265 вернул обратно clone(), т.к. ниже есть currentImageMap.dispose() } // ============== @@ -143,15 +141,12 @@ package ru.mail.commands if (imageTransform.multiPassResize && maxScale < 0.5) { - trace ("multi-step "); - var curWidth:Number = currentImageMap.width; var curHeight:Number = currentImageMap.height; var mapToScale:BitmapData; // multi-step while(maxScale < 0.5) { - trace ("step ", maxScale); // series if x2 scalings // temp bitmapdata 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 a49c6cf7..2c5e38d9 100644 --- a/flash/core/src/ru/mail/controller/AppController.as +++ b/flash/core/src/ru/mail/controller/AppController.as @@ -514,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; } @@ -526,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) { diff --git a/flash/core/src/ru/mail/data/ImageFactory.as b/flash/core/src/ru/mail/data/ImageFactory.as index 8bfbeb85..8ca45f8e 100644 --- a/flash/core/src/ru/mail/data/ImageFactory.as +++ b/flash/core/src/ru/mail/data/ImageFactory.as @@ -202,13 +202,10 @@ package ru.mail.data trace ("bitmap created, isSuccess", event.isSuccess); LoggerJS.log('ImageFactory bitmap created, success '+ event.isSuccess); if (event.isSuccess) { - file.imageData = event.decodedBitmap.bitmapData; -/* file.imageData = new BitmapData( event.decodedBitmap.width, event.decodedBitmap.height ); file.imageData.copyPixels( event.decodedBitmap.bitmapData , new Rectangle( 0, 0, event.decodedBitmap.width, event.decodedBitmap.height ), new Point( 0, 0 )); event.decodedBitmap.bitmapData.dispose(); -*/ createImageFromSource(imageTransform); } 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/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/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 index 0db141d1..ddf97e65 100644 --- a/lib/FileAPI.Flash.Camera.js +++ b/lib/FileAPI.Flash.Camera.js @@ -11,10 +11,8 @@ var _each = api.each, _cameraQueue = []; - - if (api.support.flash && (api.media && !api.support.media)) { + 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; diff --git a/lib/FileAPI.Flash.js b/lib/FileAPI.Flash.js index d6d2b01b..6ec95736 100644 --- a/lib/FileAPI.Flash.js +++ b/lib/FileAPI.Flash.js @@ -44,6 +44,7 @@ || !api.html5 || !api.support.html5 || (api.cors && !api.support.cors) || (api.media && !api.support.media) + || api.insecureChrome ) && (function (){ var @@ -77,7 +78,7 @@ , width: 5 , height: 5 , position: 'absolute' - , zIndex: 1e6+'' // set max zIndex + , zIndex: 2147483647+'' // set max zIndex }); child.parentNode.insertBefore(dummy, child); @@ -157,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) @@ -165,7 +166,7 @@ if( api.multiFlash ){ // check state: - // i — published + // p — published // i — initialization // r — ready if( state == 'i' || state == 'r' ){ @@ -186,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' }); @@ -327,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); 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 d17ef8ba..87b7e500 100644 --- a/lib/FileAPI.Image.js +++ b/lib/FileAPI.Image.js @@ -12,6 +12,9 @@ 8: 270 , 3: 180 , 6: 90 + , 7: 270 + , 4: 180 + , 5: 90 } ; @@ -70,7 +73,7 @@ }, resize: function (w, h, strategy){ - if( /min|max/.test(h) ){ + if( /min|max|height|width/.test(h) ){ strategy = h; h = w; } @@ -253,6 +256,12 @@ } } } + else if( strategy == 'height' ){ + dw = dh * sf; + } + else if( strategy == 'width' ){ + dh = dw / sf; + } else if( strategy ){ if( !(sw > dw || sh > dh) ){ dw = sw; @@ -339,9 +348,9 @@ 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 ){ @@ -362,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){ diff --git a/lib/FileAPI.XHR.js b/lib/FileAPI.XHR.js index 98ef2893..d4f2472b 100644 --- a/lib/FileAPI.XHR.js +++ b/lib/FileAPI.XHR.js @@ -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); @@ -96,29 +101,6 @@ // legacy options.upload(options, _this); - xhr = document.createElement('div'); - xhr.innerHTML = '
' - + '' - + (jsonp && (options.url.indexOf('=?') < 0) ? '' : '') - + '
' - ; - - // get form-data & transport - var - form = xhr.getElementsByTagName('form')[0] - , transport = xhr.getElementsByTagName('iframe')[0] - ; - - form.appendChild(data); - - api.log(form.parentNode.innerHTML); - - // append to DOM - document.body.appendChild(xhr); - - // keep a reference to node-transport - _this.xhr.node = xhr; - var onPostMessage = function (evt){ if( ~url.indexOf(evt.origin) ){ @@ -140,7 +122,7 @@ _this.end(status, statusText); api.event.off(window, 'message', onPostMessage); - window[uid] = xhr = transport = transport.onload = null; + window[uid] = xhr = transport = window[onLoadFnName] = null; } ; @@ -156,7 +138,7 @@ api.event.on(window, 'message', onPostMessage); - transport.onload = function (){ + window[onLoadFnName] = function (){ try { var win = transport.contentWindow @@ -169,9 +151,36 @@ } }; + xhr = document.createElement('div'); + xhr.innerHTML = '
' + + '' + + (jsonp && (options.url.indexOf('=?') < 0) ? '' : '') + + '
' + ; + + // get form-data & transport + var + form = xhr.getElementsByTagName('form')[0] + , transport = xhr.getElementsByTagName('iframe')[0] + ; + + form.appendChild(data); + + api.log(form.parentNode.innerHTML); + + // append to DOM + document.body.appendChild(xhr); + + // keep a reference to node-transport + _this.xhr.node = xhr; + // send _this.readyState = 2; // loaded - form.submit(); + try { + form.submit(); + } catch (err) { + api.log('iframe.error: ' + err); + } form = null; } else { @@ -189,9 +198,11 @@ url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&"); } - xhr.open('POST', url, true); + xhr.open(options.uploadMethod || 'POST', url, true); - if( api.withCredentials ){ + if (typeof options.uploadCredentials === 'boolean') { + xhr.withCredentials = options.uploadCredentials ? 'true' : null; + } else if( api.withCredentials ){ xhr.withCredentials = "true"; } @@ -373,7 +384,7 @@ } } else { - // FormData + // FormData xhr.send(data); } } diff --git a/lib/FileAPI.core.js b/lib/FileAPI.core.js index 2eda36d6..14c7b467 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, @@ -184,13 +193,14 @@ * FileAPI (core object) */ api = { - version: '2.0.4', + version: '2.1.1', cors: false, html5: true, media: false, formData: true, multiPassResize: true, + insecureChrome: insecureChrome, debug: false, pingUrl: false, @@ -244,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 { @@ -462,19 +472,25 @@ /** - * 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 html5 && blob && (blob instanceof Blob); + return this.isFile(blob) || (_toString.call(blob) === '[object Blob]'); }, + /** * Is canvas element * @@ -600,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); @@ -693,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(); }); } @@ -825,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 ){ @@ -1034,7 +1106,7 @@ 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, @@ -1048,15 +1120,17 @@ 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; + // 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); @@ -1203,7 +1277,13 @@ 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); @@ -1387,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.isBlob(file) && _hasSupportReadAs(as) ){ + function _readAs(file, fn, method, encoding){ + if( api.isBlob(file) && _hasSupportReadAs(method) ){ var Reader = new FileReader; // Add event listener @@ -1415,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){ @@ -1426,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 { @@ -1461,6 +1546,11 @@ } + function _isEntry(item){ + return item && (item.isFile || item.isDirectory); + } + + function _getAsEntry(item){ var entry; if( item.getAsEntry ){ entry = item.getAsEntry(); } @@ -1472,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); @@ -1589,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 @@ -1622,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 { @@ -1646,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); }; @@ -1684,7 +1801,13 @@ }); - // @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'; } 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 ff6f1cea..8f4707c9 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,43 @@ { "name": "fileapi", "exportName": "FileAPI", - "version": "2.0.4", + "version": "2.2.0", "devDependencies": { - "grunt": "~0.4.0", - "grunt-version": "~0.2.2", - "grunt-contrib-jshint": "~0.2.0", - "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-uglify": "~0.2.7", - "grunt-contrib-watch": "~0.5.3", - "grunt-contrib-connect": "~0.5.0", - "eventemitter2": "~0.4.9", - "semver": "~1.0.14", - "temporary": "~0.0.4", - "phantomjs": "~1.9.2-4", - "grunt-curl": "~1.4.0", - "grunt-mxmlc": "~0.2.0", - "grunt-contrib-compress": "~0.6.0" + "connect-busboy": "~0.0.2", + "eventemitter2": "~0.4.13", + "express": "~4.12.3", + "flex-sdk": "^4.6.0-0", + "grunt": "^0.4.5", + "grunt-cli": "^1.3.2", + "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", + "*.js" + ], + "jam": { + "name": "FileAPI" + }, "scripts": { - "test": "grunt tests --verbose" + "test": "grunt tests --verbose", + "build": "grunt build" }, "repository": { "type": "git", @@ -38,7 +54,8 @@ "contributors": [ "Vladimir Demidov ", "Ilya Lebedev ", - "Mikhail Bezoyan " + "Mikhail Bezoyan " ], - "license": "BSD" + "license": "BSD", + "dependencies": {} } 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(/\n \n
\n
Choose files
\n \n
\n
\n
\n\n \n \n ","ru":"
\n \n
\n
Choose files
\n \n
\n
\n
\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":"новая высота"},"strategy":{"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":"strategy","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\t
  • Multiupload: all browsers that support HTML5 or Flash
  • \n\t
  • Drag'n'Drop upload: files (HTML5) & directories (Chrome 21+)
  • \n\t
  • Chunked file upload (HTML5)
  • \n\t
  • Upload 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\t
    • crop, resize, preview & rotate (HTML5 or Flash)
    • \n\t\t\t
    • auto orientation by exif (HTML5, if include FileAPI.exif.js or Flash)
    • \n\t\t
    \n\t
  • \n
","ru":"
    \n\t
  • Multiupload: все браузеры поддерживающие HTML5 или Flash
  • \n\t
  • Drag'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\t
    • crop, 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\t
  • Content-Range: bytes <start-offset>-<end-offset>/<total>
  • \n\t
  • Content-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\t
  • 200 - last chunk is uploaded
  • \n\t
  • 201 - chunk is successfully saved
  • \n\t
  • 416 - range is not acceptable error, recoverable
  • \n\t
  • 500 - server error, recoverable
  • \n
\nFor recoverable errors server tries to resend chunk `chunkUploadRetry` times then fails.
\n\t
  • X-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\t
    • Content-Range: bytes <start-offset>-<end-offset>/<total>
    • \n\t
    • Content-Disposition: attachment; filename=<file-name>
    • \n
    \nДругие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика.
    \nВ ответ на передаваемый chunk сервер может отвечать следующими кодами:
    \n
      \n\t
    • 200, 201 — chunk сохранён успешно
    • \n\t
    • 416, 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
    \n
    Upload files
    \n \n
    ","ru":"\n
    \n
    Upload files
    \n \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\t
    • Improved 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\t
    • Fixed support \"accept\" attribute for Flash.
    • \n\t
    • Fixed 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\t
    • Fixed auto orientation image by EXIF (Flash)
    • \n\t
    • Fixed 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\t
    • Fixed progress event, [#66](https://github.com/mailru/FileAPI/issues/66)
    • \n\t
    • Increase 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\t
    • added `changelog`
    • \n
    "}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":"
      \n\t
    • first release
    • \n
    "}}},"fn":{}}} +{"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
    Choose files
    \n \n
    \n
    \n
    \n\n \n \n ","ru":"
    \n \n
    \n
    Choose files
    \n \n
    \n
    \n
    \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\t
    • Multiupload: all browsers that support HTML5 or Flash
    • \n\t
    • Drag'n'Drop upload: files (HTML5) & directories (Chrome 21+)
    • \n\t
    • Chunked file upload (HTML5)
    • \n\t
    • Upload 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\t
      • crop, resize, preview & rotate (HTML5 or Flash)
      • \n\t\t\t
      • auto orientation by exif (HTML5, if include FileAPI.exif.js or Flash)
      • \n\t\t
      \n\t
    • \n
    ","ru":"
      \n\t
    • Multiupload: все браузеры поддерживающие HTML5 или Flash
    • \n\t
    • Drag'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\t
      • crop, 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\t
    • Content-Range: bytes <start-offset>-<end-offset>/<total>
    • \n\t
    • Content-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\t
    • 200 - last chunk is uploaded
    • \n\t
    • 201 - chunk is successfully saved
    • \n\t
    • 416 - range is not acceptable error, recoverable
    • \n\t
    • 500 - server error, recoverable
    • \n
    \nFor recoverable errors server tries to resend chunk `chunkUploadRetry` times then fails.
    \n\t
  • X-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\t
    • Content-Range: bytes <start-offset>-<end-offset>/<total>
    • \n\t
    • Content-Disposition: attachment; filename=<file-name>
    • \n
    \nДругие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика.
    \nВ ответ на передаваемый chunk сервер может отвечать следующими кодами:
    \n
      \n\t
    • 200, 201 — chunk сохранён успешно
    • \n\t
    • 416, 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
    \n
    Upload files
    \n \n
    ","ru":"\n
    \n
    Upload files
    \n \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\t
    • Improved 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\t
    • Fixed support \"accept\" attribute for Flash.
    • \n\t
    • Fixed 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\t
    • Fixed auto orientation image by EXIF (Flash)
    • \n\t
    • Fixed 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\t
    • Fixed progress event, [#66](https://github.com/mailru/FileAPI/issues/66)
    • \n\t
    • Increase 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\t
    • added `changelog`
    • \n
    "}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":"
      \n\t
    • first release
    • \n
    "}}},"fn":{}}} 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/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 + + + + + + + + + + + +
    + + + +
    +
    + +
    + +
    + +
    +			
    +		
    +
    + + + + + + + diff --git a/tests/index.html b/tests/index.html index dc234521..5cdabe36 100644 --- a/tests/index.html +++ b/tests/index.html @@ -7,11 +7,12 @@ FileAPI :: Tests @@ -32,7 +33,7 @@
    -
    1px.gif --
    +
    1px.gif --
    big.jpg --
    hello.txt --
    image.jpg --
    diff --git a/tests/tests.js b/tests/tests.js index 42d399f9..4352c815 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -14,6 +14,7 @@ module('FileAPI'); } + var controllerUrl = 'http://127.0.0.1:8000/upload'; var uploadForm = document.forms.upload; var base64_1px_gif = 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; var browser = (navigator.userAgent.match(/(phantomjs|safari|firefox|chrome)/i) || ['', 'chrome'])[1].toLowerCase(); @@ -136,9 +137,10 @@ module('FileAPI'); return fail; } + console.log('\nStart testing\n'); test('1px.gif', function (){ - var file = FileAPI.getFiles(uploadForm['1px.gif'])[0]; + var file = FileAPI.getFiles(uploadForm['1px_gif'])[0]; // File checkFile(file, '1px.gif', 'image/gif', 34); @@ -205,23 +207,20 @@ module('FileAPI'); }); stop(); - FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', - files: { image: file }, - imageTransform: { - width: 1024, - height: 768, - type: 'image/jpeg' - }, - complete: function (err, res){ - start(); - } - }); - + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { + width: 1024, + height: 768, + type: 'image/jpeg' + }, + complete: function (err, res){ + start(); + } + }); }); -// if (false) { - test('hello.txt', function (){ var file = FileAPI.getFiles(uploadForm['hello.txt'])[0]; @@ -238,7 +237,6 @@ module('FileAPI'); } }); - test('image.jpg', function (){ var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; @@ -259,7 +257,6 @@ module('FileAPI'); }); }); - test('filterFiles', function (){ var files = FileAPI.getFiles(uploadForm['multiple']); @@ -279,24 +276,22 @@ module('FileAPI'); }) }); - - - test('upload without files', function (){ stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, data: { str: 'foo', num: 1, array: [1, 2, 3], object: { foo: 'bar' } }, headers: { 'x-foo': 'bar' }, complete: function (err, xhr){ - var res = FileAPI.parseJSON(xhr.responseText).data._REQUEST; - var headers = FileAPI.parseJSON(xhr.responseText).data.HEADERS; + var res = err ? {} : FileAPI.parseJSON(xhr.responseText).data._REQUEST; + var headers = err ? err : FileAPI.parseJSON(xhr.responseText).data.HEADERS; start(); + ok(!err, 'upload done'); equal(res.str, 'foo', 'string'); equal(res.num, '1', 'number'); - equal(headers['X-Foo'], 'bar', 'headers.X-Foo'); + equal(headers['x-foo'], 'bar', 'headers.x-foo'); if( !FileAPI.html5 || !FileAPI.support.html5 ){ deepEqual(res.array, { "0": '1', "1": '2', "2": '3' }, 'array'); @@ -310,18 +305,16 @@ module('FileAPI'); }) }); - - test('upload input', function (){ var rnd = Math.random(); expect(15); stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, data: { foo: 'bar' }, headers: { 'x-foo': 'bar', 'x-rnd': rnd }, - files: uploadForm['1px.gif'], + files: uploadForm['1px_gif'], upload: function (){ ok(true, 'upload event'); }, @@ -338,13 +331,15 @@ module('FileAPI'); equal(res.data._REQUEST.foo, 'bar'); equal(res.data._REQUEST.bar, 'qux'); - equal(res.data.HEADERS['X-Foo'], 'bar', 'headers.X-Foo'); - equal(res.data.HEADERS['X-Rnd'], rnd, 'headers.X-Rnd'); + equal(res.data.HEADERS['x-foo'], 'bar', 'headers.x-foo'); + equal(res.data.HEADERS['x-rnd'], rnd, 'headers.x-rnd'); if( res.data._FILES['1px_gif'] ){ var type = res.data._FILES['1px_gif'].type; equal(res.data._FILES['1px_gif'].name, '1px.gif', 'file.name'); equal(type, /application/.test(type) ? 'application/octet-stream' : 'image/gif', 'file.type'); + } else { + ok(false, "res.data._FILES['1px_gif'] not found"); } if( res.images['1px_gif'] ){ @@ -352,6 +347,8 @@ module('FileAPI'); equal(res.images['1px_gif'].mime, 'image/gif', 'mime'); equal(res.images['1px_gif'].width, 1, 'width'); equal(res.images['1px_gif'].height, 1, 'height'); + } else { + ok(false, "res.images['1px_gif'] not found"); } }, complete: function (err, xhr){ @@ -361,14 +358,12 @@ module('FileAPI'); }); }); - - test('upload file', function (){ var _progressFail = false, _progress = 0; stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { text: FileAPI.getFiles(uploadForm['hello.txt']) }, progress: function (evt){ _progressFail = _progressFail || _checkProgressEvent(evt); @@ -388,8 +383,6 @@ module('FileAPI'); }); }); - - test('multiupload', function (){ stop(); var @@ -401,7 +394,7 @@ module('FileAPI'); ; FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: uploadForm['multiple'], fileupload: function (){ _start++; @@ -433,15 +426,19 @@ module('FileAPI'); }); }); - FileAPI.html5 && test('upload FileAPI.Image', function (){ var file = FileAPI.getFiles(uploadForm['dino.png'])[0]; var image = FileAPI.Image(file).rotate(90+360).preview(100); - var _progressFail = false, _progress = 0; + var _progressFail = false, + _progress = 0, + _fileprogress = 0, + _filecomplete, + _filecompleteErr + ; stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, headers: { 'x-foo': 'bar' }, files: { image: image }, progress: function (evt){ @@ -453,21 +450,33 @@ module('FileAPI'); } _progress = evt.loaded; }, + fileprogress: function (evt) { + if (_fileprogress < evt.loaded) { + _fileprogress = evt.loaded; + } + }, + filecomplete: function (err, res){ + _filecomplete = res.responseText; + _filecompleteErr = err; + }, complete: function (err, res){ - var res = FileAPI.parseJSON(res.responseText); + var json = FileAPI.parseJSON(res.responseText); ok(_progress > 0, 'progress event'); - equal(res.data.HEADERS['X-Foo'], 'bar', 'X-Foo'); + ok(_fileprogress > 0, 'fileprogress event'); + + equal(err, _filecompleteErr, 'filecomplete.err'); + equal(res.responseText, _filecomplete, 'filecomplete.response'); - imageEqual(res.images.image.dataURL, 'files/samples/'+browser+'-dino-90deg-100x100.png?1', 'dino 90deg 100x100', function (){ + equal(json.data.HEADERS['x-foo'], 'bar', 'x-foo'); + + imageEqual(json.images.image.dataURL, 'files/samples/'+browser+'-dino-90deg-100x100.png?1', 'dino 90deg 100x100', function (){ start(); }); } }); }); - - FileAPI.html5 && test('upload + imageTransform (min, max, preview)', function (){ var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; var queue = FileAPI.queue(start); @@ -477,7 +486,7 @@ module('FileAPI'); // strategy: 'min' queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { width: 100, height: 100, strategy: 'min' }, complete: function (err, res){ @@ -491,7 +500,7 @@ module('FileAPI'); // strategy: 'max' queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { width: 100, height: 100, strategy: 'max' }, complete: function (err, res){ @@ -502,10 +511,38 @@ module('FileAPI'); } }); + // strategy: 'height' + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { width: 100, height: 100, strategy: 'height' }, + complete: function (err, res){ + queue.next(); + var res = FileAPI.parseJSON(res.responseText); + equal(res.images['image'].width, 141, 'height.width'); + equal(res.images['image'].height, 100, 'height.height'); + } + }); + + // strategy: 'width' + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { width: 100, height: 100, strategy: 'width' }, + complete: function (err, res){ + queue.next(); + var res = FileAPI.parseJSON(res.responseText); + equal(res.images['image'].width, 100, 'width.width'); + equal(res.images['image'].height, 70, 'width.height'); + } + }); + // preview queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { width: 100, height: 100, rotate: 'auto', preview: true }, complete: function (err, res){ @@ -518,7 +555,6 @@ module('FileAPI'); }); }); - test('upload + autoOrientation', function (){ var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; var queue = FileAPI.queue(start); @@ -533,7 +569,7 @@ module('FileAPI'); queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageAutoOrientation: true, complete: check.bind('imageAutoOrientation') @@ -541,7 +577,7 @@ module('FileAPI'); queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { rotate: 'auto' }, complete: check.bind('imageTransform.rotate.auto') @@ -549,13 +585,12 @@ module('FileAPI'); queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: FileAPI.Image(file).rotate('auto') }, complete: check.bind('FileAPI.Image.fn.rotate.auto') }); }); - FileAPI.html5 && test('upload + CamanJS', function (){ stop(); FileAPI.Image(FileAPI.getFiles(uploadForm['dino.png'])[0]) @@ -565,7 +600,7 @@ module('FileAPI'); equal(canvas.nodeName.toLowerCase(), 'canvas'); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: { name: 'my-file', @@ -583,13 +618,12 @@ module('FileAPI'); ; }); - - FileAPI.html5 && test('upload + multi imageTransform', function (){ + 0 && FileAPI.html5 && test('upload + multi imageTransform', function (){ var file = FileAPI.getFiles(uploadForm['dino.png'])[0]; stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { 'jpeg': { @@ -620,13 +654,12 @@ module('FileAPI'); }); }); - FileAPI.html5 && test('upload + imageTransform with postName', function (){ var file = FileAPI.getFiles(uploadForm['dino.png'])[0]; stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { image: file }, imageTransform: { '180deg': { @@ -647,8 +680,7 @@ module('FileAPI'); }); }); - - test('iframe', function (){ + 0 && test('iframe', function (){ var html5 = FileAPI.support.html5; var queue = FileAPI.queue(function (){ start(); @@ -661,7 +693,7 @@ module('FileAPI'); // default callback queue.inc(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, complete: function (err, xhr){ var json = FileAPI.parseJSON(xhr.responseText); equal(json.jsonp, 'callback', 'default'); @@ -691,7 +723,6 @@ module('FileAPI'); }); }); - FileAPI.html5 && test('WebCam', function (){ stop(); FileAPI.Camera.publish(document.getElementById('web-cam'), function (err, cam){ @@ -703,7 +734,7 @@ module('FileAPI'); var shot = cam.shot(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: { shot: shot }, complete: function (err, res){ var res = FileAPI.parseJSON(res.responseText); @@ -716,5 +747,5 @@ module('FileAPI'); } }); }); -// } + })();