Showing preview only (967K chars total). Download the full file or copy to clipboard to get everything.
Repository: danialfarid/ng-file-upload
Branch: master
Commit: a4c4187c11bc
Files: 58
Total size: 934.9 KB
Directory structure:
gitextract_ye9rh2i7/
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── LICENSE
├── README.md
├── demo/
│ ├── C#/
│ │ ├── UploadController.js
│ │ ├── UploadHandler.ashx
│ │ └── UploadHandler.ashx.cs
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── df/
│ │ └── angularfileupload/
│ │ ├── CORSFilter.java
│ │ ├── FileUpload.java
│ │ └── S3Signature.java
│ ├── resources/
│ │ ├── META-INF/
│ │ │ ├── jdoconfig.xml
│ │ │ └── persistence.xml
│ │ └── log4j.properties
│ └── webapp/
│ ├── WEB-INF/
│ │ ├── appengine-web.xml
│ │ ├── logging.properties
│ │ └── web.xml
│ ├── common.css
│ ├── crossdomain.xml
│ ├── donate.html
│ ├── index.html
│ └── js/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── ng-file-upload-all.js
│ ├── ng-file-upload-shim.js
│ ├── ng-file-upload.js
│ ├── ng-img-crop.css
│ ├── ng-img-crop.js
│ └── upload.js
├── dist/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── ng-file-upload-all.js
│ ├── ng-file-upload-shim.js
│ └── ng-file-upload.js
├── index.js
├── nuget/
│ ├── Package.nuspec
│ ├── build.bat
│ └── nuget.sh
├── package.json
├── release.sh
├── src/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── data-url.js
│ ├── drop.js
│ ├── exif.js
│ ├── model.js
│ ├── resize.js
│ ├── select.js
│ ├── shim-elem.js
│ ├── shim-filereader.js
│ ├── shim-upload.js
│ ├── upload.js
│ └── validate.js
└── test/
├── .bowerrc
├── bower.json
├── index.html
└── spec/
└── test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
.tmp
bower_components
.settings
.metadata
*.war
RemoteSystemsTempFiles/
.DS_Store
.DS_Store?
ehthumbs.db
Thumbs.db
.Spotlight-V100
.Trashes
classes/
._*
*.jar
release-local.sh
npm-debug.log
.idea/
target/
*.iml
================================================
FILE: .jshintrc
================================================
{
"node": true,
"browser": true,
"esnext": true,
"camelcase": true,
"curly": false,
"eqeqeq": true,
"eqnull": true,
"immed": true,
"indent": 4,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"undef": true,
"unused": true,
"trailing": true,
"smarttabs": true,
"jquery": true,
"evil": true,
"globals": {
"angular":false,
"FileAPI":false,
"ngFileUpload":true,
"FormData":true,
"Blob":true,
"ActiveXObject":false,
"$document":false
}
}
================================================
FILE: Gruntfile.js
================================================
'use strict';
module.exports = function (grunt) {
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
all: {
options: {
process: function (content) {
return grunt.template.process(content);
}
},
files: {
'dist/ng-file-upload.js': ['src/upload.js', 'src/model.js', 'src/select.js', 'src/data-url.js',
'src/validate.js', 'src/resize.js', 'src/drop.js', 'src/exif.js'],
'dist/ng-file-upload-shim.js': ['src/shim-upload.js', 'src/shim-elem.js', 'src/shim-filereader.js'],
'dist/ng-file-upload-all.js': ['dist/ng-file-upload-shim.js', 'dist/ng-file-upload.js']
}
}
},
uglify: {
options: {
preserveComments: 'some',
banner: '/*! <%= pkg.version %> */\n'
},
build: {
files: [{
'dist/ng-file-upload.min.js': 'dist/ng-file-upload.js',
'dist/ng-file-upload-shim.min.js': 'dist/ng-file-upload-shim.js',
'dist/ng-file-upload-all.min.js': 'dist/ng-file-upload-all.js',
'dist/FileAPI.min.js': 'dist/FileAPI.js'
}]
}
},
copy: {
build: {
files: [{
expand: true,
cwd: 'dist/',
src: '*',
dest: 'demo/src/main/webapp/js/',
flatten: true,
filter: 'isFile'
}]
},
fileapi: {
files: {
'dist/FileAPI.flash.swf': 'src/FileAPI.flash.swf',
'dist/FileAPI.js': 'src/FileAPI.js'
}
},
bower: {
files: [{
expand: true,
cwd: 'dist/',
src: '*',
dest: '../angular-file-upload-bower/',
flatten: true,
filter: 'isFile'
}, {
expand: true,
cwd: 'dist/',
src: '*',
dest: '../angular-file-upload-shim-bower/',
flatten: true,
filter: 'isFile'
}]
}
},
serve: {
options: {
port: 9000
},
'path': 'demo/src/main/webapp'
},
watch: {
js: {
files: ['src/{,*/}*.js'],
tasks: ['jshint:all', 'concat:all', 'copy:build']
}
},
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'src/{,*/}*.js',
'!src/FileAPI*.*',
'test/spec/{,*/}*.js'
]
},
replace: {
version: {
src: ['nuget/Package.nuspec', '../angular-file-upload-bower/package.js'],
overwrite: true,
replacements: [{
from: /"version" *: *".*"/g,
to: '"version": "<%= pkg.version %>"'
}, {
from: /<version>.*<\/version>/g,
to: '<version><%= pkg.version %></version>'
}]
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'dist',
'!dist/.git*'
]
}]
}
}
});
grunt.registerTask('dev', ['jshint:all', 'concat:all', 'uglify', 'copy:build', 'watch']);
grunt.registerTask('default', ['jshint:all', 'clean:dist', 'concat:all',
'copy:fileapi', 'uglify', 'copy:build', 'copy:bower', 'replace:version']);
};
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013 danialfarid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
[](http://badge.fury.io/js/ng-file-upload)
[](https://npmjs.org/package/ng-file-upload)
[](http://issuestats.com/github/danialfarid/ng-file-upload)
[](http://issuestats.com/github/danialfarid/ng-file-upload)<br/>
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danial%2efarid%40gmail%2ecom&lc=CA&item_name=ng%2dfile%2dupload&item_number=ng%2dfile%2dupload¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
[](https://gratipay.com/ng-file-upload/)
ng-file-upload
===================
Lightweight Angular directive to upload files.
**See the <a href="https://angular-file-upload.appspot.com/" target="_blank">DEMO</a> page.** Reference docs [here](https://github.com/danialfarid/ng-file-upload/blob/master/README.md#full-reference)
**Migration notes**: [version 3.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/3.0.0) [version 3.1.x](https://github.com/danialfarid/ng-file-upload/releases/tag/3.1.0) [version 3.2.x](https://github.com/danialfarid/ng-file-upload/releases/tag/3.2.3) [version 4.x.x](https://github.com/danialfarid/ng-file-upload/releases/tag/4.0.0) [version 5.x.x](https://github.com/danialfarid/ng-file-upload/releases/tag/5.0.0) [version 6.x.x](https://github.com/danialfarid/ng-file-upload/releases/tag/6.0.0) [version 6.2.x](https://github.com/danialfarid/ng-file-upload/releases/tag/6.2.0) [version 7.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/7.0.0) [version 7.2.x](https://github.com/danialfarid/ng-file-upload/releases/tag/7.2.0) [version 8.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/8.0.1) [version 9.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/9.0.0) [version 10.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/10.0.0) [version 11.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/11.0.0) [version 12.0.x](https://github.com/danialfarid/ng-file-upload/releases/tag/12.0.0) [version 12.1.x](https://github.com/danialfarid/ng-file-upload/releases/tag/12.1.0) [version 12.2.x](https://github.com/danialfarid/ng-file-upload/releases/tag/12.2.3)
Ask questions on [StackOverflow](http://stackoverflow.com/) under the [ng-file-upload](http://stackoverflow.com/tags/ng-file-upload/) tag.<br/>
For bug report or feature request please search through existing [issues](https://github.com/danialfarid/ng-file-upload/issues) first then open a new one [here](https://github.com/danialfarid/ng-file-upload/issues/new). For faster response provide steps to reproduce/versions with a jsfiddle link. If you need support for your company contact [me](mailto:danial.farid@gmail.com).<br/>
If you like this plugin give it a thumbs up at [ngmodules](http://ngmodules.org/modules/ng-file-upload) or get me a <a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danial%2efarid%40gmail%2ecom&lc=CA&item_name=ng%2dfile%2dupload&item_number=ng%2dfile%2dupload¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted">cup of tea <img src="https://angular-file-upload.appspot.com/img/tea.png" width="40" height="24" title="Icon made by Freepik.com"></a>. Contributions are welcomed.
Table of Content:
* [Features](#features)
* [Install](#install) ([Manual](#manual), [Bower](#bower), [NuGet](#nuget), [NPM](#npm))
* [Usage](#usage)
* [Old Browsers](#old_browsers)
* [Server Side](#server)
* [Samples](#server) ([Java](#java), [Spring](#spring), [Node.js](#node), [Rails](#rails), [PHP](#php), [.Net](#net))
* [CORS](#cors)
* [Amazon S3 Upload](#s3)
## <a name="features"></a> Features
* file upload progress, cancel/abort
* file drag and drop (html5 only)
* image paste from clipboard and drag and drop from browser pages (html5 only).
* image resize and center crop (native) and user controlled crop through [ngImgCrop](https://github.com/alexk111/ngImgCrop). See [crop sample](http://jsfiddle.net/danialfarid/xxo3sk41/590/) (html5 only)
* orientation fix for jpeg image files with exif orientation data
* resumable uploads: pause/resume upload (html5 only)
* native validation support for file type/size, image width/height/aspect ratio, video/audio duration, and `ng-required` with pluggable custom sync or async validations.
* show thumbnail or preview of selected images/audio/videos
* supports CORS and direct upload of file's binary data using `Upload.$http()`
* plenty of sample server side code, available on nuget
* on demand flash [FileAPI](https://github.com/mailru/FileAPI) shim loading no extra load for html5 browsers.
* HTML5 FileReader.readAsDataURL shim for IE8-9
* available on [npm](https://www.npmjs.com/package/ng-file-upload), [bower](https://libraries.io/bower/ng-file-upload), [meteor](https://atmospherejs.com/danialf/ng-file-upload), [nuget](https://www.nuget.org/packages/angular-file-upload)
## <a name="install"></a> Install
* <a name="manual"></a>**Manual**: download latest from [here](https://github.com/danialfarid/ng-file-upload-bower/releases/latest)
* <a name="bower"></a>**Bower**:
* `bower install ng-file-upload-shim --save`(for non html5 suppport)
* `bower install ng-file-upload --save`
* <a name="nuget"></a>**NuGet**: `PM> Install-Package angular-file-upload` (thanks to [Georgios Diamantopoulos](https://github.com/georgiosd))
* <a name="npm"></a>**NPM**: `npm install ng-file-upload`
```html
<script src="angular(.min).js"></script>
<script src="ng-file-upload-shim(.min).js"></script> <!-- for no html5 browsers support -->
<script src="ng-file-upload(.min).js"></script>
```
## <a name="usage"></a> Usage
### Samples:
* Upload with form submit and validations: [http://jsfiddle.net/danialfarid/maqbzv15/1118/](http://jsfiddle.net/danialfarid/maqbzv15/1118/)
* Upload multiple files one by one on file select:
[http://jsfiddle.net/danialfarid/2vq88rfs/136/](http://jsfiddle.net/danialfarid/2vq88rfs/136/)
* Upload multiple files in one request on file select (html5 only):
[http://jsfiddle.net/danialfarid/huhjo9jm/5/](http://jsfiddle.net/danialfarid/huhjo9jm/5/)
* Upload single file on file select:
[http://jsfiddle.net/danialfarid/0mz6ff9o/135/](http://jsfiddle.net/danialfarid/0mz6ff9o/135/)
* Drop and upload with $watch:
[http://jsfiddle.net/danialfarid/s8kc7wg0/400/](http://jsfiddle.net/danialfarid/s8kc7wg0/400/)
* Image Crop and Upload
[http://jsfiddle.net/danialfarid/xxo3sk41/590/](http://jsfiddle.net/danialfarid/xxo3sk41/590/)
```html
<script src="angular.min.js"></script>
<!-- shim is needed to support non-HTML5 FormData browsers (IE8-9)-->
<script src="ng-file-upload-shim.min.js"></script>
<script src="ng-file-upload.min.js"></script>
Upload on form submit or button click
<form ng-app="fileUpload" ng-controller="MyCtrl" name="form">
Single Image with validations
<div class="button" ngf-select ng-model="file" name="file" ngf-pattern="'image/*'"
ngf-accept="'image/*'" ngf-max-size="20MB" ngf-min-height="100"
ngf-resize="{width: 100, height: 100}">Select</div>
Multiple files
<div class="button" ngf-select ng-model="files" ngf-multiple="true">Select</div>
Drop files: <div ngf-drop ng-model="files" class="drop-box">Drop</div>
<button type="submit" ng-click="submit()">submit</button>
</form>
Upload right away after file selection:
<div class="button" ngf-select="upload($file)">Upload on file select</div>
<div class="button" ngf-select="uploadFiles($files)" multiple="multiple">Upload on file select</div>
Drop File:
<div ngf-drop="uploadFiles($files)" class="drop-box"
ngf-drag-over-class="'dragover'" ngf-multiple="true"
ngf-pattern="'image/*,application/pdf'">Drop Images or PDFs files here</div>
<div ngf-no-file-drop>File Drag/Drop is not supported for this browser</div>
Image thumbnail: <img ngf-thumbnail="file || '/thumb.jpg'">
Audio preview: <audio controls ngf-src="file"></audio>
Video preview: <video controls ngf-src="file"></video>
```
Javascript code:
```js
//inject directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('MyCtrl', ['$scope', 'Upload', function ($scope, Upload) {
// upload later on form submit or something similar
$scope.submit = function() {
if ($scope.form.file.$valid && $scope.file) {
$scope.upload($scope.file);
}
};
// upload on file select or drop
$scope.upload = function (file) {
Upload.upload({
url: 'upload/url',
data: {file: file, 'username': $scope.username}
}).then(function (resp) {
console.log('Success ' + resp.config.data.file.name + 'uploaded. Response: ' + resp.data);
}, function (resp) {
console.log('Error status: ' + resp.status);
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
});
};
// for multiple files:
$scope.uploadFiles = function (files) {
if (files && files.length) {
for (var i = 0; i < files.length; i++) {
Upload.upload({..., data: {file: files[i]}, ...})...;
}
// or send them all together for HTML5 browsers:
Upload.upload({..., data: {file: files}, ...})...;
}
}
}]);
```
### Full reference
#### File select and drop
At least one of the `ngf-select` or `ngf-drop` are mandatory for the plugin to link to the element.
`ngf-select` only attributes are marked with * and `ngf-drop` only attributes are marked with +.
```html
<div|button|input type="file"|ngf-select|ngf-drop...
ngf-select="" or "upload($files, ...)" <!-- called when files are selected or cleared -->
ngf-drop="" or "upload($files, ...)" <!-- called when files being dropped
You can use ng-model or ngf-change instead of specifying function for ngf-drop and ngf-select
function parameters are the same as ngf-change -->
ngf-change="upload($files, $file, $newFiles, $duplicateFiles, $invalidFiles, $event)"
<!-- called when files are selected, dropped, or cleared -->
ng-model="myFiles" <!-- binds the valid selected/dropped file or files to the scope model
could be an array or single file depending on ngf-multiple and ngf-keep values. -->
ngf-model-options="{updateOn: 'change click drop dropUrl paste', allowInvalid: false, debounce: 0}"
<!-- updateOn could be used to disable resetting on click, or updating on paste, browser
image drop, etc. allowInvalid default is false could allow invalid files in the model
debouncing will postpone model update (miliseconds). See angular ng-model-options for more details. -->
ngf-model-invalid="invalidFile(s)" <!-- binds the invalid selected/dropped file or files to this model. -->
ngf-before-model-change="beforeChange($files, ...)" <!-- called after file select/drop and before
model change, validation and resize is processed -->
ng-disabled="boolean" <!-- disables this element -->
ngf-select-disabled="boolean" <!-- default false, disables file select on this element -->
ngf-drop-disabled="boolean" <!-- default false, disables file drop on this element -->
ngf-multiple="boolean" <!-- default false, allows selecting multiple files -->
ngf-keep="true|false|'distinct'" <!-- default false, keep the previous ng-model files and
append the new files. "'distinct'" removes duplicate files
$newFiles and $duplicateFiles are set in ngf-change/select/drop functions. -->
ngf-fix-orientation="boolean" <!-- default false, would rotate the jpeg image files that have
exif orientation data. See #745. Could be a boolean function like shouldFixOrientation($file)
to decide wethere to fix that file or not. -->
*ngf-capture="'camera'" or "'other'" <!-- allows mobile devices to capture using camera -->
*ngf-accept="'image/*'" <!-- standard HTML accept attr, browser specific select popup window -->
+ngf-allow-dir="boolean" <!-- default true, allow dropping files only for Chrome webkit browser -->
+ngf-include-dir="boolean" <!-- default false, include directories in the dropped file array.
You can detect if they are directory or not by checking the type === 'directory'. -->
+ngf-drag-over-class="{pattern: 'image/*', accept:'acceptClass', reject:'rejectClass', delay:100}"
or "'myDragOverClass'" or "calcDragOverClass($event)"
<!-- default "dragover". drag over css class behaviour. could be a string, a function
returning class name or a json object.
accept/reject class only works in Chrome, validating only the file mime type.
if pattern is not specified ngf-pattern will be used. See following docs for more info. -->
+ngf-drag="drag($isDragging, $class, $event)" <!-- function called on drag over/leave events.
$isDragging: boolean true if is dragging over(dragover), false if drag has left (dragleave)
$class is the class that is being set for the element calculated by ngf-drag-over-class -->
+ngf-drop-available="dropSupported" <!-- set the value of scope model to true or false
based on file drag&drop support for this browser -->
+ngf-stop-propagation="boolean" <!-- default false, whether to propagate drag/drop events. -->
+ngf-hide-on-drop-not-available="boolean" <!-- default false, hides element if file drag&drop is not -->
+ngf-enable-firefox-paste="boolean" <!-- *experimental* default false, enable firefox image paste
by making element contenteditable -->
ngf-resize="{width: 100, height: 100, quality: .8, type: 'image/jpeg',
ratio: '1:2', centerCrop: true, pattern='.jpg', restoreExif: false}"
or resizeOptions() <!-- a function returning a promise which resolves into the options.
resizes the image to the given width/height or ratio. Quality is optional between 0.1 and 1.0),
type is optional convert it to the given image type format.
centerCrop true will center crop the image if it does not fit within the given width/height or ratio.
centerCrop false (default) will not crop the image and will fit it within the given width/height or ratio
so the resulting image width (or height) could be less than given width (or height).
pattern is to resize only the files that their name or type matches the pattern similar to ngf-pattern.
restoreExif boolean default true, will restore exif info on the resized image. -->
ngf-resize-if="$width > 1000 || $height > 1000" or "resizeCondition($file, $width, $height)"
<!-- apply ngf-resize only if this function returns true. To filter specific images to be resized. -->
ngf-validate-after-resize="boolean" <!-- default false, if true all validation will be run after
the images are being resized, so any validation error before resize will be ignored. -->
<!-- validations: -->
ngf-max-files="10" <!-- maximum number of files allowed to be selected or dropped, validate error name: maxFiles -->
ngf-pattern="'.pdf,.jpg,video/*,!.jog'" <!-- comma separated wildcard to filter file names and
types allowed you can exclude specific files by ! at the beginning. validate error name: pattern -->
ngf-min-size, ngf-max-size, ngf-max-total-size="100" in bytes or "'10KB'" or "'10MB'" or "'10GB'"
<!-- validate as form.file.$error.maxSize=true and file.$error='maxSize'
ngf-max-total-size is for multiple file select and validating the total size of all files. -->
ngf-min-height, ngf-max-height, ngf-min-width, ngf-max-width="1000" in pixels only images
<!-- validate error names: minHeight, maxHeight, minWidth, maxWidth -->
ngf-ratio="8:10,1.6" <!-- list of comma separated valid aspect ratio of images in float or 2:3 format
validate error name: ratio -->
ngf-min-ratio, ngf-max-ratio="8:10" <!-- min or max allowed aspect ratio for the image. -->
ngf-dimensions="$width > 1000 || $height > 1000" or "validateDimension($file, $width, $height)"
<!-- validate the image dimensions, validate error name: dimensions -->
ngf-min-duration, ngf-max-duration="100.5" in seconds or "'10s'" or "'10m'" or "'10h'" only audio, video
<!-- validate error name: maxDuration -->
ngf-duration="$duration > 1000" or "validateDuration($file, $duration)"
<!-- validate the media duration, validate error name: duration -->
ngf-validate="{size: {min: 10, max: '20MB'}, width: {min: 100, max:10000}, height: {min: 100, max: 300}
ratio: '2x1', duration: {min: '10s', max: '5m'}, pattern: '.jpg'}"
<!-- shorthand form for above validations in one place. -->
ngf-validate-fn="validate($file)" <!-- custom validation function, return boolean or string
containing the error. validate error name: validateFn -->
ngf-validate-async-fn="validate($file)" <!-- custom validation function, return a promise that
resolve to boolean or string containing the error. validate error name: validateAsyncFn -->
ngf-validate-force="boolean" <!-- default false, if true file.$error will be set if
the dimension or duration values for validations cannot be calculated for example
image load error or unsupported video by the browser. by default it would assume the file
is valid if the duration or dimension cannot be calculated by the browser. -->
ngf-ignore-invalid="'pattern maxSize'" <!-- ignore the files that fail the specified validations.
They will just be ignored and will not show up in ngf-model-invalid or make the form invalid.
space separated list of validate error names. -->
ngf-run-all-validations="boolean" <!-- default false. Runs all the specified validate directives.
By default once a validation fails for a file it would stop running other validations for that file. -->
>Upload/Drop</div>
<div|... ngf-no-file-drop>File Drag/drop is not supported</div>
<!-- filter to convert the file to base64 data url. -->
<a href="file | ngfDataUrl">image</a>
```
#### File preview
```html
<img|audio|video|div
*ngf-src="file" <!-- To preview the selected file, sets src attribute to the file data url. -->
*ngf-background="file" <!-- sets background-image style to the file data url. -->
ngf-resize="{width: 20, height: 20, quality: 0.9}" <!-- only for image resizes the image
before setting it as src or background image. quality is optional. -->
ngf-no-object-url="true or false" <!-- see #887 to force base64 url generation instead of
object url. Default false -->
>
<div|span|...
*ngf-thumbnail="file" <!-- Generates a thumbnail version of the image file -->
ngf-size="{width: 20, height: 20, quality: 0.9}" the image will be resized to this size
<!-- if not specified will be resized to this element`s client width and height. -->
ngf-as-background="boolean" <!-- if true it will set the background image style instead of src attribute. -->
>
```
#### Upload service:
```js
var upload = Upload.upload({
*url: 'server/upload/url', // upload.php script, node.js route, or servlet url
/*
Specify the file and optional data to be sent to the server.
Each field including nested objects will be sent as a form data multipart.
Samples: {pic: file, username: username}
{files: files, otherInfo: {id: id, person: person,...}} multiple files (html5)
{profiles: {[{pic: file1, username: username1}, {pic: file2, username: username2}]} nested array multiple files (html5)
{file: file, info: Upload.json({id: id, name: name, ...})} send fields as json string
{file: file, info: Upload.jsonBlob({id: id, name: name, ...})} send fields as json blob, 'application/json' content_type
{picFile: Upload.rename(file, 'profile.jpg'), title: title} send file with picFile key and profile.jpg file name*/
*data: {key: file, otherInfo: uploadInfo},
/*
This is to accommodate server implementations expecting nested data object keys in .key or [key] format.
Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file
data: {rec: {name: 'N', pic: file}}, objectKey: '.k' sent as: rec.name -> N, rec.pic -> file */
objectKey: '[k]' or '.k' // default is '[k]'
/*
This is to accommodate server implementations expecting array data object keys in '[i]' or '[]' or
''(multiple entries with same key) format.
Example: data: {rec: [file[0], file[1], ...]} sent as: rec[0] -> file[0], rec[1] -> file[1],...
data: {rec: {rec: [f[0], f[1], ...], arrayKey: '[]'} sent as: rec[] -> f[0], rec[] -> f[1],...*/
arrayKey: '[i]' or '[]' or '.i' or '' //default is '[i]'
method: 'POST' or 'PUT'(html5), default POST,
headers: {'Authorization': 'xxx'}, // only for html5
withCredentials: boolean,
/*
See resumable upload guide below the code for more details (html5 only) */
resumeSizeUrl: '/uploaded/size/url?file=' + file.name // uploaded file size so far on the server.
resumeSizeResponseReader: function(data) {return data.size;} // reads the uploaded file size from resumeSizeUrl GET response
resumeSize: function() {return promise;} // function that returns a prommise which will be
// resolved to the upload file size on the server.
resumeChunkSize: 10000 or '10KB' or '10MB' // upload in chunks of specified size
disableProgress: boolean // default false, experimental as hotfix for potential library conflicts with other plugins
... and all other angular $http() options could be used here.
})
// returns a promise
upload.then(function(resp) {
// file is uploaded successfully
console.log('file ' + resp.config.data.file.name + 'is uploaded successfully. Response: ' + resp.data);
}, function(resp) {
// handle error
}, function(evt) {
// progress notify
console.log('progress: ' + parseInt(100.0 * evt.loaded / evt.total) + '% file :'+ evt.config.data.file.name);
});
upload.catch(errorCallback);
upload.finally(callback, notifyCallback);
/* access or attach event listeners to the underlying XMLHttpRequest */
upload.xhr(function(xhr){
xhr.upload.addEventListener(...)
});
/* cancel/abort the upload in progress. */
upload.abort();
/*
alternative way of uploading, send the file binary with the file's content-type.
Could be used to upload files to CouchDB, imgur, etc... html5 FileReader is needed.
This is equivalent to angular $http() but allow you to listen to the progress event for HTML5 browsers.*/
Upload.http({
url: '/server/upload/url',
headers : {
'Content-Type': file.type
},
data: file
})
/* Set the default values for ngf-select and ngf-drop directives*/
Upload.setDefaults({ngfMinSize: 20000, ngfMaxSize:20000000, ...})
// These two defaults could be decreased if you experience out of memory issues
// or could be increased if your app needs to show many images on the page.
// Each image in ngf-src, ngf-background or ngf-thumbnail is stored and referenced as a blob url
// and will only be released if the max value of the followings is reached.
Upload.defaults.blobUrlsMaxMemory = 268435456 // default max total size of files stored in blob urls.
Upload.defaults.blobUrlsMaxQueueSize = 200 // default max number of blob urls stored by this application.
/* Convert a single file or array of files to a single or array of
base64 data url representation of the file(s).
Could be used to send file in base64 format inside json to the databases */
Upload.base64DataUrl(files).then(function(urls){...});
/* Convert the file to blob url object or base64 data url based on boolean disallowObjectUrl value */
Upload.dataUrl(file, boolean).then(function(url){...});
/* Get image file dimensions*/
Upload.imageDimensions(file).then(function(dimensions){console.log(dimensions.width, dimensions.height);});
/* Get audio/video duration*/
Upload.mediaDuration(file).then(function(durationInSeconds){...});
/* Resizes an image. Returns a promise */
// options: width, height, quality, type, ratio, centerCrop, resizeIf, restoreExif
//resizeIf(width, height) returns boolean. See ngf-resize directive for more details of options.
Upload.resize(file, options).then(function(resizedFile){...});
/* returns boolean showing if image resize is supported by this browser*/
Upload.isResizeSupported()
/* returns boolean showing if resumable upload is supported by this browser*/
Upload.isResumeSupported()
/* returns a file which will be uploaded with the newName instead of original file name */
Upload.rename(file, newName)
/* converts the object to a Blob object with application/json content type
for jsob byte streaming support #359 (html5 only)*/
Upload.jsonBlob(obj)
/* converts the value to json to send data as json string. Same as angular.toJson(obj) */
Upload.json(obj)
/* converts a dataUrl to Blob object.*/
var blob = upload.dataUrltoBlob(dataurl, name);
/* returns true if there is an upload in progress. Can be used to prompt user before closing browser tab */
Upload.isUploadInProgress() boolean
/* downloads and converts a given url to Blob object which could be added to files model */
Upload.urlToBlob(url).then(function(blob) {...});
/* returns boolean to check if the object is file and could be used as file in Upload.upload()/http() */
Upload.isFile(obj);
/* fixes the exif orientation of the jpeg image file*/
Upload.applyExifRotation(file).then(...)
```
**ng-model**
The model value will be a single file instead of an array if all of the followings are true:
* `ngf-multiple` is not set or is resolved to false.
* `multiple` attribute is not set on the element
* `ngf-keep` is not set or is resolved to false.
**validation**
When any of the validation directives specified the form validation will take place and
you can access the value of the validation using `myForm.myFileInputName.$error.<validate error name>`
for example `form.file.$error.pattern`.
If multiple file selection is allowed you can specify `ngf-model-invalid="invalidFiles"` to assing the invalid files to
a model and find the error of each individual file with `file.$error` and description of it with `file.$errorParam`.
You can use angular ngf-model-options to allow invalid files to be set to the ng-model `ngf-model-options="{allowInvalid: true}"`.
**Upload multiple files**: Only for HTML5 FormData browsers (not IE8-9) you have an array of files or more than one file in your `data` to
send them all in one request . Non-html5 browsers due to flash limitation will upload each file one by one in a separate request.
You should iterate over the files and send them one by one for a cross browser solution.
**drag and drop styling**: For file drag and drop, `ngf-drag-over-class` could be used to style the drop zone.
It can be a function that returns a class name based on the $event. Default is "dragover" string.
Only in chrome It could be a json object `{accept: 'a', 'reject': 'r', pattern: 'image/*', delay: 10}` that specify the
class name for the accepted or rejected drag overs. The `pattern` specified or `ngf-pattern` will be used to validate the file's `mime-type`
since that is the only property of the file that is reported by the browser on drag. So you cannot validate
the file name/extension, size or other validations on drag. There is also some limitation on some file types which are not reported by Chrome.
`delay` default is 100, and is used to fix css3 transition issues from dragging over/out/over [#277](https://github.com/danialfarid/angular-file-upload/issues/277).
**Upload.setDefaults()**:
If you have many file selects or drops you can set the default values for the directives by calling `Upload.setDefaults(options)`. `options` would be a json object with directive names in camelcase and their default values.
**Resumable Uploads:**
The plugin supports resumable uploads for large files.
On your server you need to keep track of what files are being uploaded and how much of the file is uploaded.
* `url` upload endpoint need to reassemble the file chunks by appending uploading content to the end of the file or correct chunk position if it already exists.
* `resumeSizeUrl` server endpoint to return uploaded file size so far on the server to be able to resume the upload from
where it is ended. It should return zero if the file has not been uploaded yet. <br/>A GET request will be made to that
url for each upload to determine if part of the file is already uploaded or not. You need a unique way of identifying the file
on the server so you can pass the file name or generated id for the file as a request parameter.<br/>
By default it will assume that the response
content is an integer or a json object with `size` integer property. If you return other formats from the endpoint you can specify
`resumeSizeResponseReader` function to return the size value from the response. Alternatively instead of `resumeSizeUrl` you can use
`resumeSize` function which returns a promise that resolves to the size of the uploaded file so far.
Make sure when the file is fully uploaded without any error/abort this endpoint returns zero for the file size
if you want to let the user to upload the same file again. Or optionally you could have a restart endpoint to
set that back to zero to allow re-uploading the same file.
* `resumeChunkSize` optionally you can specify this to upload the file in chunks to the server. This will allow uploading to GAE or other servers that have
file size limitation and trying to upload the whole request before passing it for internal processing.<br/>
If this option is set the requests will have the following extra fields:
`_chunkSize`, `_currentChunkSize`, `_chunkNumber` (zero starting), and `_totalSize` to help the server to write the uploaded chunk to
the correct position.
Uploading in chunks could slow down the overall upload time specially if the chunk size is too small.
When you provide `resumeChunkSize` option one of the `resumeSizeUrl` or `resumeSize` is mandatory to know how much of the file is uploaded so far.
## <a name="old_browsers"></a> Old browsers
For browsers not supporting HTML5 FormData (IE8, IE9, ...) [FileAPI](https://github.com/mailru/FileAPI) module is used.
**Note**: You need Flash installed on your browser since `FileAPI` uses Flash to upload files.
These two files **`FileAPI.min.js`, `FileAPI.flash.swf`** will be loaded by the module on demand (no need to be included in the html) if the browser does not supports HTML5 FormData to avoid extra load for HTML5 browsers.
You can place these two files beside `angular-file-upload-shim(.min).js` on your server to be loaded automatically from the same path or you can specify the path to those files if they are in a different path using the following script:
```html
<script>
//optional need to be loaded before angular-file-upload-shim(.min).js
FileAPI = {
//only one of jsPath or jsUrl.
jsPath: '/js/FileAPI.min.js/folder/',
jsUrl: 'yourcdn.com/js/FileAPI.min.js',
//only one of staticPath or flashUrl.
staticPath: '/flash/FileAPI.flash.swf/folder/',
flashUrl: 'yourcdn.com/js/FileAPI.flash.swf',
//forceLoad: true, html5: false //to debug flash in HTML5 browsers
//noContentTimeout: 10000 (see #528)
}
</script>
<script src="angular-file-upload-shim.min.js"></script>...
```
**Old browsers known issues**:
* Because of a Flash limitation/bug if the server doesn't send any response body the status code of the response will be always `204 'No Content'`. So if you have access to your server upload code at least return a character in the response for the status code to work properly.
* Custom headers will not work due to a Flash limitation [#111](https://github.com/danialfarid/ng-file-upload/issues/111) [#224](https://github.com/danialfarid/ng-file-upload/issues/224) [#129](https://github.com/danialfarid/ng-file-upload/issues/129)
* Due to Flash bug [#92](https://github.com/danialfarid/ng-file-upload/issues/92) Server HTTP error code 400 will be returned as 200 to the client. So avoid returning 400 on your server side for upload response otherwise it will be treated as a success response on the client side.
* In case of an error response (http code >= 400) the custom error message returned from the server may not be available. For some error codes flash just provide a generic error message and ignores the response text. [#310](https://github.com/danialfarid/ng-file-upload/issues/310)
* Older browsers won't allow `PUT` requests. [#261](https://github.com/danialfarid/ng-file-upload/issues/261)
## <a name="server"></a>Server Side
* <a name="java"></a>**Java**
You can find the sample server code in Java/GAE [here](https://github.com/danialfarid/ng-file-upload/blob/master/demo/src/main/java/com/df/angularfileupload/)
* <a name="spring"></a>**Spring MVC**
[Wiki Sample](https://github.com/danialfarid/ng-file-upload/wiki/spring-mvc-example) provided by [zouroto](https://github.com/zouroto)
* <a name="node"></a>**Node.js**
[Wiki Sample](https://github.com/danialfarid/ng-file-upload/wiki/node.js-example) provided by [chovy](https://github.com/chovy).
[Another wiki](https://github.com/danialfarid/ng-file-upload/wiki/Node-example) using Express 4.0 and the Multiparty provided by [Jonathan White](https://github.com/JonathanZWhite)
* <a name="rails"></a>**Rails**
* [Wiki Sample](https://github.com/danialfarid/ng-file-upload/wiki/Rails-Example) provided by [guptapriyank](https://github.com/guptapriyank).
* [Blog post](http://www.coshx.com/blog/2015/07/10/file-attachments-in-angular/)
provided by [Coshx Labs](http://www.coshx.com/).
* **Rails progress event**: If your server is Rails and Apache you may need to modify server configurations for the server to support upload progress. See [#207](https://github.com/danialfarid/ng-file-upload/issues/207)
* <a name="php"></a>**PHP**
[Wiki Sample](https://github.com/danialfarid/ng-file-upload/wiki/PHP-Example) and related issue [only one file in $_FILES when uploading multiple files](https://github.com/danialfarid/ng-file-upload/issues/475)
* <a name="net"></a>**.Net**
* [Demo](https://github.com/stewartm83/angular-fileupload-sample) showing how to use ng-file-upload with Asp.Net Web Api.
* Sample client and server code [demo/C#](https://github.com/danialfarid/ng-file-upload/tree/master/demo/C%23) provided by [AtomStar](https://github.com/AtomStar)
## <a name="cors"></a>CORS
To support CORS upload your server needs to allow cross domain requests. You can achieve that by having a filter or interceptor on your upload file server to add CORS headers to the response similar to this:
([sample java code](https://github.com/danialfarid/ng-file-upload/blob/master/demo/src/main/java/com/df/angularfileupload/CORSFilter.java))
```java
httpResp.setHeader("Access-Control-Allow-Methods", "POST, PUT, OPTIONS");
httpResp.setHeader("Access-Control-Allow-Origin", "your.other.server.com");
httpResp.setHeader("Access-Control-Allow-Headers", "Content-Type"));
```
For non-HTML5 IE8-9 browsers you would also need a `crossdomain.xml` file at the root of your server to allow CORS for flash:
<a name="crossdomain"></a>([sample xml](https://angular-file-upload.appspot.com/crossdomain.xml))
```xml
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="angular-file-upload.appspot.com"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
</cross-domain-policy>
```
#### <a name="s3"></a>Amazon AWS S3 Upload
For Amazon authentication version 4 [see this comment](https://github.com/danialfarid/ng-file-upload/issues/1128#issuecomment-196203268)
The <a href="https://angular-file-upload.appspot.com/" target="_blank">demo</a> page has an option to upload to S3.
Here is a sample config options:
```js
Upload.upload({
url: 'https://angular-file-upload.s3.amazonaws.com/', //S3 upload url including bucket name
method: 'POST',
data: {
key: file.name, // the key to store the file on S3, could be file name or customized
AWSAccessKeyId: <YOUR AWS AccessKey Id>,
acl: 'private', // sets the access to the uploaded file in the bucket: private, public-read, ...
policy: $scope.policy, // base64-encoded json policy (see article below)
signature: $scope.signature, // base64-encoded signature based on policy string (see article below)
"Content-Type": file.type != '' ? file.type : 'application/octet-stream', // content type of the file (NotEmpty)
filename: file.name, // this is needed for Flash polyfill IE8-9
file: file
}
});
```
[This article](http://aws.amazon.com/articles/1434/) explains more about these fields and provides instructions on how to generate the policy and signature using a server side tool.
These two values are generated from the json policy document which looks like this:
```js
{
"expiration": "2020-01-01T00:00:00Z",
"conditions": [
{"bucket": "angular-file-upload"},
["starts-with", "$key", ""],
{"acl": "private"},
["starts-with", "$Content-Type", ""],
["starts-with", "$filename", ""],
["content-length-range", 0, 524288000]
]
}
```
The [demo](https://angular-file-upload.appspot.com/) page provide a helper tool to generate the policy and signature from you from the json policy document. **Note**: Please use https protocol to access demo page if you are using this tool to generate signature and policy to protect your aws secret key which should never be shared.
Make sure that you provide upload and CORS post to your bucket at AWS -> S3 -> bucket name -> Properties -> Edit bucket policy and Edit CORS Configuration. Samples of these two files:
```js
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadFile",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::xxxx:user/xxx"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::angular-file-upload/*"
},
{
"Sid": "crossdomainAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::angular-file-upload/crossdomain.xml"
}
]
}
```
```xml
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://angular-file-upload.appspot.com</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
```
For IE8-9 flash polyfill you need to have a <a href='#crossdomain'>crossdomain.xml</a> file at the root of you S3 bucket. Make sure the content-type of crossdomain.xml is text/xml and you provide read access to this file in your bucket policy.
You can also have a look at [https://github.com/nukulb/s3-angular-file-upload](https://github.com/nukulb/s3-angular-file-upload) for another example with [this](https://github.com/danialfarid/ng-file-upload/issues/814#issuecomment-112198426) fix.
================================================
FILE: demo/C#/UploadController.js
================================================
(function () {
'use strict';
angular
.module('app')
.controller('UploadCtrl', UploadCtrl);
UploadCtrl.$inject = ['$location', '$upload'];
function UploadCtrl($location, $upload) {
/* jshint validthis:true */
var vm = this;
vm.title = 'UploadCtrl';
vm.onFileSelect = function ($files, user) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
vm.upload = $upload.upload({
url: 'Uploads/UploadHandler.ashx',
data: { name: user.Name },
file: file, // or list of files ($files) for html5 only
}).progress(function (evt) {
//console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function (data, status, headers, config) {
alert('Uploaded successfully ' + file.name);
}).error(function (err) {
alert('Error occured during upload');
});
}
};
}
})();
================================================
FILE: demo/C#/UploadHandler.ashx
================================================
<%@ WebHandler Language="C#" CodeBehind="UploadHandler.ashx.cs" Class="MyApp.Uploads.UploadHandler" %>
================================================
FILE: demo/C#/UploadHandler.ashx.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyApp.Uploads
{
/// <summary>
/// Summary description for UploadHandler
/// </summary>
public class UploadHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.Files.Count > 0)
{
HttpFileCollection files = context.Request.Files;
var userName = context.Request.Form["name"];
for (int i = 0; i < files.Count; i++)
{
HttpPostedFile file = files[i];
string fname = context.Server.MapPath("Uploads\\" + userName.ToUpper() + "\\" + file.FileName);
file.SaveAs(fname);
}
}
context.Response.ContentType = "text/plain";
context.Response.Write("File/s uploaded successfully!");
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
================================================
FILE: demo/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<version>0.0.1</version>
<artifactId>ng-file-upload-demo-server</artifactId>
<groupId>com.df.ng-file-upload.demo.server</groupId>
<properties>
<appengine.app.version>1</appengine.app.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<prerequisites>
<maven>3.1.0</maven>
</prerequisites>
<dependencies>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2</version>
</dependency>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.9.18</version>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-endpoints</artifactId>
<version>1.9.18</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
</dependencies>
<build>
<!-- for hot reload of the web application-->
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>3.2</version>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<archiveClasses>true</archiveClasses>
<webResources>
<!– in order to interpolate version from pom into appengine-web.xml –>
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
<filtering>true</filtering>
<targetPath>WEB-INF</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>1.9.18</version>
<configuration>
<enableJarClasses>false</enableJarClasses>
<!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
<address>0.0.0.0</address>
<port>8888</port>
<fullScanSeconds>1</fullScanSeconds>
<jvmFlags>
<jvmFlag>-Xdebug</jvmFlag>
<jvmFlag>-Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n</jvmFlag>
</jvmFlags>
</configuration>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: demo/src/main/java/com/df/angularfileupload/CORSFilter.java
================================================
package com.df.angularfileupload;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CORSFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
HttpServletResponse httpResp = (HttpServletResponse) resp;
HttpServletRequest httpReq = (HttpServletRequest) req;
httpResp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
httpResp.setHeader("Access-Control-Allow-Origin", "*");
if (httpReq.getMethod().equalsIgnoreCase("OPTIONS")) {
httpResp.setHeader("Access-Control-Allow-Headers",
httpReq.getHeader("Access-Control-Request-Headers"));
}
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
@Override
public void destroy() {
}
}
================================================
FILE: demo/src/main/java/com/df/angularfileupload/FileUpload.java
================================================
package com.df.angularfileupload;
import com.google.appengine.repackaged.org.joda.time.LocalDateTime;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = -8244073279641189889L;
private final Logger log = Logger.getLogger(FileUpload.class.getName());
class SizeEntry {
public int size;
public LocalDateTime time;
}
static Map<String, SizeEntry> sizeMap = new ConcurrentHashMap<>();
int counter;
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
try {
clearOldValuesInSizeMap();
String ipAddress = req.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = req.getRemoteAddr();
}
if (req.getMethod().equalsIgnoreCase("GET")) {
if (req.getParameter("restart") != null) {
sizeMap.remove(ipAddress + req.getParameter("name"));
}
SizeEntry entry = sizeMap.get(ipAddress + req.getParameter("name"));
res.getWriter().write("{\"size\":" + (entry == null ? 0 : entry.size) + "}");
res.setContentType("application/json");
return;
}
req.setCharacterEncoding("utf-8");
if (!"OPTIONS".equalsIgnoreCase(req.getMethod()) && req.getParameter("errorCode") != null) {
// res.getWriter().write(req.getParameter("errorMessage"));
// res.getWriter().flush();
res.sendError(Integer.parseInt(req.getParameter("errorCode")), req.getParameter("errorMessage"));
return;
}
StringBuilder sb = new StringBuilder("{\"result\": [");
if (req.getHeader("Content-Type") != null
&& req.getHeader("Content-Type").startsWith("multipart/form-data")) {
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterator = upload.getItemIterator(req);
while (iterator.hasNext()) {
FileItemStream item = iterator.next();
sb.append("{");
sb.append("\"fieldName\":\"").append(item.getFieldName()).append("\",");
if (item.getName() != null) {
sb.append("\"name\":\"").append(item.getName()).append("\",");
}
if (item.getName() != null) {
sb.append("\"size\":\"").append(size(ipAddress + item.getName(), item.openStream())).append("\"");
} else {
sb.append("\"value\":\"").append(read(item.openStream()).replace("\"", "'")).append("\"");
}
sb.append("}");
if (iterator.hasNext()) {
sb.append(",");
}
}
} else {
sb.append("{\"size\":\"" + size(ipAddress, req.getInputStream()) + "\"}");
}
sb.append("]");
sb.append(", \"requestHeaders\": {");
@SuppressWarnings("unchecked")
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
sb.append("\"").append(header).append("\":\"").append(req.getHeader(header)).append("\"");
if (headerNames.hasMoreElements()) {
sb.append(",");
}
}
sb.append("}}");
res.setCharacterEncoding("utf-8");
res.getWriter().write(sb.toString());
} catch (Exception ex) {
throw new ServletException(ex);
}
}
private void clearOldValuesInSizeMap() {
if (counter++ == 100) {
for (Map.Entry<String, SizeEntry> entry : sizeMap.entrySet()) {
if (entry.getValue().time.isBefore(LocalDateTime.now().minusHours(1))) {
sizeMap.remove(entry.getKey());
}
}
counter = 0;
}
}
protected int size(String key, InputStream stream) {
int length = sizeMap.get(key) == null ? 0 : sizeMap.get(key).size;
try {
byte[] buffer = new byte[200000];
int size;
while ((size = stream.read(buffer)) != -1) {
length += size;
SizeEntry entry = new SizeEntry();
entry.size = length;
entry.time = LocalDateTime.now();
sizeMap.put(key, entry);
// for (int i = 0; i < size; i++) {
// System.out.print((char) buffer[i]);
// }
}
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println(length);
return length;
}
protected String read(InputStream stream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
try {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
reader.close();
} catch (IOException e) {
//ignore
}
}
return sb.toString();
}
}
================================================
FILE: demo/src/main/java/com/df/angularfileupload/S3Signature.java
================================================
package com.df.angularfileupload;
import com.google.api.server.spi.IoUtil;
import com.google.appengine.repackaged.com.google.common.io.BaseEncoding;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class S3Signature extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String policy_document = IoUtil.readStream(req.getInputStream());
System.out.println(policy_document);
String policy = BaseEncoding.base64().encode(policy_document.getBytes("UTF-8")).
replaceAll("\n","").replaceAll("\r","");
Mac hmac;
try {
hmac = Mac.getInstance("HmacSHA1");
String aws_secret_key = req.getParameter("aws-secret-key");
System.out.println(aws_secret_key);
hmac.init(new SecretKeySpec(aws_secret_key.getBytes("UTF-8"), "HmacSHA1"));
String signature = BaseEncoding.base64().encode(
hmac.doFinal(policy.getBytes("UTF-8")))
.replaceAll("\n", "");
res.setStatus(HttpServletResponse.SC_OK);
res.setContentType("application/json");
res.getWriter().write("{\"signature\":\"" + signature + "\",\"policy\":\"" + policy + "\"}");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
}
}
================================================
FILE: demo/src/main/resources/META-INF/jdoconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig http://java.sun.com/xml/ns/jdo/jdoconfig_3_0.xsd">
<persistence-manager-factory name="transactions-optional">
<property name="javax.jdo.PersistenceManagerFactoryClass"
value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
<property name="javax.jdo.option.ConnectionURL" value="appengine"/>
<property name="javax.jdo.option.NontransactionalRead" value="true"/>
<property name="javax.jdo.option.NontransactionalWrite" value="true"/>
<property name="javax.jdo.option.RetainValues" value="true"/>
<property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
<property name="datanucleus.appengine.singletonPMFForName" value="true"/>
</persistence-manager-factory>
</jdoconfig>
================================================
FILE: demo/src/main/resources/META-INF/persistence.xml
================================================
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="transactions-optional">
<provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
<properties>
<property name="datanucleus.NontransactionalRead" value="true"/>
<property name="datanucleus.NontransactionalWrite" value="true"/>
<property name="datanucleus.ConnectionURL" value="appengine"/>
</properties>
</persistence-unit>
</persistence>
================================================
FILE: demo/src/main/resources/log4j.properties
================================================
# A default log4j configuration for log4j users.
#
# To use this configuration, deploy it into your application's WEB-INF/classes
# directory. You are also encouraged to edit it as you like.
# Configure the console as our one appender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
# tighten logging on the DataNucleus Categories
log4j.category.DataNucleus.JDO=WARN, A1
log4j.category.DataNucleus.Persistence=WARN, A1
log4j.category.DataNucleus.Cache=WARN, A1
log4j.category.DataNucleus.MetaData=WARN, A1
log4j.category.DataNucleus.General=WARN, A1
log4j.category.DataNucleus.Utility=WARN, A1
log4j.category.DataNucleus.Transaction=WARN, A1
log4j.category.DataNucleus.Datastore=WARN, A1
log4j.category.DataNucleus.ClassLoading=WARN, A1
log4j.category.DataNucleus.Plugin=WARN, A1
log4j.category.DataNucleus.ValueGeneration=WARN, A1
log4j.category.DataNucleus.Enhancer=WARN, A1
log4j.category.DataNucleus.SchemaTool=WARN, A1
================================================
FILE: demo/src/main/webapp/WEB-INF/appengine-web.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>angular-file-upload</application>
<version>9-0-0</version>
<!--
Allows App Engine to send multiple requests to one instance in parallel:
-->
<threadsafe>true</threadsafe>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
<!--
HTTP Sessions are disabled by default. To enable HTTP sessions specify:
<sessions-enabled>true</sessions-enabled>
It's possible to reduce request latency by configuring your application to
asynchronously write HTTP session data to the datastore:
<async-session-persistence enabled="true" />
With this feature enabled, there is a very small chance your app will see
stale session data. For details, see
http://code.google.com/appengine/docs/java/config/appconfig.html#Enabling_Sessions
-->
<static-files>
<exclude path="/*.html" />
</static-files>
</appengine-web-app>
================================================
FILE: demo/src/main/webapp/WEB-INF/logging.properties
================================================
# A default java.util.logging configuration.
# (All App Engine logging is through java.util.logging by default).
#
# To use this configuration, copy it into your application's WEB-INF
# folder and add the following to your appengine-web.xml:
#
# <system-properties>
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
# </system-properties>
#
# Set the default logging level for all loggers to WARNING
.level = WARNING
================================================
FILE: demo/src/main/webapp/WEB-INF/web.xml
================================================
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>file_upload</servlet-name>
<servlet-class>com.df.angularfileupload.FileUpload</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>file_upload</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>s3_sign</servlet-name>
<servlet-class>com.df.angularfileupload.S3Signature</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>s3_sign</servlet-name>
<url-pattern>/s3sign</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>SystemServiceServlet</servlet-name>
<servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value />
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>cors_filter</filter-name>
<filter-class>com.df.angularfileupload.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors_filter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
</web-app>
================================================
FILE: demo/src/main/webapp/common.css
================================================
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
}
/* object {
border: 3px solid red;
} */
.upload-buttons input[type="file"] {
width: 6.3em \0/ IE9;
}
.upload-button {
Height: 26px;
line-height: 30px;
padding: 0 10px;
background: #CCC;
appearance: button;
-moz-appearance: button; /* Firefox */
-webkit-appearance: button; /* Safari and Chrome */
position: relative;
text-align: center;
top: 7px;
cursor: pointer;
}
.sel-file {
padding: 1px 5px;
font-size: smaller;
color: grey;
}
.response {
padding: 0;
padding-top: 10px;
margin: 3px 0;
clear: both;
list-style: none;
}
.response .sel-file li, .response .reqh {
color: blue;
padding-bottom: 5px;
}
fieldset {
border: 1px solid #DDD;
width: 620px;
padding: 10px;
line-height: 23px;
}
fieldset label {
/*font-size: smaller;*/
}
.progress {
display: inline-block;
width: 100px;
border: 3px groove #CCC;
}
.progress div {
font-size: smaller;
background: orange;
width: 0;
}
.drop-box {
background: #F8F8F8;
border: 5px dashed #DDD;
width: 170px;
text-align: center;
padding: 50px 10px;
margin-left: 10px;
}
.up-buttons {
float: right;
}
.drop-box.dragover {
border: 5px dashed blue;
}
.drop-box.dragover-err {
border: 5px dashed red;
}
/* for IE*/
.js-fileapi-wrapper {
display: inline-block;
vertical-align: middle;
}
button {
padding: 1px 5px;
font-size: smaller;
margin: 0 3px;
}
.ng-v {
float: right;
}
.thumb {
float: left;
width: 18px;
height: 18px;
padding-right: 10px;
}
form .thumb {
width: 24px;
height: 24px;
float: none;
position: relative;
top: 7px;
}
form .progress {
line-height: 15px;
}
.edit-area {
font-size: 14px;
background: black;
color: #f9f9f9;
padding: 5px 1px;
}
#htmlEdit {
margin-bottom: 25px;
}
.edit-div {
font-size: smaller;
}
.CodeMirror {
font-size: 14px;
border: 1px solid #ccc;
margin-bottom: 15px;
}
form button {
padding: 3px 10px;
font-weight: bold;
margin-top: 10px;
}
.sub {
font-size: smaller;
color: #777;
padding-top: 5px;
padding-left: 25px;
}
.err {
font-size: 12px;
color: #C53F00;
margin: 15px;
padding: 15px;
background-color: #F0F0F0;
border: 1px solid black;
}
.s3 {
font-size: smaller;
color: #333;
margin-left: 20px;
}
.s3 fieldset {
border: 1px solid #AAA;
}
.s3 label {
width: 180px;
display: inline-block;
}
.s3 input {
width: 300px;
}
.s3 .helper {
margin-left: 5px;
}
.howto {
margin-left: 10px;
line-height: 20px;
}
.server {
margin-bottom: 20px;
}
.srv-title {
font-weight: bold;
padding: 5px 0 10px 0;
}
:not(output):-moz-ui-invalid {
box-shadow: none;
}
.preview {
clear: both;
}
.preview img, .preview audio, .preview video {
max-width: 300px;
max-height: 150px;
float: right;
}
.custom {
font-size: 14px;
margin-left: 20px;
}
================================================
FILE: demo/src/main/webapp/crossdomain.xml
================================================
<?xml version="1.0" ?>
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all" />
<allow-access-from domain="*" secure="false" />
<allow-access-from domain="angular-file-upload-cors.appspot.com" />
<allow-access-from domain="angular-file-upload-cors-srv.appspot.com" />
<allow-http-request-headers-from domain="*" headers="*" secure="false" />
</cross-domain-policy>
================================================
FILE: demo/src/main/webapp/donate.html
================================================
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="common.css">
<title>Angular file upload donate</title>
</head>
<body>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top" id="frm">
<input type="hidden" name="cmd" value="_donations">
<input type="hidden" name="business" value="danial.farid@gmail.com">
<input type="hidden" name="lc" value="CA">
<input type="hidden" name="item_name" value="angular-file-upload plugin">
<input type="hidden" name="no_note" value="0">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="bn" value="PP-DonationsBF:tea.jpg:NonHostedGuest">
<input type="image" src="img/tea.jpg" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
<script>
window.onload = function() {
document.getElementById("frm").submit();
}
</script>
</body>
</html>
================================================
FILE: demo/src/main/webapp/index.html
================================================
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<link type="text/css" rel="stylesheet" href="common.css">
<title>Angular file upload sample</title>
<script type="text/javascript">
FileAPI = {
debug: true,
//forceLoad: true, html5: false //to debug flash in HTML5 browsers
//wrapInsideDiv: true, //experimental for fixing css issues
//only one of jsPath or jsUrl.
//jsPath: '/js/FileAPI.min.js/folder/',
//jsUrl: 'yourcdn.com/js/FileAPI.min.js',
//only one of staticPath or flashUrl.
//staticPath: '/flash/FileAPI.flash.swf/folder/'
//flashUrl: 'yourcdn.com/js/FileAPI.flash.swf'
};
</script>
<!-- <script src="//code.jquery.com/jquery-1.9.0.min.js"></script> -->
<script type="text/javascript">
// load angularjs specific version
var angularVersion = window.location.hash.substring(1);
if (angularVersion.indexOf('/') == 0) angularVersion = angularVersion.substring(1);
document.write('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/' +
(angularVersion || '1.2.24') + '/angular.js"><\/script>');
</script>
<script src="js/ng-file-upload-shim.js"></script>
<script src="js/ng-file-upload.js"></script>
<script src="js/upload.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/codemirror.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/codemirror.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/mode/htmlmixed/htmlmixed.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/mode/htmlembedded/htmlembedded.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/mode/xml/xml.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.8.0/mode/javascript/javascript.min.js"></script>
</head>
<body ng-app="fileUpload" ng-controller="MyCtrl">
<div class="ng-v">
Angular Version: <input type="text" ng-model="angularVersion">
<input type="button" data-ng-click="changeAngularVersion()" value="Go">
</div>
<h2>Angular file upload Demo</h2>
<h3>
Visit <a href="https://github.com/danialfarid/ng-file-upload">ng-file-upload</a> on github
</h3>
<div class="upload-div">
<div class="edit-div">
<a ng-click="showEdit = !showEdit" href="javascript:void(0)">+ edit upload html</a>
<a ng-show="showEdit" ng-click="confirm() && (editHtml = defaultHtml)" href="javascript:void(0)">reset to
default</a><br/><br/>
<div ng-show="showEdit" id="htmlEdit"></div>
</div>
<div class="upload-buttons">
<div id="editArea">
<div>
<div>
<form name="myForm">
<fieldset>
<legend>Upload on form submit</legend>
Username: <input type="text" name="userName" ng-model="username" size="39" required>
<i ng-show="myForm.userName.$error.required">*required</i><br>
Profile Picture: <input type="file" ngf-select ng-model="picFile" name="file" ngf-accept="'image/*'" required>
<i ng-show="myForm.file.$error.required">*required</i>
<br/>
<button ng-disabled="!myForm.$valid" ng-click="uploadPic(picFile)">Submit</button>
<img ngf-src="picFile" class="thumb">
<span class="progress" ng-show="picFile.progress >= 0">
<div style="width:{{picFile.progress}}%" ng-bind="picFile.progress + '%'"></div>
</span>
<span ng-show="picFile.result">Upload Successful</span>
</fieldset>
<br/>
</form>
</div>
<fieldset>
<legend>Play with options</legend>
<div class="up-buttons">
<div ngf-select ngf-drop ng-model="files" ngf-model-invalid="invalidFiles"
ngf-model-options="modelOptionsObj"
ngf-multiple="multiple" ngf-pattern="pattern" ngf-accept="acceptSelect"
ng-disabled="disabled" ngf-capture="capture"
ngf-drag-over-class="dragOverClassObj"
ngf-validate="validateObj"
ngf-resize="resizeObj"
ngf-resize-if="resizeIfFn($file, $width, $height)"
ngf-dimensions="dimensionsFn($file, $width, $height)"
ngf-duration="durationFn($file, $duration)"
ngf-keep="keepDistinct ? 'distinct' : keep"
ngf-fix-orientation="orientation"
ngf-max-files="maxFiles"
ngf-ignore-invalid="ignoreInvalid"
ngf-run-all-validations="runAllValidations"
ngf-allow-dir="allowDir" class="drop-box" ngf-drop-available="dropAvailable">Select File
<span ng-show="dropAvailable"> or Drop File</span>
</div>
<br/>
<div ngf-drop ng-model="files" ngf-model-invalid="invalidFiles"
ngf-model-options="modelOptionsObj"
ngf-multiple="multiple" ngf-pattern="'image/*'"
ng-disabled="disabled"
ngf-drag-over-class="dragOverClassObj"
ngf-validate="validateObj"
ngf-resize="resizeObj"
ngf-resize-if="resizeIfFn($file, $width, $height)"
ngf-dimensions="dimensionsFn($file, $width, $height)"
ngf-duration="durationFn($file, $duration)"
ngf-keep="keepDistinct ? 'distinct' : keep"
ngf-enable-firefox-paste="true"
ngf-fix-orientation="orientation"
ngf-max-files="maxFiles"
ngf-ignore-invalid="ignoreInvalid"
ngf-run-all-validations="runAllValidations"
ngf-allow-dir="allowDir" class="drop-box" ng-show="dropAvailable">
<span>Paste or Drop Image from browser</span></div>
</div>
<div class="custom">
<label>accept (for select browser dependent): <input type="text" ng-model="acceptSelect"></label><br/>
<label>ngf-capture (for mobile): <input type="text" ng-model="capture"></label><br/>
<label>ngf-pattern (validate file model): <input type="text" ng-model="pattern"></label><br/>
<label>ngf-validate: <input type="text" ng-model="validate" size="49"></label><br/>
<label>ngf-drag-over-class (chrome): <input size="31" type="text" ng-model="dragOverClass"></label><br/>
<label>ngf-model-options: <input type="text" size="43" ng-model="modelOptions"></label><br/>
<label>ngf-resize: <input type="text" size="43" ng-model="resize"></label><br/>
<label>ngf-resize-if: <input type="text" size="43" ng-model="resizeIf"></label><br/>
<label>ngf-dimensions: <input type="text" size="43" ng-model="dimensions"></label><br/>
<label>ngf-duration: <input type="text" size="43" ng-model="duration"></label><br/>
<label>ngf-max-files: <input type="text" size="43" ng-model="maxFiles"></label><br/>
<label>ngf-ignore-invalid: <input type="text" size="43" ng-model="ignoreInvalid"></label><br/>
<label><input type="checkbox" ng-model="multiple">ngf-multiple (allow multiple files)</label>
<label><input type="checkbox" ng-model="disabled">ng-disabled</label><br/>
<label><input type="checkbox" ng-model="allowDir">ngf-allow-dir (allow directory drop Chrome
only)</label><br/>
<label><input type="checkbox" ng-model="keep">ngf-keep (keep the previous model values in
ng-model)</label><br/>
<label><input type="checkbox" ng-model="keepDistinct">ngf-keep="distinct" (do not allow
duplicates)</label><br/>
<label><input type="checkbox" ng-model="orientation">ngf-fix-orientation (for exif jpeg files)</label><br/>
<label><input type="checkbox" ng-model="runAllValidations">ngf-run-all-validations</label><br/>
<label>Upload resumable chunk size: <input type="text" ng-model="chunkSize"></label><br/>
</div>
<div class="preview">
<div>Preview image/audio/video:</div>
<img ngf-src="!files[0].$error && files[0]">
<audio controls ngf-src="!files[0].$error && files[0]"></audio>
<video controls ngf-src="!files[0].$error && files[0]"></video>
</div>
</fieldset>
<br/>
</div>
</div>
</div>
<ul style="clear:both" class="response">
<li class="sel-file" ng-repeat="f in files">
<div>
<img ngf-thumbnail="!f.$error && f" class="thumb">
<span class="progress" ng-show="f.progress >= 0">
<div style="width:{{f.progress}}%">{{f.progress}}%</div>
</span>
<button class="button" ng-click="f.upload.abort();f.upload.aborted=true"
ng-show="f.upload != null && f.progress < 100 && !f.upload.aborted">
Abort<span ng-show="isResumeSupported">/Pause</span>
</button>
<button class="button" ng-click="upload(f, true);f.upload.aborted=false"
ng-show="isResumeSupported && f.upload != null && f.upload.aborted">Resume
</button>
<button class="button" ng-click="restart(f);f.upload.aborted=false"
ng-show="isResumeSupported && f.upload != null && (f.progress == 100 || f.upload.aborted)">Restart
</button>
{{f.name}} - size: {{f.size}}B - type: {{f.type}}
<a ng-show="f.result" href="javascript:void(0)" ng-click="f.showDetail = !f.showDetail">details</a>
<div ng-show="f.showDetail">
<br/>
<div data-ng-show="f.result.result == null">{{f.result}}</div>
<ul>
<li ng-repeat="item in f.result.result">
<div data-ng-show="item.name">file name: {{item.name}}</div>
<div data-ng-show="item.fieldName">name: {{item.fieldName}}</div>
<div data-ng-show="item.size">size on the serve: {{item.size}}</div>
<div data-ng-show="item.value">value: {{item.value}}</div>
</li>
</ul>
<div data-ng-show="f.result.requestHeaders" class="reqh">request headers: {{f.result.requestHeaders}}</div>
</div>
</div>
</li>
<li class="sel-file" ng-repeat="f in invalidFiles">
<div>Invalid File: {{f.$errorMessages}} {{f.$errorParam}}, {{f.name}} - size: {{f.size}}B - type:
{{f.type}}
</div>
</li>
</ul>
<br/>
<div style="clear:both" class="err" ng-show="errorMsg != null">{{errorMsg}}</div>
</div>
<div style="clear:both" class="server">
<div class="srv-title">How to upload to the server:</div>
<div class="howto">
<label><input type="radio" name="howToSend" ng-model="howToSend" value="1" ng-init="howToSend = 1">Upload.upload():
multipart/form-data upload cross browser</label>
<br/>
<label><input type="radio" name="howToSend" ng-model="howToSend" value="2"
ng-disabled="usingFlash">Upload.http(): binary content with file's
Content-Type</label>
<div class="sub">Can be used to upload files directory into <a
href="https://github.com/danialfarid/angular-file-upload/issues/88">CouchDB</a>,
<a href="https://github.com/danialfarid/angular-file-upload/issues/87">imgur</a>, etc... without multipart form
data (HTML5 FileReader browsers only)<br/>
</div>
<label><input type="radio" name="howToSend" ng-model="howToSend" value="3">Amazon S3 bucket upload</label>
<form ng-show="howToSend==3" class="s3" id="s3form" action="http://localhost:8888" onsubmit="return false">
<br/>
Provide S3 upload parameters. <a href="http://aws.amazon.com/articles/1434/" target="_blank">Click here for
detailed documentation</a><br/></br>
<label>S3 upload url including bucket name:</label> <input type="text" ng-model="s3url"> <br/>
<label>AWSAccessKeyId:</label> <input type="Text" name="AWSAccessKeyId" ng-model="AWSAccessKeyId"> <br/>
<label>acl (private or public):</label> <input type="text" ng-model="acl" value="private"><br/>
<label>success_action_redirect:</label> <input type="text" ng-model="success_action_redirect"><br/>
<label>policy:</label> <input type="text" ng-model="policy"><br/>
<label>signature:</label> <input type="text" ng-model="signature"><br/>
<br/>
<button ng-click="showHelper=!showHelper">S3 Policy signing helper (Optional)</button>
<br/><br/>
<div class="helper" ng-show="showHelper">
If you don't have your policy and signature you can use this tool to generate them by providing these two fields
and clicking on sign<br/>
<label>AWS Secret Key:</label> <input type="text" ng-model="AWSSecretKey"><br/>
<label>JSON policy:</label><br/> <textarea type="text" ng-model="jsonPolicy" rows=10 cols=70></textarea>
<button ng-click="generateSignature()">Sign</button>
</div>
</form>
<br/>
<br/>
<div ng-show="howToSend != 3">
<input type="checkbox" ng-model="generateErrorOnServer">Return server error with http code: <input type="text"
ng-model="serverErrorCode"
size="5"> and
message: <input type="text" ng-model="serverErrorMsg">
<br/>
</div>
<br/>
</div>
</div>
<a style="position:fixed;bottom:28px;right:10px;font-size:smaller;" target="_blank"
href="https://angular-file-upload.appspot.com/donate.html">donate</a>
<a style="position:fixed;bottom:45px;right:10px;font-size:smaller;"
href="https://github.com/danialfarid/angular-file-upload/issues/new">Feedback/Issues</a>
</body>
</html>
================================================
FILE: demo/src/main/webapp/js/FileAPI.js
================================================
/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git
* FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF.
*/
/*
* JavaScript Canvas to Blob 2.0.5
* https://github.com/blueimp/JavaScript-Canvas-to-Blob
*
* Copyright 2012, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*
* Based on stackoverflow user Stoive's code snippet:
* http://stackoverflow.com/q/4998908
*/
/*jslint nomen: true, regexp: true */
/*global window, atob, Blob, ArrayBuffer, Uint8Array */
(function (window) {
'use strict';
var CanvasPrototype = window.HTMLCanvasElement &&
window.HTMLCanvasElement.prototype,
hasBlobConstructor = window.Blob && (function () {
try {
return Boolean(new Blob());
} catch (e) {
return false;
}
}()),
hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
(function () {
try {
return new Blob([new Uint8Array(100)]).size === 100;
} catch (e) {
return false;
}
}()),
BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
window.MozBlobBuilder || window.MSBlobBuilder,
dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
window.ArrayBuffer && window.Uint8Array && function (dataURI) {
var byteString,
arrayBuffer,
intArray,
i,
mimeString,
bb;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataURI.split(',')[1]);
} else {
// Convert base64/URLEncoded data component to raw binary data:
byteString = decodeURIComponent(dataURI.split(',')[1]);
}
// Write the bytes of the string to an ArrayBuffer:
arrayBuffer = new ArrayBuffer(byteString.length);
intArray = new Uint8Array(arrayBuffer);
for (i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
// Separate out the mime component:
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// Write the ArrayBuffer (or ArrayBufferView) to a blob:
if (hasBlobConstructor) {
return new Blob(
[hasArrayBufferViewSupport ? intArray : arrayBuffer],
{type: mimeString}
);
}
bb = new BlobBuilder();
bb.append(arrayBuffer);
return bb.getBlob(mimeString);
};
if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
if (CanvasPrototype.mozGetAsFile) {
CanvasPrototype.toBlob = function (callback, type, quality) {
if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
callback(dataURLtoBlob(this.toDataURL(type, quality)));
} else {
callback(this.mozGetAsFile('blob', type));
}
};
} else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
CanvasPrototype.toBlob = function (callback, type, quality) {
callback(dataURLtoBlob(this.toDataURL(type, quality)));
};
}
}
window.dataURLtoBlob = dataURLtoBlob;
})(window);
/*jslint evil: true */
/*global window, URL, webkitURL, ActiveXObject */
(function (window, undef){
'use strict';
var
gid = 1,
noop = function (){},
document = window.document,
doctype = document.doctype || {},
userAgent = window.navigator.userAgent,
// 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),
Blob = window.Blob,
File = window.File,
FileReader = window.FileReader,
FormData = window.FormData,
XMLHttpRequest = window.XMLHttpRequest,
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
cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice),
// https://github.com/blueimp/JavaScript-Canvas-to-Blob
dataURLtoBlob = window.dataURLtoBlob,
_rimg = /img/i,
_rcanvas = /canvas/i,
_rimgcanvas = /img|canvas/i,
_rinput = /input/i,
_rdata = /^data:[^,]+,/,
_toString = {}.toString,
Math = window.Math,
_SIZE_CONST = function (pow){
pow = new window.Number(Math.pow(1024, pow));
pow.from = function (sz){ return Math.round(sz * this); };
return pow;
},
_elEvents = {}, // element event listeners
_infoReader = [], // list of file info processors
_readerEvents = 'abort progress error load loadend',
_xhrPropsExport = 'status statusText readyState response responseXML responseText responseBody'.split(' '),
currentTarget = 'currentTarget', // for minimize
preventDefault = 'preventDefault', // and this too
_isArray = function (ar) {
return ar && ('length' in ar);
},
/**
* Iterate over a object or array
*/
_each = function (obj, fn, ctx){
if( obj ){
if( _isArray(obj) ){
for( var i = 0, n = obj.length; i < n; i++ ){
if( i in obj ){
fn.call(ctx, obj[i], i, obj);
}
}
}
else {
for( var key in obj ){
if( obj.hasOwnProperty(key) ){
fn.call(ctx, obj[key], key, obj);
}
}
}
}
},
/**
* Merge the contents of two or more objects together into the first object
*/
_extend = function (dst){
var args = arguments, i = 1, _ext = function (val, key){ dst[key] = val; };
for( ; i < args.length; i++ ){
_each(args[i], _ext);
}
return dst;
},
/**
* Add event listener
*/
_on = function (el, type, fn){
if( el ){
var uid = api.uid(el);
if( !_elEvents[uid] ){
_elEvents[uid] = {};
}
var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
if( jQuery && !isFileReader){
jQuery.event.add(el, type, fn);
} else {
if( !_elEvents[uid][type] ){
_elEvents[uid][type] = [];
}
_elEvents[uid][type].push(fn);
if( el.addEventListener ){ el.addEventListener(type, fn, false); }
else if( el.attachEvent ){ el.attachEvent('on'+type, fn); }
else { el['on'+type] = fn; }
}
});
}
},
/**
* Remove event listener
*/
_off = function (el, type, fn){
if( el ){
var uid = api.uid(el), events = _elEvents[uid] || {};
var isFileReader = (FileReader && el) && (el instanceof FileReader);
_each(type.split(/\s+/), function (type){
if( jQuery && !isFileReader){
jQuery.event.remove(el, type, fn);
}
else {
var fns = events[type] || [], i = fns.length;
while( i-- ){
if( fns[i] === fn ){
fns.splice(i, 1);
break;
}
}
if( el.addEventListener ){ el.removeEventListener(type, fn, false); }
else if( el.detachEvent ){ el.detachEvent('on'+type, fn); }
else { el['on'+type] = null; }
}
});
}
},
_one = function(el, type, fn){
_on(el, type, function _(evt){
_off(el, type, _);
fn(evt);
});
},
_fixEvent = function (evt){
if( !evt.target ){ evt.target = window.event && window.event.srcElement || document; }
if( evt.target.nodeType === 3 ){ evt.target = evt.target.parentNode; }
return evt;
},
_supportInputAttr = function (attr){
var input = document.createElement('input');
input.setAttribute('type', "file");
return attr in input;
},
/**
* FileAPI (core object)
*/
api = {
version: '2.0.7',
cors: false,
html5: true,
media: false,
formData: true,
multiPassResize: true,
debug: false,
pingUrl: false,
multiFlash: false,
flashAbortTimeout: 0,
withCredentials: true,
staticPath: './dist/',
flashUrl: 0, // @default: './FileAPI.flash.swf'
flashImageUrl: 0, // @default: './FileAPI.flash.image.swf'
postNameConcat: function (name, idx){
return name + (idx != null ? '['+ idx +']' : '');
},
ext2mime: {
jpg: 'image/jpeg'
, tif: 'image/tiff'
, txt: 'text/plain'
},
// Fallback for flash
accept: {
'image/*': 'art bm bmp dwg dxf cbr cbz fif fpx gif ico iefs jfif jpe jpeg jpg jps jut mcf nap nif pbm pcx pgm pict pm png pnm qif qtif ras rast rf rp svf tga tif tiff xbm xbm xpm xwd'
, 'audio/*': 'm4a flac aac rm mpa wav wma ogg mp3 mp2 m3u mod amf dmf dsm far gdm imf it m15 med okt s3m stm sfx ult uni xm sid ac3 dts cue aif aiff wpl ape mac mpc mpp shn wv nsf spc gym adplug adx dsp adp ymf ast afc hps xs'
, 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl'
},
uploadRetry : 0,
networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down
chunkSize : 0,
chunkUploadRetry : 0,
chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down
KB: _SIZE_CONST(1),
MB: _SIZE_CONST(2),
GB: _SIZE_CONST(3),
TB: _SIZE_CONST(4),
EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=',
expando: 'fileapi' + (new Date).getTime(),
uid: function (obj){
return obj
? (obj[api.expando] = obj[api.expando] || api.uid())
: (++gid, api.expando + gid)
;
},
log: function (){
// ngf fix for IE8 #1071
if( api.debug && api._supportConsoleLog ){
if( api._supportConsoleLogApply ){
console.log.apply(console, arguments);
}
else {
console.log([].join.call(arguments, ' '));
}
}
},
/**
* Create new image
*
* @param {String} [src]
* @param {Function} [fn] 1. error -- boolean, 2. img -- Image element
* @returns {HTMLElement}
*/
newImage: function (src, fn){
var img = document.createElement('img');
if( fn ){
api.event.one(img, 'error load', function (evt){
fn(evt.type == 'error', img);
img = null;
});
}
img.src = src;
return img;
},
/**
* Get XHR
* @returns {XMLHttpRequest}
*/
getXHR: function (){
var xhr;
if( XMLHttpRequest ){
xhr = new XMLHttpRequest;
}
else if( window.ActiveXObject ){
try {
xhr = new ActiveXObject('MSXML2.XMLHttp.3.0');
} catch (e) {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
}
return xhr;
},
isArray: _isArray,
support: {
dnd: cors && ('ondrop' in document.createElement('div')),
cors: cors,
html5: html5,
chunked: chunked,
dataURI: true,
accept: _supportInputAttr('accept'),
multiple: _supportInputAttr('multiple')
},
event: {
on: _on
, off: _off
, one: _one
, fix: _fixEvent
},
throttle: function(fn, delay) {
var id, args;
return function _throttle(){
args = arguments;
if( !id ){
fn.apply(window, args);
id = setTimeout(function (){
id = 0;
fn.apply(window, args);
}, delay);
}
};
},
F: function (){},
parseJSON: function (str){
var json;
if( window.JSON && JSON.parse ){
json = JSON.parse(str);
}
else {
json = (new Function('return ('+str.replace(/([\r\n])/g, '\\$1')+');'))();
}
return json;
},
trim: function (str){
str = String(str);
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
},
/**
* Simple Defer
* @return {Object}
*/
defer: function (){
var
list = []
, result
, error
, defer = {
resolve: function (err, res){
defer.resolve = noop;
error = err || false;
result = res;
while( res = list.shift() ){
res(error, result);
}
},
then: function (fn){
if( error !== undef ){
fn(error, result);
} else {
list.push(fn);
}
}
};
return defer;
},
queue: function (fn){
var
_idx = 0
, _length = 0
, _fail = false
, _end = false
, queue = {
inc: function (){
_length++;
},
next: function (){
_idx++;
setTimeout(queue.check, 0);
},
check: function (){
(_idx >= _length) && !_fail && queue.end();
},
isFail: function (){
return _fail;
},
fail: function (){
!_fail && fn(_fail = true);
},
end: function (){
if( !_end ){
_end = true;
fn();
}
}
}
;
return queue;
},
/**
* For each object
*
* @param {Object|Array} obj
* @param {Function} fn
* @param {*} [ctx]
*/
each: _each,
/**
* Async for
* @param {Array} array
* @param {Function} callback
*/
afor: function (array, callback){
var i = 0, n = array.length;
if( _isArray(array) && n-- ){
(function _next(){
callback(n != i && _next, array[i], i++);
})();
}
else {
callback(false);
}
},
/**
* Merge the contents of two or more objects together into the first object
*
* @param {Object} dst
* @return {Object}
*/
extend: _extend,
/**
* Is file?
* @param {File} file
* @return {Boolean}
*/
isFile: function (file){
return _toString.call(file) === '[object File]';
},
/**
* Is blob?
* @param {Blob} blob
* @returns {Boolean}
*/
isBlob: function (blob) {
return this.isFile(blob) || (_toString.call(blob) === '[object Blob]');
},
/**
* Is canvas element
*
* @param {HTMLElement} el
* @return {Boolean}
*/
isCanvas: function (el){
return el && _rcanvas.test(el.nodeName);
},
getFilesFilter: function (filter){
filter = typeof filter == 'string' ? filter : (filter.getAttribute && filter.getAttribute('accept') || '');
return filter ? new RegExp('('+ filter.replace(/\./g, '\\.').replace(/,/g, '|') +')$', 'i') : /./;
},
/**
* Read as DataURL
*
* @param {File|Element} file
* @param {Function} fn
*/
readAsDataURL: function (file, fn){
if( api.isCanvas(file) ){
_emit(file, fn, 'load', api.toDataURL(file));
}
else {
_readAs(file, fn, 'DataURL');
}
},
/**
* Read as Binary string
*
* @param {File} file
* @param {Function} fn
*/
readAsBinaryString: function (file, fn){
if( _hasSupportReadAs('BinaryString') ){
_readAs(file, fn, 'BinaryString');
} else {
// Hello IE10!
_readAs(file, function (evt){
if( evt.type == 'load' ){
try {
// dataURL -> binaryString
evt.result = api.toBinaryString(evt.result);
} catch (e){
evt.type = 'error';
evt.message = e.toString();
}
}
fn(evt);
}, 'DataURL');
}
},
/**
* Read as ArrayBuffer
*
* @param {File} file
* @param {Function} fn
*/
readAsArrayBuffer: function(file, fn){
_readAs(file, fn, 'ArrayBuffer');
},
/**
* Read as text
*
* @param {File} file
* @param {String} encoding
* @param {Function} [fn]
*/
readAsText: function(file, encoding, fn){
if( !fn ){
fn = encoding;
encoding = 'utf-8';
}
_readAs(file, fn, 'Text', encoding);
},
/**
* Convert image or canvas to DataURL
*
* @param {Element} el Image or Canvas element
* @param {String} [type] mime-type
* @return {String}
*/
toDataURL: function (el, type){
if( typeof el == 'string' ){
return el;
}
else if( el.toDataURL ){
return el.toDataURL(type || 'image/png');
}
},
/**
* Canvert string, image or canvas to binary string
*
* @param {String|Element} val
* @return {String}
*/
toBinaryString: function (val){
return window.atob(api.toDataURL(val).replace(_rdata, ''));
},
/**
* Read file or DataURL as ImageElement
*
* @param {File|String} file
* @param {Function} fn
* @param {Boolean} [progress]
*/
readAsImage: function (file, fn, progress){
if( api.isFile(file) ){
if( apiURL ){
/** @namespace apiURL.createObjectURL */
var data = apiURL.createObjectURL(file);
if( data === undef ){
_emit(file, fn, 'error');
}
else {
api.readAsImage(data, fn, progress);
}
}
else {
api.readAsDataURL(file, function (evt){
if( evt.type == 'load' ){
api.readAsImage(evt.result, fn, progress);
}
else if( progress || evt.type == 'error' ){
_emit(file, fn, evt, null, { loaded: evt.loaded, total: evt.total });
}
});
}
}
else if( api.isCanvas(file) ){
_emit(file, fn, 'load', file);
}
else if( _rimg.test(file.nodeName) ){
if( file.complete ){
_emit(file, fn, 'load', file);
}
else {
var events = 'error abort load';
_one(file, events, function _fn(evt){
if( evt.type == 'load' && apiURL ){
/** @namespace apiURL.revokeObjectURL */
apiURL.revokeObjectURL(file.src);
}
_off(file, events, _fn);
_emit(file, fn, evt, file);
});
}
}
else if( file.iframe ){
_emit(file, fn, { type: 'error' });
}
else {
// Created image
var img = api.newImage(file.dataURL || file);
api.readAsImage(img, fn, progress);
}
},
/**
* Make file by name
*
* @param {String} name
* @return {Array}
*/
checkFileObj: function (name){
var file = {}, accept = api.accept;
if( typeof name == 'object' ){
file = name;
}
else {
file.name = (name + '').split(/\\|\//g).pop();
}
if( file.type == null ){
file.type = file.name.split('.').pop();
}
_each(accept, function (ext, type){
ext = new RegExp(ext.replace(/\s/g, '|'), 'i');
if( ext.test(file.type) || api.ext2mime[file.type] ){
file.type = api.ext2mime[file.type] || (type.split('/')[0] +'/'+ file.type);
}
});
return file;
},
/**
* Get drop files
*
* @param {Event} evt
* @param {Function} callback
*/
getDropFiles: function (evt, callback){
var
files = []
, dataTransfer = _getDataTransfer(evt)
, entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0])
, queue = api.queue(function (){ callback(files); })
;
_each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){
queue.inc();
try {
if( entrySupport ){
_readEntryAsFiles(item, function (err, entryFiles){
if( err ){
api.log('[err] getDropFiles:', err);
} else {
files.push.apply(files, entryFiles);
}
queue.next();
});
}
else {
_isRegularFile(item, function (yes){
yes && files.push(item);
queue.next();
});
}
}
catch( err ){
queue.next();
api.log('[err] getDropFiles: ', err);
}
});
queue.check();
},
/**
* Get file list
*
* @param {HTMLInputElement|Event} input
* @param {String|Function} [filter]
* @param {Function} [callback]
* @return {Array|Null}
*/
getFiles: function (input, filter, callback){
var files = [];
if( callback ){
api.filterFiles(api.getFiles(input), filter, callback);
return null;
}
if( input.jquery ){
// jQuery object
input.each(function (){
files = files.concat(api.getFiles(this));
});
input = files;
files = [];
}
if( typeof filter == 'string' ){
filter = api.getFilesFilter(filter);
}
if( input.originalEvent ){
// jQuery event
input = _fixEvent(input.originalEvent);
}
else if( input.srcElement ){
// IE Event
input = _fixEvent(input);
}
if( input.dataTransfer ){
// Drag'n'Drop
input = input.dataTransfer;
}
else if( input.target ){
// Event
input = input.target;
}
if( input.files ){
// Input[type="file"]
files = input.files;
if( !html5 ){
// Partial support for file api
files[0].blob = input;
files[0].iframe = true;
}
}
else if( !html5 && isInputFile(input) ){
if( api.trim(input.value) ){
files = [api.checkFileObj(input.value)];
files[0].blob = input;
files[0].iframe = true;
}
}
else if( _isArray(input) ){
files = input;
}
return api.filter(files, function (file){ return !filter || filter.test(file.name); });
},
/**
* Get total file size
* @param {Array} files
* @return {Number}
*/
getTotalSize: function (files){
var size = 0, i = files && files.length;
while( i-- ){
size += files[i].size;
}
return size;
},
/**
* Get image information
*
* @param {File} file
* @param {Function} fn
*/
getInfo: function (file, fn){
var info = {}, readers = _infoReader.concat();
if( api.isFile(file) ){
(function _next(){
var reader = readers.shift();
if( reader ){
if( reader.test(file.type) ){
reader(file, function (err, res){
if( err ){
fn(err);
}
else {
_extend(info, res);
_next();
}
});
}
else {
_next();
}
}
else {
fn(false, info);
}
})();
}
else {
fn('not_support_info', info);
}
},
/**
* Add information reader
*
* @param {RegExp} mime
* @param {Function} fn
*/
addInfoReader: function (mime, fn){
fn.test = function (type){ return mime.test(type); };
_infoReader.push(fn);
},
/**
* Filter of array
*
* @param {Array} input
* @param {Function} fn
* @return {Array}
*/
filter: function (input, fn){
var result = [], i = 0, n = input.length, val;
for( ; i < n; i++ ){
if( i in input ){
val = input[i];
if( fn.call(val, val, i, input) ){
result.push(val);
}
}
}
return result;
},
/**
* Filter files
*
* @param {Array} files
* @param {Function} eachFn
* @param {Function} resultFn
*/
filterFiles: function (files, eachFn, resultFn){
if( files.length ){
// HTML5 or Flash
var queue = files.concat(), file, result = [], deleted = [];
(function _next(){
if( queue.length ){
file = queue.shift();
api.getInfo(file, function (err, info){
(eachFn(file, err ? false : info) ? result : deleted).push(file);
_next();
});
}
else {
resultFn(result, deleted);
}
})();
}
else {
resultFn([], files);
}
},
upload: function (options){
options = _extend({
jsonp: 'callback'
, prepare: api.F
, beforeupload: api.F
, upload: api.F
, fileupload: api.F
, fileprogress: api.F
, filecomplete: api.F
, progress: api.F
, complete: api.F
, pause: api.F
, imageOriginal: true
, chunkSize: api.chunkSize
, chunkUploadRetry: api.chunkUploadRetry
, uploadRetry: api.uploadRetry
}, options);
if( options.imageAutoOrientation && !options.imageTransform ){
options.imageTransform = { rotate: 'auto' };
}
var
proxyXHR = new api.XHR(options)
, dataArray = this._getFilesDataArray(options.files)
, _this = this
, _total = 0
, _loaded = 0
, _nextFile
, _complete = false
;
// calc total size
_each(dataArray, function (data){
_total += data.size;
});
// Array of files
proxyXHR.files = [];
_each(dataArray, function (data){
proxyXHR.files.push(data.file);
});
// Set upload status props
proxyXHR.total = _total;
proxyXHR.loaded = 0;
proxyXHR.filesLeft = dataArray.length;
// emit "beforeupload" event
options.beforeupload(proxyXHR, options);
// Upload by file
_nextFile = function (){
var
data = dataArray.shift()
, _file = data && data.file
, _fileLoaded = false
, _fileOptions = _simpleClone(options)
;
proxyXHR.filesLeft = dataArray.length;
if( _file && _file.name === api.expando ){
_file = null;
api.log('[warn] FileAPI.upload() — called without files');
}
if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){
// Mark active job
_complete = false;
// Set current upload file
proxyXHR.currentFile = _file;
// Prepare file options
if (_file && options.prepare(_file, _fileOptions) === false) {
_nextFile.call(_this);
return;
}
_fileOptions.file = _file;
_this._getFormData(_fileOptions, data, function (form){
if( !_loaded ){
// emit "upload" event
options.upload(proxyXHR, options);
}
var xhr = new api.XHR(_extend({}, _fileOptions, {
upload: _file ? function (){
// emit "fileupload" event
options.fileupload(_file, xhr, _fileOptions);
} : noop,
progress: _file ? function (evt){
if( !_fileLoaded ){
// For ignore the double calls.
_fileLoaded = (evt.loaded === evt.total);
// emit "fileprogress" event
options.fileprogress({
type: 'progress'
, total: data.total = evt.total
, loaded: data.loaded = evt.loaded
}, _file, xhr, _fileOptions);
// emit "progress" event
options.progress({
type: 'progress'
, total: _total
, loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0
}, _file, xhr, _fileOptions);
}
} : noop,
complete: function (err){
_each(_xhrPropsExport, function (name){
proxyXHR[name] = xhr[name];
});
if( _file ){
data.total = (data.total || data.size);
data.loaded = data.total;
if( !err ) {
// emulate 100% "progress"
this.progress(data);
// fixed throttle event
_fileLoaded = true;
// bytes loaded
_loaded += data.size; // data.size != data.total, it's desirable fix this
proxyXHR.loaded = _loaded;
}
// emit "filecomplete" event
options.filecomplete(err, xhr, _file, _fileOptions);
}
// upload next file
setTimeout(function () {_nextFile.call(_this);}, 0);
}
})); // xhr
// ...
proxyXHR.abort = function (current){
if (!current) { dataArray.length = 0; }
this.current = current;
xhr.abort();
};
// Start upload
xhr.send(form);
});
}
else {
var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204;
options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options);
// Mark done state
_complete = true;
}
};
// Next tick
setTimeout(_nextFile, 0);
// Append more files to the existing request
// first - add them to the queue head/tail
proxyXHR.append = function (files, first) {
files = api._getFilesDataArray([].concat(files));
_each(files, function (data) {
_total += data.size;
proxyXHR.files.push(data.file);
if (first) {
dataArray.unshift(data);
} else {
dataArray.push(data);
}
});
proxyXHR.statusText = "";
if( _complete ){
_nextFile.call(_this);
}
};
// Removes file from queue by file reference and returns it
proxyXHR.remove = function (file) {
var i = dataArray.length, _file;
while( i-- ){
if( dataArray[i].file == file ){
_file = dataArray.splice(i, 1);
_total -= _file.size;
}
}
return _file;
};
return proxyXHR;
},
_getFilesDataArray: function (data){
var files = [], oFiles = {};
if( isInputFile(data) ){
var tmp = api.getFiles(data);
oFiles[data.name || 'file'] = data.getAttribute('multiple') !== null ? tmp : tmp[0];
}
else if( _isArray(data) && isInputFile(data[0]) ){
_each(data, function (input){
oFiles[input.name || 'file'] = api.getFiles(input);
});
}
else {
oFiles = data;
}
_each(oFiles, function add(file, name){
if( _isArray(file) ){
_each(file, function (file){
add(file, name);
});
}
else if( file && (file.name || file.image) ){
files.push({
name: name
, file: file
, size: file.size
, total: file.size
, loaded: 0
});
}
});
if( !files.length ){
// Create fake `file` object
files.push({ file: { name: api.expando } });
}
return files;
},
_getFormData: function (options, data, fn){
var
file = data.file
, name = data.name
, filename = file.name
, filetype = file.type
, trans = api.support.transform && options.imageTransform
, Form = new api.Form
, queue = api.queue(function (){ fn(Form); })
, isOrignTrans = trans && _isOriginTransform(trans)
, postNameConcat = api.postNameConcat
;
// Append data
_each(options.data, function add(val, name){
if( typeof val == 'object' ){
_each(val, function (v, i){
add(v, postNameConcat(name, i));
});
}
else {
Form.append(name, val);
}
});
(function _addFile(file/**Object*/){
if( file.image ){ // This is a FileAPI.Image
queue.inc();
file.toData(function (err, image){
// @todo: error
filename = filename || (new Date).getTime()+'.png';
_addFile(image);
queue.next();
});
}
else if( api.Image && trans && (/^image/.test(file.type) || _rimgcanvas.test(file.nodeName)) ){
queue.inc();
if( isOrignTrans ){
// Convert to array for transform function
trans = [trans];
}
api.Image.transform(file, trans, options.imageAutoOrientation, function (err, images){
if( isOrignTrans && !err ){
if( !dataURLtoBlob && !api.flashEngine ){
// Canvas.toBlob or Flash not supported, use multipart
Form.multipart = true;
}
Form.append(name, images[0], filename, trans[0].type || filetype);
}
else {
var addOrigin = 0;
if( !err ){
_each(images, function (image, idx){
if( !dataURLtoBlob && !api.flashEngine ){
Form.multipart = true;
}
if( !trans[idx].postName ){
addOrigin = 1;
}
Form.append(trans[idx].postName || postNameConcat(name, idx), image, filename, trans[idx].type || filetype);
});
}
if( err || options.imageOriginal ){
Form.append(postNameConcat(name, (addOrigin ? 'original' : null)), file, filename, filetype);
}
}
queue.next();
});
}
else if( filename !== api.expando ){
Form.append(name, file, filename);
}
})(file);
queue.check();
},
reset: function (inp, notRemove){
var parent, clone;
if( jQuery ){
clone = jQuery(inp).clone(true).insertBefore(inp).val('')[0];
if( !notRemove ){
jQuery(inp).remove();
}
} else {
parent = inp.parentNode;
clone = parent.insertBefore(inp.cloneNode(true), inp);
clone.value = '';
if( !notRemove ){
parent.removeChild(inp);
}
_each(_elEvents[api.uid(inp)], function (fns, type){
_each(fns, function (fn){
_off(inp, type, fn);
_on(clone, type, fn);
});
});
}
return clone;
},
/**
* Load remote file
*
* @param {String} url
* @param {Function} fn
* @return {XMLHttpRequest}
*/
load: function (url, fn){
var xhr = api.getXHR();
if( xhr ){
xhr.open('GET', url, true);
if( xhr.overrideMimeType ){
xhr.overrideMimeType('text/plain; charset=x-user-defined');
}
_on(xhr, 'progress', function (/**Event*/evt){
/** @namespace evt.lengthComputable */
if( evt.lengthComputable ){
fn({ type: evt.type, loaded: evt.loaded, total: evt.total }, xhr);
}
});
xhr.onreadystatechange = function(){
if( xhr.readyState == 4 ){
xhr.onreadystatechange = null;
if( xhr.status == 200 ){
url = url.split('/');
/** @namespace xhr.responseBody */
var file = {
name: url[url.length-1]
, size: xhr.getResponseHeader('Content-Length')
, type: xhr.getResponseHeader('Content-Type')
};
file.dataURL = 'data:'+file.type+';base64,' + api.encode64(xhr.responseBody || xhr.responseText);
fn({ type: 'load', result: file }, xhr);
}
else {
fn({ type: 'error' }, xhr);
}
}
};
xhr.send(null);
} else {
fn({ type: 'error' });
}
return xhr;
},
encode64: function (str){
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', outStr = '', i = 0;
if( typeof str !== 'string' ){
str = String(str);
}
while( i < str.length ){
//all three "& 0xff" added below are there to fix a known bug
//with bytes returned by xhr.responseText
var
byte1 = str.charCodeAt(i++) & 0xff
, byte2 = str.charCodeAt(i++) & 0xff
, byte3 = str.charCodeAt(i++) & 0xff
, enc1 = byte1 >> 2
, enc2 = ((byte1 & 3) << 4) | (byte2 >> 4)
, enc3, enc4
;
if( isNaN(byte2) ){
enc3 = enc4 = 64;
} else {
enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
enc4 = isNaN(byte3) ? 64 : byte3 & 63;
}
outStr += b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);
}
return outStr;
}
} // api
;
function _emit(target, fn, name, res, ext){
var evt = {
type: name.type || name
, target: target
, result: res
};
_extend(evt, ext);
fn(evt);
}
function _hasSupportReadAs(as){
return FileReader && !!FileReader.prototype['readAs'+as];
}
function _readAs(file, fn, as, encoding){
if( api.isBlob(file) && _hasSupportReadAs(as) ){
var Reader = new FileReader;
// Add event listener
_on(Reader, _readerEvents, function _fn(evt){
var type = evt.type;
if( type == 'progress' ){
_emit(file, fn, evt, evt.target.result, { loaded: evt.loaded, total: evt.total });
}
else if( type == 'loadend' ){
_off(Reader, _readerEvents, _fn);
Reader = null;
}
else {
_emit(file, fn, evt, evt.target.result);
}
});
try {
// ReadAs ...
if( encoding ){
Reader['readAs'+as](file, encoding);
}
else {
Reader['readAs'+as](file);
}
}
catch (err){
_emit(file, fn, 'error', undef, { error: err.toString() });
}
}
else {
_emit(file, fn, 'error', undef, { error: 'FileReader_not_support_'+as });
}
}
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( FileReader ){
try {
var Reader = new FileReader();
_one(Reader, _readerEvents, function (evt){
var isFile = evt.type != 'error';
callback(isFile);
if( isFile ){
Reader.abort();
}
});
Reader.readAsDataURL(file);
} catch( err ){
callback(false);
}
}
else {
callback(null);
}
}
else {
callback(true);
}
}
function _getAsEntry(item){
var entry;
if( item.getAsEntry ){ entry = item.getAsEntry(); }
else if( item.webkitGetAsEntry ){ entry = item.webkitGetAsEntry(); }
return entry;
}
function _readEntryAsFiles(entry, callback){
if( !entry ){
// error
callback('invalid entry');
}
else if( entry.isFile ){
// Read as file
entry.file(function(file){
// success
file.fullPath = entry.fullPath;
callback(false, [file]);
}, function (err){
// error
callback('FileError.code: '+err.code);
});
}
else if( entry.isDirectory ){
var reader = entry.createReader(), result = [];
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( next ){
next();
}
else {
callback(false, result);
}
});
});
}, function (err){
// error
callback('directory_reader: ' + err);
});
}
else {
_readEntryAsFiles(_getAsEntry(entry), callback);
}
}
function _simpleClone(obj){
var copy = {};
_each(obj, function (val, key){
if( val && (typeof val === 'object') && (val.nodeType === void 0) ){
val = _extend({}, val);
}
copy[key] = val;
});
return copy;
}
function isInputFile(el){
return _rinput.test(el && el.tagName);
}
function _getDataTransfer(evt){
return (evt.originalEvent || evt || '').dataTransfer || {};
}
function _isOriginTransform(trans){
var key;
for( key in trans ){
if( trans.hasOwnProperty(key) ){
if( !(trans[key] instanceof Object || key === 'overlay' || key === 'filter') ){
return true;
}
}
}
return false;
}
// Add default image info reader
api.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){
if( !file.__dimensions ){
var defer = file.__dimensions = api.defer();
api.readAsImage(file, function (evt){
var img = evt.target;
defer.resolve(evt.type == 'load' ? false : 'error', {
width: img.width
, height: img.height
});
img.src = api.EMPTY_PNG;
img = null;
});
}
file.__dimensions.then(callback);
});
/**
* Drag'n'Drop special event
*
* @param {HTMLElement} el
* @param {Function} onHover
* @param {Function} onDrop
*/
api.event.dnd = function (el, onHover, onDrop){
var _id, _type;
if( !onDrop ){
onDrop = onHover;
onHover = api.F;
}
if( FileReader ){
// Hover
_on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){
var
types = _getDataTransfer(evt).types
, i = types && types.length
, debounceTrigger = false
;
while( i-- ){
if( ~types[i].indexOf('File') ){
evt[preventDefault]();
if( _type !== evt.type ){
_type = evt.type; // Store current type of event
if( _type != 'dragleave' ){
onHover.call(evt[currentTarget], true, evt);
}
debounceTrigger = true;
}
break; // exit from "while"
}
}
if( debounceTrigger ){
clearTimeout(_id);
_id = setTimeout(function (){
onHover.call(evt[currentTarget], _type != 'dragleave', evt);
}, 50);
}
});
// 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);
});
});
}
else {
api.log("Drag'n'Drop -- not supported");
}
};
/**
* Remove drag'n'drop
* @param {HTMLElement} el
* @param {Function} onHover
* @param {Function} onDrop
*/
api.event.dnd.off = function (el, onHover, onDrop){
_off(el, 'dragenter dragleave dragover', onHover.ff);
_off(el, 'drop', onDrop.ff);
};
// Support jQuery
if( jQuery && !jQuery.fn.dnd ){
jQuery.fn.dnd = function (onHover, onDrop){
return this.each(function (){
api.event.dnd(this, onHover, onDrop);
});
};
jQuery.fn.offdnd = function (onHover, onDrop){
return this.each(function (){
api.event.dnd.off(this, onHover, onDrop);
});
};
}
// @export
window.FileAPI = _extend(api, window.FileAPI);
// Debug info
api.log('FileAPI: ' + api.version);
api.log('protocol: ' + window.location.protocol);
api.log('doctype: [' + doctype.name + '] ' + doctype.publicId + ' ' + doctype.systemId);
// @detect 'x-ua-compatible'
_each(document.getElementsByTagName('meta'), function (meta){
if( /x-ua-compatible/i.test(meta.getAttribute('http-equiv')) ){
api.log('meta.http-equiv: ' + meta.getAttribute('content'));
}
});
// configuration
try {
api._supportConsoleLog = !!console.log;
api._supportConsoleLogApply = !!console.log.apply;
} catch (err) {}
if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; }
if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; }
if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; }
})(window, void 0);
/*global window, FileAPI, document */
(function (api, document, undef) {
'use strict';
var
min = Math.min,
round = Math.round,
getCanvas = function () { return document.createElement('canvas'); },
support = false,
exifOrientation = {
8: 270
, 3: 180
, 6: 90
, 7: 270
, 4: 180
, 5: 90
}
;
try {
support = getCanvas().toDataURL('image/png').indexOf('data:image/png') > -1;
}
catch (e){}
function Image(file){
if( file instanceof Image ){
var img = new Image(file.file);
api.extend(img.matrix, file.matrix);
return img;
}
else if( !(this instanceof Image) ){
return new Image(file);
}
this.file = file;
this.size = file.size || 100;
this.matrix = {
sx: 0,
sy: 0,
sw: 0,
sh: 0,
dx: 0,
dy: 0,
dw: 0,
dh: 0,
resize: 0, // min, max OR preview
deg: 0,
quality: 1, // jpeg quality
filter: 0
};
}
Image.prototype = {
image: true,
constructor: Image,
set: function (attrs){
api.extend(this.matrix, attrs);
return this;
},
crop: function (x, y, w, h){
if( w === undef ){
w = x;
h = y;
x = y = 0;
}
return this.set({ sx: x, sy: y, sw: w, sh: h || w });
},
resize: function (w, h, strategy){
if( /min|max/.test(h) ){
strategy = h;
h = w;
}
return this.set({ dw: w, dh: h || w, resize: strategy });
},
preview: function (w, h){
return this.resize(w, h || w, 'preview');
},
rotate: function (deg){
return this.set({ deg: deg });
},
filter: function (filter){
return this.set({ filter: filter });
},
overlay: function (images){
return this.set({ overlay: images });
},
clone: function (){
return new Image(this);
},
_load: function (image, fn){
var self = this;
if( /img|video/i.test(image.nodeName) ){
fn.call(self, null, image);
}
else {
api.readAsImage(image, function (evt){
fn.call(self, evt.type != 'load', evt.result);
});
}
},
_apply: function (image, fn){
var
canvas = getCanvas()
, m = this.getMatrix(image)
, ctx = canvas.getContext('2d')
, width = image.videoWidth || image.width
, height = image.videoHeight || image.height
, deg = m.deg
, dw = m.dw
, dh = m.dh
, w = width
, h = height
, filter = m.filter
, copy // canvas copy
, buffer = image
, overlay = m.overlay
, queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); })
, renderImageToCanvas = api.renderImageToCanvas
;
// Normalize angle
deg = deg - Math.floor(deg/360)*360;
// For `renderImageToCanvas`
image._type = this.file.type;
while(m.multipass && min(w/dw, h/dh) > 2 ){
w = (w/2 + 0.5)|0;
h = (h/2 + 0.5)|0;
copy = getCanvas();
copy.width = w;
copy.height = h;
if( buffer !== image ){
renderImageToCanvas(copy, buffer, 0, 0, buffer.width, buffer.height, 0, 0, w, h);
buffer = copy;
}
else {
buffer = copy;
renderImageToCanvas(buffer, image, m.sx, m.sy, m.sw, m.sh, 0, 0, w, h);
m.sx = m.sy = m.sw = m.sh = 0;
}
}
canvas.width = (deg % 180) ? dh : dw;
canvas.height = (deg % 180) ? dw : dh;
canvas.type = m.type;
canvas.quality = m.quality;
ctx.rotate(deg * Math.PI / 180);
renderImageToCanvas(ctx.canvas, buffer
, m.sx, m.sy
, m.sw || buffer.width
, m.sh || buffer.height
, (deg == 180 || deg == 270 ? -dw : 0)
, (deg == 90 || deg == 180 ? -dh : 0)
, dw, dh
);
dw = canvas.width;
dh = canvas.height;
// Apply overlay
overlay && api.each([].concat(overlay), function (over){
queue.inc();
// preload
var img = new window.Image, fn = function (){
var
x = over.x|0
, y = over.y|0
, w = over.w || img.width
, h = over.h || img.height
, rel = over.rel
;
// center | right | left
x = (rel == 1 || rel == 4 || rel == 7) ? (dw - w + x)/2 : (rel == 2 || rel == 5 || rel == 8 ? dw - (w + x) : x);
// center | bottom | top
y = (rel == 3 || rel == 4 || rel == 5) ? (dh - h + y)/2 : (rel >= 6 ? dh - (h + y) : y);
api.event.off(img, 'error load abort', fn);
try {
ctx.globalAlpha = over.opacity || 1;
ctx.drawImage(img, x, y, w, h);
}
catch (er){}
queue.next();
};
api.event.on(img, 'error load abort', fn);
img.src = over.src;
if( img.complete ){
fn();
}
});
if( filter ){
queue.inc();
Image.applyFilter(canvas, filter, queue.next);
}
queue.check();
},
getMatrix: function (image){
var
m = api.extend({}, this.matrix)
, sw = m.sw = m.sw || image.videoWidth || image.naturalWidth || image.width
, sh = m.sh = m.sh || image.videoHeight || image.naturalHeight || image.height
, dw = m.dw = m.dw || sw
, dh = m.dh = m.dh || sh
, sf = sw/sh, df = dw/dh
, strategy = m.resize
;
if( strategy == 'preview' ){
if( dw != sw || dh != sh ){
// Make preview
var w, h;
if( df >= sf ){
w = sw;
h = w / df;
} else {
h = sh;
w = h * df;
}
if( w != sw || h != sh ){
m.sx = ~~((sw - w)/2);
m.sy = ~~((sh - h)/2);
sw = w;
sh = h;
}
}
}
else if( strategy ){
if( !(sw > dw || sh > dh) ){
dw = sw;
dh = sh;
}
else if( strategy == 'min' ){
dw = round(sf < df ? min(sw, dw) : dh*sf);
dh = round(sf < df ? dw/sf : min(sh, dh));
}
else {
dw = round(sf >= df ? min(sw, dw) : dh*sf);
dh = round(sf >= df ? dw/sf : min(sh, dh));
}
}
m.sw = sw;
m.sh = sh;
m.dw = dw;
m.dh = dh;
m.multipass = api.multiPassResize;
return m;
},
_trans: function (fn){
this._load(this.file, function (err, image){
if( err ){
fn(err);
}
else {
try {
this._apply(image, fn);
} catch (err){
api.log('[err] FileAPI.Image.fn._apply:', err);
fn(err);
}
}
});
},
get: function (fn){
if( api.support.transform ){
var _this = this, matrix = _this.matrix;
if( matrix.deg == 'auto' ){
api.getInfo(_this.file, function (err, info){
// rotate by exif orientation
matrix.deg = exifOrientation[info && info.exif && info.exif.Orientation] || 0;
_this._trans(fn);
});
}
else {
_this._trans(fn);
}
}
else {
fn('not_support_transform');
}
return this;
},
toData: function (fn){
return this.get(fn);
}
};
Image.exifOrientation = exifOrientation;
Image.transform = function (file, transform, autoOrientation, fn){
function _transform(err, img){
// img -- info object
var
images = {}
, queue = api.queue(function (err){
fn(err, images);
})
;
if( !err ){
api.each(transform, function (params, name){
if( !queue.isFail() ){
var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function';
if( isFn ){
params(img, ImgTrans);
}
else if( params.width ){
ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy);
}
else {
if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){
ImgTrans.resize(params.maxWidth, params.maxHeight, 'max');
}
}
if( params.crop ){
var crop = params.crop;
ImgTrans.crop(crop.x|0, crop.y|0, crop.w || crop.width, crop.h || crop.height);
}
if( params.rotate === undef && autoOrientation ){
params.rotate = 'auto';
}
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){
if( err ){
queue.fail();
}
else {
images[name] = image;
queue.next();
}
});
}
});
}
else {
queue.fail();
}
}
// @todo: Оло-ло, нужно рефакторить это место
if( file.width ){
_transform(false, file);
} else {
api.getInfo(file, _transform);
}
};
// @const
api.each(['TOP', 'CENTER', 'BOTTOM'], function (x, i){
api.each(['LEFT', 'CENTER', 'RIGHT'], function (y, j){
Image[x+'_'+y] = i*3 + j;
Image[y+'_'+x] = i*3 + j;
});
});
/**
* Trabsform element to canvas
*
* @param {Image|HTMLVideoElement} el
* @returns {Canvas}
*/
Image.toCanvas = function(el){
var canvas = document.createElement('canvas');
canvas.width = el.videoWidth || el.width;
canvas.height = el.videoHeight || el.height;
canvas.getContext('2d').drawImage(el, 0, 0);
return canvas;
};
/**
* Create image from DataURL
* @param {String} dataURL
* @param {Object} size
* @param {Function} callback
*/
Image.fromDataURL = function (dataURL, size, callback){
var img = api.newImage(dataURL);
api.extend(img, size);
callback(img);
};
/**
* Apply filter (caman.js)
*
* @param {Canvas|Image} canvas
* @param {String|Function} filter
* @param {Function} doneFn
*/
Image.applyFilter = function (canvas, filter, doneFn){
if( typeof filter == 'function' ){
filter(canvas, doneFn);
}
else if( window.Caman ){
// http://camanjs.com/guides/
window.Caman(canvas.tagName == 'IMG' ? Image.toCanvas(canvas) : canvas, function (){
if( typeof filter == 'string' ){
this[filter]();
}
else {
api.each(filter, function (val, method){
this[method](val);
}, this);
}
this.render(doneFn);
});
}
};
/**
* For load-image-ios.js
*/
api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){
try {
return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
} catch (ex) {
api.log('renderImageToCanvas failed');
throw ex;
}
};
// @export
api.support.canvas = api.support.transform = support;
api.Image = Image;
})(FileAPI, document);
/*
* JavaScript Load Image iOS scaling fixes 1.0.3
* https://github.com/blueimp/JavaScript-Load-Image
*
* Copyright 2013, Sebastian Tschan
* https://blueimp.net
*
* iOS image scaling fixes based on
* https://github.com/stomita/ios-imagefile-megapixel
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/*jslint nomen: true, bitwise: true */
/*global FileAPI, window, document */
(function (factory) {
'use strict';
factory(FileAPI);
}(function (loadImage) {
'use strict';
// Only apply fixes on the iOS platform:
if (!window.navigator || !window.navigator.platform ||
!(/iP(hone|od|ad)/).test(window.navigator.platform)) {
return;
}
var originalRenderMethod = loadImage.renderImageToCanvas;
// Detects subsampling in JPEG images:
loadImage.detectSubsampling = function (img) {
var canvas,
context;
if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
context = canvas.getContext('2d');
context.drawImage(img, -img.width + 1, 0);
// subsampled image becomes half smaller in rendering size.
// check alpha channel value to confirm image is covering edge pixel or not.
// if alpha value is 0 image is not covering, hence subsampled.
return context.getImageData(0, 0, 1, 1).data[3] === 0;
}
return false;
};
// Detects vertical squash in JPEG images:
loadImage.detectVerticalSquash = function (img, subsampled) {
var naturalHeight = img.naturalHeight || img.height,
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
data,
sy,
ey,
py,
alpha;
if (subsampled) {
naturalHeight /= 2;
}
canvas.width = 1;
canvas.height = naturalHeight;
context.drawImage(img, 0, 0);
data = context.getImageData(0, 0, 1, naturalHeight).data;
// search image edge pixel position in case it is squashed vertically:
sy = 0;
ey = naturalHeight;
py = naturalHeight;
while (py > sy) {
alpha = data[(py - 1) * 4 + 3];
if (alpha === 0) {
ey = py;
} else {
sy = py;
}
py = (ey + sy) >> 1;
}
return (py / naturalHeight) || 1;
};
// Renders image to canvas while working around iOS image scaling bugs:
// https://github.com/blueimp/JavaScript-Load-Image/issues/13
loadImage.renderImageToCanvas = function (
canvas,
img,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destX,
destY,
destWidth,
destHeight
) {
if (img._type === 'image/jpeg') {
var context = canvas.getContext('2d'),
tmpCanvas = document.createElement('canvas'),
tileSize = 1024,
tmpContext = tmpCanvas.getContext('2d'),
subsampled,
vertSquashRatio,
tileX,
tileY;
tmpCanvas.width = tileSize;
tmpCanvas.height = tileSize;
context.save();
subsampled = loadImage.detectSubsampling(img);
if (subsampled) {
sourceX /= 2;
sourceY /= 2;
sourceWidth /= 2;
sourceHeight /= 2;
}
vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled);
if (subsampled || vertSquashRatio !== 1) {
sourceY *= vertSquashRatio;
destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
destHeight = Math.ceil(
tileSize * destHeight / sourceHeight / vertSquashRatio
);
destY = 0;
tileY = 0;
while (tileY < sourceHeight) {
destX = 0;
tileX = 0;
while (tileX < sourceWidth) {
tmpContext.clearRect(0, 0, tileSize, tileSize);
tmpContext.drawImage(
img,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
-tileX,
-tileY,
sourceWidth,
sourceHeight
);
context.drawImage(
tmpCanvas,
0,
0,
tileSize,
tileSize,
destX,
destY,
destWidth,
destHeight
);
tileX += tileSize;
destX += destWidth;
}
tileY += tileSize;
destY += destHeight;
}
context.restore();
return canvas;
}
}
return originalRenderMethod(
canvas,
img,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destX,
destY,
destWidth,
destHeight
);
};
}));
/*global window, FileAPI */
(function (api, window){
"use strict";
var
document = window.document
, FormData = window.FormData
, Form = function (){ this.items = []; }
, encodeURIComponent = window.encodeURIComponent
;
Form.prototype = {
append: function (name, blob, file, type){
this.items.push({
name: name
, blob: blob && blob.blob || (blob == void 0 ? '' : blob)
, file: blob && (file || blob.name)
, type: blob && (type || blob.type)
});
},
each: function (fn){
var i = 0, n = this.items.length;
for( ; i < n; i++ ){
fn.call(this, this.items[i]);
}
},
toData: function (fn, options){
// allow chunked transfer if we have only one file to send
// flag is used below and in XHR._send
options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1;
if( !api.support.html5 ){
api.log('FileAPI.Form.toHtmlData');
this.toHtmlData(fn);
}
else if( !api.formData || this.multipart || !FormData ){
api.log('FileAPI.Form.toMultipartData');
this.toMultipartData(fn);
}
else if( options._chunked ){
api.log('FileAPI.Form.toPlainData');
this.toPlainData(fn);
}
else {
api.log('FileAPI.Form.toFormData');
this.toFormData(fn);
}
},
_to: function (data, complete, next, arg){
var queue = api.queue(function (){
complete(data);
});
this.each(function (file){
next(file, data, queue, arg);
});
queue.check();
},
toHtmlData: function (fn){
this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){
var blob = file.blob, hidden;
if( file.file ){
api.reset(blob, true);
// set new name
blob.name = file.name;
blob.disabled = false;
data.appendChild(blob);
}
else {
hidden = document.createElement('input');
hidden.name = file.name;
hidden.type = 'hidden';
hidden.value = blob;
data.appendChild(hidden);
}
});
},
toPlainData: function (fn){
this._to({}, fn, function (file, data, queue){
if( file.file ){
data.type = file.file;
}
if( file.blob.toBlob ){
// canvas
queue.inc();
_convertFile(file, function (file, blob){
data.name = file.name;
data.file = blob;
data.size = blob.length;
data.type = file.type;
queue.next();
});
}
else if( file.file ){
// file
data.name = file.blob.name;
data.file = file.blob;
data.size = file.blob.size;
data.type = file.type;
}
else {
// additional data
if( !data.params ){
data.params = [];
}
data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob));
}
data.start = -1;
data.end = data.file && data.file.FileAPIReadPosition || -1;
data.retry = 0;
});
},
toFormData: function (fn){
this._to(new FormData, fn, function (file, data, queue){
if( file.blob && file.blob.toBlob ){
queue.inc();
_convertFile(file, function (file, blob){
data.append(file.name, blob, file.file);
queue.next();
});
}
else if( file.file ){
data.append(file.name, file.blob, file.file);
}
else {
data.append(file.name, file.blob);
}
if( file.file ){
data.append('_'+file.name, file.file);
}
});
},
toMultipartData: function (fn){
this._to([], fn, function (file, data, queue, boundary){
queue.inc();
_convertFile(file, function (file, blob){
data.push(
'--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '')
+ (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '')
+ '\r\n'
+ '\r\n'+ (file.file ? blob : encodeURIComponent(blob))
+ '\r\n')
);
queue.next();
}, true);
}, api.expando);
}
};
function _convertFile(file, fn, useBinaryString){
var blob = file.blob, filename = file.file;
if( filename ){
if( !blob.toDataURL ){
// The Blob is not an image.
api.readAsBinaryString(blob, function (evt){
if( evt.type == 'load' ){
fn(file, evt.result);
}
});
return;
}
var
mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' }
, type = mime[file.type] ? file.type : 'image/png'
, ext = mime[type] || '.png'
, quality = blob.quality || 1
;
if( !filename.match(new RegExp(ext+'$', 'i')) ){
// Does not change the current extension, but add a new one.
filename += ext.replace('?', '');
}
file.file = filename;
file.type = type;
if( !useBinaryString && blob.toBlob ){
blob.toBlob(function (blob){
fn(file, blob);
}, type, quality);
}
else {
fn(file, api.toBinaryString(blob.toDataURL(type, quality)));
}
}
else {
fn(file, blob);
}
}
// @export
api.Form = Form;
})(FileAPI, window);
/*global window, FileAPI, Uint8Array */
(function (window, api){
"use strict";
var
noop = function (){}
, document = window.document
, XHR = function (options){
this.uid = api.uid();
this.xhr = {
abort: noop
, getResponseHeader: noop
, getAllResponseHeaders: noop
};
this.options = options;
},
_xhrResponsePostfix = { '': 1, XML: 1, Text: 1, Body: 1 }
;
XHR.prototype = {
status: 0,
statusText: '',
constructor: XHR,
getResponseHeader: function (name){
return this.xhr.getResponseHeader(name);
},
getAllResponseHeaders: function (){
return this.xhr.getAllResponseHeaders() || {};
},
end: function (status, statusText){
var _this = this, options = _this.options;
_this.end =
_this.abort = noop;
_this.status = status;
if( statusText ){
_this.statusText = statusText;
}
api.log('xhr.end:', status, statusText);
options.complete(status == 200 || status == 201 ? false : _this.statusText || 'unknown', _this);
if( _this.xhr && _this.xhr.node ){
setTimeout(function (){
var node = _this.xhr.node;
try { node.parentNode.removeChild(node); } catch (e){}
try { delete window[_this.uid]; } catch (e){}
window[_this.uid] = _this.xhr.node = null;
}, 9);
}
},
abort: function (){
this.end(0, 'abort');
if( this.xhr ){
this.xhr.aborted = true;
this.xhr.abort();
}
},
send: function (FormData){
var _this = this, options = this.options;
FormData.toData(function (data){
// Start uploading
options.upload(options, _this);
_this._send.call(_this, options, data);
}, options);
},
_send: function (options, data){
var _this = this, xhr, uid = _this.uid, onloadFuncName = _this.uid + "Load", url = options.url;
api.log('XHR._send:', data);
if( !options.cache ){
// No cache
url += (~url.indexOf('?') ? '&' : '?') + api.uid();
}
if( data.nodeName ){
var jsonp = options.jsonp;
// prepare callback in GET
url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid);
// legacy
options.upload(options, _this);
var
onPostMessage = function (evt){
if( ~url.indexOf(evt.origin) ){
try {
var result = api.parseJSON(evt.data);
if( result.id == uid ){
complete(result.status, result.statusText, result.response);
}
} catch( err ){
complete(0, err.message);
}
}
},
// jsonp-callack
complete = window[uid] = function (status, statusText, response){
_this.readyState = 4;
_this.responseText = response;
_this.end(status, statusText);
api.event.off(window, 'message', onPostMessage);
window[uid] = xhr = transport = window[onloadFuncName] = null;
}
;
_this.xhr.abort = function (){
try {
if( transport.stop ){ transport.stop(); }
else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); }
else { transport.contentWindow.document.execCommand('Stop'); }
}
catch (er) {}
complete(0, "abort");
};
api.event.on(window, 'message', onPostMessage);
window[onloadFuncName] = function (){
try {
var
win = transport.contentWindow
, doc = win.document
, result = win.result || api.parseJSON(doc.body.innerHTML)
;
complete(result.status, result.statusText, result.response);
} catch (e){
api.log('[transport.onload]', e);
}
};
xhr = document.createElement('div');
xhr.innerHTML = '<form target="'+ uid +'" action="'+ url +'" method="POST" enctype="multipart/form-data" style="position: absolute; top: -1000px; overflow: hidden; width: 1px; height: 1px;">'
+ '<iframe name="'+ uid +'" src="javascript:false;" onload="' + onloadFuncName + '()"></iframe>'
+ (jsonp && (options.url.indexOf('=?') < 0) ? '<input value="'+ uid +'" name="'+jsonp+'" type="hidden"/>' : '')
+ '</form>'
;
// 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();
form = null;
}
else {
// Clean url
url = url.replace(/([a-z]+)=(\?)&?/i, '');
// html5
if (this.xhr && this.xhr.aborted) {
api.log("Error: already aborted");
return;
}
xhr = _this.xhr = api.getXHR();
if (data.params) {
url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&");
}
xhr.open('POST', url, true);
if( api.withCredentials ){
xhr.withCredentials = "true";
}
if( !options.headers || !options.headers['X-Requested-With'] ){
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
api.each(options.headers, function (val, key){
xhr.setRequestHeader(key, val);
});
if ( options._chunked ) {
// chunked upload
if( xhr.upload ){
xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
if (!data.retry) {
// show progress only for correct chunk uploads
options.progress({
type: evt.type
, total: data.size
, loaded: data.start + evt.loaded
, totalSize: data.size
}, _this, options);
}
}, 100), false);
}
xhr.onreadystatechange = function (){
var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10);
_this.status = xhr.status;
_this.statusText = xhr.statusText;
_this.readyState = xhr.readyState;
if( xhr.readyState == 4 ){
try {
for( var k in _xhrResponsePostfix ){
_this['response'+k] = xhr['response'+k];
}
}catch(_){}
xhr.onreadystatechange = null;
if (!xhr.status || xhr.status - 201 > 0) {
api.log("Error: " + xhr.status);
// some kind of error
// 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action
// up - server error
if (((!xhr.status && !xhr.aborted) || 500 == xhr.status || 416 == xhr.status) && ++data.retry <= options.chunkUploadRetry) {
// let's try again the same chunk
// only applicable for recoverable error codes 500 && 416
var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout;
// inform about recoverable problems
options.pause(data.file, options);
// smart restart if server reports about the last known byte
api.log("X-Last-Known-Byte: " + lkb);
if (lkb) {
data.end = lkb;
} else {
data.end = data.start - 1;
if (416 == xhr.status) {
data.end = data.end - options.chunkSize;
}
}
setTimeout(function () {
_this._send(options, data);
}, delay);
} else {
// no mo retries
_this.end(xhr.status);
}
} else {
// success
data.retry = 0;
if (data.end == data.size - 1) {
// finished
_this.end(xhr.status);
} else {
// next chunk
// shift position if server reports about the last known byte
api.log("X-Last-Known-Byte: " + lkb);
if (lkb) {
data.end = lkb;
}
data.file.FileAPIReadPosition = data.end;
setTimeout(function () {
_this._send(options, data);
}, 0);
}
}
xhr = null;
}
};
data.start = data.end + 1;
data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start);
// Retrieve a slice of file
var
file = data.file
, slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1)
;
if( data.size && !slice.size ){
setTimeout(function (){
_this.end(-1);
});
} else {
xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name));
xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream");
xhr.send(slice);
}
file = slice = null;
} else {
// single piece upload
if( xhr.upload ){
// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
options.progress(evt, _this, options);
}, 100), false);
}
xhr.onreadystatechange = function (){
_this.status = xhr.status;
_this.statusText = xhr.statusText;
_this.readyState = xhr.readyState;
if( xhr.readyState == 4 ){
for( var k in _xhrResponsePostfix ){
_this['response'+k] = xhr['response'+k];
}
xhr.onreadystatechange = null;
if (!xhr.status || xhr.status > 201) {
api.log("Error: " + xhr.status);
if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) {
options.retry = (options.retry || 0) + 1;
var delay = api.networkDownRetryTimeout;
// inform about recoverable problems
options.pause(options.file, options);
setTimeout(function () {
_this._send(options, data);
}, delay);
} else {
//success
_this.end(xhr.status);
}
} else {
//success
_this.end(xhr.status);
}
xhr = null;
}
};
if( api.isArray(data) ){
// multipart
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
var rawData = data.join('') +'--_'+ api.expando +'--';
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
if( xhr.sendAsBinary ){
xhr.sendAsBinary(rawData);
}
else {
var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; });
xhr.send(new Uint8Array(bytes).buffer);
}
} else {
// FormData
xhr.send(data);
}
}
}
}
};
// @export
api.XHR = XHR;
})(window, FileAPI);
/**
* @class FileAPI.Camera
* @author RubaXa <trash@rubaxa.org>
* @support Chrome 21+, FF 18+, Opera 12+
*/
/*global window, FileAPI, jQuery */
/** @namespace LocalMediaStream -- https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API#LocalMediaStream */
(function (window, api){
"use strict";
var
URL = window.URL || window.webkitURL,
document = window.document,
navigator = window.navigator,
getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia,
html5 = !!getMedia
;
// Support "media"
api.support.media = html5;
var Camera = function (video){
this.video = video;
};
Camera.prototype = {
isActive: function (){
return !!this._active;
},
/**
* Start camera streaming
* @param {Function} callback
*/
start: function (callback){
var
_this = this
, video = _this.video
, _successId
, _failId
, _complete = function (err){
_this._active = !err;
clearTimeout(_failId);
clearTimeout(_successId);
// api.event.off(video, 'loadedmetadata', _complete);
callback && callback(err, _this);
}
;
getMedia.call(navigator, { video: true }, function (stream/**LocalMediaStream*/){
// Success
_this.stream = stream;
// api.event.on(video, 'loadedmetadata', function (){
// _complete(null);
// });
// Set camera stream
video.src = URL.createObjectURL(stream);
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
_successId = setInterval(function (){
if( _detectVideoSignal(video) ){
_complete(null);
}
}, 1000);
_failId = setTimeout(function (){
_complete('timeout');
}, 5000);
// Go-go-go!
video.play();
}, _complete/*error*/);
},
/**
* Stop camera streaming
*/
stop: function (){
try {
this._active = false;
this.video.pause();
this.stream.stop();
} catch( err ){ }
},
/**
* Create screenshot
* @return {FileAPI.Camera.Shot}
*/
shot: function (){
return new Shot(this.video);
}
};
/**
* Get camera element from container
*
* @static
* @param {HTMLElement} el
* @return {Camera}
*/
Camera.get = function (el){
return new Camera(el.firstChild);
};
/**
* Publish camera element into container
*
* @static
* @param {HTMLElement} el
* @param {Object} options
* @param {Function} [callback]
*/
Camera.publish = function (el, options, callback){
if( typeof options == 'function' ){
callback = options;
options = {};
}
// Dimensions of "camera"
options = api.extend({}, {
width: '100%'
, height: '100%'
, start: true
}, options);
if( el.jquery ){
// Extract first element, from jQuery collection
el = el[0];
}
var doneFn = function (err){
if( err ){
callback(err);
}
else {
// Get camera
var cam = Camera.get(el);
if( options.start ){
cam.start(callback);
}
else {
callback(null, cam);
}
}
};
el.style.width = _px(options.width);
el.style.height = _px(options.height);
if( api.html5 && html5 ){
// Create video element
var video = document.createElement('video');
// Set dimensions
video.style.width = _px(options.width);
video.style.height = _px(options.height);
// Clean container
if( window.jQuery ){
jQuery(el).empty();
} else {
el.innerHTML = '';
}
// Add "camera" to container
el.appendChild(video);
// end
doneFn();
}
else {
Camera.fallback(el, options, doneFn);
}
};
Camera.fallback = function (el, options, callback){
callback('not_support_camera');
};
/**
* @class FileAPI.Camera.Shot
*/
var Shot = function (video){
var canvas = video.nodeName ? api.Image.toCanvas(video) : video;
var shot = api.Image(canvas);
shot.type = 'image/png';
shot.width = canvas.width;
shot.height = canvas.height;
shot.size = canvas.width * canvas.height * 4;
return shot;
};
/**
* Add "px" postfix, if value is a number
*
* @private
* @param {*} val
* @return {String}
*/
function _px(val){
return val >= 0 ? val + 'px' : val;
}
/**
* @private
* @param {HTMLVideoElement} video
* @return {Boolean}
*/
function _detectVideoSignal(video){
var canvas = document.createElement('canvas'), ctx, res = false;
try {
ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, 1, 1);
res = ctx.getImageData(0, 0, 1, 1).data[4] != 255;
}
catch( e ){}
return res;
}
// @export
Camera.Shot = Shot;
api.Camera = Camera;
})(window, FileAPI);
/**
* FileAPI fallback to Flash
*
* @flash-developer "Vladimir Demidov" <v.demidov@corp.mail.ru>
*/
/*global window, ActiveXObject, FileAPI */
(function (window, jQuery, api) {
"use strict";
var
document = window.document
, location = window.location
, navigator = window.navigator
, _each = api.each
;
api.support.flash = (function (){
var mime = navigator.mimeTypes, has = false;
if( navigator.plugins && typeof navigator.plugins['Shockwave Flash'] == 'object' ){
has = navigator.plugins['Shockwave Flash'].description && !(mime && mime['application/x-shockwave-flash'] && !mime['application/x-shockwave-flash'].enabledPlugin);
}
else {
try {
has = !!(window.ActiveXObject && new ActiveXObject('ShockwaveFlash.ShockwaveFlash'));
}
catch(er){
api.log('Flash -- does not supported.');
}
}
if( has && /^file:/i.test(location) ){
api.log('[warn] Flash does not work on `file:` protocol.');
}
return has;
})();
api.support.flash
&& (0
|| !api.html5 || !api.support.html5
|| (api.cors && !api.support.cors)
|| (api.media && !api.support.media)
)
&& (function (){
var
_attr = api.uid()
, _retry = 0
, _files = {}
, _rhttp = /^https?:/i
, flash = {
_fn: {},
/**
* Publish flash-object
*
* @param {HTMLElement} el
* @param {String} id
* @param {Object} [opts]
*/
publish: function (el, id, opts){
opts = opts || {};
el.innerHTML = _makeFlashHTML({
id: id
, src: _getUrl(api.flashUrl, 'r=' + api.version)
// , src: _getUrl('http://v.demidov.boom.corp.mail.ru/uploaderfileapi/FlashFileAPI.swf?1')
, wmode: opts.camera ? '' : 'transparent'
, flashvars: 'callback=' + (opts.onEvent || 'FileAPI.Flash.onEvent')
+ '&flashId='+ id
+ '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version
+ (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : ''))
+ '&timeout='+api.flashAbortTimeout
+ (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '')
+ '&debug='+(api.debug?"1":"")
}, opts);
},
/**
* Initialization & preload flash object
*/
init: function (){
var child = document.body && document.body.firstChild;
if( child ){
do {
if( child.nodeType == 1 ){
api.log('FlashAPI.state: awaiting');
var dummy = document.createElement('div');
dummy.id = '_' + _attr;
_css(dummy, {
top: 1
, right: 1
, width: 5
, height: 5
, position: 'absolute'
, zIndex: 1e6+'' // set max zIndex
});
child.parentNode.insertBefore(dummy, child);
flash.publish(dummy, _attr);
return;
}
}
while( child = child.nextSibling );
}
if( _retry < 10 ){
setTimeout(flash.init, ++_retry*50);
}
},
ready: function (){
api.log('FlashAPI.state: ready');
flash.ready = api.F;
flash.isReady = true;
flash.patch();
flash.patchCamera && flash.patchCamera();
api.event.on(document, 'mouseover', flash.mouseover);
api.event.on(document, 'click', function (evt){
if( flash.mouseover(evt) ){
evt.preventDefault
? evt.preventDefault()
: (evt.returnValue = true)
;
}
});
},
getEl: function (){
return document.getElementById('_'+_attr);
},
getWrapper: function (node){
do {
if( /js-fileapi-wrapper/.test(node.className) ){
return node;
}
}
while( (node = node.parentNode) && (node !== document.body) );
},
disableMouseover: false,
mouseover: function (evt){
if (!flash.disableMouseover) {
var target = api.event.fix(evt).target;
if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
var
state = target.getAttribute(_attr)
, wrapper = flash.getWrapper(target)
;
if( api.multiFlash ){
// check state:
// i — published
// i — initialization
// r — ready
if( state == 'i' || state == 'r' ){
// publish fail
return false;
}
else if( state != 'p' ){
// set "init" state
target.setAttribute(_attr, 'i');
var dummy = document.createElement('div');
if( !wrapper ){
api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found');
return;
}
_css(dummy, {
top: 0
, left: 0
, width: target.offsetWidth
, height: target.offsetHeight
, zIndex: 1e6+'' // set max zIndex
, position: 'absolute'
});
wrapper.appendChild(dummy);
flash.publish(dummy, api.uid());
// set "publish" state
target.setAttribute(_attr, 'p');
}
return true;
}
else if( wrapper ){
// Use one flash element
var box = _getDimensions(wrapper);
_css(flash.getEl(), box);
// Set current input
flash.curInp = target;
}
}
else if( !/object|embed/i.test(target.nodeName) ){
_css(flash.getEl(), { top: 1, left: 1, width: 5, height: 5 });
}
}
},
onEvent: function (evt){
var type = evt.type;
if( type == 'ready' ){
try {
// set "ready" state
flash.getInput(evt.flashId).setAttribute(_attr, 'r');
} catch (e){
}
flash.ready();
setTimeout(function (){ flash.mouseenter(evt); }, 50);
return true;
}
else if( type === 'ping' ){
api.log('(flash -> js).ping:', [evt.status, evt.savedStatus], evt.error);
}
else if( type === 'log' ){
api.log('(flash -> js).log:', evt.target);
}
else if( type in flash ){
setTimeout(function (){
api.log('FlashAPI.event.'+evt.type+':', evt);
flash[type](evt);
}, 1);
}
},
mouseDown: function(evt) {
flash.disableMouseover = true;
},
cancel: function(evt) {
flash.disableMouseover = false;
},
mouseenter: function (evt){
var node = flash.getInput(evt.flashId);
if( node ){
// Set multiple mode
flash.cmd(evt, 'multiple', node.getAttribute('multiple') != null);
// Set files filter
var accept = [], exts = {};
_each((node.getAttribute('accept') || '').split(/,\s*/), function (mime){
api.accept[mime] && _each(api.accept[mime].split(' '), function (ext){
exts[ext] = 1;
});
});
_each(exts, function (i, ext){
accept.push( ext );
});
flash.cmd(evt, 'accept', accept.length ? accept.join(',')+','+accept.join(',').toUpperCase() : '*');
}
},
get: function (id){
return document[id] || window[id] || document.embeds[id];
},
getInput: function (id){
if( api.multiFlash ){
try {
var node = flash.getWrapper(flash.get(id));
if( node ){
return node.getElementsByTagName('input')[0];
}
} catch (e){
api.log('[err] Can not find "input" by flashId:', id, e);
}
} else {
return flash.curInp;
}
},
select: function (evt){
try {
var
inp = flash.getInput(evt.flashId)
, uid = api.uid(inp)
, files = evt.target.files
, event
;
_each(files, function (file){
api.checkFileObj(file);
});
_files[uid] = files;
if( document.createEvent ){
event = document.createEvent('Event');
event.files = files;
event.initEvent('change', true, true);
inp.dispatchEvent(event);
}
else if( jQuery ){
jQuery(inp).trigger({ type: 'change', files: files });
}
else {
event = document.createEventObject();
event.files = files;
inp.fireEvent('onchange', event);
}
} finally {
flash.disableMouseover = false;
}
},
interval: null,
cmd: function (id, name, data, last) {
if (flash.uploadInProgress && flash.readInProgress) {
setTimeout(function() {
flash.cmd(id, name, data, last);
}, 100);
} else {
this.cmdFn(id, name, data, last);
}
},
cmdFn: function(id, name, data, last) {
try {
api.log('(js -> flash).'+name+':', data);
return flash.get(id.flashId || id).cmd(name, data);
} catch (e){
api.log('(js -> flash).onError:', e);
if( !last ){
// try again
setTimeout(function (){ flash.cmd(id, name, data, true); }, 50);
}
}
},
patch: function (){
api.flashEngine = true;
// FileAPI
_inherit(api, {
readAsDataURL: function (file, callback){
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
}
else {
api.log('FlashAPI.readAsBase64');
flash.readInProgress = true;
flash.cmd(file, 'readAsBase64', {
id: file.id,
callback: _wrap(function _(err, base64){
flash.readInProgress = false;
_unwrap(_);
api.log('FlashAPI.readAsBase64:', err);
callback({
type: err ? 'error' : 'load'
, error: err
, result: 'data:'+ file.type +';base64,'+ base64
});
})
});
}
},
readAsText: function (file, encoding, callback){
if( callback ){
api.log('[warn] FlashAPI.readAsText not supported `encoding` param');
} else {
callback = encoding;
}
api.readAsDataURL(file, function (evt){
if( evt.type == 'load' ){
try {
evt.result = window.atob(evt.result.split(';base64,')[1]);
} catch( err ){
evt.type = 'error';
evt.error = err.toString();
}
}
callback(evt);
});
},
getFiles: function (input, filter, callback){
if( callback ){
api.filterFiles(api.getFiles(input), filter, callback);
return null;
}
var files = api.isArray(input) ? input : _files[api.uid(input.target || input.srcElement || input)];
if( !files ){
// Файлов нету, вызываем родительский метод
return this.parent.apply(this, arguments);
}
if( filter ){
filter = api.getFilesFilter(filter);
files = api.filter(files, function (file){ return filter.test(file.name); });
}
return files;
},
getInfo: function (file, fn){
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
}
else if( file.isShot ){
fn(null, file.info = {
width: file.width,
height: file.height
});
}
else {
if( !file.__info ){
var defer = file.__info = api.defer();
// flash.cmd(file, 'getFileInfo', {
// id: file.id
// , callback: _wrap(function _(err, info){
// _unwrap(_);
// defer.resolve(err, file.info = info);
// })
// });
defer.resolve(null, file.info = null);
}
file.__info.then(fn);
}
}
});
// FileAPI.Image
api.support.transform = true;
api.Image && _inherit(api.Image.prototype, {
get: function (fn, scaleMode){
this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit
return this.parent(fn);
},
_load: function (file, fn){
api.log('FlashAPI.Image._load:', file);
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
}
else {
var _this = this;
api.getInfo(file, function (err){
fn.call(_this, err, file);
});
}
},
_apply: function (file, fn){
api.log('FlashAPI.Image._apply:', file);
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
}
else {
var m = this.getMatrix(file.info), doneFn = fn;
flash.cmd(file, 'imageTransform', {
id: file.id
, matrix: m
, callback: _wrap(function _(err, base64){
api.log('FlashAPI.Image._apply.callback:', err);
_unwrap(_);
if( err ){
doneFn(err);
}
else if( !api.support.html5 && (!api.support.dataURI || base64.length > 3e4) ){
_makeFlashImage({
width: (m.deg % 180) ? m.dh : m.dw
, height: (m.deg % 180) ? m.dw : m.dh
, scale: m.scaleMode
}, base64, doneFn);
}
else {
if( m.filter ){
doneFn = function (err, img){
if( err ){
fn(err);
}
else {
api.Image.applyFilter(img, m.filter, function (){
fn(err, this.canvas);
});
}
};
}
api.newImage('data:'+ file.type +';base64,'+ base64, doneFn);
}
})
});
}
},
toData: function (fn){
var
file = this.file
, info = file.info
, matrix = this.getMatrix(info)
;
api.log('FlashAPI.Image.toData');
if( _isHtmlFile(file) ){
this.parent.apply(this, arguments);
}
else {
if( matrix.deg == 'auto' ){
matrix.deg = api.Image.exifOrientation[info && info.exif && info.exif.Orientation] || 0;
}
fn.call(this, !file.info, {
id: file.id
, flashId: file.flashId
, name: file.name
, type: file.type
, matrix: matrix
});
}
}
});
api.Image && _inherit(api.Image, {
fromDataURL: function (dataURL, size, callback){
if( !api.support.dataURI || dataURL.length > 3e4 ){
_makeFlashImage(
api.extend({ scale: 'exactFit' }, size)
, dataURL.replace(/^data:[^,]+,/, '')
, function (err, el){ callback(el); }
);
}
else {
this.parent(dataURL, size, callback);
}
}
});
// FileAPI.Form
_inherit(api.Form.prototype, {
toData: function (fn){
var items = this.items, i = items.length;
for( ; i--; ){
if( items[i].file && _isHtmlFile(items[i].blob) ){
return this.parent.apply(this, arguments);
}
}
api.log('FlashAPI.Form.toData');
fn(items);
}
});
// FileAPI.XHR
_inherit(api.XHR.prototype, {
_send: function (options, formData){
if(
formData.nodeName
|| formData.append && api.support.html5
|| api.isArray(formData) && (typeof formData[0] === 'string')
){
// HTML5, Multipart or IFrame
return this.parent.apply(this, arguments);
}
var
data = {}
, files = {}
, _this = this
, flashId
, fileId
;
_each(formData, function (item){
if( item.file ){
files[item.name] = item = _getFileDescr(item.blob);
fileId = item.id;
flashId = item.flashId;
}
else {
data[item.name] = item.blob;
}
});
if( !fileId ){
flashId = _attr;
}
if( !flashId ){
api.log('[err] FlashAPI._send: flashId -- undefined');
return this.parent.apply(this, arguments);
}
else {
api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId);
}
_this.xhr = {
headers: {},
abort: function (){ flash.uploadInProgress = false; flash.cmd(flashId, 'abort', { id: fileId }); },
getResponseHeader: function (name){ return this.headers[name]; },
getAllResponseHeaders: function (){ return this.headers; }
};
var queue = api.queue(function (){
flash.uploadInProgress = true;
flash.cmd(flashId, 'upload', {
url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, ''))
, data: data
, files: fileId ? files : null
, headers: options.headers || {}
, callback: _wrap(function upload(evt){
var type = evt.type, result = evt.result;
api.log('FlashAPI.upload.'+type);
if( type == 'progress' ){
evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme
evt.lengthComputable = true;
options.progress(evt);
}
else if( type == 'complete' ){
flash.uploadInProgress = false;
_unwrap(upload);
if( typeof result == 'string' ){
_this.responseText = result.replace(/%22/g, "\"").replace(/%5c/g, "\\").replace(/%26/g, "&").replace(/%25/g, "%");
}
_this.end(evt.status || 200);
}
else if( type == 'abort' || type == 'error' ){
flash.uploadInProgress = false;
_this.end(evt.status || 0, evt.message);
_unwrap(upload);
}
})
});
});
// #2174: FileReference.load() call while FileReference.upload() or vice versa
_each(files, function (file){
queue.inc();
api.getInfo(file, queue.next);
});
queue.check();
}
});
}
}
;
function _makeFlashHTML(opts){
return ('<object id="#id#" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'">'
+ '<param name="movie" value="#src#" />'
+ '<param name="flashvars" value="#flashvars#" />'
+ '<param name="swliveconnect" value="true" />'
+ '<param name="allowscriptaccess" value="always" />'
+ '<param name="allownetworking" value="all" />'
+ '<param name="menu" value="false" />'
+ '<param name="wmode" value="#wmode#" />'
+ '<embed flashvars="#flashvars#" swliveconnect="true" allownetworking="all" allowscriptaccess="always" name="#id#" src="#src#" width="'+(opts.width || '100%')+'" height="'+(opts.height || '100%')+'" menu="false" wmode="transparent" type="application/x-shockwave-flash"></embed>'
+ '</object>').replace(/#(\w+)#/ig, function (a, name){ return opts[name]; })
;
}
function _css(el, css){
if( el && el.style ){
var key, val;
for( key in css ){
val = css[key];
if( typeof val == 'number' ){
val += 'px';
}
try { el.style[key] = val; } catch (e) {}
}
}
}
function _inherit(obj, methods){
_each(methods, function (fn, name){
var prev = obj[name];
obj[name] = function (){
this.parent = prev;
return fn.apply(this, arguments);
};
});
}
function _isHtmlFile(file){
return file && !file.flashId;
}
function _wrap(fn){
var id = fn.wid = api.uid();
flash._fn[id] = fn;
return 'FileAPI.Flash._fn.'+id;
}
function _unwrap(fn){
try {
flash._fn[fn.wid] = null;
delete flash._fn[fn.wid];
}
catch(e){}
}
function _getUrl(url, params){
if( !_rhttp.test(url) ){
if( /^\.\//.test(url) || '/' != url.charAt(0) ){
var path = location.pathname;
path = path.substr(0, path.lastIndexOf('/'));
url = (path +'/'+ url).replace('/./', '/');
}
if( '//' != url.substr(0, 2) ){
url = '//' + location.host + url;
}
if( !_rhttp.test(url) ){
url = location.protocol + url;
}
}
if( params ){
url += (/\?/.test(url) ? '&' : '?') + params;
}
return url;
}
function _makeFlashImage(opts, base64, fn){
var
key
, flashId = api.uid()
, el = document.createElement('div')
, attempts = 10
;
for( key in opts ){
el.setAttribute(key, opts[key]);
el[key] = opts[key];
}
_css(el, opts);
opts.width = '100%';
opts.height = '100%';
el.innerHTML = _makeFlashHTML(api.extend({
id: flashId
, src: _getUrl(api.flashImageUrl, 'r='+ api.uid())
, wmode: 'opaque'
, flashvars: 'scale='+ opts.scale +'&callback='+_wrap(function _(){
_unwrap(_);
if( --attempts > 0 ){
_setImage();
}
return true;
})
}, opts));
function _setImage(){
try {
// Get flash-object by id
var img = flash.get(flashId);
img.setImage(base64);
} catch (e){
api.log('[err] FlashAPI.Preview.setImage -- can not set "base64":', e);
}
}
fn(false, el);
el = null;
}
function _getFileDescr(file){
return {
id: file.id
, name: file.name
, matrix: file.matrix
, flashId: file.flashId
};
}
function _getDimensions(el){
var
box = el.getBoundingClientRect()
, body = document.body
, docEl = (el && el.ownerDocument).documentElement
;
function getOffset(obj) {
var left, top;
left = top = 0;
if (obj.offsetParent) {
do {
left += obj.offsetLeft;
top += obj.offsetTop;
} while (obj = obj.offsetParent);
}
return {
left : left,
top : top
};
};
return {
top: getOffset(el).top
, left: getOffset(el).left
, width: el.offsetWidth
, height: el.offsetHeight
};
}
// @export
api.Flash = flash;
// Check dataURI support
api.newImage('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', function (err, img){
api.support.dataURI = !(img.width != 1 || img.height != 1);
flash.init();
});
})();
})(window, window.jQuery, FileAPI);
/**
* FileAPI fallback to Flash
*
* @flash-developer "Vladimir Demidov" <v.demidov@corp.mail.ru>
*/
/*global window, FileAPI */
(function (window, jQuery, api) {
"use strict";
var _each = api.each,
_cameraQueue = [];
if (api.support.flash && (api.media && !api.support.media)) {
(function () {
function _wrap(fn) {
var id = fn.wid = api.uid();
api.Flash._fn[id] = fn;
return 'FileAPI.Flash._fn.' + id;
}
function _unwrap(fn) {
try {
api.Flash._fn[fn.wid] = null;
delete api.Flash._fn[fn.wid];
} catch (e) {
}
}
var flash = api.Flash;
api.extend(api.Flash, {
patchCamera: function () {
api.Camera.fallback = function (el, options, callback) {
var camId = api.uid();
api.log('FlashAPI.Camera.publish: ' + camId);
flash.publish(el, camId, api.extend(options, {
camera: true,
onEvent: _wrap(function _(evt) {
if (evt.type === 'camera') {
_unwrap(_);
if (evt.error) {
api.log('FlashAPI.Camera.publish.error: ' + evt.error);
callback(evt.error);
} else {
api.log('FlashAPI.Camera.publish.success: ' + camId);
callback(null);
}
}
})
}));
};
// Run
_each(_cameraQueue, function (args) {
api.Camera.fallback.apply(api.Camera, args);
});
_cameraQueue = [];
// FileAPI.Camera:proto
api.extend(api.Camera.prototype, {
_id: function () {
return this.video.id;
},
start: function (callback) {
var _this = this;
flash.cmd(this._id(), 'camera.on', {
callback: _wrap(function _(evt) {
_unwrap(_);
if (evt.error) {
api.log('FlashAPI.camera.on.error: ' + evt.error);
callback(evt.error, _this);
} else {
api.log('FlashAPI.camera.on.success: ' + _this._id());
_this._active = true;
callback(null, _this);
}
})
});
},
stop: function () {
this._active = false;
flash.cmd(this._id(), 'camera.off');
},
shot: function () {
api.log('FlashAPI.Camera.shot:', this._id());
var shot = api.Flash.cmd(this._id(), 'shot', {});
shot.type = 'image/png';
shot.flashId = this._id();
shot.isShot = true;
return new api.Camera.Shot(shot);
}
});
}
});
api.Camera.fallback = function () {
_cameraQueue.push(arguments);
};
}());
}
}(window, window.jQuery, FileAPI));
if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); }
================================================
FILE: demo/src/main/webapp/js/ng-file-upload-all.js
================================================
/**!
* AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,
* progress, resize, thumbnail, preview, validation and CORS
* FileAPI Flash shim for old browsers not supporting FormData
* @author Danial <danial.farid@gmail.com>
* @version 12.2.13
*/
(function () {
/** @namespace FileAPI.noContentTimeout */
function patchXHR(fnName, newFn) {
window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]);
}
function redefineProp(xhr, prop, fn) {
try {
Object.defineProperty(xhr, prop, {get: fn});
} catch (e) {/*ignore*/
}
}
if (!window.FileAPI) {
window.FileAPI = {};
}
if (!window.XMLHttpRequest) {
throw 'AJAX is not supported. XMLHttpRequest is not defined.';
}
FileAPI.shouldLoad = !window.FormData || FileAPI.forceLoad;
if (FileAPI.shouldLoad) {
var initializeUploadListener = function (xhr) {
if (!xhr.__listeners) {
if (!xhr.upload) xhr.upload = {};
xhr.__listeners = [];
var origAddEventListener = xhr.upload.addEventListener;
xhr.upload.addEventListener = function (t, fn) {
xhr.__listeners[t] = fn;
if (origAddEventListener) origAddEventListener.apply(this, arguments);
};
}
};
patchXHR('open', function (orig) {
return function (m, url, b) {
initializeUploadListener(this);
this.__url = url;
try {
orig.apply(this, [m, url, b]);
} catch (e) {
if (e.message.indexOf('Access is denied') > -1) {
this.__origError = e;
orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]);
}
}
};
});
patchXHR('getResponseHeader', function (orig) {
return function (h) {
return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h]));
};
});
patchXHR('getAllResponseHeaders', function (orig) {
return function () {
return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this));
};
});
patchXHR('abort', function (orig) {
return function () {
return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this));
};
});
patchXHR('setRequestHeader', function (orig) {
return function (header, value) {
if (header === '__setXHR_') {
initializeUploadListener(this);
var val = value(this);
// fix for angular < 1.2.0
if (val instanceof Function) {
val(this);
}
} else {
this.__requestHeaders = this.__requestHeaders || {};
this.__requestHeaders[header] = value;
orig.apply(this, arguments);
}
};
});
patchXHR('send', function (orig) {
return function () {
var xhr = this;
if (arguments[0] && arguments[0].__isFileAPIShim) {
var formData = arguments[0];
var config = {
url: xhr.__url,
jsonp: false, //removes the callback form param
cache: true, //removes the ?fileapiXXX in the url
complete: function (err, fileApiXHR) {
if (err && angular.isString(err) && err.indexOf('#2174') !== -1) {
// this error seems to be fine the file is being uploaded properly.
err = null;
}
xhr.__completed = true;
if (!err && xhr.__listeners.load)
xhr.__listeners.load({
type: 'load',
loaded: xhr.__loaded,
total: xhr.__total,
target: xhr,
lengthComputable: true
});
if (!err && xhr.__listeners.loadend)
xhr.__listeners.loadend({
type: 'loadend',
loaded: xhr.__loaded,
total: xhr.__total,
target: xhr,
lengthComputable: true
});
if (err === 'abort' && xhr.__listeners.abort)
xhr.__listeners.abort({
type: 'abort',
loaded: xhr.__loaded,
total: xhr.__total,
target: xhr,
lengthComputable: true
});
if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function () {
return (fileApiXHR.status === 0 && err && err !== 'abort') ? 500 : fileApiXHR.status;
});
if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function () {
return fileApiXHR.statusText;
});
redefineProp(xhr, 'readyState', function () {
return 4;
});
if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function () {
return fileApiXHR.response;
});
var resp = fileApiXHR.responseText || (err && fileApiXHR.status === 0 && err !== 'abort' ? err : undefined);
redefineProp(xhr, 'responseText', function () {
return resp;
});
redefineProp(xhr, 'response', function () {
return resp;
});
if (err) redefineProp(xhr, 'err', function () {
return err;
});
xhr.__fileApiXHR = fileApiXHR;
if (xhr.onreadystatechange) xhr.onreadystatechange();
if (xhr.onload) xhr.onload();
},
progress: function (e) {
e.target = xhr;
if (xhr.__listeners.progress) xhr.__listeners.progress(e);
xhr.__total = e.total;
xhr.__loaded = e.loaded;
if (e.total === e.loaded) {
// fix flash issue that doesn't call complete if there is no response text from the server
var _this = this;
setTimeout(function () {
if (!xhr.__completed) {
xhr.getAllResponseHeaders = function () {
};
_this.complete(null, {status: 204, statusText: 'No Content'});
}
}, FileAPI.noContentTimeout || 10000);
}
},
headers: xhr.__requestHeaders
};
config.data = {};
config.files = {};
for (var i = 0; i < formData.data.length; i++) {
var item = formData.data[i];
if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) {
config.files[item.key] = item.val;
} else {
config.data[item.key] = item.val;
}
}
setTimeout(function () {
if (!FileAPI.hasFlash) {
throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"';
}
xhr.__fileApiXHR = FileAPI.upload(config);
}, 1);
} else {
if (this.__origError) {
throw this.__origError;
}
orig.apply(xhr, arguments);
}
};
});
window.XMLHttpRequest.__isFileAPIShim = true;
window.FormData = FormData = function () {
return {
append: function (key, val, name) {
if (val.__isFileAPIBlobShim) {
val = val.data[0];
}
this.data.push({
key: key,
val: val,
name: name
});
},
data: [],
__isFileAPIShim: true
};
};
window.Blob = Blob = function (b) {
return {
data: b,
__isFileAPIBlobShim: true
};
};
}
})();
(function () {
/** @namespac
gitextract_ye9rh2i7/
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── LICENSE
├── README.md
├── demo/
│ ├── C#/
│ │ ├── UploadController.js
│ │ ├── UploadHandler.ashx
│ │ └── UploadHandler.ashx.cs
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── df/
│ │ └── angularfileupload/
│ │ ├── CORSFilter.java
│ │ ├── FileUpload.java
│ │ └── S3Signature.java
│ ├── resources/
│ │ ├── META-INF/
│ │ │ ├── jdoconfig.xml
│ │ │ └── persistence.xml
│ │ └── log4j.properties
│ └── webapp/
│ ├── WEB-INF/
│ │ ├── appengine-web.xml
│ │ ├── logging.properties
│ │ └── web.xml
│ ├── common.css
│ ├── crossdomain.xml
│ ├── donate.html
│ ├── index.html
│ └── js/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── ng-file-upload-all.js
│ ├── ng-file-upload-shim.js
│ ├── ng-file-upload.js
│ ├── ng-img-crop.css
│ ├── ng-img-crop.js
│ └── upload.js
├── dist/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── ng-file-upload-all.js
│ ├── ng-file-upload-shim.js
│ └── ng-file-upload.js
├── index.js
├── nuget/
│ ├── Package.nuspec
│ ├── build.bat
│ └── nuget.sh
├── package.json
├── release.sh
├── src/
│ ├── FileAPI.flash.swf
│ ├── FileAPI.js
│ ├── data-url.js
│ ├── drop.js
│ ├── exif.js
│ ├── model.js
│ ├── resize.js
│ ├── select.js
│ ├── shim-elem.js
│ ├── shim-filereader.js
│ ├── shim-upload.js
│ ├── upload.js
│ └── validate.js
└── test/
├── .bowerrc
├── bower.json
├── index.html
└── spec/
└── test.js
SYMBOL INDEX (289 symbols across 25 files)
FILE: demo/C#/UploadController.js
function UploadCtrl (line 10) | function UploadCtrl($location, $upload) {
FILE: demo/C#/UploadHandler.ashx.cs
class UploadHandler (line 11) | public class UploadHandler : IHttpHandler
method ProcessRequest (line 14) | public void ProcessRequest(HttpContext context)
FILE: demo/src/main/java/com/df/angularfileupload/CORSFilter.java
class CORSFilter (line 14) | public class CORSFilter implements Filter {
method doFilter (line 16) | @Override
method init (line 31) | @Override
method destroy (line 35) | @Override
FILE: demo/src/main/java/com/df/angularfileupload/FileUpload.java
class FileUpload (line 21) | public class FileUpload extends HttpServlet {
class SizeEntry (line 25) | class SizeEntry {
method service (line 33) | @Override
method clearOldValuesInSizeMap (line 106) | private void clearOldValuesInSizeMap() {
method size (line 117) | protected int size(String key, InputStream stream) {
method read (line 140) | protected String read(InputStream stream) {
FILE: demo/src/main/java/com/df/angularfileupload/S3Signature.java
class S3Signature (line 16) | public class S3Signature extends HttpServlet {
method service (line 17) | @Override
FILE: demo/src/main/webapp/js/FileAPI.js
function _emit (line 1483) | function _emit(target, fn, name, res, ext){
function _hasSupportReadAs (line 1494) | function _hasSupportReadAs(as){
function _readAs (line 1499) | function _readAs(file, fn, as, encoding){
function _isRegularFile (line 1538) | function _isRegularFile(file, callback){
function _getAsEntry (line 1568) | function _getAsEntry(item){
function _readEntryAsFiles (line 1576) | function _readEntryAsFiles(entry, callback){
function _simpleClone (line 1625) | function _simpleClone(obj){
function isInputFile (line 1637) | function isInputFile(el){
function _getDataTransfer (line 1642) | function _getDataTransfer(evt){
function _isOriginTransform (line 1647) | function _isOriginTransform(trans){
function Image (line 1831) | function Image(file){
function _transform (line 2137) | function _transform(err, img){
function _convertFile (line 2637) | function _convertFile(file, fn, useBinaryString){
function _px (line 3297) | function _px(val){
function _detectVideoSignal (line 3307) | function _detectVideoSignal(video){
function _makeFlashHTML (line 4017) | function _makeFlashHTML(opts){
function _css (line 4032) | function _css(el, css){
function _inherit (line 4047) | function _inherit(obj, methods){
function _isHtmlFile (line 4057) | function _isHtmlFile(file){
function _wrap (line 4061) | function _wrap(fn){
function _unwrap (line 4068) | function _unwrap(fn){
function _getUrl (line 4077) | function _getUrl(url, params){
function _makeFlashImage (line 4102) | function _makeFlashImage(opts, base64, fn){
function _getFileDescr (line 4148) | function _getFileDescr(file){
function _getDimensions (line 4158) | function _getDimensions(el){
function _wrap (line 4217) | function _wrap(fn) {
function _unwrap (line 4224) | function _unwrap(fn) {
FILE: demo/src/main/webapp/js/ng-file-upload-all.js
function patchXHR (line 12) | function patchXHR(fnName, newFn) {
function redefineProp (line 16) | function redefineProp(xhr, prop, fn) {
function isInputTypeFile (line 237) | function isInputTypeFile(elem) {
function hasFlash (line 241) | function hasFlash() {
function getOffset (line 251) | function getOffset(obj) {
function sendHttp (line 460) | function sendHttp(config) {
function copy (line 645) | function copy(obj) {
function toResumeFile (line 660) | function toResumeFile(file, formData) {
function addFieldToFormData (line 682) | function addFieldToFormData(formData, val, key) {
function digestConfig (line 723) | function digestConfig() {
function applyExifRotations (line 893) | function applyExifRotations(files, attr, scope) {
function resizeFile (line 905) | function resizeFile(files, attr, scope, ngModel) {
function resizeWithParams (line 924) | function resizeWithParams(params, files, attr, scope, ngModel) {
function update (line 955) | function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
function removeDuplicates (line 992) | function removeDuplicates() {
function toArray (line 1026) | function toArray(v) {
function resizeAndUpdate (line 1030) | function resizeAndUpdate() {
function isDelayedClickSupported (line 1121) | function isDelayedClickSupported(ua) {
function linkFileSelect (line 1133) | function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $c...
function getTagType (line 1481) | function getTagType(el) {
function linkFileDirective (line 1488) | function linkFileDirective(Upload, $timeout, scope, elem, attr, directiv...
function globStringToRegex (line 1633) | function globStringToRegex(str) {
function markModelAsDirty (line 1712) | function markModelAsDirty(ngModel, files) {
function validateSync (line 1767) | function validateSync(name, validationName, fn) {
function validateAsync (line 1825) | function validateAsync(name, validationName, type, asyncFn, fn) {
function success (line 2018) | function success() {
function error (line 2027) | function error() {
function checkLoadErrorInCaseOfNoCallback (line 2036) | function checkLoadErrorInCaseOfNoCallback() {
function success (line 2085) | function success() {
function error (line 2092) | function error() {
function checkLoadError (line 2101) | function checkLoadError() {
function linkDrop (line 2307) | function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window,...
function dropAvailable (line 2604) | function dropAvailable() {
function applyTransform (line 2619) | function applyTransform(ctx, orientation, width, height) {
function arrayBufferToBase64 (line 2681) | function arrayBufferToBase64(buffer) {
FILE: demo/src/main/webapp/js/ng-file-upload-shim.js
function patchXHR (line 12) | function patchXHR(fnName, newFn) {
function redefineProp (line 16) | function redefineProp(xhr, prop, fn) {
function isInputTypeFile (line 237) | function isInputTypeFile(elem) {
function hasFlash (line 241) | function hasFlash() {
function getOffset (line 251) | function getOffset(obj) {
FILE: demo/src/main/webapp/js/ng-file-upload.js
function sendHttp (line 38) | function sendHttp(config) {
function copy (line 223) | function copy(obj) {
function toResumeFile (line 238) | function toResumeFile(file, formData) {
function addFieldToFormData (line 260) | function addFieldToFormData(formData, val, key) {
function digestConfig (line 301) | function digestConfig() {
function applyExifRotations (line 471) | function applyExifRotations(files, attr, scope) {
function resizeFile (line 483) | function resizeFile(files, attr, scope, ngModel) {
function resizeWithParams (line 502) | function resizeWithParams(params, files, attr, scope, ngModel) {
function update (line 533) | function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
function removeDuplicates (line 570) | function removeDuplicates() {
function toArray (line 604) | function toArray(v) {
function resizeAndUpdate (line 608) | function resizeAndUpdate() {
function isDelayedClickSupported (line 699) | function isDelayedClickSupported(ua) {
function linkFileSelect (line 711) | function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $c...
function getTagType (line 1059) | function getTagType(el) {
function linkFileDirective (line 1066) | function linkFileDirective(Upload, $timeout, scope, elem, attr, directiv...
function globStringToRegex (line 1211) | function globStringToRegex(str) {
function markModelAsDirty (line 1290) | function markModelAsDirty(ngModel, files) {
function validateSync (line 1345) | function validateSync(name, validationName, fn) {
function validateAsync (line 1403) | function validateAsync(name, validationName, type, asyncFn, fn) {
function success (line 1596) | function success() {
function error (line 1605) | function error() {
function checkLoadErrorInCaseOfNoCallback (line 1614) | function checkLoadErrorInCaseOfNoCallback() {
function success (line 1663) | function success() {
function error (line 1670) | function error() {
function checkLoadError (line 1679) | function checkLoadError() {
function linkDrop (line 1885) | function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window,...
function dropAvailable (line 2182) | function dropAvailable() {
function applyTransform (line 2197) | function applyTransform(ctx, orientation, width, height) {
function arrayBufferToBase64 (line 2259) | function arrayBufferToBase64(buffer) {
FILE: demo/src/main/webapp/js/ng-img-crop.js
function addEvent (line 883) | function addEvent(element, event, handler) {
function imageHasData (line 891) | function imageHasData(img) {
function base64ToArrayBuffer (line 895) | function base64ToArrayBuffer(base64, contentType) {
function objectURLToBlob (line 908) | function objectURLToBlob(url, callback) {
function getImageData (line 920) | function getImageData(img, callback) {
function findEXIFinJPEG (line 969) | function findEXIFinJPEG(file) {
function findIPTCinJPEG (line 1009) | function findIPTCinJPEG(file) {
function readIPTCData (line 1072) | function readIPTCData(file, startOffset, sectionLength){
function readTags (line 1106) | function readTags(file, tiffStart, dirStart, strings, bigEnd) {
function readTagValue (line 1121) | function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
function getStringFromDB (line 1214) | function getStringFromDB(buffer, start, length) {
function readEXIFData (line 1222) | function readEXIFData(file, start) {
function drawScene (line 1417) | function drawScene() {
FILE: demo/src/main/webapp/js/upload.js
function uploadUsingUpload (line 76) | function uploadUsingUpload(file, resumable) {
function uploadUsing$http (line 104) | function uploadUsing$http(file) {
function uploadS3 (line 126) | function uploadS3(file) {
function storeS3UploadConfigInLocalStore (line 177) | function storeS3UploadConfigInLocalStore() {
function htmlEdit (line 193) | function htmlEdit() {
FILE: dist/FileAPI.js
function _emit (line 1483) | function _emit(target, fn, name, res, ext){
function _hasSupportReadAs (line 1494) | function _hasSupportReadAs(as){
function _readAs (line 1499) | function _readAs(file, fn, as, encoding){
function _isRegularFile (line 1538) | function _isRegularFile(file, callback){
function _getAsEntry (line 1568) | function _getAsEntry(item){
function _readEntryAsFiles (line 1576) | function _readEntryAsFiles(entry, callback){
function _simpleClone (line 1625) | function _simpleClone(obj){
function isInputFile (line 1637) | function isInputFile(el){
function _getDataTransfer (line 1642) | function _getDataTransfer(evt){
function _isOriginTransform (line 1647) | function _isOriginTransform(trans){
function Image (line 1831) | function Image(file){
function _transform (line 2137) | function _transform(err, img){
function _convertFile (line 2637) | function _convertFile(file, fn, useBinaryString){
function _px (line 3297) | function _px(val){
function _detectVideoSignal (line 3307) | function _detectVideoSignal(video){
function _makeFlashHTML (line 4017) | function _makeFlashHTML(opts){
function _css (line 4032) | function _css(el, css){
function _inherit (line 4047) | function _inherit(obj, methods){
function _isHtmlFile (line 4057) | function _isHtmlFile(file){
function _wrap (line 4061) | function _wrap(fn){
function _unwrap (line 4068) | function _unwrap(fn){
function _getUrl (line 4077) | function _getUrl(url, params){
function _makeFlashImage (line 4102) | function _makeFlashImage(opts, base64, fn){
function _getFileDescr (line 4148) | function _getFileDescr(file){
function _getDimensions (line 4158) | function _getDimensions(el){
function _wrap (line 4217) | function _wrap(fn) {
function _unwrap (line 4224) | function _unwrap(fn) {
FILE: dist/ng-file-upload-all.js
function patchXHR (line 12) | function patchXHR(fnName, newFn) {
function redefineProp (line 16) | function redefineProp(xhr, prop, fn) {
function isInputTypeFile (line 237) | function isInputTypeFile(elem) {
function hasFlash (line 241) | function hasFlash() {
function getOffset (line 251) | function getOffset(obj) {
function sendHttp (line 460) | function sendHttp(config) {
function copy (line 645) | function copy(obj) {
function toResumeFile (line 660) | function toResumeFile(file, formData) {
function addFieldToFormData (line 682) | function addFieldToFormData(formData, val, key) {
function digestConfig (line 723) | function digestConfig() {
function applyExifRotations (line 893) | function applyExifRotations(files, attr, scope) {
function resizeFile (line 905) | function resizeFile(files, attr, scope, ngModel) {
function resizeWithParams (line 924) | function resizeWithParams(params, files, attr, scope, ngModel) {
function update (line 955) | function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
function removeDuplicates (line 992) | function removeDuplicates() {
function toArray (line 1026) | function toArray(v) {
function resizeAndUpdate (line 1030) | function resizeAndUpdate() {
function isDelayedClickSupported (line 1121) | function isDelayedClickSupported(ua) {
function linkFileSelect (line 1133) | function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $c...
function getTagType (line 1481) | function getTagType(el) {
function linkFileDirective (line 1488) | function linkFileDirective(Upload, $timeout, scope, elem, attr, directiv...
function globStringToRegex (line 1633) | function globStringToRegex(str) {
function markModelAsDirty (line 1712) | function markModelAsDirty(ngModel, files) {
function validateSync (line 1767) | function validateSync(name, validationName, fn) {
function validateAsync (line 1825) | function validateAsync(name, validationName, type, asyncFn, fn) {
function success (line 2018) | function success() {
function error (line 2027) | function error() {
function checkLoadErrorInCaseOfNoCallback (line 2036) | function checkLoadErrorInCaseOfNoCallback() {
function success (line 2085) | function success() {
function error (line 2092) | function error() {
function checkLoadError (line 2101) | function checkLoadError() {
function linkDrop (line 2307) | function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window,...
function dropAvailable (line 2604) | function dropAvailable() {
function applyTransform (line 2619) | function applyTransform(ctx, orientation, width, height) {
function arrayBufferToBase64 (line 2681) | function arrayBufferToBase64(buffer) {
FILE: dist/ng-file-upload-shim.js
function patchXHR (line 12) | function patchXHR(fnName, newFn) {
function redefineProp (line 16) | function redefineProp(xhr, prop, fn) {
function isInputTypeFile (line 237) | function isInputTypeFile(elem) {
function hasFlash (line 241) | function hasFlash() {
function getOffset (line 251) | function getOffset(obj) {
FILE: dist/ng-file-upload.js
function sendHttp (line 38) | function sendHttp(config) {
function copy (line 223) | function copy(obj) {
function toResumeFile (line 238) | function toResumeFile(file, formData) {
function addFieldToFormData (line 260) | function addFieldToFormData(formData, val, key) {
function digestConfig (line 301) | function digestConfig() {
function applyExifRotations (line 471) | function applyExifRotations(files, attr, scope) {
function resizeFile (line 483) | function resizeFile(files, attr, scope, ngModel) {
function resizeWithParams (line 502) | function resizeWithParams(params, files, attr, scope, ngModel) {
function update (line 533) | function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
function removeDuplicates (line 570) | function removeDuplicates() {
function toArray (line 604) | function toArray(v) {
function resizeAndUpdate (line 608) | function resizeAndUpdate() {
function isDelayedClickSupported (line 699) | function isDelayedClickSupported(ua) {
function linkFileSelect (line 711) | function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $c...
function getTagType (line 1059) | function getTagType(el) {
function linkFileDirective (line 1066) | function linkFileDirective(Upload, $timeout, scope, elem, attr, directiv...
function globStringToRegex (line 1211) | function globStringToRegex(str) {
function markModelAsDirty (line 1290) | function markModelAsDirty(ngModel, files) {
function validateSync (line 1345) | function validateSync(name, validationName, fn) {
function validateAsync (line 1403) | function validateAsync(name, validationName, type, asyncFn, fn) {
function success (line 1596) | function success() {
function error (line 1605) | function error() {
function checkLoadErrorInCaseOfNoCallback (line 1614) | function checkLoadErrorInCaseOfNoCallback() {
function success (line 1663) | function success() {
function error (line 1670) | function error() {
function checkLoadError (line 1679) | function checkLoadError() {
function linkDrop (line 1885) | function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window,...
function dropAvailable (line 2182) | function dropAvailable() {
function applyTransform (line 2197) | function applyTransform(ctx, orientation, width, height) {
function arrayBufferToBase64 (line 2259) | function arrayBufferToBase64(buffer) {
FILE: src/FileAPI.js
function _emit (line 1483) | function _emit(target, fn, name, res, ext){
function _hasSupportReadAs (line 1494) | function _hasSupportReadAs(as){
function _readAs (line 1499) | function _readAs(file, fn, as, encoding){
function _isRegularFile (line 1538) | function _isRegularFile(file, callback){
function _getAsEntry (line 1568) | function _getAsEntry(item){
function _readEntryAsFiles (line 1576) | function _readEntryAsFiles(entry, callback){
function _simpleClone (line 1625) | function _simpleClone(obj){
function isInputFile (line 1637) | function isInputFile(el){
function _getDataTransfer (line 1642) | function _getDataTransfer(evt){
function _isOriginTransform (line 1647) | function _isOriginTransform(trans){
function Image (line 1831) | function Image(file){
function _transform (line 2137) | function _transform(err, img){
function _convertFile (line 2637) | function _convertFile(file, fn, useBinaryString){
function _px (line 3297) | function _px(val){
function _detectVideoSignal (line 3307) | function _detectVideoSignal(video){
function _makeFlashHTML (line 4017) | function _makeFlashHTML(opts){
function _css (line 4032) | function _css(el, css){
function _inherit (line 4047) | function _inherit(obj, methods){
function _isHtmlFile (line 4057) | function _isHtmlFile(file){
function _wrap (line 4061) | function _wrap(fn){
function _unwrap (line 4068) | function _unwrap(fn){
function _getUrl (line 4077) | function _getUrl(url, params){
function _makeFlashImage (line 4102) | function _makeFlashImage(opts, base64, fn){
function _getFileDescr (line 4148) | function _getFileDescr(file){
function _getDimensions (line 4158) | function _getDimensions(el){
function _wrap (line 4217) | function _wrap(fn) {
function _unwrap (line 4224) | function _unwrap(fn) {
FILE: src/data-url.js
function getTagType (line 109) | function getTagType(el) {
function linkFileDirective (line 116) | function linkFileDirective(Upload, $timeout, scope, elem, attr, directiv...
FILE: src/drop.js
function linkDrop (line 33) | function linkDrop(scope, elem, attr, ngModel, $parse, $timeout, $window,...
function dropAvailable (line 330) | function dropAvailable() {
FILE: src/exif.js
function applyTransform (line 9) | function applyTransform(ctx, orientation, width, height) {
function arrayBufferToBase64 (line 71) | function arrayBufferToBase64(buffer) {
FILE: src/model.js
function applyExifRotations (line 70) | function applyExifRotations(files, attr, scope) {
function resizeFile (line 82) | function resizeFile(files, attr, scope, ngModel) {
function resizeWithParams (line 101) | function resizeWithParams(params, files, attr, scope, ngModel) {
function update (line 132) | function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) {
function removeDuplicates (line 169) | function removeDuplicates() {
function toArray (line 203) | function toArray(v) {
function resizeAndUpdate (line 207) | function resizeAndUpdate() {
FILE: src/select.js
function isDelayedClickSupported (line 4) | function isDelayedClickSupported(ua) {
function linkFileSelect (line 16) | function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $c...
FILE: src/shim-elem.js
function isInputTypeFile (line 6) | function isInputTypeFile(elem) {
function hasFlash (line 10) | function hasFlash() {
function getOffset (line 20) | function getOffset(obj) {
FILE: src/shim-upload.js
function patchXHR (line 12) | function patchXHR(fnName, newFn) {
function redefineProp (line 16) | function redefineProp(xhr, prop, fn) {
FILE: src/upload.js
function sendHttp (line 38) | function sendHttp(config) {
function copy (line 223) | function copy(obj) {
function toResumeFile (line 238) | function toResumeFile(file, formData) {
function addFieldToFormData (line 260) | function addFieldToFormData(formData, val, key) {
function digestConfig (line 301) | function digestConfig() {
FILE: src/validate.js
function globStringToRegex (line 4) | function globStringToRegex(str) {
function markModelAsDirty (line 83) | function markModelAsDirty(ngModel, files) {
function validateSync (line 138) | function validateSync(name, validationName, fn) {
function validateAsync (line 196) | function validateAsync(name, validationName, type, asyncFn, fn) {
function success (line 389) | function success() {
function error (line 398) | function error() {
function checkLoadErrorInCaseOfNoCallback (line 407) | function checkLoadErrorInCaseOfNoCallback() {
function success (line 456) | function success() {
function error (line 463) | function error() {
function checkLoadError (line 472) | function checkLoadError() {
Condensed preview — 58 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,042K chars).
[
{
"path": ".gitignore",
"chars": 225,
"preview": "node_modules\n.tmp\nbower_components\n.settings\n.metadata\n*.war\nRemoteSystemsTempFiles/\n.DS_Store\n.DS_Store?\nehthumbs.db\nTh"
},
{
"path": ".jshintrc",
"chars": 525,
"preview": "{\n \"node\": true,\n \"browser\": true,\n \"esnext\": true,\n \"camelcase\": true,\n \"curly\": false,\n \"eqeqeq\": true,\n \"eqnul"
},
{
"path": "Gruntfile.js",
"chars": 3334,
"preview": "'use strict';\n\nmodule.exports = function (grunt) {\n // Load grunt tasks automatically\n require('load-grunt-tasks')(gru"
},
{
"path": "LICENSE",
"chars": 1078,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2013 danialfarid\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "README.md",
"chars": 39513,
"preview": "[](http://badge.fury.io/js/ng-file-upload)\n[ {\n 'use strict';\n\n angular\n .module('app')\n .controller('UploadCtrl', UploadCtrl);\n\n "
},
{
"path": "demo/C#/UploadHandler.ashx",
"chars": 104,
"preview": "<%@ WebHandler Language=\"C#\" CodeBehind=\"UploadHandler.ashx.cs\" Class=\"MyApp.Uploads.UploadHandler\" %>\n"
},
{
"path": "demo/C#/UploadHandler.ashx.cs",
"chars": 1088,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Web;\n\nnamespace MyApp.Uploads\n{\n ///"
},
{
"path": "demo/pom.xml",
"chars": 3899,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
},
{
"path": "demo/src/main/java/com/df/angularfileupload/CORSFilter.java",
"chars": 1134,
"preview": "package com.df.angularfileupload;\n\nimport java.io.IOException;\n\nimport javax.servlet.Filter;\nimport javax.servlet.Filter"
},
{
"path": "demo/src/main/java/com/df/angularfileupload/FileUpload.java",
"chars": 6132,
"preview": "package com.df.angularfileupload;\n\nimport com.google.appengine.repackaged.org.joda.time.LocalDateTime;\nimport org.apache"
},
{
"path": "demo/src/main/java/com/df/angularfileupload/S3Signature.java",
"chars": 1621,
"preview": "package com.df.angularfileupload;\n\nimport com.google.api.server.spi.IoUtil;\nimport com.google.appengine.repackaged.com.g"
},
{
"path": "demo/src/main/resources/META-INF/jdoconfig.xml",
"chars": 987,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<jdoconfig xmlns=\"http://java.sun.com/xml/ns/jdo/jdoconfig\"\n xmlns:xsi=\"http:/"
},
{
"path": "demo/src/main/resources/META-INF/persistence.xml",
"chars": 746,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\"\n xmlns:xsi=\"http:"
},
{
"path": "demo/src/main/resources/log4j.properties",
"chars": 1063,
"preview": "# A default log4j configuration for log4j users.\n#\n# To use this configuration, deploy it into your application's WEB-IN"
},
{
"path": "demo/src/main/webapp/WEB-INF/appengine-web.xml",
"chars": 1100,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<appengine-web-app xmlns=\"http://appengine.google.com/ns/1.0\">\n <application>ang"
},
{
"path": "demo/src/main/webapp/WEB-INF/logging.properties",
"chars": 457,
"preview": "# A default java.util.logging configuration.\n# (All App Engine logging is through java.util.logging by default).\n#\n# To "
},
{
"path": "demo/src/main/webapp/WEB-INF/web.xml",
"chars": 1551,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n<web-app xmlns=\"http://java.sun.com/xml/ns/javaee\" xmlns:web=\"htt"
},
{
"path": "demo/src/main/webapp/common.css",
"chars": 3099,
"preview": "body {\n font-family: Helvetica, arial, freesans, clean, sans-serif;\n}\n\n/* object {\n\tborder: 3px solid red;\n} */\n\n.upl"
},
{
"path": "demo/src/main/webapp/crossdomain.xml",
"chars": 389,
"preview": "<?xml version=\"1.0\" ?>\n<cross-domain-policy>\n\t<site-control permitted-cross-domain-policies=\"all\" />\n\t<allow-access-from"
},
{
"path": "demo/src/main/webapp/donate.html",
"chars": 1058,
"preview": "<!doctype html>\n<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n<link type=\"text/css\" "
},
{
"path": "demo/src/main/webapp/index.html",
"chars": 14054,
"preview": "<!doctype html>\n<html>\n<head>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n <meta name=\"viewpo"
},
{
"path": "demo/src/main/webapp/js/FileAPI.js",
"chars": 104258,
"preview": "/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git\n * FileAPI — a set of javascript tools for working with f"
},
{
"path": "demo/src/main/webapp/js/ng-file-upload-all.js",
"chars": 96077,
"preview": "/**!\n * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "demo/src/main/webapp/js/ng-file-upload-shim.js",
"chars": 14394,
"preview": "/**!\n * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "demo/src/main/webapp/js/ng-file-upload.js",
"chars": 81682,
"preview": "/**!\n * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "demo/src/main/webapp/js/ng-img-crop.css",
"chars": 381,
"preview": "/* line 1, ../../source/scss/ng-img-crop.scss */\nimg-crop {\n width: 100%;\n height: 100%;\n display: block;\n position:"
},
{
"path": "demo/src/main/webapp/js/ng-img-crop.js",
"chars": 62618,
"preview": "/*!\n * ngImgCrop v0.3.2\n * https://github.com/alexk111/ngImgCrop\n *\n * Copyright (c) 2014 Alex Kaul\n * License: MIT\n *\n "
},
{
"path": "demo/src/main/webapp/js/upload.js",
"chars": 12216,
"preview": "'use strict';\n\n\nvar app = angular.module('fileUpload', ['ngFileUpload']);\nvar version = '11.1.3';\n\napp.controller('MyCtr"
},
{
"path": "dist/FileAPI.js",
"chars": 104258,
"preview": "/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git\n * FileAPI — a set of javascript tools for working with f"
},
{
"path": "dist/ng-file-upload-all.js",
"chars": 96077,
"preview": "/**!\n * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "dist/ng-file-upload-shim.js",
"chars": 14394,
"preview": "/**!\n * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "dist/ng-file-upload.js",
"chars": 81682,
"preview": "/**!\n * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "index.js",
"chars": 70,
"preview": "require('./dist/ng-file-upload-all');\nmodule.exports = 'ngFileUpload';"
},
{
"path": "nuget/Package.nuspec",
"chars": 728,
"preview": "<?xml version=\"1.0\"?>\n<package >\n <metadata>\n <id>angular-file-upload</id>\n <title>Angular file upload</title>\n "
},
{
"path": "nuget/build.bat",
"chars": 250,
"preview": "NuGet Update -self\n\nrmdir /s /q content\nmkdir content\nmkdir content\\scripts\ncopy ..\\dist\\* content\\scripts\ndel angular-f"
},
{
"path": "nuget/nuget.sh",
"chars": 342,
"preview": "#!/bin/sh\n# add a simple 'nuget' command to Mac OS X under Mono\n# get NuGet.exe binary from http://nuget.codeplex.com/re"
},
{
"path": "package.json",
"chars": 1136,
"preview": "{\n \"name\": \"ng-file-upload\",\n \"version\": \"12.2.13\",\n \"devDependencies\": {\n \"grunt\": \"^0.4.5\",\n \"grunt-contrib-c"
},
{
"path": "release.sh",
"chars": 913,
"preview": "echo version: $2\necho message: $1\n\ngrunt\ngit add .\ngit add -u .\ngit commit -am \"$1\"\ngit pull\ngit push\ncd ../angular-file"
},
{
"path": "src/FileAPI.js",
"chars": 104258,
"preview": "/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git\n * FileAPI — a set of javascript tools for working with f"
},
{
"path": "src/data-url.js",
"chars": 9567,
"preview": "(function () {\n\n ngFileUpload.service('UploadDataUrl', ['UploadBase', '$timeout', '$q', function (UploadBase, $timeout,"
},
{
"path": "src/drop.js",
"chars": 11925,
"preview": "(function () {\n ngFileUpload.directive('ngfDrop', ['$parse', '$timeout', '$window', 'Upload', '$http', '$q',\n functi"
},
{
"path": "src/exif.js",
"chars": 8821,
"preview": "// customized version of https://github.com/exif-js/exif-js\nngFileUpload.service('UploadExif', ['UploadResize', '$q', fu"
},
{
"path": "src/model.js",
"chars": 9349,
"preview": "ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', '$q', 'UploadExif', function ($parse, $timeout, $compi"
},
{
"path": "src/resize.js",
"chars": 5100,
"preview": "ngFileUpload.service('UploadResize', ['UploadValidate', '$q', function (UploadValidate, $q) {\n var upload = UploadValid"
},
{
"path": "src/select.js",
"chars": 8100,
"preview": "ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', function ($parse, $timeout, $compile, U"
},
{
"path": "src/shim-elem.js",
"chars": 4462,
"preview": "(function () {\n /** @namespace FileAPI.forceLoad */\n /** @namespace window.FileAPI.jsUrl */\n /** @namespace window.Fi"
},
{
"path": "src/shim-filereader.js",
"chars": 1931,
"preview": "if (!window.FileReader) {\n window.FileReader = function () {\n var _this = this, loadStarted = false;\n this.listen"
},
{
"path": "src/shim-upload.js",
"chars": 8010,
"preview": "/**!\n * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "src/upload.js",
"chars": 12647,
"preview": "/**!\n * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort,\n * progre"
},
{
"path": "src/validate.js",
"chars": 16215,
"preview": "ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) {\n va"
},
{
"path": "test/.bowerrc",
"chars": 40,
"preview": "{\n \"directory\": \"bower_components\"\n}\n"
},
{
"path": "test/bower.json",
"chars": 134,
"preview": "{\n \"name\": \"web\",\n \"private\": true,\n \"dependencies\": {\n \"chai\": \"~1.8.0\",\n \"mocha\": \"~1.14.0\"\n },\n \"devDepend"
},
{
"path": "test/index.html",
"chars": 668,
"preview": "<!doctype html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Mocha Spec Runner</title>\n <link rel=\"stylesheet\""
},
{
"path": "test/spec/test.js",
"chars": 256,
"preview": "/* global describe, it */\n\n(function () {\n 'use strict';\n\n describe('Give it some context', function () {\n describe"
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the danialfarid/ng-file-upload GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 58 files (934.9 KB), approximately 247.7k tokens, and a symbol index with 289 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.