├── server
├── node
│ ├── tmp
│ │ └── .gitignore
│ ├── public
│ │ └── files
│ │ │ ├── thumbnail
│ │ │ └── .gitignore
│ │ │ └── .gitignore
│ ├── .gitignore
│ ├── package.json
│ └── server.js
├── gae-go
│ ├── static
│ │ ├── robots.txt
│ │ └── favicon.ico
│ ├── app.yaml
│ └── app
│ │ └── main.go
├── gae-python
│ ├── static
│ │ ├── robots.txt
│ │ └── favicon.ico
│ ├── app.yaml
│ └── main.py
└── php
│ ├── files
│ ├── .gitignore
│ └── .htaccess
│ └── index.php
├── .gitignore
├── img
├── loading.gif
└── progressbar.gif
├── css
├── style.css
├── jquery.fileupload-ui-noscript.css
├── demo-ie8.css
├── jquery.fileupload-noscript.css
├── jquery.fileupload.css
├── jquery.fileupload-ui.css
└── demo.css
├── cors
├── result.html
└── postmessage.html
├── blueimp-file-upload.jquery.json
├── Gruntfile.js
├── package.json
├── js
├── main.js
├── cors
│ ├── jquery.xdr-transport.js
│ └── jquery.postmessage-transport.js
├── jquery.fileupload-audio.js
├── jquery.fileupload-video.js
├── jquery.fileupload-validate.js
├── app.js
├── jquery.fileupload-jquery-ui.js
├── jquery.fileupload-process.js
├── jquery.iframe-transport.js
├── jquery.fileupload-image.js
├── jquery.fileupload-angular.js
└── vendor
│ └── jquery.ui.widget.js
├── CONTRIBUTING.md
├── bower.json
├── templates
└── fileform.html
├── .jshintrc
├── angularjs.html
├── basic.html
├── test
└── index.html
├── README.md
├── basic-plus.html
├── jquery-ui.html
└── index.html
/server/node/tmp/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/node/public/files/thumbnail/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/node/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
--------------------------------------------------------------------------------
/server/node/public/files/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/server/gae-go/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/server/gae-python/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/server/php/files/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 | !.htaccess
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pyc
3 | node_modules
4 |
5 | /bower_components/
6 |
7 | .idea
8 |
--------------------------------------------------------------------------------
/img/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DominicBoettger/jQuery-File-Upload/HEAD/img/loading.gif
--------------------------------------------------------------------------------
/img/progressbar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DominicBoettger/jQuery-File-Upload/HEAD/img/progressbar.gif
--------------------------------------------------------------------------------
/server/gae-go/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DominicBoettger/jQuery-File-Upload/HEAD/server/gae-go/static/favicon.ico
--------------------------------------------------------------------------------
/server/gae-python/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DominicBoettger/jQuery-File-Upload/HEAD/server/gae-python/static/favicon.ico
--------------------------------------------------------------------------------
/server/gae-go/app.yaml:
--------------------------------------------------------------------------------
1 | application: jquery-file-upload
2 | version: 2
3 | runtime: go
4 | api_version: go1
5 |
6 | handlers:
7 | - url: /(favicon\.ico|robots\.txt)
8 | static_files: static/\1
9 | upload: static/(.*)
10 | expiration: '1d'
11 | - url: /.*
12 | script: _go_app
13 |
--------------------------------------------------------------------------------
/server/gae-python/app.yaml:
--------------------------------------------------------------------------------
1 | application: jquery-file-upload
2 | version: 1
3 | runtime: python27
4 | api_version: 1
5 | threadsafe: true
6 |
7 | builtins:
8 | - deferred: on
9 |
10 | handlers:
11 | - url: /(favicon\.ico|robots\.txt)
12 | static_files: static/\1
13 | upload: static/(.*)
14 | expiration: '1d'
15 | - url: /.*
16 | script: main.app
17 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*
3 | * jQuery File Upload Plugin CSS Example 8.8.2
4 | * https://github.com/blueimp/jQuery-File-Upload
5 | *
6 | * Copyright 2013, Sebastian Tschan
7 | * https://blueimp.net
8 | *
9 | * Licensed under the MIT license:
10 | * http://www.opensource.org/licenses/MIT
11 | */
12 |
13 | body {
14 | padding-top: 60px;
15 | }
16 |
--------------------------------------------------------------------------------
/server/php/index.php:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 | ').prop('href', options.postMessage)[0],
62 | target = loc.protocol + '//' + loc.host,
63 | xhrUpload = options.xhr().upload;
64 | return {
65 | send: function (_, completeCallback) {
66 | counter += 1;
67 | var message = {
68 | id: 'postmessage-transport-' + counter
69 | },
70 | eventName = 'message.' + message.id;
71 | iframe = $(
72 | ''
75 | ).bind('load', function () {
76 | $.each(names, function (i, name) {
77 | message[name] = options[name];
78 | });
79 | message.dataType = message.dataType.replace('postmessage ', '');
80 | $(window).bind(eventName, function (e) {
81 | e = e.originalEvent;
82 | var data = e.data,
83 | ev;
84 | if (e.origin === target && data.id === message.id) {
85 | if (data.type === 'progress') {
86 | ev = document.createEvent('Event');
87 | ev.initEvent(data.type, false, true);
88 | $.extend(ev, data);
89 | xhrUpload.dispatchEvent(ev);
90 | } else {
91 | completeCallback(
92 | data.status,
93 | data.statusText,
94 | {postmessage: data.result},
95 | data.headers
96 | );
97 | iframe.remove();
98 | $(window).unbind(eventName);
99 | }
100 | }
101 | });
102 | iframe[0].contentWindow.postMessage(
103 | message,
104 | target
105 | );
106 | }).appendTo(document.body);
107 | },
108 | abort: function () {
109 | if (iframe) {
110 | iframe.remove();
111 | }
112 | }
113 | };
114 | }
115 | });
116 |
117 | }));
118 |
--------------------------------------------------------------------------------
/js/jquery.fileupload-validate.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload Validation Plugin 1.1.2
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* global define, window */
13 |
14 | (function (factory) {
15 | 'use strict';
16 | if (typeof define === 'function' && define.amd) {
17 | // Register as an anonymous AMD module:
18 | define([
19 | 'jquery',
20 | './jquery.fileupload-process'
21 | ], factory);
22 | } else {
23 | // Browser globals:
24 | factory(
25 | window.jQuery
26 | );
27 | }
28 | }(function ($) {
29 | 'use strict';
30 |
31 | // Append to the default processQueue:
32 | $.blueimp.fileupload.prototype.options.processQueue.push(
33 | {
34 | action: 'validate',
35 | // Always trigger this action,
36 | // even if the previous action was rejected:
37 | always: true,
38 | // Options taken from the global options map:
39 | acceptFileTypes: '@',
40 | maxFileSize: '@',
41 | minFileSize: '@',
42 | maxNumberOfFiles: '@',
43 | disabled: '@disableValidation'
44 | }
45 | );
46 |
47 | // The File Upload Validation plugin extends the fileupload widget
48 | // with file validation functionality:
49 | $.widget('blueimp.fileupload', $.blueimp.fileupload, {
50 |
51 | options: {
52 | /*
53 | // The regular expression for allowed file types, matches
54 | // against either file type or file name:
55 | acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
56 | // The maximum allowed file size in bytes:
57 | maxFileSize: 10000000, // 10 MB
58 | // The minimum allowed file size in bytes:
59 | minFileSize: undefined, // No minimal file size
60 | // The limit of files to be uploaded:
61 | maxNumberOfFiles: 10,
62 | */
63 |
64 | // Function returning the current number of files,
65 | // has to be overriden for maxNumberOfFiles validation:
66 | getNumberOfFiles: $.noop,
67 |
68 | // Error and info messages:
69 | messages: {
70 | maxNumberOfFiles: 'Maximum number of files exceeded',
71 | acceptFileTypes: 'File type not allowed',
72 | maxFileSize: 'File is too large',
73 | minFileSize: 'File is too small'
74 | }
75 | },
76 |
77 | processActions: {
78 |
79 | validate: function (data, options) {
80 | if (options.disabled) {
81 | return data;
82 | }
83 | var dfd = $.Deferred(),
84 | settings = this.options,
85 | file = data.files[data.index],
86 | fileSize;
87 | if (options.minFileSize || options.maxFileSize) {
88 | fileSize = file.size;
89 | }
90 | if ($.type(options.maxNumberOfFiles) === 'number' &&
91 | (settings.getNumberOfFiles() || 0) + data.files.length >
92 | options.maxNumberOfFiles) {
93 | file.error = settings.i18n('maxNumberOfFiles');
94 | } else if (options.acceptFileTypes && !(options.acceptFileTypes.test(file.type) ||
95 | options.acceptFileTypes.test(file.name))) {
96 | file.error = settings.i18n('acceptFileTypes');
97 | } else if (fileSize > options.maxFileSize) {
98 | file.error = settings.i18n('maxFileSize');
99 | } else if ($.type(fileSize) === 'number' &&
100 | fileSize < options.minFileSize) {
101 | file.error = settings.i18n('minFileSize');
102 | } else {
103 | delete file.error;
104 | }
105 | if (file.error || data.files.error) {
106 | data.files.error = true;
107 | dfd.rejectWith(this, [data]);
108 | } else {
109 | dfd.resolveWith(this, [data]);
110 | }
111 | return dfd.promise();
112 | }
113 |
114 | }
115 |
116 | });
117 |
118 | }));
119 |
--------------------------------------------------------------------------------
/templates/fileform.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload Plugin Angular JS Example 1.2.1
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* jshint nomen:false */
13 | /* global angular */
14 |
15 | (function () {
16 | 'use strict';
17 | var app = angular.module('uploadModule', [
18 | 'blueimp.fileupload'
19 | ]);
20 |
21 | app.directive('ngUploadForm', [function () {
22 | return {
23 | restrict: 'E',
24 | templateUrl: './templates/fileform.html',
25 | scope: {
26 | allowed: '@',
27 | url: '@',
28 | autoUpload: '@',
29 | sizeLimit: '@',
30 | ngModel: '=',
31 | name: '@'
32 | },
33 | controller: ['$scope', '$element', 'fileUpload', function (
34 | $scope, $element, fileUpload) {
35 | $scope.$on('fileuploaddone', function (e, data) {
36 | fileUpload.addFieldData($scope.name, data._response.result.files[0].result);
37 | });
38 |
39 | $scope.options = {
40 | url: $scope.url,
41 | dropZone: $element,
42 | maxFileSize: $scope.sizeLimit,
43 | autoUpload: $scope.autoUpload
44 | };
45 | $scope.loadingFiles = false;
46 |
47 | if (!$scope.queue) {
48 | $scope.queue = [];
49 | }
50 |
51 | var generateFileObject = function generateFileObjects(objects) {
52 | angular.forEach(objects, function (value, key) {
53 | var fileObject = {
54 | name: value.filename,
55 | size: value.length,
56 | url: value.url,
57 | thumbnailUrl: value.url,
58 | deleteUrl: value.url,
59 | deleteType: 'DELETE',
60 | result: value
61 | };
62 |
63 | if (fileObject.url && fileObject.url.charAt(0) !== '/') {
64 | fileObject.url = '/' + fileObject.url;
65 | }
66 |
67 | if (fileObject.deleteUrl && fileObject.deleteUrl.charAt(0) !== '/') {
68 | fileObject.deleteUrl = '/' + fileObject.deleteUrl;
69 | }
70 |
71 | if (fileObject.thumbnailUrl && fileObject.thumbnailUrl.charAt(0) !== '/') {
72 | fileObject.thumbnailUrl = '/' + fileObject.thumbnailUrl;
73 | }
74 |
75 | $scope.queue[key] = fileObject;
76 | });
77 | };
78 | fileUpload.registerField($scope.name);
79 | $scope.filequeue = fileUpload.fieldData[$scope.name];
80 |
81 | $scope.$watchCollection('filequeue', function (newval) {
82 | generateFileObject(newval);
83 | });
84 | }]
85 | };
86 | }])
87 | .controller('FileDestroyController', ['$scope', '$http', 'fileUpload', function (
88 | $scope, $http, fileUpload) {
89 | var file = $scope.file,
90 | state;
91 |
92 | if ($scope.$parent && $scope.$parent.$parent && $scope.$parent.$parent.$parent.name) {
93 | $scope.fieldname = $scope.$parent.$parent.$parent.name;
94 | }
95 |
96 | if (!fileUpload.fieldData[$scope.name]) {
97 | fileUpload.fieldData[$scope.name] = [];
98 | }
99 |
100 | $scope.filequeue = fileUpload.fieldData;
101 |
102 | if (file.url) {
103 | file.$state = function () {
104 | return state;
105 | };
106 | file.$destroy = function () {
107 | state = 'pending';
108 | return $http({
109 | url: file.deleteUrl,
110 | method: file.deleteType
111 | }).then(
112 | function () {
113 | state = 'resolved';
114 | fileUpload.removeFieldData($scope.fieldname, file.result._id);
115 | $scope.clear(file);
116 | },
117 | function () {
118 | state = 'rejected';
119 | fileUpload.removeFieldData($scope.fieldname, file.result._id);
120 | $scope.clear(file);
121 | }
122 | );
123 |
124 |
125 | };
126 | } else if (!file.$cancel && !file._index) {
127 | file.$cancel = function () {
128 | $scope.clear(file);
129 | };
130 | }
131 | }
132 | ]);
133 | })();
--------------------------------------------------------------------------------
/js/jquery.fileupload-jquery-ui.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload jQuery UI Plugin 8.7.1
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* jshint nomen:false */
13 | /* global define, window */
14 |
15 | (function (factory) {
16 | 'use strict';
17 | if (typeof define === 'function' && define.amd) {
18 | // Register as an anonymous AMD module:
19 | define(['jquery', './jquery.fileupload-ui'], factory);
20 | } else {
21 | // Browser globals:
22 | factory(window.jQuery);
23 | }
24 | }(function ($) {
25 | 'use strict';
26 |
27 | $.widget('blueimp.fileupload', $.blueimp.fileupload, {
28 |
29 | options: {
30 | processdone: function (e, data) {
31 | data.context.find('.start').button('enable');
32 | },
33 | progress: function (e, data) {
34 | if (data.context) {
35 | data.context.find('.progress').progressbar(
36 | 'option',
37 | 'value',
38 | parseInt(data.loaded / data.total * 100, 10)
39 | );
40 | }
41 | },
42 | progressall: function (e, data) {
43 | var $this = $(this);
44 | $this.find('.fileupload-progress')
45 | .find('.progress').progressbar(
46 | 'option',
47 | 'value',
48 | parseInt(data.loaded / data.total * 100, 10)
49 | ).end()
50 | .find('.progress-extended').each(function () {
51 | $(this).html(
52 | ($this.data('blueimp-fileupload') ||
53 | $this.data('fileupload'))
54 | ._renderExtendedProgress(data)
55 | );
56 | });
57 | }
58 | },
59 |
60 | _renderUpload: function (func, files) {
61 | var node = this._super(func, files),
62 | showIconText = $(window).width() > 480;
63 | node.find('.progress').empty().progressbar();
64 | node.find('.start').button({
65 | icons: {primary: 'ui-icon-circle-arrow-e'},
66 | text: showIconText
67 | });
68 | node.find('.cancel').button({
69 | icons: {primary: 'ui-icon-cancel'},
70 | text: showIconText
71 | });
72 | if (node.hasClass('fade')) {
73 | node.hide();
74 | }
75 | return node;
76 | },
77 |
78 | _renderDownload: function (func, files) {
79 | var node = this._super(func, files),
80 | showIconText = $(window).width() > 480;
81 | node.find('.delete').button({
82 | icons: {primary: 'ui-icon-trash'},
83 | text: showIconText
84 | });
85 | if (node.hasClass('fade')) {
86 | node.hide();
87 | }
88 | return node;
89 | },
90 |
91 | _startHandler: function (e) {
92 | $(e.currentTarget).button('disable');
93 | this._super(e);
94 | },
95 |
96 | _transition: function (node) {
97 | var deferred = $.Deferred();
98 | if (node.hasClass('fade')) {
99 | node.fadeToggle(
100 | this.options.transitionDuration,
101 | this.options.transitionEasing,
102 | function () {
103 | deferred.resolveWith(node);
104 | }
105 | );
106 | } else {
107 | deferred.resolveWith(node);
108 | }
109 | return deferred;
110 | },
111 |
112 | _create: function () {
113 | this._super();
114 | this.element
115 | .find('.fileupload-buttonbar')
116 | .find('.fileinput-button').each(function () {
117 | var input = $(this).find('input:file').detach();
118 | $(this)
119 | .button({icons: {primary: 'ui-icon-plusthick'}})
120 | .append(input);
121 | })
122 | .end().find('.start')
123 | .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
124 | .end().find('.cancel')
125 | .button({icons: {primary: 'ui-icon-cancel'}})
126 | .end().find('.delete')
127 | .button({icons: {primary: 'ui-icon-trash'}})
128 | .end().find('.progress').progressbar();
129 | },
130 |
131 | _destroy: function () {
132 | this.element
133 | .find('.fileupload-buttonbar')
134 | .find('.fileinput-button').each(function () {
135 | var input = $(this).find('input:file').detach();
136 | $(this)
137 | .button('destroy')
138 | .append(input);
139 | })
140 | .end().find('.start')
141 | .button('destroy')
142 | .end().find('.cancel')
143 | .button('destroy')
144 | .end().find('.delete')
145 | .button('destroy')
146 | .end().find('.progress').progressbar('destroy');
147 | this._super();
148 | }
149 |
150 | });
151 |
152 | }));
153 |
--------------------------------------------------------------------------------
/js/jquery.fileupload-process.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload Processing Plugin 1.3.0
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2012, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* jshint nomen:false */
13 | /* global define, window */
14 |
15 | (function (factory) {
16 | 'use strict';
17 | if (typeof define === 'function' && define.amd) {
18 | // Register as an anonymous AMD module:
19 | define([
20 | 'jquery',
21 | './jquery.fileupload'
22 | ], factory);
23 | } else {
24 | // Browser globals:
25 | factory(
26 | window.jQuery
27 | );
28 | }
29 | }(function ($) {
30 | 'use strict';
31 |
32 | var originalAdd = $.blueimp.fileupload.prototype.options.add;
33 |
34 | // The File Upload Processing plugin extends the fileupload widget
35 | // with file processing functionality:
36 | $.widget('blueimp.fileupload', $.blueimp.fileupload, {
37 |
38 | options: {
39 | // The list of processing actions:
40 | processQueue: [
41 | /*
42 | {
43 | action: 'log',
44 | type: 'debug'
45 | }
46 | */
47 | ],
48 | add: function (e, data) {
49 | var $this = $(this);
50 | data.process(function () {
51 | return $this.fileupload('process', data);
52 | });
53 | originalAdd.call(this, e, data);
54 | }
55 | },
56 |
57 | processActions: {
58 | /*
59 | log: function (data, options) {
60 | console[options.type](
61 | 'Processing "' + data.files[data.index].name + '"'
62 | );
63 | }
64 | */
65 | },
66 |
67 | _processFile: function (data, originalData) {
68 | var that = this,
69 | dfd = $.Deferred().resolveWith(that, [data]),
70 | chain = dfd.promise();
71 | this._trigger('process', null, data);
72 | $.each(data.processQueue, function (i, settings) {
73 | var func = function (data) {
74 | if (originalData.errorThrown) {
75 | return $.Deferred()
76 | .rejectWith(that, [originalData]).promise();
77 | }
78 | return that.processActions[settings.action].call(
79 | that,
80 | data,
81 | settings
82 | );
83 | };
84 | chain = chain.pipe(func, settings.always && func);
85 | });
86 | chain
87 | .done(function () {
88 | that._trigger('processdone', null, data);
89 | that._trigger('processalways', null, data);
90 | })
91 | .fail(function () {
92 | that._trigger('processfail', null, data);
93 | that._trigger('processalways', null, data);
94 | });
95 | return chain;
96 | },
97 |
98 | // Replaces the settings of each processQueue item that
99 | // are strings starting with an "@", using the remaining
100 | // substring as key for the option map,
101 | // e.g. "@autoUpload" is replaced with options.autoUpload:
102 | _transformProcessQueue: function (options) {
103 | var processQueue = [];
104 | $.each(options.processQueue, function () {
105 | var settings = {},
106 | action = this.action,
107 | prefix = this.prefix === true ? action : this.prefix;
108 | $.each(this, function (key, value) {
109 | if ($.type(value) === 'string' &&
110 | value.charAt(0) === '@') {
111 | settings[key] = options[
112 | value.slice(1) || (prefix ? prefix +
113 | key.charAt(0).toUpperCase() + key.slice(1) : key)
114 | ];
115 | } else {
116 | settings[key] = value;
117 | }
118 |
119 | });
120 | processQueue.push(settings);
121 | });
122 | options.processQueue = processQueue;
123 | },
124 |
125 | // Returns the number of files currently in the processsing queue:
126 | processing: function () {
127 | return this._processing;
128 | },
129 |
130 | // Processes the files given as files property of the data parameter,
131 | // returns a Promise object that allows to bind callbacks:
132 | process: function (data) {
133 | var that = this,
134 | options = $.extend({}, this.options, data);
135 | if (options.processQueue && options.processQueue.length) {
136 | this._transformProcessQueue(options);
137 | if (this._processing === 0) {
138 | this._trigger('processstart');
139 | }
140 | $.each(data.files, function (index) {
141 | var opts = index ? $.extend({}, options) : options,
142 | func = function () {
143 | if (data.errorThrown) {
144 | return $.Deferred()
145 | .rejectWith(that, [data]).promise();
146 | }
147 | return that._processFile(opts, data);
148 | };
149 | opts.index = index;
150 | that._processing += 1;
151 | that._processingQueue = that._processingQueue.pipe(func, func)
152 | .always(function () {
153 | that._processing -= 1;
154 | if (that._processing === 0) {
155 | that._trigger('processstop');
156 | }
157 | });
158 | });
159 | }
160 | return this._processingQueue;
161 | },
162 |
163 | _create: function () {
164 | this._super();
165 | this._processing = 0;
166 | this._processingQueue = $.Deferred().resolveWith(this)
167 | .promise();
168 | }
169 |
170 | });
171 |
172 | }));
173 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
3 | "camelcase" : true, // true: Identifiers must be in camelCase
4 | "curly" : true, // true: Require {} for every new block or scope
5 | "eqeqeq" : true, // true: Require triple equals (===) for comparison
6 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
7 | "immed" : true, // true: Require immediate invocations to be wrapped in parens
8 | // e.g. `(function () { } ());`
9 | "indent" : 2, // {int} Number of spaces to use for indentation
10 | "latedef" : true, // true: Require variables/functions to be defined before being used
11 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
12 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
13 | "noempty" : true, // true: Prohibit use of empty blocks
14 | "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment)
15 | "plusplus" : false, // true: Prohibit use of `++` & `--`
16 | "quotmark" : "single", // Quotation mark consistency:
17 | // false : do nothing (default)
18 | // true : ensure whatever is used is consistent
19 | // "single" : require single quotes
20 | // "double" : require double quotes
21 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
22 | "unused" : true, // true: Require all defined variables be used
23 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode
24 | "trailing" : true, // true: Prohibit trailing whitespaces
25 | "maxparams" : false, // {int} Max number of formal params allowed per function
26 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
27 | "maxstatements" : false, // {int} Max number statements per function
28 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
29 | "maxlen" : false, // {int} Max number of characters per line
30 |
31 | // Relaxing
32 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
33 | "boss" : false, // true: Tolerate assignments where comparisons would be expected
34 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
35 | "eqnull" : false, // true: Tolerate use of `== null`
36 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
37 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
38 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
39 | // (ex: `for each`, multiple try/catch, function expression…)
40 | "evil" : false, // true: Tolerate use of `eval` and `new Function()`
41 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
42 | "funcscope" : false, // true: Tolerate defining variables inside control statements"
43 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
44 | "iterator" : false, // true: Tolerate using the `__iterator__` property
45 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
46 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
47 | "laxcomma" : false, // true: Tolerate comma-first style coding
48 | "loopfunc" : false, // true: Tolerate functions being defined in loops
49 | "multistr" : false, // true: Tolerate multi-line strings
50 | "proto" : false, // true: Tolerate using the `__proto__` property
51 | "scripturl" : false, // true: Tolerate script-targeted URLs
52 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
53 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
54 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
55 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
56 | "validthis" : false, // true: Tolerate using this in a non-constructor function
57 |
58 | // Environments
59 | "browser" : false, // Web Browser (window, document, etc)
60 | "couch" : false, // CouchDB
61 | "devel" : false, // Development/debugging (alert, confirm, etc)
62 | "dojo" : false, // Dojo Toolkit
63 | "jquery" : false, // jQuery
64 | "mootools" : false, // MooTools
65 | "node" : false, // Node.js
66 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
67 | "prototypejs" : false, // Prototype and Scriptaculous
68 | "rhino" : false, // Rhino
69 | "worker" : false, // Web Workers
70 | "wsh" : false, // Windows Scripting Host
71 | "yui" : false, // Yahoo User Interface
72 |
73 | // Legacy
74 | "nomen" : true, // true: Prohibit dangling `_` in variables
75 | "onevar" : false, // true: Allow only one `var` statement per function
76 | "passfail" : false, // true: Stop on first error
77 | "white" : true, // true: Check against strict whitespace and indentation rules
78 |
79 | // Custom Globals
80 | "globals" : {} // additional predefined global variables
81 | }
82 |
--------------------------------------------------------------------------------
/server/gae-python/main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # jQuery File Upload Plugin GAE Python Example 2.1.1
4 | # https://github.com/blueimp/jQuery-File-Upload
5 | #
6 | # Copyright 2011, Sebastian Tschan
7 | # https://blueimp.net
8 | #
9 | # Licensed under the MIT license:
10 | # http://www.opensource.org/licenses/MIT
11 | #
12 |
13 | from __future__ import with_statement
14 | from google.appengine.api import files, images
15 | from google.appengine.ext import blobstore, deferred
16 | from google.appengine.ext.webapp import blobstore_handlers
17 | import json
18 | import re
19 | import urllib
20 | import webapp2
21 |
22 | WEBSITE = 'http://blueimp.github.io/jQuery-File-Upload/'
23 | MIN_FILE_SIZE = 1 # bytes
24 | MAX_FILE_SIZE = 5000000 # bytes
25 | IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
26 | ACCEPT_FILE_TYPES = IMAGE_TYPES
27 | THUMBNAIL_MODIFICATOR = '=s80' # max width / height
28 | EXPIRATION_TIME = 300 # seconds
29 |
30 |
31 | def cleanup(blob_keys):
32 | blobstore.delete(blob_keys)
33 |
34 |
35 | class UploadHandler(webapp2.RequestHandler):
36 |
37 | def initialize(self, request, response):
38 | super(UploadHandler, self).initialize(request, response)
39 | self.response.headers['Access-Control-Allow-Origin'] = '*'
40 | self.response.headers[
41 | 'Access-Control-Allow-Methods'
42 | ] = 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
43 | self.response.headers[
44 | 'Access-Control-Allow-Headers'
45 | ] = 'Content-Type, Content-Range, Content-Disposition'
46 |
47 | def validate(self, file):
48 | if file['size'] < MIN_FILE_SIZE:
49 | file['error'] = 'File is too small'
50 | elif file['size'] > MAX_FILE_SIZE:
51 | file['error'] = 'File is too big'
52 | elif not ACCEPT_FILE_TYPES.match(file['type']):
53 | file['error'] = 'Filetype not allowed'
54 | else:
55 | return True
56 | return False
57 |
58 | def get_file_size(self, file):
59 | file.seek(0, 2) # Seek to the end of the file
60 | size = file.tell() # Get the position of EOF
61 | file.seek(0) # Reset the file position to the beginning
62 | return size
63 |
64 | def write_blob(self, data, info):
65 | blob = files.blobstore.create(
66 | mime_type=info['type'],
67 | _blobinfo_uploaded_filename=info['name']
68 | )
69 | with files.open(blob, 'a') as f:
70 | f.write(data)
71 | files.finalize(blob)
72 | return files.blobstore.get_blob_key(blob)
73 |
74 | def handle_upload(self):
75 | results = []
76 | blob_keys = []
77 | for name, fieldStorage in self.request.POST.items():
78 | if type(fieldStorage) is unicode:
79 | continue
80 | result = {}
81 | result['name'] = re.sub(
82 | r'^.*\\',
83 | '',
84 | fieldStorage.filename
85 | )
86 | result['type'] = fieldStorage.type
87 | result['size'] = self.get_file_size(fieldStorage.file)
88 | if self.validate(result):
89 | blob_key = str(
90 | self.write_blob(fieldStorage.value, result)
91 | )
92 | blob_keys.append(blob_key)
93 | result['deleteType'] = 'DELETE'
94 | result['deleteUrl'] = self.request.host_url +\
95 | '/?key=' + urllib.quote(blob_key, '')
96 | if (IMAGE_TYPES.match(result['type'])):
97 | try:
98 | result['url'] = images.get_serving_url(
99 | blob_key,
100 | secure_url=self.request.host_url.startswith(
101 | 'https'
102 | )
103 | )
104 | result['thumbnailUrl'] = result['url'] +\
105 | THUMBNAIL_MODIFICATOR
106 | except: # Could not get an image serving url
107 | pass
108 | if not 'url' in result:
109 | result['url'] = self.request.host_url +\
110 | '/' + blob_key + '/' + urllib.quote(
111 | result['name'].encode('utf-8'), '')
112 | results.append(result)
113 | deferred.defer(
114 | cleanup,
115 | blob_keys,
116 | _countdown=EXPIRATION_TIME
117 | )
118 | return results
119 |
120 | def options(self):
121 | pass
122 |
123 | def head(self):
124 | pass
125 |
126 | def get(self):
127 | self.redirect(WEBSITE)
128 |
129 | def post(self):
130 | if (self.request.get('_method') == 'DELETE'):
131 | return self.delete()
132 | result = {'files': self.handle_upload()}
133 | s = json.dumps(result, separators=(',', ':'))
134 | redirect = self.request.get('redirect')
135 | if redirect:
136 | return self.redirect(str(
137 | redirect.replace('%s', urllib.quote(s, ''), 1)
138 | ))
139 | if 'application/json' in self.request.headers.get('Accept'):
140 | self.response.headers['Content-Type'] = 'application/json'
141 | self.response.write(s)
142 |
143 | def delete(self):
144 | key = self.request.get('key') or ''
145 | blobstore.delete(key)
146 | s = json.dumps({key: True}, separators=(',', ':'))
147 | if 'application/json' in self.request.headers.get('Accept'):
148 | self.response.headers['Content-Type'] = 'application/json'
149 | self.response.write(s)
150 |
151 |
152 | class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler):
153 | def get(self, key, filename):
154 | if not blobstore.get(key):
155 | self.error(404)
156 | else:
157 | # Prevent browsers from MIME-sniffing the content-type:
158 | self.response.headers['X-Content-Type-Options'] = 'nosniff'
159 | # Cache for the expiration time:
160 | self.response.headers['Cache-Control'] = 'public,max-age=%d' % EXPIRATION_TIME
161 | # Send the file forcing a download dialog:
162 | self.send_blob(key, save_as=filename, content_type='application/octet-stream')
163 |
164 | app = webapp2.WSGIApplication(
165 | [
166 | ('/', UploadHandler),
167 | ('/([^/]+)/([^/]+)', DownloadHandler)
168 | ],
169 | debug=True
170 | )
171 |
--------------------------------------------------------------------------------
/angularjs.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
20 |
21 | jQuery File Upload Demo - AngularJS version
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
64 |
65 |
jQuery File Upload Demo
66 |
AngularJS version
67 |
74 |
75 |
76 | File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for AngularJS.
77 | Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
78 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
Demo Notes
89 |
90 |
91 |
92 | The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
93 | Only image files (JPG, GIF, PNG ) are allowed in this demo (by default there is no file type restriction).
94 | Uploaded files will be deleted automatically after 5 minutes (demo setting).
95 | You can drag & drop files from your desktop on this webpage (see Browser support ).
96 | Please refer to the project website and documentation for more information.
97 | Built with Twitter's Bootstrap CSS framework and Icons from Glyphicons .
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
‹
107 |
›
108 |
×
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 | jQuery File Upload Demo - Basic version
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
50 |
51 |
jQuery File Upload Demo
52 |
Basic version
53 |
60 |
61 |
62 | File Upload widget with multiple file selection, drag&drop support and progress bar for jQuery.
63 | Supports cross-domain, chunked and resumable file uploads.
64 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
65 |
66 |
67 |
68 |
69 |
70 | Select files...
71 |
72 |
73 |
74 |
75 |
76 |
77 |
80 |
81 |
82 |
83 |
84 |
85 |
Demo Notes
86 |
87 |
88 |
89 | The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
90 | Only image files (JPG, GIF, PNG ) are allowed in this demo (by default there is no file type restriction).
91 | Uploaded files will be deleted automatically after 5 minutes (demo setting).
92 | You can drag & drop files from your desktop on this webpage (see Browser support ).
93 | Please refer to the project website and documentation for more information.
94 | Built with Twitter's Bootstrap CSS framework and Icons from Glyphicons .
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
20 |
21 | jQuery File Upload Plugin Test
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
77 |
78 |
110 |
111 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/server/gae-go/app/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload Plugin GAE Go Example 3.1.1
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2011, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | package app
13 |
14 | import (
15 | "appengine"
16 | "appengine/blobstore"
17 | "appengine/image"
18 | "appengine/taskqueue"
19 | "bytes"
20 | "encoding/json"
21 | "fmt"
22 | "io"
23 | "log"
24 | "mime/multipart"
25 | "net/http"
26 | "net/url"
27 | "regexp"
28 | "strings"
29 | "time"
30 | )
31 |
32 | const (
33 | WEBSITE = "http://blueimp.github.io/jQuery-File-Upload/"
34 | MIN_FILE_SIZE = 1 // bytes
35 | MAX_FILE_SIZE = 5000000 // bytes
36 | IMAGE_TYPES = "image/(gif|p?jpeg|(x-)?png)"
37 | ACCEPT_FILE_TYPES = IMAGE_TYPES
38 | EXPIRATION_TIME = 300 // seconds
39 | THUMBNAIL_PARAM = "=s80"
40 | )
41 |
42 | var (
43 | imageTypes = regexp.MustCompile(IMAGE_TYPES)
44 | acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
45 | )
46 |
47 | type FileInfo struct {
48 | Key appengine.BlobKey `json:"-"`
49 | Url string `json:"url,omitempty"`
50 | ThumbnailUrl string `json:"thumbnailUrl,omitempty"`
51 | Name string `json:"name"`
52 | Type string `json:"type"`
53 | Size int64 `json:"size"`
54 | Error string `json:"error,omitempty"`
55 | DeleteUrl string `json:"deleteUrl,omitempty"`
56 | DeleteType string `json:"deleteType,omitempty"`
57 | }
58 |
59 | func (fi *FileInfo) ValidateType() (valid bool) {
60 | if acceptFileTypes.MatchString(fi.Type) {
61 | return true
62 | }
63 | fi.Error = "Filetype not allowed"
64 | return false
65 | }
66 |
67 | func (fi *FileInfo) ValidateSize() (valid bool) {
68 | if fi.Size < MIN_FILE_SIZE {
69 | fi.Error = "File is too small"
70 | } else if fi.Size > MAX_FILE_SIZE {
71 | fi.Error = "File is too big"
72 | } else {
73 | return true
74 | }
75 | return false
76 | }
77 |
78 | func (fi *FileInfo) CreateUrls(r *http.Request, c appengine.Context) {
79 | u := &url.URL{
80 | Scheme: r.URL.Scheme,
81 | Host: appengine.DefaultVersionHostname(c),
82 | Path: "/",
83 | }
84 | uString := u.String()
85 | fi.Url = uString + escape(string(fi.Key)) + "/" +
86 | escape(string(fi.Name))
87 | fi.DeleteUrl = fi.Url + "?delete=true"
88 | fi.DeleteType = "DELETE"
89 | if imageTypes.MatchString(fi.Type) {
90 | servingUrl, err := image.ServingURL(
91 | c,
92 | fi.Key,
93 | &image.ServingURLOptions{
94 | Secure: strings.HasSuffix(u.Scheme, "s"),
95 | Size: 0,
96 | Crop: false,
97 | },
98 | )
99 | check(err)
100 | fi.ThumbnailUrl = servingUrl.String() + THUMBNAIL_PARAM
101 | }
102 | }
103 |
104 | func check(err error) {
105 | if err != nil {
106 | panic(err)
107 | }
108 | }
109 |
110 | func escape(s string) string {
111 | return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
112 | }
113 |
114 | func delayedDelete(c appengine.Context, fi *FileInfo) {
115 | if key := string(fi.Key); key != "" {
116 | task := &taskqueue.Task{
117 | Path: "/" + escape(key) + "/-",
118 | Method: "DELETE",
119 | Delay: time.Duration(EXPIRATION_TIME) * time.Second,
120 | }
121 | taskqueue.Add(c, task, "")
122 | }
123 | }
124 |
125 | func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
126 | fi = &FileInfo{
127 | Name: p.FileName(),
128 | Type: p.Header.Get("Content-Type"),
129 | }
130 | if !fi.ValidateType() {
131 | return
132 | }
133 | defer func() {
134 | if rec := recover(); rec != nil {
135 | log.Println(rec)
136 | fi.Error = rec.(error).Error()
137 | }
138 | }()
139 | lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
140 | context := appengine.NewContext(r)
141 | w, err := blobstore.Create(context, fi.Type)
142 | defer func() {
143 | w.Close()
144 | fi.Size = MAX_FILE_SIZE + 1 - lr.N
145 | fi.Key, err = w.Key()
146 | check(err)
147 | if !fi.ValidateSize() {
148 | err := blobstore.Delete(context, fi.Key)
149 | check(err)
150 | return
151 | }
152 | delayedDelete(context, fi)
153 | fi.CreateUrls(r, context)
154 | }()
155 | check(err)
156 | _, err = io.Copy(w, lr)
157 | return
158 | }
159 |
160 | func getFormValue(p *multipart.Part) string {
161 | var b bytes.Buffer
162 | io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
163 | return b.String()
164 | }
165 |
166 | func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
167 | fileInfos = make([]*FileInfo, 0)
168 | mr, err := r.MultipartReader()
169 | check(err)
170 | r.Form, err = url.ParseQuery(r.URL.RawQuery)
171 | check(err)
172 | part, err := mr.NextPart()
173 | for err == nil {
174 | if name := part.FormName(); name != "" {
175 | if part.FileName() != "" {
176 | fileInfos = append(fileInfos, handleUpload(r, part))
177 | } else {
178 | r.Form[name] = append(r.Form[name], getFormValue(part))
179 | }
180 | }
181 | part, err = mr.NextPart()
182 | }
183 | return
184 | }
185 |
186 | func get(w http.ResponseWriter, r *http.Request) {
187 | if r.URL.Path == "/" {
188 | http.Redirect(w, r, WEBSITE, http.StatusFound)
189 | return
190 | }
191 | parts := strings.Split(r.URL.Path, "/")
192 | if len(parts) == 3 {
193 | if key := parts[1]; key != "" {
194 | blobKey := appengine.BlobKey(key)
195 | bi, err := blobstore.Stat(appengine.NewContext(r), blobKey)
196 | if err == nil {
197 | w.Header().Add("X-Content-Type-Options", "nosniff")
198 | if !imageTypes.MatchString(bi.ContentType) {
199 | w.Header().Add("Content-Type", "application/octet-stream")
200 | w.Header().Add(
201 | "Content-Disposition",
202 | fmt.Sprintf("attachment; filename=\"%s\"", parts[2]),
203 | )
204 | }
205 | w.Header().Add(
206 | "Cache-Control",
207 | fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
208 | )
209 | blobstore.Send(w, blobKey)
210 | return
211 | }
212 | }
213 | }
214 | http.Error(w, "404 Not Found", http.StatusNotFound)
215 | }
216 |
217 | func post(w http.ResponseWriter, r *http.Request) {
218 | result := make(map[string][]*FileInfo, 1)
219 | result["files"] = handleUploads(r)
220 | b, err := json.Marshal(result)
221 | check(err)
222 | if redirect := r.FormValue("redirect"); redirect != "" {
223 | if strings.Contains(redirect, "%s") {
224 | redirect = fmt.Sprintf(
225 | redirect,
226 | escape(string(b)),
227 | )
228 | }
229 | http.Redirect(w, r, redirect, http.StatusFound)
230 | return
231 | }
232 | w.Header().Set("Cache-Control", "no-cache")
233 | jsonType := "application/json"
234 | if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
235 | w.Header().Set("Content-Type", jsonType)
236 | }
237 | fmt.Fprintln(w, string(b))
238 | }
239 |
240 | func delete(w http.ResponseWriter, r *http.Request) {
241 | parts := strings.Split(r.URL.Path, "/")
242 | if len(parts) != 3 {
243 | return
244 | }
245 | result := make(map[string]bool, 1)
246 | if key := parts[1]; key != "" {
247 | c := appengine.NewContext(r)
248 | blobKey := appengine.BlobKey(key)
249 | err := blobstore.Delete(c, blobKey)
250 | check(err)
251 | err = image.DeleteServingURL(c, blobKey)
252 | check(err)
253 | result[key] = true
254 | }
255 | jsonType := "application/json"
256 | if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
257 | w.Header().Set("Content-Type", jsonType)
258 | }
259 | b, err := json.Marshal(result)
260 | check(err)
261 | fmt.Fprintln(w, string(b))
262 | }
263 |
264 | func handle(w http.ResponseWriter, r *http.Request) {
265 | params, err := url.ParseQuery(r.URL.RawQuery)
266 | check(err)
267 | w.Header().Add("Access-Control-Allow-Origin", "*")
268 | w.Header().Add(
269 | "Access-Control-Allow-Methods",
270 | "OPTIONS, HEAD, GET, POST, PUT, DELETE",
271 | )
272 | w.Header().Add(
273 | "Access-Control-Allow-Headers",
274 | "Content-Type, Content-Range, Content-Disposition",
275 | )
276 | switch r.Method {
277 | case "OPTIONS":
278 | case "HEAD":
279 | case "GET":
280 | get(w, r)
281 | case "POST":
282 | if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
283 | delete(w, r)
284 | } else {
285 | post(w, r)
286 | }
287 | case "DELETE":
288 | delete(w, r)
289 | default:
290 | http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
291 | }
292 | }
293 |
294 | func init() {
295 | http.HandleFunc("/", handle)
296 | }
297 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jQuery File Upload Plugin - AngularJS Fork
2 |
3 | This is a fork of [Blueimp File Upload](https://github.com/blueimp/jQuery-File-Upload) with improved angular integration.
4 | Additionally all js files are bundled into dist/.
5 |
6 | The whole plugin has been bundled into a angular directive which can be changed via html attributes.
7 |
8 | ## Usage
9 |
10 | ```
11 |
12 | ```
13 |
14 | ## Demo
15 | [Demo File Upload](http://blueimp.github.io/jQuery-File-Upload/)
16 |
17 | ## Description
18 | File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery.
19 | Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
20 |
21 | ## Setup
22 | * [How to setup the plugin on your website](https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
23 | * [How to use only the basic plugin (minimal setup guide).](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin)
24 |
25 | ## Support
26 |
27 | * **[Support Forum](https://groups.google.com/d/forum/jquery-fileupload)**
28 | **Support requests** and **general discussions** about the File Upload plugin can be posted to the official
29 | [Support Forum](https://groups.google.com/d/forum/jquery-fileupload).
30 | If your question is not directly related to the File Upload plugin, you might have a better chance to get a reply by posting to [Stack Overflow](http://stackoverflow.com/questions/tagged/blueimp+jquery+file-upload).
31 |
32 | * Bugs and Feature requests
33 | **Bugs** and **Feature requests** can be reported using the [issues tracker](https://github.com/blueimp/jQuery-File-Upload/issues).
34 | Please read the [issue guidelines](https://github.com/blueimp/jQuery-File-Upload/blob/master/CONTRIBUTING.md) before posting.
35 |
36 | ## Features
37 | * **Multiple file upload:**
38 | Allows to select multiple files at once and upload them simultaneously.
39 | * **Drag & Drop support:**
40 | Allows to upload files by dragging them from your desktop or filemanager and dropping them on your browser window.
41 | * **Upload progress bar:**
42 | Shows a progress bar indicating the upload progress for individual files and for all uploads combined.
43 | * **Cancelable uploads:**
44 | Individual file uploads can be canceled to stop the upload progress.
45 | * **Resumable uploads:**
46 | Aborted uploads can be resumed with browsers supporting the Blob API.
47 | * **Chunked uploads:**
48 | Large files can be uploaded in smaller chunks with browsers supporting the Blob API.
49 | * **Client-side image resizing:**
50 | Images can be automatically resized on client-side with browsers supporting the required JS APIs.
51 | * **Preview images, audio and video:**
52 | A preview of image, audio and video files can be displayed before uploading with browsers supporting the required APIs.
53 | * **No browser plugins (e.g. Adobe Flash) required:**
54 | The implementation is based on open standards like HTML5 and JavaScript and requires no additional browser plugins.
55 | * **Graceful fallback for legacy browsers:**
56 | Uploads files via XMLHttpRequests if supported and uses iframes as fallback for legacy browsers.
57 | * **HTML file upload form fallback:**
58 | Allows progressive enhancement by using a standard HTML file upload form as widget element.
59 | * **Cross-site file uploads:**
60 | Supports uploading files to a different domain with cross-site XMLHttpRequests or iframe redirects.
61 | * **Multiple plugin instances:**
62 | Allows to use multiple plugin instances on the same webpage.
63 | * **Customizable and extensible:**
64 | Provides an API to set individual options and define callBack methods for various upload events.
65 | * **Multipart and file contents stream uploads:**
66 | Files can be uploaded as standard "multipart/form-data" or file contents stream (HTTP PUT file upload).
67 | * **Compatible with any server-side application platform:**
68 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
69 |
70 | ## Requirements
71 |
72 | ### Mandatory requirements
73 | * [jQuery](http://jquery.com/) v. 1.6+
74 | * [jQuery UI widget factory](http://api.jqueryui.com/jQuery.widget/) v. 1.9+ (included)
75 | * [jQuery Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js) (included)
76 |
77 | The jQuery UI widget factory is a requirement for the basic File Upload plugin, but very lightweight without any other dependencies from the jQuery UI suite.
78 |
79 | The jQuery Iframe Transport is required for [browsers without XHR file upload support](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).
80 |
81 | ### Optional requirements
82 | * [JavaScript Templates engine](https://github.com/blueimp/JavaScript-Templates) v. 2.5.3+
83 | * [JavaScript Load Image library](https://github.com/blueimp/JavaScript-Load-Image) v. 1.11.0+
84 | * [JavaScript Canvas to Blob polyfill](https://github.com/blueimp/JavaScript-Canvas-to-Blob) v. 2.1.0+
85 | * [blueimp Gallery](https://github.com/blueimp/Gallery) v. 2.12.0+
86 | * [Bootstrap CSS framework](http://getbootstrap.com/) v. 3.0.0+
87 | * [Glyphicons](http://glyphicons.com/)
88 |
89 | The JavaScript Templates engine is used to render the selected and uploaded files for the Basic Plus UI and jQuery UI versions.
90 |
91 | The JavaScript Load Image library and JavaScript Canvas to Blob polyfill are required for the image previews and resizing functionality.
92 |
93 | The blueimp Gallery is used to display the uploaded images in a lightbox.
94 |
95 | The user interface of all versions except the jQuery UI version is built with Twitter's [Bootstrap](http://getbootstrap.com/) framework and icons from [Glyphicons](http://glyphicons.com/).
96 |
97 | ### Cross-domain requirements
98 | [Cross-domain File Uploads](https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads) using the [Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js) require a redirect back to the origin server to retrieve the upload results. The [example implementation](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/main.js) makes use of [result.html](https://github.com/blueimp/jQuery-File-Upload/blob/master/cors/result.html) as a static redirect page for the origin server.
99 |
100 | The repository also includes the [jQuery XDomainRequest Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/cors/jquery.xdr-transport.js), which enables limited cross-domain AJAX requests in Microsoft Internet Explorer 8 and 9 (IE 10 supports cross-domain XHR requests).
101 | The XDomainRequest object allows GET and POST requests only and doesn't support file uploads. It is used on the [Demo](http://blueimp.github.io/jQuery-File-Upload/) to delete uploaded files from the cross-domain demo file upload service.
102 |
103 | ## Browsers
104 |
105 | ### Desktop browsers
106 | The File Upload plugin is regularly tested with the latest browser versions and supports the following minimal versions:
107 |
108 | * Google Chrome
109 | * Apple Safari 4.0+
110 | * Mozilla Firefox 3.0+
111 | * Opera 11.0+
112 | * Microsoft Internet Explorer 6.0+
113 |
114 | ### Mobile browsers
115 | The File Upload plugin has been tested with and supports the following mobile browsers:
116 |
117 | * Apple Safari on iOS 6.0+
118 | * Google Chrome on iOS 6.0+
119 | * Google Chrome on Android 4.0+
120 | * Default Browser on Android 2.3+
121 | * Opera Mobile 12.0+
122 |
123 | ### Supported features
124 | For a detailed overview of the features supported by each browser version please have a look at the [Extended browser support information](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).
125 |
126 | ## License
127 | Released under the [MIT license](http://www.opensource.org/licenses/MIT).
128 |
129 | ## Donations
130 | jQuery File Upload is free software, but you can donate to support the developer, Sebastian Tschan:
131 |
132 | Flattr: [](https://flattr.com/thing/286433/jQuery-File-Upload-Plugin)
133 |
134 | PayPal: [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PYWYSYP77KL54)
135 |
--------------------------------------------------------------------------------
/js/jquery.iframe-transport.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Iframe Transport Plugin 1.8.2
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2011, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* global define, window, document */
13 |
14 | (function (factory) {
15 | 'use strict';
16 | if (typeof define === 'function' && define.amd) {
17 | // Register as an anonymous AMD module:
18 | define(['jquery'], factory);
19 | } else {
20 | // Browser globals:
21 | factory(window.jQuery);
22 | }
23 | }(function ($) {
24 | 'use strict';
25 |
26 | // Helper variable to create unique names for the transport iframes:
27 | var counter = 0;
28 |
29 | // The iframe transport accepts four additional options:
30 | // options.fileInput: a jQuery collection of file input fields
31 | // options.paramName: the parameter name for the file form data,
32 | // overrides the name property of the file input field(s),
33 | // can be a string or an array of strings.
34 | // options.formData: an array of objects with name and value properties,
35 | // equivalent to the return data of .serializeArray(), e.g.:
36 | // [{name: 'a', value: 1}, {name: 'b', value: 2}]
37 | // options.initialIframeSrc: the URL of the initial iframe src,
38 | // by default set to "javascript:false;"
39 | $.ajaxTransport('iframe', function (options) {
40 | if (options.async) {
41 | // javascript:false as initial iframe src
42 | // prevents warning popups on HTTPS in IE6:
43 | /*jshint scripturl: true */
44 | var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
45 | /*jshint scripturl: false */
46 | form,
47 | iframe,
48 | addParamChar;
49 | return {
50 | send: function (_, completeCallback) {
51 | form = $('');
52 | form.attr('accept-charset', options.formAcceptCharset);
53 | addParamChar = /\?/.test(options.url) ? '&' : '?';
54 | // XDomainRequest only supports GET and POST:
55 | if (options.type === 'DELETE') {
56 | options.url = options.url + addParamChar + '_method=DELETE';
57 | options.type = 'POST';
58 | } else if (options.type === 'PUT') {
59 | options.url = options.url + addParamChar + '_method=PUT';
60 | options.type = 'POST';
61 | } else if (options.type === 'PATCH') {
62 | options.url = options.url + addParamChar + '_method=PATCH';
63 | options.type = 'POST';
64 | }
65 | // IE versions below IE8 cannot set the name property of
66 | // elements that have already been added to the DOM,
67 | // so we set the name along with the iframe HTML markup:
68 | counter += 1;
69 | iframe = $(
70 | ''
72 | ).bind('load', function () {
73 | var fileInputClones,
74 | paramNames = $.isArray(options.paramName) ?
75 | options.paramName : [options.paramName];
76 | iframe
77 | .unbind('load')
78 | .bind('load', function () {
79 | var response;
80 | // Wrap in a try/catch block to catch exceptions thrown
81 | // when trying to access cross-domain iframe contents:
82 | try {
83 | response = iframe.contents();
84 | // Google Chrome and Firefox do not throw an
85 | // exception when calling iframe.contents() on
86 | // cross-domain requests, so we unify the response:
87 | if (!response.length || !response[0].firstChild) {
88 | throw new Error();
89 | }
90 | } catch (e) {
91 | response = undefined;
92 | }
93 | // The complete callback returns the
94 | // iframe content document as response object:
95 | completeCallback(
96 | 200,
97 | 'success',
98 | {'iframe': response}
99 | );
100 | // Fix for IE endless progress bar activity bug
101 | // (happens on form submits to iframe targets):
102 | $('')
103 | .appendTo(form);
104 | window.setTimeout(function () {
105 | // Removing the form in a setTimeout call
106 | // allows Chrome's developer tools to display
107 | // the response result
108 | form.remove();
109 | }, 0);
110 | });
111 | form
112 | .prop('target', iframe.prop('name'))
113 | .prop('action', options.url)
114 | .prop('method', options.type);
115 | if (options.formData) {
116 | $.each(options.formData, function (index, field) {
117 | $(' ')
118 | .prop('name', field.name)
119 | .val(field.value)
120 | .appendTo(form);
121 | });
122 | }
123 | if (options.fileInput && options.fileInput.length &&
124 | options.type === 'POST') {
125 | fileInputClones = options.fileInput.clone();
126 | // Insert a clone for each file input field:
127 | options.fileInput.after(function (index) {
128 | return fileInputClones[index];
129 | });
130 | if (options.paramName) {
131 | options.fileInput.each(function (index) {
132 | $(this).prop(
133 | 'name',
134 | paramNames[index] || options.paramName
135 | );
136 | });
137 | }
138 | // Appending the file input fields to the hidden form
139 | // removes them from their original location:
140 | form
141 | .append(options.fileInput)
142 | .prop('enctype', 'multipart/form-data')
143 | // enctype must be set as encoding for IE:
144 | .prop('encoding', 'multipart/form-data');
145 | // Remove the HTML5 form attribute from the input(s):
146 | options.fileInput.removeAttr('form');
147 | }
148 | form.submit();
149 | // Insert the file input fields at their original location
150 | // by replacing the clones with the originals:
151 | if (fileInputClones && fileInputClones.length) {
152 | options.fileInput.each(function (index, input) {
153 | var clone = $(fileInputClones[index]);
154 | // Restore the original name and form properties:
155 | $(input)
156 | .prop('name', clone.prop('name'))
157 | .attr('form', clone.attr('form'));
158 | clone.replaceWith(input);
159 | });
160 | }
161 | });
162 | form.append(iframe).appendTo(document.body);
163 | },
164 | abort: function () {
165 | if (iframe) {
166 | // javascript:false as iframe src aborts the request
167 | // and prevents warning popups on HTTPS in IE6.
168 | // concat is used to avoid the "Script URL" JSLint error:
169 | iframe
170 | .unbind('load')
171 | .prop('src', initialIframeSrc);
172 | }
173 | if (form) {
174 | form.remove();
175 | }
176 | }
177 | };
178 | }
179 | });
180 |
181 | // The iframe transport returns the iframe content document as response.
182 | // The following adds converters from iframe to text, json, html, xml
183 | // and script.
184 | // Please note that the Content-Type for JSON responses has to be text/plain
185 | // or text/html, if the browser doesn't include application/json in the
186 | // Accept header, else IE will show a download dialog.
187 | // The Content-Type for XML responses on the other hand has to be always
188 | // application/xml or text/xml, so IE properly parses the XML response.
189 | // See also
190 | // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
191 | $.ajaxSetup({
192 | converters: {
193 | 'iframe text': function (iframe) {
194 | return iframe && $(iframe[0].body).text();
195 | },
196 | 'iframe json': function (iframe) {
197 | return iframe && $.parseJSON($(iframe[0].body).text());
198 | },
199 | 'iframe html': function (iframe) {
200 | return iframe && $(iframe[0].body).html();
201 | },
202 | 'iframe xml': function (iframe) {
203 | var xmlDoc = iframe && iframe[0];
204 | return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
205 | $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
206 | $(xmlDoc.body).html());
207 | },
208 | 'iframe script': function (iframe) {
209 | return iframe && $.globalEval($(iframe[0].body).text());
210 | }
211 | }
212 | });
213 |
214 | }));
215 |
--------------------------------------------------------------------------------
/js/jquery.fileupload-image.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload Image Preview & Resize Plugin 1.7.2
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* jshint nomen:false */
13 | /* global define, window, Blob */
14 |
15 | (function (factory) {
16 | 'use strict';
17 | if (typeof define === 'function' && define.amd) {
18 | // Register as an anonymous AMD module:
19 | define([
20 | 'jquery',
21 | 'load-image',
22 | 'load-image-meta',
23 | 'load-image-exif',
24 | 'load-image-ios',
25 | 'canvas-to-blob',
26 | './jquery.fileupload-process'
27 | ], factory);
28 | } else {
29 | // Browser globals:
30 | factory(
31 | window.jQuery,
32 | window.loadImage
33 | );
34 | }
35 | }(function ($, loadImage) {
36 | 'use strict';
37 |
38 | // Prepend to the default processQueue:
39 | $.blueimp.fileupload.prototype.options.processQueue.unshift(
40 | {
41 | action: 'loadImageMetaData',
42 | disableImageHead: '@',
43 | disableExif: '@',
44 | disableExifThumbnail: '@',
45 | disableExifSub: '@',
46 | disableExifGps: '@',
47 | disabled: '@disableImageMetaDataLoad'
48 | },
49 | {
50 | action: 'loadImage',
51 | // Use the action as prefix for the "@" options:
52 | prefix: true,
53 | fileTypes: '@',
54 | maxFileSize: '@',
55 | noRevoke: '@',
56 | disabled: '@disableImageLoad'
57 | },
58 | {
59 | action: 'resizeImage',
60 | // Use "image" as prefix for the "@" options:
61 | prefix: 'image',
62 | maxWidth: '@',
63 | maxHeight: '@',
64 | minWidth: '@',
65 | minHeight: '@',
66 | crop: '@',
67 | orientation: '@',
68 | forceResize: '@',
69 | disabled: '@disableImageResize'
70 | },
71 | {
72 | action: 'saveImage',
73 | quality: '@imageQuality',
74 | type: '@imageType',
75 | disabled: '@disableImageResize'
76 | },
77 | {
78 | action: 'saveImageMetaData',
79 | disabled: '@disableImageMetaDataSave'
80 | },
81 | {
82 | action: 'resizeImage',
83 | // Use "preview" as prefix for the "@" options:
84 | prefix: 'preview',
85 | maxWidth: '@',
86 | maxHeight: '@',
87 | minWidth: '@',
88 | minHeight: '@',
89 | crop: '@',
90 | orientation: '@',
91 | thumbnail: '@',
92 | canvas: '@',
93 | disabled: '@disableImagePreview'
94 | },
95 | {
96 | action: 'setImage',
97 | name: '@imagePreviewName',
98 | disabled: '@disableImagePreview'
99 | },
100 | {
101 | action: 'deleteImageReferences',
102 | disabled: '@disableImageReferencesDeletion'
103 | }
104 | );
105 |
106 | // The File Upload Resize plugin extends the fileupload widget
107 | // with image resize functionality:
108 | $.widget('blueimp.fileupload', $.blueimp.fileupload, {
109 |
110 | options: {
111 | // The regular expression for the types of images to load:
112 | // matched against the file type:
113 | loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
114 | // The maximum file size of images to load:
115 | loadImageMaxFileSize: 10000000, // 10MB
116 | // The maximum width of resized images:
117 | imageMaxWidth: 1920,
118 | // The maximum height of resized images:
119 | imageMaxHeight: 1080,
120 | // Defines the image orientation (1-8) or takes the orientation
121 | // value from Exif data if set to true:
122 | imageOrientation: false,
123 | // Define if resized images should be cropped or only scaled:
124 | imageCrop: false,
125 | // Disable the resize image functionality by default:
126 | disableImageResize: true,
127 | // The maximum width of the preview images:
128 | previewMaxWidth: 80,
129 | // The maximum height of the preview images:
130 | previewMaxHeight: 80,
131 | // Defines the preview orientation (1-8) or takes the orientation
132 | // value from Exif data if set to true:
133 | previewOrientation: true,
134 | // Create the preview using the Exif data thumbnail:
135 | previewThumbnail: true,
136 | // Define if preview images should be cropped or only scaled:
137 | previewCrop: false,
138 | // Define if preview images should be resized as canvas elements:
139 | previewCanvas: true
140 | },
141 |
142 | processActions: {
143 |
144 | // Loads the image given via data.files and data.index
145 | // as img element, if the browser supports the File API.
146 | // Accepts the options fileTypes (regular expression)
147 | // and maxFileSize (integer) to limit the files to load:
148 | loadImage: function (data, options) {
149 | if (options.disabled) {
150 | return data;
151 | }
152 | var that = this,
153 | file = data.files[data.index],
154 | dfd = $.Deferred();
155 | if (($.type(options.maxFileSize) === 'number' &&
156 | file.size > options.maxFileSize) ||
157 | (options.fileTypes && !options.fileTypes.test(file.type)) || !loadImage(
158 | file,
159 | function (img) {
160 | if (img.src) {
161 | data.img = img;
162 | }
163 | dfd.resolveWith(that, [data]);
164 | },
165 | options
166 | )) {
167 | return data;
168 | }
169 | return dfd.promise();
170 | },
171 |
172 | // Resizes the image given as data.canvas or data.img
173 | // and updates data.canvas or data.img with the resized image.
174 | // Also stores the resized image as preview property.
175 | // Accepts the options maxWidth, maxHeight, minWidth,
176 | // minHeight, canvas and crop:
177 | resizeImage: function (data, options) {
178 | if (options.disabled || !(data.canvas || data.img)) {
179 | return data;
180 | }
181 | options = $.extend({canvas: true}, options);
182 | var that = this,
183 | dfd = $.Deferred(),
184 | img = (options.canvas && data.canvas) || data.img,
185 | resolve = function (newImg) {
186 | if (newImg && (newImg.width !== img.width ||
187 | newImg.height !== img.height ||
188 | options.forceResize)) {
189 | data[newImg.getContext ? 'canvas' : 'img'] = newImg;
190 | }
191 | data.preview = newImg;
192 | dfd.resolveWith(that, [data]);
193 | },
194 | thumbnail;
195 | if (data.exif) {
196 | if (options.orientation === true) {
197 | options.orientation = data.exif.get('Orientation');
198 | }
199 | if (options.thumbnail) {
200 | thumbnail = data.exif.get('Thumbnail');
201 | if (thumbnail) {
202 | loadImage(thumbnail, resolve, options);
203 | return dfd.promise();
204 | }
205 | }
206 | // Prevent orienting the same image twice:
207 | if (data.orientation) {
208 | delete options.orientation;
209 | } else {
210 | data.orientation = options.orientation;
211 | }
212 | }
213 | if (img) {
214 | resolve(loadImage.scale(img, options));
215 | return dfd.promise();
216 | }
217 | return data;
218 | },
219 |
220 | // Saves the processed image given as data.canvas
221 | // inplace at data.index of data.files:
222 | saveImage: function (data, options) {
223 | if (!data.canvas || options.disabled) {
224 | return data;
225 | }
226 | var that = this,
227 | file = data.files[data.index],
228 | dfd = $.Deferred();
229 | if (data.canvas.toBlob) {
230 | data.canvas.toBlob(
231 | function (blob) {
232 | if (!blob.name) {
233 | if (file.type === blob.type) {
234 | blob.name = file.name;
235 | } else if (file.name) {
236 | blob.name = file.name.replace(
237 | /\..+$/,
238 | '.' + blob.type.substr(6)
239 | );
240 | }
241 | }
242 | // Don't restore invalid meta data:
243 | if (file.type !== blob.type) {
244 | delete data.imageHead;
245 | }
246 | // Store the created blob at the position
247 | // of the original file in the files list:
248 | data.files[data.index] = blob;
249 | dfd.resolveWith(that, [data]);
250 | },
251 | options.type || file.type,
252 | options.quality
253 | );
254 | } else {
255 | return data;
256 | }
257 | return dfd.promise();
258 | },
259 |
260 | loadImageMetaData: function (data, options) {
261 | if (options.disabled) {
262 | return data;
263 | }
264 | var that = this,
265 | dfd = $.Deferred();
266 | loadImage.parseMetaData(data.files[data.index], function (result) {
267 | $.extend(data, result);
268 | dfd.resolveWith(that, [data]);
269 | }, options);
270 | return dfd.promise();
271 | },
272 |
273 | saveImageMetaData: function (data, options) {
274 | if (!(data.imageHead && data.canvas &&
275 | data.canvas.toBlob && !options.disabled)) {
276 | return data;
277 | }
278 | var file = data.files[data.index],
279 | blob = new Blob([
280 | data.imageHead,
281 | // Resized images always have a head size of 20 bytes,
282 | // including the JPEG marker and a minimal JFIF header:
283 | this._blobSlice.call(file, 20)
284 | ], {type: file.type});
285 | blob.name = file.name;
286 | data.files[data.index] = blob;
287 | return data;
288 | },
289 |
290 | // Sets the resized version of the image as a property of the
291 | // file object, must be called after "saveImage":
292 | setImage: function (data, options) {
293 | if (data.preview && !options.disabled) {
294 | data.files[data.index][options.name || 'preview'] = data.preview;
295 | }
296 | return data;
297 | },
298 |
299 | deleteImageReferences: function (data, options) {
300 | if (!options.disabled) {
301 | delete data.img;
302 | delete data.canvas;
303 | delete data.preview;
304 | delete data.imageHead;
305 | }
306 | return data;
307 | }
308 |
309 | }
310 |
311 | });
312 |
313 | }));
314 |
--------------------------------------------------------------------------------
/basic-plus.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 | jQuery File Upload Demo - Basic Plus version
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
50 |
51 |
jQuery File Upload Demo
52 |
Basic Plus version
53 |
60 |
61 |
62 | File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery.
63 | Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
64 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
65 |
66 |
67 |
68 |
69 |
70 | Add files...
71 |
72 |
73 |
74 |
75 |
76 |
77 |
80 |
81 |
82 |
83 |
84 |
85 |
Demo Notes
86 |
87 |
88 |
89 | The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
90 | Only image files (JPG, GIF, PNG ) are allowed in this demo (by default there is no file type restriction).
91 | Uploaded files will be deleted automatically after 5 minutes (demo setting).
92 | You can drag & drop files from your desktop on this webpage (see Browser support ).
93 | Please refer to the project website and documentation for more information.
94 | Built with Twitter's Bootstrap CSS framework and Icons from Glyphicons .
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/server/node/server.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * jQuery File Upload Plugin Node.js Example 2.1.1
4 | * https://github.com/blueimp/jQuery-File-Upload
5 | *
6 | * Copyright 2012, Sebastian Tschan
7 | * https://blueimp.net
8 | *
9 | * Licensed under the MIT license:
10 | * http://www.opensource.org/licenses/MIT
11 | */
12 |
13 | /* jshint nomen:false */
14 | /* global require, __dirname, unescape, console */
15 |
16 | (function (port) {
17 | 'use strict';
18 | if (typeof String.prototype.startsWith !== 'function') {
19 | // see below for better implementation!
20 | String.prototype.startsWith = function (str) {
21 | return this.indexOf(str) === 0;
22 | };
23 | }
24 | if (typeof String.prototype.endsWith !== 'function') {
25 | String.prototype.endsWith = function (str) {
26 | return this.substring(this.length - str.length, this.length) === str;
27 | };
28 | }
29 | var path = require('path'),
30 | fs = require('fs'),
31 | // Since Node 0.8, .existsSync() moved from path to fs:
32 | _existsSync = fs.existsSync || path.existsSync,
33 | formidable = require('formidable'),
34 | nodeStatic = require('node-static'),
35 | imageMagick = require('imagemagick'),
36 | options = {
37 | tmpDir: __dirname + '/tmp',
38 | publicDir: __dirname + '/public',
39 | uploadDir: __dirname + '/public/files',
40 | uploadUrl: '/files/',
41 | frontendDir: __dirname + '/../..',
42 | maxPostSize: 11000000000, // 11 GB
43 | minFileSize: 1,
44 | maxFileSize: 10000000000, // 10 GB
45 | acceptFileTypes: /.+/i,
46 | // Files not matched by this regular expression force a download dialog,
47 | // to prevent executing any scripts in the context of the service domain:
48 | inlineFileTypes: /\.(gif|jpe?g|png)$/i,
49 | imageTypes: /\.(gif|jpe?g|png)$/i,
50 | imageVersions: {
51 | 'thumbnail': {
52 | width: 80,
53 | height: 80
54 | }
55 | },
56 | accessControl: {
57 | allowOrigin: '*',
58 | allowMethods: 'OPTIONS, HEAD, GET, POST, PUT, DELETE',
59 | allowHeaders: 'Content-Type, Content-Range, Content-Disposition'
60 | },
61 | /* Uncomment and edit this section to provide the service via HTTPS:
62 | ssl: {
63 | key: fs.readFileSync('/Applications/XAMPP/etc/ssl.key/server.key'),
64 | cert: fs.readFileSync('/Applications/XAMPP/etc/ssl.crt/server.crt')
65 | },
66 | */
67 | nodeStatic: {
68 | cache: 3600 // seconds to cache served files
69 | }
70 | },
71 | utf8encode = function (str) {
72 | return unescape(encodeURIComponent(str));
73 | },
74 | fileServer = new nodeStatic.Server(options.publicDir, options.nodeStatic),
75 | frontendServer = new nodeStatic.Server(options.frontendDir, options.nodeStatic),
76 | nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/,
77 | nameCountFunc = function (s, index, ext) {
78 | return ' (' + ((parseInt(index, 10) || 0) + 1) + ')' + (ext || '');
79 | },
80 | FileInfo = function (file) {
81 | this.name = file.name;
82 | this.size = file.size;
83 | this.type = file.type;
84 | this.deleteType = 'DELETE';
85 | },
86 | UploadHandler = function (req, res, callback) {
87 | this.req = req;
88 | this.res = res;
89 | this.callback = callback;
90 | },
91 | serve = function (req, res) {
92 | res.setHeader(
93 | 'Access-Control-Allow-Origin',
94 | options.accessControl.allowOrigin
95 | );
96 | res.setHeader(
97 | 'Access-Control-Allow-Methods',
98 | options.accessControl.allowMethods
99 | );
100 | res.setHeader(
101 | 'Access-Control-Allow-Headers',
102 | options.accessControl.allowHeaders
103 | );
104 | var handleResult = function (result, redirect) {
105 | if (redirect) {
106 | res.writeHead(302, {
107 | 'Location': redirect.replace(
108 | /%s/,
109 | encodeURIComponent(JSON.stringify(result))
110 | )
111 | });
112 | res.end();
113 | } else {
114 | res.writeHead(200, {
115 | 'Content-Type': req.headers.accept
116 | .indexOf('application/json') !== -1 ?
117 | 'application/json' : 'text/plain'
118 | });
119 | res.end(JSON.stringify(result));
120 | }
121 | },
122 | setNoCacheHeaders = function () {
123 | res.setHeader('Pragma', 'no-cache');
124 | res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
125 | res.setHeader('Content-Disposition', 'inline; filename="files.json"');
126 | },
127 | handler = new UploadHandler(req, res, handleResult);
128 | switch (req.method) {
129 | case 'OPTIONS':
130 | res.end();
131 | break;
132 | case 'HEAD':
133 | case 'GET':
134 | console.log(req.url);
135 | if (req.url === '/') {
136 | setNoCacheHeaders();
137 | if (req.method === 'GET') {
138 | handler.get();
139 | } else {
140 | res.end();
141 | }
142 | } else if (req.url.endsWith('.html') || req.url.startsWith('/css/') || req.url.startsWith('/js/') || req.url.startsWith('/dist/') || req.url.startsWith('/bower_components/') || req.url.startsWith('/img/') || req.url.startsWith('/templates/')) {
143 | frontendServer.serve(req, res);
144 | } else {
145 | fileServer.serve(req, res);
146 | }
147 | break;
148 | case 'POST':
149 | setNoCacheHeaders();
150 | handler.post();
151 | break;
152 | case 'DELETE':
153 | handler.destroy();
154 | break;
155 | default:
156 | res.statusCode = 405;
157 | res.end();
158 | }
159 | };
160 | fileServer.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
161 | // Prevent browsers from MIME-sniffing the content-type:
162 | _headers['X-Content-Type-Options'] = 'nosniff';
163 | if (!options.inlineFileTypes.test(files[0])) {
164 | // Force a download dialog for unsafe file extensions:
165 | _headers['Content-Type'] = 'application/octet-stream';
166 | _headers['Content-Disposition'] = 'attachment; filename="' +
167 | utf8encode(path.basename(files[0])) + '"';
168 | }
169 | nodeStatic.Server.prototype.respond
170 | .call(this, pathname, status, _headers, files, stat, req, res, finish);
171 | };
172 | FileInfo.prototype.validate = function () {
173 | if (options.minFileSize && options.minFileSize > this.size) {
174 | this.error = 'File is too small';
175 | } else if (options.maxFileSize && options.maxFileSize < this.size) {
176 | this.error = 'File is too big';
177 | } else if (!options.acceptFileTypes.test(this.name)) {
178 | this.error = 'Filetype not allowed';
179 | }
180 | return !this.error;
181 | };
182 | FileInfo.prototype.safeName = function () {
183 | // Prevent directory traversal and creating hidden system files:
184 | this.name = path.basename(this.name).replace(/^\.+/, '');
185 | // Prevent overwriting existing files:
186 | while (_existsSync(options.uploadDir + '/' + this.name)) {
187 | this.name = this.name.replace(nameCountRegexp, nameCountFunc);
188 | }
189 | };
190 | FileInfo.prototype.initUrls = function (req) {
191 | if (!this.error) {
192 | var that = this,
193 | baseUrl = (options.ssl ? 'https:' : 'http:') +
194 | '//' + req.headers.host + options.uploadUrl;
195 | this.url = this.deleteUrl = baseUrl + encodeURIComponent(this.name);
196 | Object.keys(options.imageVersions).forEach(function (version) {
197 | if (_existsSync(
198 | options.uploadDir + '/' + version + '/' + that.name
199 | )) {
200 | that[version + 'Url'] = baseUrl + version + '/' +
201 | encodeURIComponent(that.name);
202 | }
203 | });
204 | }
205 | };
206 | UploadHandler.prototype.get = function () {
207 | var handler = this,
208 | files = [];
209 | fs.readdir(options.uploadDir, function (err, list) {
210 | list.forEach(function (name) {
211 | var stats = fs.statSync(options.uploadDir + '/' + name),
212 | fileInfo;
213 | if (stats.isFile() && name[0] !== '.') {
214 | fileInfo = new FileInfo({
215 | name: name,
216 | size: stats.size
217 | });
218 | fileInfo.initUrls(handler.req);
219 | files.push(fileInfo);
220 | }
221 | });
222 | handler.callback({files: files});
223 | });
224 | };
225 | UploadHandler.prototype.post = function () {
226 | var handler = this,
227 | form = new formidable.IncomingForm(),
228 | tmpFiles = [],
229 | files = [],
230 | map = {},
231 | counter = 1,
232 | redirect,
233 | finish = function () {
234 | counter -= 1;
235 | if (!counter) {
236 | files.forEach(function (fileInfo) {
237 | fileInfo.initUrls(handler.req);
238 | });
239 | handler.callback({files: files}, redirect);
240 | }
241 | };
242 | form.uploadDir = options.tmpDir;
243 | form.on('fileBegin', function (name, file) {
244 | tmpFiles.push(file.path);
245 | var fileInfo = new FileInfo(file, handler.req, true);
246 | fileInfo.safeName();
247 | map[path.basename(file.path)] = fileInfo;
248 | files.push(fileInfo);
249 | }).on('field', function (name, value) {
250 | if (name === 'redirect') {
251 | redirect = value;
252 | }
253 | }).on('file', function (name, file) {
254 | var fileInfo = map[path.basename(file.path)];
255 | fileInfo.size = file.size;
256 | if (!fileInfo.validate()) {
257 | fs.unlink(file.path);
258 | return;
259 | }
260 | fs.renameSync(file.path, options.uploadDir + '/' + fileInfo.name);
261 | if (options.imageTypes.test(fileInfo.name)) {
262 | Object.keys(options.imageVersions).forEach(function (version) {
263 | counter += 1;
264 | var opts = options.imageVersions[version];
265 | imageMagick.resize({
266 | width: opts.width,
267 | height: opts.height,
268 | srcPath: options.uploadDir + '/' + fileInfo.name,
269 | dstPath: options.uploadDir + '/' + version + '/' +
270 | fileInfo.name
271 | }, finish);
272 | });
273 | }
274 | }).on('aborted', function () {
275 | tmpFiles.forEach(function (file) {
276 | fs.unlink(file);
277 | });
278 | }).on('error', function (e) {
279 | console.log(e);
280 | }).on('progress', function (bytesReceived) {
281 | if (bytesReceived > options.maxPostSize) {
282 | handler.req.connection.destroy();
283 | }
284 | }).on('end', finish).parse(handler.req);
285 | };
286 | UploadHandler.prototype.destroy = function () {
287 | var handler = this,
288 | fileName;
289 | if (handler.req.url.slice(0, options.uploadUrl.length) === options.uploadUrl) {
290 | fileName = path.basename(decodeURIComponent(handler.req.url));
291 | if (fileName[0] !== '.') {
292 | fs.unlink(options.uploadDir + '/' + fileName, function (ex) {
293 | Object.keys(options.imageVersions).forEach(function (version) {
294 | fs.unlink(options.uploadDir + '/' + version + '/' + fileName);
295 | });
296 | handler.callback({success: !ex});
297 | });
298 | return;
299 | }
300 | }
301 | handler.callback({success: false});
302 | };
303 | if (options.ssl) {
304 | require('https').createServer(options.ssl, serve).listen(port);
305 | } else {
306 | require('http').createServer(serve).listen(port);
307 | }
308 | }(8888));
309 |
--------------------------------------------------------------------------------
/jquery-ui.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
20 |
21 | jQuery File Upload Demo - jQuery UI version
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
54 | jQuery File Upload Demo
55 | jQuery UI version
56 |
85 |
92 |
93 | File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery UI.
94 | Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
95 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
96 |
97 |
98 |
127 |
128 | Demo Notes
129 |
130 | The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
131 | Only image files (JPG, GIF, PNG ) are allowed in this demo (by default there is no file type restriction).
132 | Uploaded files will be deleted automatically after 5 minutes (demo setting).
133 | You can drag & drop files from your desktop on this webpage (see Browser support ).
134 | Please refer to the project website and documentation for more information.
135 | Built with jQuery UI .
136 |
137 |
138 |
139 |
140 |
141 |
‹
142 |
›
143 |
×
144 |
145 |
146 |
147 |
148 |
173 |
174 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
245 |
246 |
249 |
250 |
251 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
20 |
21 | jQuery File Upload Demo
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
58 |
59 |
jQuery File Upload Demo
60 |
Basic Plus UI version
61 |
68 |
69 |
70 | File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery.
71 | Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
72 | Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
73 |
74 |
75 |
76 |
117 |
118 |
119 |
120 |
Demo Notes
121 |
122 |
123 |
124 | The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
125 | Only image files (JPG, GIF, PNG ) are allowed in this demo (by default there is no file type restriction).
126 | Uploaded files will be deleted automatically after 5 minutes (demo setting).
127 | You can drag & drop files from your desktop on this webpage (see Browser support ).
128 | Please refer to the project website and documentation for more information.
129 | Built with Twitter's Bootstrap CSS framework and Icons from Glyphicons .
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
‹
139 |
›
140 |
×
141 |
142 |
143 |
144 |
145 |
176 |
177 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
254 |
255 |
256 |
--------------------------------------------------------------------------------
/js/jquery.fileupload-angular.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery File Upload AngularJS Plugin 2.2.0
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | /* jshint nomen:false */
13 | /* global define, angular */
14 |
15 | (function (factory) {
16 | 'use strict';
17 | if (typeof define === 'function' && define.amd) {
18 | // Register as an anonymous AMD module:
19 | define([
20 | 'jquery',
21 | 'angular',
22 | './jquery.fileupload-image',
23 | './jquery.fileupload-audio',
24 | './jquery.fileupload-video',
25 | './jquery.fileupload-validate'
26 | ], factory);
27 | } else {
28 | factory();
29 | }
30 | }(function () {
31 | 'use strict';
32 |
33 | angular.module('blueimp.fileupload', [])
34 |
35 | // The fileUpload service provides configuration options
36 | // for the fileUpload directive and default handlers for
37 | // File Upload events:
38 | .provider('fileUpload', function () {
39 |
40 | var scopeEvalAsync = function (expression) {
41 | var scope = angular.element(this)
42 | .fileupload('option', 'scope');
43 | // Schedule a new $digest cycle if not already inside of one
44 | // and evaluate the given expression:
45 | scope.$evalAsync(expression);
46 | },
47 | addFileMethods = function (scope, data) {
48 | var files = data.files,
49 | file = files[0];
50 | angular.forEach(files, function (file, index) {
51 | file._index = index;
52 | file.$state = function () {
53 | return data.state();
54 | };
55 | file.$processing = function () {
56 | return data.processing();
57 | };
58 | file.$progress = function () {
59 | return data.progress();
60 | };
61 | file.$response = function () {
62 | return data.response();
63 | };
64 | });
65 | file.$submit = function () {
66 | if (!file.error) {
67 | return data.submit();
68 | }
69 | };
70 | file.$cancel = function () {
71 | return data.abort();
72 | };
73 | },
74 | $config;
75 | $config = this.defaults = {
76 | handleResponse: function (e, data) {
77 | var files = data.result && data.result.files;
78 | if (files) {
79 | data.scope.replace(data.files, files);
80 | } else if (data.errorThrown ||
81 | data.textStatus === 'error') {
82 | data.files[0].error = data.errorThrown ||
83 | data.textStatus;
84 | }
85 | },
86 | add: function (e, data) {
87 | if (e.isDefaultPrevented()) {
88 | return false;
89 | }
90 | var scope = data.scope,
91 | filesCopy = [];
92 | angular.forEach(data.files, function (file) {
93 | filesCopy.push(file);
94 | });
95 | scope.$apply(function () {
96 | addFileMethods(scope, data);
97 | var method = scope.option('prependFiles') ?
98 | 'unshift' : 'push';
99 | Array.prototype[method].apply(scope.queue, data.files);
100 | });
101 | data.process(function () {
102 | return scope.process(data);
103 | }).always(function () {
104 | scope.$apply(function () {
105 | addFileMethods(scope, data);
106 | scope.replace(filesCopy, data.files);
107 | });
108 | }).then(function () {
109 | if ((scope.option('autoUpload') ||
110 | data.autoUpload) &&
111 | data.autoUpload !== false) {
112 | data.submit();
113 | }
114 | });
115 | },
116 | progress: function (e, data) {
117 | if (e.isDefaultPrevented()) {
118 | return false;
119 | }
120 | data.scope.$apply();
121 | },
122 | done: function (e, data) {
123 | if (e.isDefaultPrevented()) {
124 | return false;
125 | }
126 | var that = this;
127 | data.scope.$apply(function () {
128 | data.handleResponse.call(that, e, data);
129 | });
130 | },
131 | fail: function (e, data) {
132 | if (e.isDefaultPrevented()) {
133 | return false;
134 | }
135 | var that = this,
136 | scope = data.scope;
137 | if (data.errorThrown === 'abort') {
138 | scope.clear(data.files);
139 | return;
140 | }
141 | scope.$apply(function () {
142 | data.handleResponse.call(that, e, data);
143 | });
144 | },
145 | stop: scopeEvalAsync,
146 | processstart: scopeEvalAsync,
147 | processstop: scopeEvalAsync,
148 | getNumberOfFiles: function () {
149 | var scope = this.scope;
150 | return scope.queue.length - scope.processing();
151 | },
152 | dataType: 'json',
153 | autoUpload: false
154 | };
155 |
156 | /**
157 | * Create field data object
158 | * @type {{}}
159 | */
160 | var fieldData = {};
161 |
162 | /**
163 | * Add field Data to field data object with fieldname
164 | * @param fieldName
165 | * @param fileData
166 | */
167 | var addFieldData = function addFieldData(fieldName, fileData) {
168 | fieldData[fieldName].push(fileData);
169 | };
170 |
171 | /**
172 | * Remove field data
173 | * @param fieldName
174 | * @param fileId
175 | */
176 | var removeFieldData = function removeFieldData(fieldName, fileId) {
177 | angular.forEach(fieldData[fieldName], function (value, key) {
178 | if (value && value._id) {
179 | if (value._id === fileId) {
180 | fieldData[fieldName].splice(key, 1);
181 | }
182 | }
183 | });
184 | };
185 |
186 | /**
187 | * Register the field
188 | * @param fieldName
189 | * @param fieldData
190 | * @todo prefill with existing data
191 | */
192 | var registerField = function registerField(fieldName) {
193 | if (!fieldData[fieldName]) {
194 | fieldData[fieldName] = [];
195 | }
196 | };
197 |
198 | this.$get = [
199 | function () {
200 | return {
201 | fieldData: fieldData,
202 | defaults: $config,
203 | addFieldData: addFieldData,
204 | removeFieldData: removeFieldData,
205 | registerField: registerField
206 | };
207 | }
208 | ];
209 | })
210 |
211 | // Format byte numbers to readable presentations:
212 | .provider('formatFileSizeFilter', function () {
213 | var $config = {
214 | // Byte units following the IEC format
215 | // http://en.wikipedia.org/wiki/Kilobyte
216 | units: [
217 | {size: 1000000000, suffix: ' GB'},
218 | {size: 1000000, suffix: ' MB'},
219 | {size: 1000, suffix: ' KB'}
220 | ]
221 | };
222 | this.defaults = $config;
223 | this.$get = function () {
224 | return function (bytes) {
225 | if (!angular.isNumber(bytes)) {
226 | return '';
227 | }
228 | var unit = true,
229 | i = 0,
230 | prefix,
231 | suffix;
232 | while (unit) {
233 | unit = $config.units[i];
234 | prefix = unit.prefix || '';
235 | suffix = unit.suffix || '';
236 | if (i === $config.units.length - 1 || bytes >= unit.size) {
237 | return prefix + (bytes / unit.size).toFixed(2) + suffix;
238 | }
239 | i += 1;
240 | }
241 | };
242 | };
243 | })
244 |
245 | // The FileUploadController initializes the fileupload widget and
246 | // provides scope methods to control the File Upload functionality:
247 | .controller('FileUploadController', [
248 | '$scope', '$element', '$attrs', '$window', 'fileUpload',
249 | function ($scope, $element, $attrs, $window, fileUpload) {
250 | var uploadMethods = {
251 | progress: function () {
252 | return $element.fileupload('progress');
253 | },
254 | active: function () {
255 | return $element.fileupload('active');
256 | },
257 | option: function (option, data) {
258 | if (arguments.length === 1) {
259 | return $element.fileupload('option', option);
260 | }
261 | $element.fileupload('option', option, data);
262 | },
263 | add: function (data) {
264 | return $element.fileupload('add', data);
265 | },
266 | send: function (data) {
267 | return $element.fileupload('send', data);
268 | },
269 | process: function (data) {
270 | return $element.fileupload('process', data);
271 | },
272 | processing: function (data) {
273 | return $element.fileupload('processing', data);
274 | }
275 | };
276 | $scope.disabled = !$window.jQuery.support.fileInput;
277 | $scope.queue = $scope.queue || [];
278 | $scope.clear = function (files) {
279 | var queue = this.queue,
280 | i = queue.length,
281 | file = files,
282 | length = 1;
283 | if (angular.isArray(files)) {
284 | file = files[0];
285 | length = files.length;
286 | }
287 | while (i) {
288 | i -= 1;
289 | if (queue[i] === file) {
290 | return queue.splice(i, length);
291 | }
292 | }
293 | };
294 | $scope.replace = function (oldFiles, newFiles) {
295 | var queue = this.queue,
296 | file = oldFiles[0],
297 | i,
298 | j;
299 | for (i = 0; i < queue.length; i += 1) {
300 | if (queue[i] === file) {
301 | for (j = 0; j < newFiles.length; j += 1) {
302 | queue[i + j] = newFiles[j];
303 | }
304 | return;
305 | }
306 | }
307 | };
308 | $scope.applyOnQueue = function (method) {
309 | var list = this.queue.slice(0),
310 | i,
311 | file;
312 | for (i = 0; i < list.length; i += 1) {
313 | file = list[i];
314 | if (file[method]) {
315 | file[method]();
316 | }
317 | }
318 | };
319 | $scope.submit = function () {
320 | this.applyOnQueue('$submit');
321 | };
322 | $scope.cancel = function () {
323 | this.applyOnQueue('$cancel');
324 | };
325 | // Add upload methods to the scope:
326 | angular.extend($scope, uploadMethods);
327 | // The fileupload widget will initialize with
328 | // the options provided via "data-"-parameters,
329 | // as well as those given via options object:
330 | $element.fileupload(angular.extend(
331 | {scope: $scope},
332 | fileUpload.defaults
333 | )).on('fileuploadadd', function (e, data) {
334 | data.scope = $scope;
335 | }).on('fileuploadfail', function (e, data) {
336 | if (data.errorThrown === 'abort') {
337 | return;
338 | }
339 | if (data.dataType &&
340 | data.dataType.indexOf('json') === data.dataType.length - 4) {
341 | try {
342 | data.result = angular.fromJson(data.jqXHR.responseText);
343 | } catch (ignore) {
344 | }
345 | }
346 | }).on([
347 | 'fileuploadadd',
348 | 'fileuploadsubmit',
349 | 'fileuploadsend',
350 | 'fileuploaddone',
351 | 'fileuploadfail',
352 | 'fileuploadalways',
353 | 'fileuploadprogress',
354 | 'fileuploadprogressall',
355 | 'fileuploadstart',
356 | 'fileuploadstop',
357 | 'fileuploadchange',
358 | 'fileuploadpaste',
359 | 'fileuploaddrop',
360 | 'fileuploaddragover',
361 | 'fileuploadchunksend',
362 | 'fileuploadchunkdone',
363 | 'fileuploadchunkfail',
364 | 'fileuploadchunkalways',
365 | 'fileuploadprocessstart',
366 | 'fileuploadprocess',
367 | 'fileuploadprocessdone',
368 | 'fileuploadprocessfail',
369 | 'fileuploadprocessalways',
370 | 'fileuploadprocessstop'
371 | ].join(' '), function (e, data) {
372 | if ($scope.$emit(e.type, data).defaultPrevented) {
373 | e.preventDefault();
374 | }
375 | }).on('remove', function () {
376 | // Remove upload methods from the scope,
377 | // when the widget is removed:
378 | var method;
379 | for (method in uploadMethods) {
380 | if (uploadMethods.hasOwnProperty(method)) {
381 | delete $scope[method];
382 | }
383 | }
384 | });
385 | // Observe option changes:
386 | $scope.$watch(
387 | $attrs.fileUpload,
388 | function (newOptions) {
389 | if (newOptions) {
390 | $element.fileupload('option', newOptions);
391 | }
392 | }
393 | );
394 | }
395 | ])
396 |
397 | // Provide File Upload progress feedback:
398 | .controller('FileUploadProgressController', [
399 | '$scope', '$attrs', '$parse',
400 | function ($scope, $attrs, $parse) {
401 | var fn = $parse($attrs.fileUploadProgress),
402 | update = function () {
403 | var progress = fn($scope);
404 |
405 | if (!progress || !progress.total) {
406 | return;
407 | }
408 | $scope.num = Math.floor(
409 | progress.loaded / progress.total * 100
410 | );
411 | };
412 | update();
413 | $scope.$watch(
414 | $attrs.fileUploadProgress + '.loaded',
415 | function (newValue, oldValue) {
416 | if (newValue !== oldValue) {
417 | update();
418 | }
419 | }
420 | );
421 | }
422 | ])
423 |
424 | // Display File Upload previews:
425 | .controller('FileUploadPreviewController', [
426 | '$scope', '$element', '$attrs',
427 | function ($scope, $element, $attrs) {
428 | $scope.$watch(
429 | $attrs.fileUploadPreview + '.preview',
430 | function (preview) {
431 | $element.empty();
432 | if (preview) {
433 | $element.append(preview);
434 | }
435 | }
436 | );
437 | }
438 | ])
439 |
440 | .directive('fileUpload', function () {
441 | return {
442 | controller: 'FileUploadController',
443 | scope: true
444 | };
445 | })
446 |
447 | .directive('fileUploadProgress', function () {
448 | return {
449 | controller: 'FileUploadProgressController',
450 | scope: true
451 | };
452 | })
453 |
454 | .directive('fileUploadPreview', function () {
455 | return {
456 | controller: 'FileUploadPreviewController'
457 | };
458 | })
459 |
460 | // Enhance the HTML5 download attribute to
461 | // allow drag&drop of files to the desktop:
462 | .directive('download', function () {
463 | return function (scope, elm) {
464 | elm.on('dragstart', function (e) {
465 | try {
466 | e.originalEvent.dataTransfer.setData(
467 | 'DownloadURL',
468 | [
469 | 'application/octet-stream',
470 | elm.prop('download'),
471 | elm.prop('href')
472 | ].join(':')
473 | );
474 | } catch (ignore) {
475 | }
476 | });
477 | };
478 | });
479 |
480 | }));
--------------------------------------------------------------------------------
/js/vendor/jquery.ui.widget.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery UI Widget 1.10.4+amd
3 | * https://github.com/blueimp/jQuery-File-Upload
4 | *
5 | * Copyright 2014 jQuery Foundation and other contributors
6 | * Released under the MIT license.
7 | * http://jquery.org/license
8 | *
9 | * http://api.jqueryui.com/jQuery.widget/
10 | */
11 |
12 | (function (factory) {
13 | if (typeof define === "function" && define.amd) {
14 | // Register as an anonymous AMD module:
15 | define(["jquery"], factory);
16 | } else {
17 | // Browser globals:
18 | factory(jQuery);
19 | }
20 | }(function ($, undefined) {
21 |
22 | var uuid = 0,
23 | slice = Array.prototype.slice,
24 | _cleanData = $.cleanData;
25 | $.cleanData = function (elems) {
26 | for (var i = 0, elem; (elem = elems[i]) != null; i++) {
27 | try {
28 | $(elem).triggerHandler("remove");
29 | // http://bugs.jquery.com/ticket/8235
30 | } catch (e) {
31 | }
32 | }
33 | _cleanData(elems);
34 | };
35 |
36 | $.widget = function (name, base, prototype) {
37 | var fullName, existingConstructor, constructor, basePrototype,
38 | // proxiedPrototype allows the provided prototype to remain unmodified
39 | // so that it can be used as a mixin for multiple widgets (#8876)
40 | proxiedPrototype = {},
41 | namespace = name.split(".")[ 0 ];
42 |
43 | name = name.split(".")[ 1 ];
44 | fullName = namespace + "-" + name;
45 |
46 | if (!prototype) {
47 | prototype = base;
48 | base = $.Widget;
49 | }
50 |
51 | // create selector for plugin
52 | $.expr[ ":" ][ fullName.toLowerCase() ] = function (elem) {
53 | return !!$.data(elem, fullName);
54 | };
55 |
56 | $[ namespace ] = $[ namespace ] || {};
57 | existingConstructor = $[ namespace ][ name ];
58 | constructor = $[ namespace ][ name ] = function (options, element) {
59 | // allow instantiation without "new" keyword
60 | if (!this._createWidget) {
61 | return new constructor(options, element);
62 | }
63 |
64 | // allow instantiation without initializing for simple inheritance
65 | // must use "new" keyword (the code above always passes args)
66 | if (arguments.length) {
67 | this._createWidget(options, element);
68 | }
69 | };
70 | // extend with the existing constructor to carry over any static properties
71 | $.extend(constructor, existingConstructor, {
72 | version: prototype.version,
73 | // copy the object used to create the prototype in case we need to
74 | // redefine the widget later
75 | _proto: $.extend({}, prototype),
76 | // track widgets that inherit from this widget in case this widget is
77 | // redefined after a widget inherits from it
78 | _childConstructors: []
79 | });
80 |
81 | basePrototype = new base();
82 | // we need to make the options hash a property directly on the new instance
83 | // otherwise we'll modify the options hash on the prototype that we're
84 | // inheriting from
85 | basePrototype.options = $.widget.extend({}, basePrototype.options);
86 | $.each(prototype, function (prop, value) {
87 | if (!$.isFunction(value)) {
88 | proxiedPrototype[ prop ] = value;
89 | return;
90 | }
91 | proxiedPrototype[ prop ] = (function () {
92 | var _super = function () {
93 | return base.prototype[ prop ].apply(this, arguments);
94 | },
95 | _superApply = function (args) {
96 | return base.prototype[ prop ].apply(this, args);
97 | };
98 | return function () {
99 | var __super = this._super,
100 | __superApply = this._superApply,
101 | returnValue;
102 |
103 | this._super = _super;
104 | this._superApply = _superApply;
105 |
106 | returnValue = value.apply(this, arguments);
107 |
108 | this._super = __super;
109 | this._superApply = __superApply;
110 |
111 | return returnValue;
112 | };
113 | })();
114 | });
115 | constructor.prototype = $.widget.extend(basePrototype, {
116 | // TODO: remove support for widgetEventPrefix
117 | // always use the name + a colon as the prefix, e.g., draggable:start
118 | // don't prefix for widgets that aren't DOM-based
119 | widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
120 | }, proxiedPrototype, {
121 | constructor: constructor,
122 | namespace: namespace,
123 | widgetName: name,
124 | widgetFullName: fullName
125 | });
126 |
127 | // If this widget is being redefined then we need to find all widgets that
128 | // are inheriting from it and redefine all of them so that they inherit from
129 | // the new version of this widget. We're essentially trying to replace one
130 | // level in the prototype chain.
131 | if (existingConstructor) {
132 | $.each(existingConstructor._childConstructors, function (i, child) {
133 | var childPrototype = child.prototype;
134 |
135 | // redefine the child widget using the same prototype that was
136 | // originally used, but inherit from the new version of the base
137 | $.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);
138 | });
139 | // remove the list of existing child constructors from the old constructor
140 | // so the old child constructors can be garbage collected
141 | delete existingConstructor._childConstructors;
142 | } else {
143 | base._childConstructors.push(constructor);
144 | }
145 |
146 | $.widget.bridge(name, constructor);
147 | };
148 |
149 | $.widget.extend = function (target) {
150 | var input = slice.call(arguments, 1),
151 | inputIndex = 0,
152 | inputLength = input.length,
153 | key,
154 | value;
155 | for (; inputIndex < inputLength; inputIndex++) {
156 | for (key in input[ inputIndex ]) {
157 | value = input[ inputIndex ][ key ];
158 | if (input[ inputIndex ].hasOwnProperty(key) && value !== undefined) {
159 | // Clone objects
160 | if ($.isPlainObject(value)) {
161 | target[ key ] = $.isPlainObject(target[ key ]) ?
162 | $.widget.extend({}, target[ key ], value) :
163 | // Don't extend strings, arrays, etc. with objects
164 | $.widget.extend({}, value);
165 | // Copy everything else by reference
166 | } else {
167 | target[ key ] = value;
168 | }
169 | }
170 | }
171 | }
172 | return target;
173 | };
174 |
175 | $.widget.bridge = function (name, object) {
176 | var fullName = object.prototype.widgetFullName || name;
177 | $.fn[ name ] = function (options) {
178 | var isMethodCall = typeof options === "string",
179 | args = slice.call(arguments, 1),
180 | returnValue = this;
181 |
182 | // allow multiple hashes to be passed on init
183 | options = !isMethodCall && args.length ?
184 | $.widget.extend.apply(null, [ options ].concat(args)) :
185 | options;
186 |
187 | if (isMethodCall) {
188 | this.each(function () {
189 | var methodValue,
190 | instance = $.data(this, fullName);
191 | if (!instance) {
192 | return $.error("cannot call methods on " + name + " prior to initialization; " +
193 | "attempted to call method '" + options + "'");
194 | }
195 | if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
196 | return $.error("no such method '" + options + "' for " + name + " widget instance");
197 | }
198 | methodValue = instance[ options ].apply(instance, args);
199 | if (methodValue !== instance && methodValue !== undefined) {
200 | returnValue = methodValue && methodValue.jquery ?
201 | returnValue.pushStack(methodValue.get()) :
202 | methodValue;
203 | return false;
204 | }
205 | });
206 | } else {
207 | this.each(function () {
208 | var instance = $.data(this, fullName);
209 | if (instance) {
210 | instance.option(options || {})._init();
211 | } else {
212 | $.data(this, fullName, new object(options, this));
213 | }
214 | });
215 | }
216 |
217 | return returnValue;
218 | };
219 | };
220 |
221 | $.Widget = function (/* options, element */) {
222 | };
223 | $.Widget._childConstructors = [];
224 |
225 | $.Widget.prototype = {
226 | widgetName: "widget",
227 | widgetEventPrefix: "",
228 | defaultElement: "",
229 | options: {
230 | disabled: false,
231 |
232 | // callbacks
233 | create: null
234 | },
235 | _createWidget: function (options, element) {
236 | element = $(element || this.defaultElement || this)[ 0 ];
237 | this.element = $(element);
238 | this.uuid = uuid++;
239 | this.eventNamespace = "." + this.widgetName + this.uuid;
240 | this.options = $.widget.extend({},
241 | this.options,
242 | this._getCreateOptions(),
243 | options);
244 |
245 | this.bindings = $();
246 | this.hoverable = $();
247 | this.focusable = $();
248 |
249 | if (element !== this) {
250 | $.data(element, this.widgetFullName, this);
251 | this._on(true, this.element, {
252 | remove: function (event) {
253 | if (event.target === element) {
254 | this.destroy();
255 | }
256 | }
257 | });
258 | this.document = $(element.style ?
259 | // element within the document
260 | element.ownerDocument :
261 | // element is window or document
262 | element.document || element);
263 | this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
264 | }
265 |
266 | this._create();
267 | this._trigger("create", null, this._getCreateEventData());
268 | this._init();
269 | },
270 | _getCreateOptions: $.noop,
271 | _getCreateEventData: $.noop,
272 | _create: $.noop,
273 | _init: $.noop,
274 |
275 | destroy: function () {
276 | this._destroy();
277 | // we can probably remove the unbind calls in 2.0
278 | // all event bindings should go through this._on()
279 | this.element
280 | .unbind(this.eventNamespace)
281 | // 1.9 BC for #7810
282 | // TODO remove dual storage
283 | .removeData(this.widgetName)
284 | .removeData(this.widgetFullName)
285 | // support: jquery <1.6.3
286 | // http://bugs.jquery.com/ticket/9413
287 | .removeData($.camelCase(this.widgetFullName));
288 | this.widget()
289 | .unbind(this.eventNamespace)
290 | .removeAttr("aria-disabled")
291 | .removeClass(
292 | this.widgetFullName + "-disabled " +
293 | "ui-state-disabled");
294 |
295 | // clean up events and states
296 | this.bindings.unbind(this.eventNamespace);
297 | this.hoverable.removeClass("ui-state-hover");
298 | this.focusable.removeClass("ui-state-focus");
299 | },
300 | _destroy: $.noop,
301 |
302 | widget: function () {
303 | return this.element;
304 | },
305 |
306 | option: function (key, value) {
307 | var options = key,
308 | parts,
309 | curOption,
310 | i;
311 |
312 | if (arguments.length === 0) {
313 | // don't return a reference to the internal hash
314 | return $.widget.extend({}, this.options);
315 | }
316 |
317 | if (typeof key === "string") {
318 | // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
319 | options = {};
320 | parts = key.split(".");
321 | key = parts.shift();
322 | if (parts.length) {
323 | curOption = options[ key ] = $.widget.extend({}, this.options[ key ]);
324 | for (i = 0; i < parts.length - 1; i++) {
325 | curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
326 | curOption = curOption[ parts[ i ] ];
327 | }
328 | key = parts.pop();
329 | if (arguments.length === 1) {
330 | return curOption[ key ] === undefined ? null : curOption[ key ];
331 | }
332 | curOption[ key ] = value;
333 | } else {
334 | if (arguments.length === 1) {
335 | return this.options[ key ] === undefined ? null : this.options[ key ];
336 | }
337 | options[ key ] = value;
338 | }
339 | }
340 |
341 | this._setOptions(options);
342 |
343 | return this;
344 | },
345 | _setOptions: function (options) {
346 | var key;
347 |
348 | for (key in options) {
349 | this._setOption(key, options[ key ]);
350 | }
351 |
352 | return this;
353 | },
354 | _setOption: function (key, value) {
355 | this.options[ key ] = value;
356 |
357 | if (key === "disabled") {
358 | this.widget()
359 | .toggleClass(this.widgetFullName + "-disabled ui-state-disabled", !!value)
360 | .attr("aria-disabled", value);
361 | this.hoverable.removeClass("ui-state-hover");
362 | this.focusable.removeClass("ui-state-focus");
363 | }
364 |
365 | return this;
366 | },
367 |
368 | enable: function () {
369 | return this._setOption("disabled", false);
370 | },
371 | disable: function () {
372 | return this._setOption("disabled", true);
373 | },
374 |
375 | _on: function (suppressDisabledCheck, element, handlers) {
376 | var delegateElement,
377 | instance = this;
378 |
379 | // no suppressDisabledCheck flag, shuffle arguments
380 | if (typeof suppressDisabledCheck !== "boolean") {
381 | handlers = element;
382 | element = suppressDisabledCheck;
383 | suppressDisabledCheck = false;
384 | }
385 |
386 | // no element argument, shuffle and use this.element
387 | if (!handlers) {
388 | handlers = element;
389 | element = this.element;
390 | delegateElement = this.widget();
391 | } else {
392 | // accept selectors, DOM elements
393 | element = delegateElement = $(element);
394 | this.bindings = this.bindings.add(element);
395 | }
396 |
397 | $.each(handlers, function (event, handler) {
398 | function handlerProxy() {
399 | // allow widgets to customize the disabled handling
400 | // - disabled as an array instead of boolean
401 | // - disabled class as method for disabling individual parts
402 | if (!suppressDisabledCheck &&
403 | ( instance.options.disabled === true ||
404 | $(this).hasClass("ui-state-disabled") )) {
405 | return;
406 | }
407 | return ( typeof handler === "string" ? instance[ handler ] : handler )
408 | .apply(instance, arguments);
409 | }
410 |
411 | // copy the guid so direct unbinding works
412 | if (typeof handler !== "string") {
413 | handlerProxy.guid = handler.guid =
414 | handler.guid || handlerProxy.guid || $.guid++;
415 | }
416 |
417 | var match = event.match(/^(\w+)\s*(.*)$/),
418 | eventName = match[1] + instance.eventNamespace,
419 | selector = match[2];
420 | if (selector) {
421 | delegateElement.delegate(selector, eventName, handlerProxy);
422 | } else {
423 | element.bind(eventName, handlerProxy);
424 | }
425 | });
426 | },
427 |
428 | _off: function (element, eventName) {
429 | eventName = (eventName || "").split(" ").join(this.eventNamespace + " ") + this.eventNamespace;
430 | element.unbind(eventName).undelegate(eventName);
431 | },
432 |
433 | _delay: function (handler, delay) {
434 | function handlerProxy() {
435 | return ( typeof handler === "string" ? instance[ handler ] : handler )
436 | .apply(instance, arguments);
437 | }
438 |
439 | var instance = this;
440 | return setTimeout(handlerProxy, delay || 0);
441 | },
442 |
443 | _hoverable: function (element) {
444 | this.hoverable = this.hoverable.add(element);
445 | this._on(element, {
446 | mouseenter: function (event) {
447 | $(event.currentTarget).addClass("ui-state-hover");
448 | },
449 | mouseleave: function (event) {
450 | $(event.currentTarget).removeClass("ui-state-hover");
451 | }
452 | });
453 | },
454 |
455 | _focusable: function (element) {
456 | this.focusable = this.focusable.add(element);
457 | this._on(element, {
458 | focusin: function (event) {
459 | $(event.currentTarget).addClass("ui-state-focus");
460 | },
461 | focusout: function (event) {
462 | $(event.currentTarget).removeClass("ui-state-focus");
463 | }
464 | });
465 | },
466 |
467 | _trigger: function (type, event, data) {
468 | var prop, orig,
469 | callback = this.options[ type ];
470 |
471 | data = data || {};
472 | event = $.Event(event);
473 | event.type = ( type === this.widgetEventPrefix ?
474 | type :
475 | this.widgetEventPrefix + type ).toLowerCase();
476 | // the original event may come from any element
477 | // so we need to reset the target on the new event
478 | event.target = this.element[ 0 ];
479 |
480 | // copy original event properties over to the new event
481 | orig = event.originalEvent;
482 | if (orig) {
483 | for (prop in orig) {
484 | if (!( prop in event )) {
485 | event[ prop ] = orig[ prop ];
486 | }
487 | }
488 | }
489 |
490 | this.element.trigger(event, data);
491 | return !( $.isFunction(callback) &&
492 | callback.apply(this.element[0], [ event ].concat(data)) === false ||
493 | event.isDefaultPrevented() );
494 | }
495 | };
496 |
497 | $.each({ show: "fadeIn", hide: "fadeOut" }, function (method, defaultEffect) {
498 | $.Widget.prototype[ "_" + method ] = function (element, options, callback) {
499 | if (typeof options === "string") {
500 | options = { effect: options };
501 | }
502 | var hasOptions,
503 | effectName = !options ?
504 | method :
505 | options === true || typeof options === "number" ?
506 | defaultEffect :
507 | options.effect || defaultEffect;
508 | options = options || {};
509 | if (typeof options === "number") {
510 | options = { duration: options };
511 | }
512 | hasOptions = !$.isEmptyObject(options);
513 | options.complete = callback;
514 | if (options.delay) {
515 | element.delay(options.delay);
516 | }
517 | if (hasOptions && $.effects && $.effects.effect[ effectName ]) {
518 | element[ method ](options);
519 | } else if (effectName !== method && element[ effectName ]) {
520 | element[ effectName ](options.duration, options.easing, callback);
521 | } else {
522 | element.queue(function (next) {
523 | $(this)[ method ]();
524 | if (callback) {
525 | callback.call(element[ 0 ]);
526 | }
527 | next();
528 | });
529 | }
530 | };
531 | });
532 |
533 | }));
534 |
--------------------------------------------------------------------------------