diff --git a/.gitignore b/.gitignore index 3c3629e6..91832922 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ node_modules +dist/FileAPI.html5ok.js +dist/FileAPI.html5ok.min.js +dist/FileAPI.ok.js +dist/FileAPI.ok.min.js +.idea + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..1fdfc355 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 4.5 +before_script: + - npm install -g grunt-cli diff --git a/Gruntfile.js b/Gruntfile.js index 5d9b3f45..9276a4b6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function (grunt){ +module.exports = function (grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -10,6 +10,7 @@ module.exports = function (grunt){ 'Gruntfile.js' , 'lib/**/*.js' , 'plugins/jquery.fileapi.js' + , 'node/**/*.js' ], options: { @@ -25,7 +26,6 @@ module.exports = function (grunt){ , eqnull: true , node: true - , es5: true , expr: true // - "Expected an assignment or function call and instead saw an expression." , supernew: true // - "Missing '()' invoking a constructor." , laxcomma: true @@ -38,63 +38,121 @@ module.exports = function (grunt){ src: 'lib/FileAPI.core.js' }, - qunit: { - options: { - files: { - '1px.gif': ['tests/files/1px.gif'] - , 'hello.txt': ['tests/files/hello.txt'] - , 'image.jpg': ['tests/files/image.jpg'] - , 'dino.png': ['tests/files/dino.png'] - , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json'] + connect: { + server: { + options: { + port: 9001, + base: '.' } }, - all: ['tests/*.html'] + standalone: { + options: { + hostname: '*', + keepalive: true, + port: 9001, + base: '.' + } + } + }, + + curl: { + jpg: { + src: 'https://dl.dropboxusercontent.com/u/49592745/BigJPG.jpg', + dest: 'tests/files/big.jpg' + } + }, + + qunit: { + all: { + options: { + timeout: 5 * 60 * 1000, // 5min + files: { + '1px_gif': ['tests/files/1px.gif'] + , 'big.jpg': ['tests/files/big.jpg'] + , 'hello.txt': ['tests/files/hello.txt'] + , 'image.jpg': ['tests/files/image.jpg'] + , 'dino.png': ['tests/files/dino.png'] + , 'multiple': ['tests/files/1px.gif', 'tests/files/hello.txt', 'tests/files/image.jpg', 'tests/files/dino.png', 'tests/files/lebowski.json'] + }, + urls: ['http://127.0.0.1:<%=connect.server.options.port%>/tests/index.html'] + } + } }, concat: { options: { - banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %>\n' + + banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %>\n' + ' * <%= pkg.description %>\n' + ' */\n\n', - footer: 'if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }' + footer: 'if( typeof define === "function" && define.amd ){ define("<%= pkg.jam.name %>", [], function (){ return FileAPI; }); }' }, all: { src: [ - 'lib/FileAPI.core.js' + 'lib/canvas-to-blob.js' + , 'lib/FileAPI.core.js' , 'lib/FileAPI.Image.js' + , 'lib/load-image-ios.js' , 'lib/FileAPI.Form.js' , 'lib/FileAPI.XHR.js' , 'lib/FileAPI.Camera.js' , 'lib/FileAPI.Flash.js' - , 'lib/load-image-ios.js' - , 'lib/canvas-to-blob.js' + , 'lib/FileAPI.Flash.Camera.js' ], - dest: 'dist/<%= pkg.name %>.js' + dest: 'dist/<%= pkg.exportName %>.js' }, html5: { src: [ - 'lib/FileAPI.core.js' + 'lib/canvas-to-blob.js' + , 'lib/FileAPI.core.js' , 'lib/FileAPI.Image.js' + , 'lib/load-image-ios.js' , 'lib/FileAPI.Form.js' , 'lib/FileAPI.XHR.js' , 'lib/FileAPI.Camera.js' - , 'lib/load-image-ios.js' - , 'lib/canvas-to-blob.js' + , 'lib/FileAPI.Flash.Camera.js' ], - dest: 'dist/<%= pkg.name %>.html5.js' + dest: 'dist/<%= pkg.exportName %>.html5.js' } }, uglify: { - options: { banner: '/*! <%= pkg.name %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' }, + options: { banner: '/*! <%= pkg.exportName %> <%= pkg.version %> - <%= pkg.license %> | <%= pkg.repository.url %> */\n' }, dist: { files: { - 'dist/<%= pkg.name %>.min.js': ['<%= concat.all.dest %>'] - , 'dist/<%= pkg.name %>.html5.min.js': ['<%= concat.html5.dest %>'] - , 'dist/jquery.fileapi.min.js': ['plugins/jquery.fileapi.js'] + 'dist/<%= pkg.exportName %>.min.js': ['<%= concat.all.dest %>'] + , 'dist/<%= pkg.exportName %>.html5.min.js': ['<%= concat.html5.dest %>'] + } + } + }, + + mxmlc: { + core: { + options: { + rawConfig: '-target-player=10.1 -static-link-runtime-shared-libraries=true -compiler.debug=false' + + ' -library-path+=flash/core/lib/blooddy_crypto.swc -library-path+=flash/core/lib/EnginesLibrary.swc' + }, + files: { + 'dist/<%= pkg.exportName %>.flash.swf': ['flash/core/src/FileAPI_flash.as'] + } + }, + image: { + options: { + rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' + + ' -library-path+=flash/image/lib/blooddy_crypto.swc' + }, + files: { + 'dist/<%= pkg.exportName %>.flash.image.swf': ['flash/image/src/FileAPI_flash_image.as'] + } + }, + camera: { + options: { + rawConfig: '-static-link-runtime-shared-libraries=true -compiler.debug=false' + }, + files: { + 'dist/<%= pkg.exportName %>.flash.camera.swf': ['flash/camera/src/FileAPI_flash_camera.as'] } } }, @@ -108,18 +166,41 @@ module.exports = function (grunt){ } }); - // These plugins provide necessary tasks. grunt.loadNpmTasks('grunt-version'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-compress'); + grunt.loadNpmTasks('grunt-mxmlc'); + grunt.loadNpmTasks('grunt-curl'); // Load custom QUnit task, based on grunt-contrib-qunit, but support "files" option. grunt.loadTasks('./tests/grunt-task/'); + grunt.loadTasks('./custom-tasks/'); // "npm build" runs these tasks - grunt.registerTask('build', ['version', 'concat', 'uglify', 'qunit']); - grunt.registerTask('default', ['jshint', 'build']); + grunt.registerTask('prepare-test-files', function (){ + // big.jpg added to git + /*if (!grunt.file.exists('tests/files/big.jpg')) { + grunt.task.run('curl'); + }*/ + }); + + grunt.registerTask('express', 'Start a custom web server.', function() { + var done = this.async(); + + require('./node/server.js').createServer(8000, function () { + done(); + }); + }); + + grunt.registerTask('server', ['connect:server', 'express']); + grunt.registerTask('dev', ['concat', 'server', 'watch']); + grunt.registerTask('tests', ['jshint', 'concat', 'server', 'prepare-test-files', 'qunit']); + grunt.registerTask('build', ['version', 'concat', 'uglify']); + grunt.registerTask('build-all', ['build', 'mxmlc']); + grunt.registerTask('default', ['tests', 'build']); }; diff --git a/README.md b/README.md index cd67d5e3..ac75807d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@  -## FileAPI -A set of javascript tools for working with files. +## FileAPI +A set of JavaScript tools for working with files. ### Get started +Download the files from the [dist](https://github.com/mailru/FileAPI/tree/master/dist) directory, and then: + ```html
-
+
Choose files
- +
@@ -18,6 +20,7 @@ A set of javascript tools for working with files. @@ -1189,7 +1220,7 @@ Submit Query FileAPI::makeResponse(array( 'status' => FileAPI::OK , 'statusText' => 'OK' - , 'body' => array('count' => sizeof($files) + , 'body' => array('count' => sizeof($files)) ), $jsonp); exit; } @@ -1249,7 +1280,7 @@ Response headers: -All the other codes - fatal error, user's involvement is recommend. +All the other codes - fatal error, user's involvement is recommended. --- @@ -1378,6 +1409,124 @@ Button like link. ## Changelog +### 2.0.20 + + + +### 2.0.19 + + + +### 2.0.18 + + + +### 2.0.16-2.0.17 + + + +### 2.0.12-2.0.15 (!) + + + +### 2.0.11 + + + +### 2.0.10 + + + +### 2.0.9 + + + +### 2.0.8 + + + +### 2.0.7 + + + +### 2.0.6 + + + +### 2.0.5 + + + +### 2.0.4 + + +### 2.0.3 + + +### 2.0.2 + + +### 2.0.1 + + ### 2.0.0 \nAll the other codes - fatal error, user's involvement is recommend.","ru":"Всё общение между клиентом и сервером ведётся на уровне HTTP заголовков.
\nДля передачи отдельного chunk'а клиент устанавливает заголовки:
\n\nДругие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика.
\nВ ответ на передаваемый chunk сервер может отвечать следующими кодами:
\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":""}},"1.2.6":{"name":"1.2.6","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.5":{"name":"1.2.5","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.4":{"name":"1.2.4","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.3":{"name":"1.2.3","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.2":{"name":"1.2.2","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.1":{"name":"1.2.1","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.0":{"name":"1.2.0","type":-1,"label":"Changelog","descr":{"en":""}},"1.1.0":{"name":"1.1.0","type":-1,"label":"Changelog","descr":{"en":""}},"1.0.1":{"name":"1.0.1","type":-1,"label":"Changelog","descr":{"en":""}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":""}}},"fn":{}}} \ No newline at end of file +{"FileAPI":{"label":"FileAPI","class":"FileAPI","descr":{"en":"A set of javascript tools for working with files.","ru":"Набор JavaScript инструментов для работы с файлами."},"props":{"Get started":{"name":"Get started","type":-1,"label":"started","descr":{"en":"","ru":""},"code":{"type":"html","source":{"en":"
\n \n
\n
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":"","ru":""},"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\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\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Другие заголовки не используются, отслеживание уникальности имени передаваемого файла не реализуется и оставлено на усмотрение разработчика.
    \nВ ответ на передаваемый chunk сервер может отвечать следующими кодами:
    \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":""}},"1.2.6":{"name":"1.2.6","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.5":{"name":"1.2.5","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.4":{"name":"1.2.4","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.3":{"name":"1.2.3","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.2":{"name":"1.2.2","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.1":{"name":"1.2.1","type":-1,"label":"Changelog","descr":{"en":""}},"1.2.0":{"name":"1.2.0","type":-1,"label":"Changelog","descr":{"en":""}},"1.1.0":{"name":"1.1.0","type":-1,"label":"Changelog","descr":{"en":""}},"1.0.1":{"name":"1.0.1","type":-1,"label":"Changelog","descr":{"en":""}},"1.0.0":{"name":"1.0.0","type":-1,"label":"Changelog","descr":{"en":""}}},"fn":{}}} diff --git a/tests/.DS_Store b/tests/.DS_Store deleted file mode 100644 index 2ba8b680..00000000 Binary files a/tests/.DS_Store and /dev/null differ diff --git a/tests/files/.DS_Store b/tests/files/.DS_Store deleted file mode 100644 index 87622715..00000000 Binary files a/tests/files/.DS_Store and /dev/null differ diff --git a/tests/files/big.jpg b/tests/files/big.jpg new file mode 100644 index 00000000..d0dc3b17 Binary files /dev/null and b/tests/files/big.jpg differ diff --git a/tests/files/samples/firefox-vintage.png b/tests/files/samples/firefox-vintage.png new file mode 100644 index 00000000..ac85e950 Binary files /dev/null and b/tests/files/samples/firefox-vintage.png differ diff --git a/tests/files/samples/phantomjs-vintage.png b/tests/files/samples/phantomjs-vintage.png new file mode 100644 index 00000000..ac85e950 Binary files /dev/null and b/tests/files/samples/phantomjs-vintage.png differ diff --git a/tests/flash.html b/tests/flash.html new file mode 100644 index 00000000..057ec35b --- /dev/null +++ b/tests/flash.html @@ -0,0 +1,114 @@ + + + + + + + FileAPI :: Tests + + + + + + + + + + + +
    + + + +
    +
    + +
    + +
    + +
    +			
    +		
    +
    + + + + + + + diff --git a/tests/index.html b/tests/index.html index 4aaa102e..5cdabe36 100644 --- a/tests/index.html +++ b/tests/index.html @@ -7,17 +7,19 @@ FileAPI :: Tests + @@ -31,7 +33,8 @@
    -
    1px.gif --
    +
    1px.gif --
    +
    big.jpg --
    hello.txt --
    image.jpg --
    dino.png --
    diff --git a/tests/json.html b/tests/json.html new file mode 100644 index 00000000..30e1e0cb --- /dev/null +++ b/tests/json.html @@ -0,0 +1 @@ +{"status":200,"statusText":"OK","response":"done"} diff --git a/tests/pure-resize-tests.html b/tests/pure-resize-tests.html new file mode 100644 index 00000000..019209a5 --- /dev/null +++ b/tests/pure-resize-tests.html @@ -0,0 +1,97 @@ + + + + + + + + Time to load: n/ams + Time to render: n/ams + Time to encode: n/ams +
    + +
    + +
    + +
    + + \ No newline at end of file diff --git a/tests/tests.js b/tests/tests.js index 5b39cf41..4352c815 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,6 +1,20 @@ module('FileAPI'); (function (){ + if( !Function.prototype.bind ){ + Function.prototype.bind = function (ctx){ + if( !ctx ) { + return this; + } + var fn = this; + return function (){ + return fn.apply(ctx, arguments); + }; + }; + } + + + 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(); @@ -53,7 +67,7 @@ module('FileAPI'); } - function imageEqual(left, right, text, callback){ + function imageEqual(left, right, text, callback, delta){ loadImage(left, function (left){ left.setAttribute('style', 'border: 2px solid red; padding: 2px;'); document.body.appendChild(left.cloneNode()); @@ -87,7 +101,7 @@ module('FileAPI'); } } - ok(failPixels/pixels < .01, text + ' (fail pixels: '+ (failPixels/pixels) +')'); + ok(failPixels/pixels < (delta || .01), text + ' (fail pixels: '+ (failPixels/pixels) +')'); } callback(); @@ -102,8 +116,31 @@ module('FileAPI'); } + function _checkProgressEvent(evt){ + var fail = false; + + if( isNaN(evt.loaded / evt.total) ){ + fail = true; + ok(false, "progress: evt.loaded/evt.total - is NaN"); + } + + if( isNaN(evt.loaded) ){ + fail = true; + ok(false, "progress: evt.loaded - is NaN"); + } + + if( isNaN(evt.total) ){ + fail = true; + ok(false, "progress: evt.total - is NaN"); + } + + 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); @@ -149,7 +186,40 @@ module('FileAPI'); } }); - + test('big.jpg', function (){ + console.log('Entering big.jpg'); + var file = FileAPI.getFiles(uploadForm['big.jpg'])[0]; + + // File + console.log('[big.jpg] before check file'); + checkFile(file, 'big.jpg', 'image/jpeg', 71674475); + console.log('[big.jpg] after check file'); + + // File info + stop(); + console.log('[big.jpg] before getInfo'); + FileAPI.getInfo(file, function (err, info){ + console.log('[big.jpg] after getInfo'); + start(); + ok(!err); + equal(info.width, 10000, 'getInfo.width'); + equal(info.height, 10000, 'getInfo.height'); + }); + + stop(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { + width: 1024, + height: 768, + type: 'image/jpeg' + }, + complete: function (err, res){ + start(); + } + }); + }); test('hello.txt', function (){ var file = FileAPI.getFiles(uploadForm['hello.txt'])[0]; @@ -167,8 +237,6 @@ module('FileAPI'); } }); - - test('image.jpg', function (){ var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; @@ -189,7 +257,6 @@ module('FileAPI'); }); }); - test('filterFiles', function (){ var files = FileAPI.getFiles(uploadForm['multiple']); @@ -209,44 +276,45 @@ 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 res = err ? {} : FileAPI.parseJSON(xhr.responseText).data._REQUEST; + var headers = err ? err : FileAPI.parseJSON(xhr.responseText).data.HEADERS; start(); - equal(res.str, 'foo'); - equal(res.num, '1'); + ok(!err, 'upload done'); + equal(res.str, 'foo', 'string'); + equal(res.num, '1', 'number'); + equal(headers['x-foo'], 'bar', 'headers.x-foo'); - if( !FileAPI.html5 ){ - deepEqual(res.array, { "0": '1', "1": '2', "2": '3' }); + if( !FileAPI.html5 || !FileAPI.support.html5 ){ + deepEqual(res.array, { "0": '1', "1": '2', "2": '3' }, 'array'); } else { - deepEqual(res.array, ['1', '2', '3']); + deepEqual(res.array, ['1', '2', '3'], 'array'); } - deepEqual(res.object, { foo: 'bar' }); + deepEqual(res.object, { foo: 'bar' }, 'object'); } }) }); - - test('upload input', function (){ - expect(13); + var rnd = Math.random(); + expect(15); stop(); FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, data: { foo: 'bar' }, - files: uploadForm['1px.gif'], + headers: { 'x-foo': 'bar', 'x-rnd': rnd }, + files: uploadForm['1px_gif'], upload: function (){ ok(true, 'upload event'); }, @@ -263,11 +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'); 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'] ){ @@ -275,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){ @@ -284,13 +358,21 @@ 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); + if( !_progressFail && (_progress >= evt.loaded) ){ + _progressFail = true; + ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded); + } + _progress = evt.loaded; + }, complete: function (err, res){ start(); var res = FileAPI.parseJSON(res.responseText).data._FILES['text']; @@ -301,8 +383,6 @@ module('FileAPI'); }); }); - - test('multiupload', function (){ stop(); var @@ -314,7 +394,7 @@ module('FileAPI'); ; FileAPI.upload({ - url: 'http://rubaxa.org/FileAPI/server/ctrl.php', + url: controllerUrl, files: uploadForm['multiple'], fileupload: function (){ _start++; @@ -325,8 +405,10 @@ module('FileAPI'); _files[file.name] = file; }, progress: function (evt){ - if( _progress > evt.loaded ){ + _progressFail = _progressFail || _checkProgressEvent(evt); + if( !_progressFail && (_progress >= evt.loaded) ){ _progressFail = true; + ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded); } _progress = evt.loaded; }, @@ -334,71 +416,214 @@ module('FileAPI'); start(); equal(_start, _complete, 'uploaded'); - equal(_progressFail, false, 'progress'); checkFile(_files['1px.gif'], '1px.gif', 'image/gif', 34); checkFile(_files['dino.png'], 'dino.png', 'image/png', 461003); checkFile(_files['hello.txt'], 'hello.txt', 'text/plain', 15); checkFile(_files['image.jpg'], 'image.jpg', 'image/jpeg', 108338); - - // @todo: Сейчас через phantom "application/octet-stream" - // checkFile(_files['lebowski.json'], 'lebowski.json', 'application/json', 5392); +// checkFile(_files['lebowski.json'], 'lebowski.json', 'application/json', 5392); } }); }); - FileAPI.html5 && test('upload FileAPI.Image', function (){ var file = FileAPI.getFiles(uploadForm['dino.png'])[0]; - var image = FileAPI.Image(file).rotate(90).preview(100); + var image = FileAPI.Image(file).rotate(90+360).preview(100); + 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){ + _progressFail = _progressFail || _checkProgressEvent(evt); + + if( !_progressFail && (_progress >= evt.loaded) ){ + _progressFail = true; + ok(false, 'progress evt.loaded: '+_progress+' -> '+evt.loaded); + } + _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'); + 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', function (){ + FileAPI.html5 && test('upload + imageTransform (min, max, preview)', function (){ var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; + var queue = FileAPI.queue(start); stop(); + + // strategy: 'min' + 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 - }, + imageTransform: { width: 100, height: 100, strategy: 'min' }, + complete: function (err, res){ + queue.next(); + var res = FileAPI.parseJSON(res.responseText); + equal(res.images['image'].width, 141, 'min.width'); + equal(res.images['image'].height, 100, 'min.height'); + } + }); + + // strategy: 'max' + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { width: 100, height: 100, strategy: 'max' }, + complete: function (err, res){ + queue.next(); + var res = FileAPI.parseJSON(res.responseText); + equal(res.images['image'].width, 100, 'max.width'); + equal(res.images['image'].height, 71, 'max.height'); + } + }); + + // 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: controllerUrl, + files: { image: file }, + imageTransform: { width: 100, height: 100, rotate: 'auto', preview: true }, complete: function (err, res){ var res = FileAPI.parseJSON(res.responseText); imageEqual(res.images.image.dataURL, 'files/samples/'+browser+'-image-auto-100x100.jpeg', 'image auto 100x100.png', function (){ - start(); + queue.next(); }); } }); }); + test('upload + autoOrientation', function (){ + var file = FileAPI.getFiles(uploadForm['image.jpg'])[0]; + var queue = FileAPI.queue(start); + var check = function (err, res){ + var res = FileAPI.parseJSON(res.responseText); + equal(res.images.image.width, 448, this+'.width'); + equal(res.images.image.height, 632, this+'.height'); + queue.next(); + }; + + stop(); + + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageAutoOrientation: true, + complete: check.bind('imageAutoOrientation') + }); + + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + files: { image: file }, + imageTransform: { rotate: 'auto' }, + complete: check.bind('imageTransform.rotate.auto') + }); + + queue.inc(); + FileAPI.upload({ + 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]) + .preview(50, 30) + .filter('vintage') + .get(function (err, canvas){ + equal(canvas.nodeName.toLowerCase(), 'canvas'); + FileAPI.upload({ + url: controllerUrl, + files: { + image: { + name: 'my-file', + blob: canvas + } + }, + complete: function (err, xhr){ + var res = FileAPI.parseJSON(xhr.responseText); + imageEqual(res.images['image'].dataURL, 'files/samples/'+browser+'-vintage.png', 'caman vintage', function (){ + start(); + }, .9); + } + }) + }) + ; + }); - 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': { @@ -429,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': { @@ -456,6 +680,48 @@ module('FileAPI'); }); }); + 0 && test('iframe', function (){ + var html5 = FileAPI.support.html5; + var queue = FileAPI.queue(function (){ + start(); + FileAPI.support.html5 = html5; + }); + + stop(); + FileAPI.support.html5 = false; + + // default callback + queue.inc(); + FileAPI.upload({ + url: controllerUrl, + complete: function (err, xhr){ + var json = FileAPI.parseJSON(xhr.responseText); + equal(json.jsonp, 'callback', 'default'); + queue.next(); + } + }); + + // callback in GET + queue.inc(); + FileAPI.upload({ + url: 'http://rubaxa.org/FileAPI/server/ctrl.php?fn=?', + complete: function (err, xhr){ + var json = FileAPI.parseJSON(xhr.responseText); + equal(json.jsonp, 'fn', 'custom'); + queue.next(); + } + }); + + // 302: redirect + queue.inc(); + FileAPI.upload({ + url: 'http://rubaxa.org/FileAPI/server/redirect.php?page=json.html', + complete: function (err, xhr){ + equal(xhr.responseText, 'done', '302'); + queue.next(); + } + }); + }); FileAPI.html5 && test('WebCam', function (){ stop(); @@ -468,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); @@ -481,4 +747,5 @@ module('FileAPI'); } }); }); + })();