h2ocube_rails_assets 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +45 -0
  5. data/Rakefile +1 -0
  6. data/h2ocube_rails_assets.gemspec +20 -0
  7. data/lib/h2ocube_rails_assets.rb +8 -0
  8. data/vendor/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  9. data/vendor/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  10. data/vendor/assets/images/jquery.mobile/ajax-loader.gif +0 -0
  11. data/vendor/assets/images/jquery.mobile/icons-18-black.png +0 -0
  12. data/vendor/assets/images/jquery.mobile/icons-18-white.png +0 -0
  13. data/vendor/assets/images/jquery.mobile/icons-36-black.png +0 -0
  14. data/vendor/assets/images/jquery.mobile/icons-36-white.png +0 -0
  15. data/vendor/assets/images/jquery.ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/vendor/assets/images/jquery.ui/ui-bg_flat_75_ffffff_40x100.png +0 -0
  17. data/vendor/assets/images/jquery.ui/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  18. data/vendor/assets/images/jquery.ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
  19. data/vendor/assets/images/jquery.ui/ui-bg_glass_75_dadada_1x400.png +0 -0
  20. data/vendor/assets/images/jquery.ui/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  21. data/vendor/assets/images/jquery.ui/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  22. data/vendor/assets/images/jquery.ui/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  23. data/vendor/assets/images/jquery.ui/ui-icons_222222_256x240.png +0 -0
  24. data/vendor/assets/images/jquery.ui/ui-icons_2e83ff_256x240.png +0 -0
  25. data/vendor/assets/images/jquery.ui/ui-icons_454545_256x240.png +0 -0
  26. data/vendor/assets/images/jquery.ui/ui-icons_888888_256x240.png +0 -0
  27. data/vendor/assets/images/jquery.ui/ui-icons_cd0a0a_256x240.png +0 -0
  28. data/vendor/assets/javascripts/bootstrap.js +2025 -0
  29. data/vendor/assets/javascripts/jquery.cookie.js +72 -0
  30. data/vendor/assets/javascripts/jquery.fileupload.js +1111 -0
  31. data/vendor/assets/javascripts/jquery.js +9472 -0
  32. data/vendor/assets/javascripts/jquery.mobile.js +9162 -0
  33. data/vendor/assets/javascripts/jquery.pnotify.js +912 -0
  34. data/vendor/assets/javascripts/jquery.timeago.coffee +152 -0
  35. data/vendor/assets/javascripts/jquery.ui.js +14912 -0
  36. data/vendor/assets/javascripts/jquery.validate.js +1257 -0
  37. data/vendor/assets/javascripts/sammy.js +2106 -0
  38. data/vendor/assets/javascripts/underscore.js +1200 -0
  39. data/vendor/assets/stylesheets/bootstrap.responsive.scss +1088 -0
  40. data/vendor/assets/stylesheets/bootstrap.scss +5893 -0
  41. data/vendor/assets/stylesheets/jquery.mobile.scss +2332 -0
  42. data/vendor/assets/stylesheets/jquery.pnotify.css +83 -0
  43. data/vendor/assets/stylesheets/jquery.ui.scss +474 -0
  44. metadata +200 -0
@@ -0,0 +1,72 @@
1
+ /*!
2
+ * jQuery Cookie Plugin v1.3
3
+ * https://github.com/carhartl/jquery-cookie
4
+ *
5
+ * Copyright 2011, Klaus Hartl
6
+ * Dual licensed under the MIT or GPL Version 2 licenses.
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.opensource.org/licenses/GPL-2.0
9
+ */
10
+ (function ($, document, undefined) {
11
+
12
+ var pluses = /\+/g;
13
+
14
+ function raw(s) {
15
+ return s;
16
+ }
17
+
18
+ function decoded(s) {
19
+ return decodeURIComponent(s.replace(pluses, ' '));
20
+ }
21
+
22
+ var config = $.cookie = function (key, value, options) {
23
+
24
+ // write
25
+ if (value !== undefined) {
26
+ options = $.extend({}, config.defaults, options);
27
+
28
+ if (value === null) {
29
+ options.expires = -1;
30
+ }
31
+
32
+ if (typeof options.expires === 'number') {
33
+ var days = options.expires, t = options.expires = new Date();
34
+ t.setDate(t.getDate() + days);
35
+ }
36
+
37
+ value = config.json ? JSON.stringify(value) : String(value);
38
+
39
+ return (document.cookie = [
40
+ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
41
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
42
+ options.path ? '; path=' + options.path : '',
43
+ options.domain ? '; domain=' + options.domain : '',
44
+ options.secure ? '; secure' : ''
45
+ ].join(''));
46
+ }
47
+
48
+ // read
49
+ var decode = config.raw ? raw : decoded;
50
+ var cookies = document.cookie.split('; ');
51
+ for (var i = 0, l = cookies.length; i < l; i++) {
52
+ var parts = cookies[i].split('=');
53
+ if (decode(parts.shift()) === key) {
54
+ var cookie = decode(parts.join('='));
55
+ return config.json ? JSON.parse(cookie) : cookie;
56
+ }
57
+ }
58
+
59
+ return null;
60
+ };
61
+
62
+ config.defaults = {};
63
+
64
+ $.removeCookie = function (key, options) {
65
+ if ($.cookie(key) !== null) {
66
+ $.cookie(key, null, options);
67
+ return true;
68
+ }
69
+ return false;
70
+ };
71
+
72
+ })(jQuery, document);
@@ -0,0 +1,1111 @@
1
+ /*
2
+ * jQuery File Upload Plugin 5.19.4
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2010, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true, regexp: true */
13
+ /*global define, window, document, File, Blob, FormData, location */
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.ui.widget'
22
+ ], factory);
23
+ } else {
24
+ // Browser globals:
25
+ factory(window.jQuery);
26
+ }
27
+ }(function ($) {
28
+ 'use strict';
29
+
30
+ // The FileReader API is not actually used, but works as feature detection,
31
+ // as e.g. Safari supports XHR file uploads via the FormData API,
32
+ // but not non-multipart XHR file uploads:
33
+ $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
34
+ $.support.xhrFormDataFileUpload = !!window.FormData;
35
+
36
+ // The fileupload widget listens for change events on file input fields defined
37
+ // via fileInput setting and paste or drop events of the given dropZone.
38
+ // In addition to the default jQuery Widget methods, the fileupload widget
39
+ // exposes the "add" and "send" methods, to add or directly send files using
40
+ // the fileupload API.
41
+ // By default, files added via file input selection, paste, drag & drop or
42
+ // "add" method are uploaded immediately, but it is possible to override
43
+ // the "add" callback option to queue file uploads.
44
+ $.widget('blueimp.fileupload', {
45
+
46
+ options: {
47
+ // The drop target element(s), by the default the complete document.
48
+ // Set to null to disable drag & drop support:
49
+ dropZone: $(document),
50
+ // The paste target element(s), by the default the complete document.
51
+ // Set to null to disable paste support:
52
+ pasteZone: $(document),
53
+ // The file input field(s), that are listened to for change events.
54
+ // If undefined, it is set to the file input fields inside
55
+ // of the widget element on plugin initialization.
56
+ // Set to null to disable the change listener.
57
+ fileInput: undefined,
58
+ // By default, the file input field is replaced with a clone after
59
+ // each input field change event. This is required for iframe transport
60
+ // queues and allows change events to be fired for the same file
61
+ // selection, but can be disabled by setting the following option to false:
62
+ replaceFileInput: true,
63
+ // The parameter name for the file form data (the request argument name).
64
+ // If undefined or empty, the name property of the file input field is
65
+ // used, or "files[]" if the file input name property is also empty,
66
+ // can be a string or an array of strings:
67
+ paramName: undefined,
68
+ // By default, each file of a selection is uploaded using an individual
69
+ // request for XHR type uploads. Set to false to upload file
70
+ // selections in one request each:
71
+ singleFileUploads: true,
72
+ // To limit the number of files uploaded with one XHR request,
73
+ // set the following option to an integer greater than 0:
74
+ limitMultiFileUploads: undefined,
75
+ // Set the following option to true to issue all file upload requests
76
+ // in a sequential order:
77
+ sequentialUploads: false,
78
+ // To limit the number of concurrent uploads,
79
+ // set the following option to an integer greater than 0:
80
+ limitConcurrentUploads: undefined,
81
+ // Set the following option to true to force iframe transport uploads:
82
+ forceIframeTransport: false,
83
+ // Set the following option to the location of a redirect url on the
84
+ // origin server, for cross-domain iframe transport uploads:
85
+ redirect: undefined,
86
+ // The parameter name for the redirect url, sent as part of the form
87
+ // data and set to 'redirect' if this option is empty:
88
+ redirectParamName: undefined,
89
+ // Set the following option to the location of a postMessage window,
90
+ // to enable postMessage transport uploads:
91
+ postMessage: undefined,
92
+ // By default, XHR file uploads are sent as multipart/form-data.
93
+ // The iframe transport is always using multipart/form-data.
94
+ // Set to false to enable non-multipart XHR uploads:
95
+ multipart: true,
96
+ // To upload large files in smaller chunks, set the following option
97
+ // to a preferred maximum chunk size. If set to 0, null or undefined,
98
+ // or the browser does not support the required Blob API, files will
99
+ // be uploaded as a whole.
100
+ maxChunkSize: undefined,
101
+ // When a non-multipart upload or a chunked multipart upload has been
102
+ // aborted, this option can be used to resume the upload by setting
103
+ // it to the size of the already uploaded bytes. This option is most
104
+ // useful when modifying the options object inside of the "add" or
105
+ // "send" callbacks, as the options are cloned for each file upload.
106
+ uploadedBytes: undefined,
107
+ // By default, failed (abort or error) file uploads are removed from the
108
+ // global progress calculation. Set the following option to false to
109
+ // prevent recalculating the global progress data:
110
+ recalculateProgress: true,
111
+ // Interval in milliseconds to calculate and trigger progress events:
112
+ progressInterval: 100,
113
+ // Interval in milliseconds to calculate progress bitrate:
114
+ bitrateInterval: 500,
115
+
116
+ // Additional form data to be sent along with the file uploads can be set
117
+ // using this option, which accepts an array of objects with name and
118
+ // value properties, a function returning such an array, a FormData
119
+ // object (for XHR file uploads), or a simple object.
120
+ // The form of the first fileInput is given as parameter to the function:
121
+ formData: function (form) {
122
+ return form.serializeArray();
123
+ },
124
+
125
+ // The add callback is invoked as soon as files are added to the fileupload
126
+ // widget (via file input selection, drag & drop, paste or add API call).
127
+ // If the singleFileUploads option is enabled, this callback will be
128
+ // called once for each file in the selection for XHR file uplaods, else
129
+ // once for each file selection.
130
+ // The upload starts when the submit method is invoked on the data parameter.
131
+ // The data object contains a files property holding the added files
132
+ // and allows to override plugin options as well as define ajax settings.
133
+ // Listeners for this callback can also be bound the following way:
134
+ // .bind('fileuploadadd', func);
135
+ // data.submit() returns a Promise object and allows to attach additional
136
+ // handlers using jQuery's Deferred callbacks:
137
+ // data.submit().done(func).fail(func).always(func);
138
+ add: function (e, data) {
139
+ data.submit();
140
+ },
141
+
142
+ // Other callbacks:
143
+ // Callback for the submit event of each file upload:
144
+ // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
145
+ // Callback for the start of each file upload request:
146
+ // send: function (e, data) {}, // .bind('fileuploadsend', func);
147
+ // Callback for successful uploads:
148
+ // done: function (e, data) {}, // .bind('fileuploaddone', func);
149
+ // Callback for failed (abort or error) uploads:
150
+ // fail: function (e, data) {}, // .bind('fileuploadfail', func);
151
+ // Callback for completed (success, abort or error) requests:
152
+ // always: function (e, data) {}, // .bind('fileuploadalways', func);
153
+ // Callback for upload progress events:
154
+ // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
155
+ // Callback for global upload progress events:
156
+ // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
157
+ // Callback for uploads start, equivalent to the global ajaxStart event:
158
+ // start: function (e) {}, // .bind('fileuploadstart', func);
159
+ // Callback for uploads stop, equivalent to the global ajaxStop event:
160
+ // stop: function (e) {}, // .bind('fileuploadstop', func);
161
+ // Callback for change events of the fileInput(s):
162
+ // change: function (e, data) {}, // .bind('fileuploadchange', func);
163
+ // Callback for paste events to the pasteZone(s):
164
+ // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
165
+ // Callback for drop events of the dropZone(s):
166
+ // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
167
+ // Callback for dragover events of the dropZone(s):
168
+ // dragover: function (e) {}, // .bind('fileuploaddragover', func);
169
+
170
+ // The plugin options are used as settings object for the ajax calls.
171
+ // The following are jQuery ajax settings required for the file uploads:
172
+ processData: false,
173
+ contentType: false,
174
+ cache: false
175
+ },
176
+
177
+ // A list of options that require a refresh after assigning a new value:
178
+ _refreshOptionsList: [
179
+ 'fileInput',
180
+ 'dropZone',
181
+ 'pasteZone',
182
+ 'multipart',
183
+ 'forceIframeTransport'
184
+ ],
185
+
186
+ _BitrateTimer: function () {
187
+ this.timestamp = +(new Date());
188
+ this.loaded = 0;
189
+ this.bitrate = 0;
190
+ this.getBitrate = function (now, loaded, interval) {
191
+ var timeDiff = now - this.timestamp;
192
+ if (!this.bitrate || !interval || timeDiff > interval) {
193
+ this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
194
+ this.loaded = loaded;
195
+ this.timestamp = now;
196
+ }
197
+ return this.bitrate;
198
+ };
199
+ },
200
+
201
+ _isXHRUpload: function (options) {
202
+ return !options.forceIframeTransport &&
203
+ ((!options.multipart && $.support.xhrFileUpload) ||
204
+ $.support.xhrFormDataFileUpload);
205
+ },
206
+
207
+ _getFormData: function (options) {
208
+ var formData;
209
+ if (typeof options.formData === 'function') {
210
+ return options.formData(options.form);
211
+ }
212
+ if ($.isArray(options.formData)) {
213
+ return options.formData;
214
+ }
215
+ if (options.formData) {
216
+ formData = [];
217
+ $.each(options.formData, function (name, value) {
218
+ formData.push({name: name, value: value});
219
+ });
220
+ return formData;
221
+ }
222
+ return [];
223
+ },
224
+
225
+ _getTotal: function (files) {
226
+ var total = 0;
227
+ $.each(files, function (index, file) {
228
+ total += file.size || 1;
229
+ });
230
+ return total;
231
+ },
232
+
233
+ _onProgress: function (e, data) {
234
+ if (e.lengthComputable) {
235
+ var now = +(new Date()),
236
+ total,
237
+ loaded;
238
+ if (data._time && data.progressInterval &&
239
+ (now - data._time < data.progressInterval) &&
240
+ e.loaded !== e.total) {
241
+ return;
242
+ }
243
+ data._time = now;
244
+ total = data.total || this._getTotal(data.files);
245
+ loaded = parseInt(
246
+ e.loaded / e.total * (data.chunkSize || total),
247
+ 10
248
+ ) + (data.uploadedBytes || 0);
249
+ this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
250
+ data.lengthComputable = true;
251
+ data.loaded = loaded;
252
+ data.total = total;
253
+ data.bitrate = data._bitrateTimer.getBitrate(
254
+ now,
255
+ loaded,
256
+ data.bitrateInterval
257
+ );
258
+ // Trigger a custom progress event with a total data property set
259
+ // to the file size(s) of the current upload and a loaded data
260
+ // property calculated accordingly:
261
+ this._trigger('progress', e, data);
262
+ // Trigger a global progress event for all current file uploads,
263
+ // including ajax calls queued for sequential file uploads:
264
+ this._trigger('progressall', e, {
265
+ lengthComputable: true,
266
+ loaded: this._loaded,
267
+ total: this._total,
268
+ bitrate: this._bitrateTimer.getBitrate(
269
+ now,
270
+ this._loaded,
271
+ data.bitrateInterval
272
+ )
273
+ });
274
+ }
275
+ },
276
+
277
+ _initProgressListener: function (options) {
278
+ var that = this,
279
+ xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
280
+ // Accesss to the native XHR object is required to add event listeners
281
+ // for the upload progress event:
282
+ if (xhr.upload) {
283
+ $(xhr.upload).bind('progress', function (e) {
284
+ var oe = e.originalEvent;
285
+ // Make sure the progress event properties get copied over:
286
+ e.lengthComputable = oe.lengthComputable;
287
+ e.loaded = oe.loaded;
288
+ e.total = oe.total;
289
+ that._onProgress(e, options);
290
+ });
291
+ options.xhr = function () {
292
+ return xhr;
293
+ };
294
+ }
295
+ },
296
+
297
+ _initXHRData: function (options) {
298
+ var formData,
299
+ file = options.files[0],
300
+ // Ignore non-multipart setting if not supported:
301
+ multipart = options.multipart || !$.support.xhrFileUpload,
302
+ paramName = options.paramName[0];
303
+ options.headers = options.headers || {};
304
+ if (options.contentRange) {
305
+ options.headers['Content-Range'] = options.contentRange;
306
+ }
307
+ if (!multipart) {
308
+ options.headers['Content-Disposition'] = 'attachment; filename="' +
309
+ encodeURI(file.name) + '"';
310
+ options.contentType = file.type;
311
+ options.data = options.blob || file;
312
+ } else if ($.support.xhrFormDataFileUpload) {
313
+ if (options.postMessage) {
314
+ // window.postMessage does not allow sending FormData
315
+ // objects, so we just add the File/Blob objects to
316
+ // the formData array and let the postMessage window
317
+ // create the FormData object out of this array:
318
+ formData = this._getFormData(options);
319
+ if (options.blob) {
320
+ formData.push({
321
+ name: paramName,
322
+ value: options.blob
323
+ });
324
+ } else {
325
+ $.each(options.files, function (index, file) {
326
+ formData.push({
327
+ name: options.paramName[index] || paramName,
328
+ value: file
329
+ });
330
+ });
331
+ }
332
+ } else {
333
+ if (options.formData instanceof FormData) {
334
+ formData = options.formData;
335
+ } else {
336
+ formData = new FormData();
337
+ $.each(this._getFormData(options), function (index, field) {
338
+ formData.append(field.name, field.value);
339
+ });
340
+ }
341
+ if (options.blob) {
342
+ options.headers['Content-Disposition'] = 'attachment; filename="' +
343
+ encodeURI(file.name) + '"';
344
+ options.headers['Content-Description'] = encodeURI(file.type);
345
+ formData.append(paramName, options.blob, file.name);
346
+ } else {
347
+ $.each(options.files, function (index, file) {
348
+ // Files are also Blob instances, but some browsers
349
+ // (Firefox 3.6) support the File API but not Blobs.
350
+ // This check allows the tests to run with
351
+ // dummy objects:
352
+ if ((window.Blob && file instanceof Blob) ||
353
+ (window.File && file instanceof File)) {
354
+ formData.append(
355
+ options.paramName[index] || paramName,
356
+ file,
357
+ file.name
358
+ );
359
+ }
360
+ });
361
+ }
362
+ }
363
+ options.data = formData;
364
+ }
365
+ // Blob reference is not needed anymore, free memory:
366
+ options.blob = null;
367
+ },
368
+
369
+ _initIframeSettings: function (options) {
370
+ // Setting the dataType to iframe enables the iframe transport:
371
+ options.dataType = 'iframe ' + (options.dataType || '');
372
+ // The iframe transport accepts a serialized array as form data:
373
+ options.formData = this._getFormData(options);
374
+ // Add redirect url to form data on cross-domain uploads:
375
+ if (options.redirect && $('<a></a>').prop('href', options.url)
376
+ .prop('host') !== location.host) {
377
+ options.formData.push({
378
+ name: options.redirectParamName || 'redirect',
379
+ value: options.redirect
380
+ });
381
+ }
382
+ },
383
+
384
+ _initDataSettings: function (options) {
385
+ if (this._isXHRUpload(options)) {
386
+ if (!this._chunkedUpload(options, true)) {
387
+ if (!options.data) {
388
+ this._initXHRData(options);
389
+ }
390
+ this._initProgressListener(options);
391
+ }
392
+ if (options.postMessage) {
393
+ // Setting the dataType to postmessage enables the
394
+ // postMessage transport:
395
+ options.dataType = 'postmessage ' + (options.dataType || '');
396
+ }
397
+ } else {
398
+ this._initIframeSettings(options, 'iframe');
399
+ }
400
+ },
401
+
402
+ _getParamName: function (options) {
403
+ var fileInput = $(options.fileInput),
404
+ paramName = options.paramName;
405
+ if (!paramName) {
406
+ paramName = [];
407
+ fileInput.each(function () {
408
+ var input = $(this),
409
+ name = input.prop('name') || 'files[]',
410
+ i = (input.prop('files') || [1]).length;
411
+ while (i) {
412
+ paramName.push(name);
413
+ i -= 1;
414
+ }
415
+ });
416
+ if (!paramName.length) {
417
+ paramName = [fileInput.prop('name') || 'files[]'];
418
+ }
419
+ } else if (!$.isArray(paramName)) {
420
+ paramName = [paramName];
421
+ }
422
+ return paramName;
423
+ },
424
+
425
+ _initFormSettings: function (options) {
426
+ // Retrieve missing options from the input field and the
427
+ // associated form, if available:
428
+ if (!options.form || !options.form.length) {
429
+ options.form = $(options.fileInput.prop('form'));
430
+ // If the given file input doesn't have an associated form,
431
+ // use the default widget file input's form:
432
+ if (!options.form.length) {
433
+ options.form = $(this.options.fileInput.prop('form'));
434
+ }
435
+ }
436
+ options.paramName = this._getParamName(options);
437
+ if (!options.url) {
438
+ options.url = options.form.prop('action') || location.href;
439
+ }
440
+ // The HTTP request method must be "POST" or "PUT":
441
+ options.type = (options.type || options.form.prop('method') || '')
442
+ .toUpperCase();
443
+ if (options.type !== 'POST' && options.type !== 'PUT') {
444
+ options.type = 'POST';
445
+ }
446
+ if (!options.formAcceptCharset) {
447
+ options.formAcceptCharset = options.form.attr('accept-charset');
448
+ }
449
+ },
450
+
451
+ _getAJAXSettings: function (data) {
452
+ var options = $.extend({}, this.options, data);
453
+ this._initFormSettings(options);
454
+ this._initDataSettings(options);
455
+ return options;
456
+ },
457
+
458
+ // Maps jqXHR callbacks to the equivalent
459
+ // methods of the given Promise object:
460
+ _enhancePromise: function (promise) {
461
+ promise.success = promise.done;
462
+ promise.error = promise.fail;
463
+ promise.complete = promise.always;
464
+ return promise;
465
+ },
466
+
467
+ // Creates and returns a Promise object enhanced with
468
+ // the jqXHR methods abort, success, error and complete:
469
+ _getXHRPromise: function (resolveOrReject, context, args) {
470
+ var dfd = $.Deferred(),
471
+ promise = dfd.promise();
472
+ context = context || this.options.context || promise;
473
+ if (resolveOrReject === true) {
474
+ dfd.resolveWith(context, args);
475
+ } else if (resolveOrReject === false) {
476
+ dfd.rejectWith(context, args);
477
+ }
478
+ promise.abort = dfd.promise;
479
+ return this._enhancePromise(promise);
480
+ },
481
+
482
+ // Parses the Range header from the server response
483
+ // and returns the uploaded bytes:
484
+ _getUploadedBytes: function (jqXHR) {
485
+ var range = jqXHR.getResponseHeader('Range'),
486
+ parts = range && range.split('-'),
487
+ upperBytesPos = parts && parts.length > 1 &&
488
+ parseInt(parts[1], 10);
489
+ return upperBytesPos && upperBytesPos + 1;
490
+ },
491
+
492
+ // Uploads a file in multiple, sequential requests
493
+ // by splitting the file up in multiple blob chunks.
494
+ // If the second parameter is true, only tests if the file
495
+ // should be uploaded in chunks, but does not invoke any
496
+ // upload requests:
497
+ _chunkedUpload: function (options, testOnly) {
498
+ var that = this,
499
+ file = options.files[0],
500
+ fs = file.size,
501
+ ub = options.uploadedBytes = options.uploadedBytes || 0,
502
+ mcs = options.maxChunkSize || fs,
503
+ slice = file.slice || file.webkitSlice || file.mozSlice,
504
+ dfd = $.Deferred(),
505
+ promise = dfd.promise(),
506
+ jqXHR,
507
+ upload;
508
+ if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
509
+ options.data) {
510
+ return false;
511
+ }
512
+ if (testOnly) {
513
+ return true;
514
+ }
515
+ if (ub >= fs) {
516
+ file.error = 'Uploaded bytes exceed file size';
517
+ return this._getXHRPromise(
518
+ false,
519
+ options.context,
520
+ [null, 'error', file.error]
521
+ );
522
+ }
523
+ // The chunk upload method:
524
+ upload = function (i) {
525
+ // Clone the options object for each chunk upload:
526
+ var o = $.extend({}, options);
527
+ o.blob = slice.call(
528
+ file,
529
+ ub,
530
+ ub + mcs
531
+ );
532
+ // Store the current chunk size, as the blob itself
533
+ // will be dereferenced after data processing:
534
+ o.chunkSize = o.blob.size;
535
+ // Expose the chunk bytes position range:
536
+ o.contentRange = 'bytes ' + ub + '-' +
537
+ (ub + o.chunkSize - 1) + '/' + fs;
538
+ // Process the upload data (the blob and potential form data):
539
+ that._initXHRData(o);
540
+ // Add progress listeners for this chunk upload:
541
+ that._initProgressListener(o);
542
+ jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
543
+ .done(function (result, textStatus, jqXHR) {
544
+ ub = that._getUploadedBytes(jqXHR) ||
545
+ (ub + o.chunkSize);
546
+ // Create a progress event if upload is done and
547
+ // no progress event has been invoked for this chunk:
548
+ if (!o.loaded) {
549
+ that._onProgress($.Event('progress', {
550
+ lengthComputable: true,
551
+ loaded: ub - o.uploadedBytes,
552
+ total: ub - o.uploadedBytes
553
+ }), o);
554
+ }
555
+ options.uploadedBytes = o.uploadedBytes = ub;
556
+ if (ub < fs) {
557
+ // File upload not yet complete,
558
+ // continue with the next chunk:
559
+ upload();
560
+ } else {
561
+ dfd.resolveWith(
562
+ o.context,
563
+ [result, textStatus, jqXHR]
564
+ );
565
+ }
566
+ })
567
+ .fail(function (jqXHR, textStatus, errorThrown) {
568
+ dfd.rejectWith(
569
+ o.context,
570
+ [jqXHR, textStatus, errorThrown]
571
+ );
572
+ });
573
+ };
574
+ this._enhancePromise(promise);
575
+ promise.abort = function () {
576
+ return jqXHR.abort();
577
+ };
578
+ upload();
579
+ return promise;
580
+ },
581
+
582
+ _beforeSend: function (e, data) {
583
+ if (this._active === 0) {
584
+ // the start callback is triggered when an upload starts
585
+ // and no other uploads are currently running,
586
+ // equivalent to the global ajaxStart event:
587
+ this._trigger('start');
588
+ // Set timer for global bitrate progress calculation:
589
+ this._bitrateTimer = new this._BitrateTimer();
590
+ }
591
+ this._active += 1;
592
+ // Initialize the global progress values:
593
+ this._loaded += data.uploadedBytes || 0;
594
+ this._total += this._getTotal(data.files);
595
+ },
596
+
597
+ _onDone: function (result, textStatus, jqXHR, options) {
598
+ if (!this._isXHRUpload(options)) {
599
+ // Create a progress event for each iframe load:
600
+ this._onProgress($.Event('progress', {
601
+ lengthComputable: true,
602
+ loaded: 1,
603
+ total: 1
604
+ }), options);
605
+ }
606
+ options.result = result;
607
+ options.textStatus = textStatus;
608
+ options.jqXHR = jqXHR;
609
+ this._trigger('done', null, options);
610
+ },
611
+
612
+ _onFail: function (jqXHR, textStatus, errorThrown, options) {
613
+ options.jqXHR = jqXHR;
614
+ options.textStatus = textStatus;
615
+ options.errorThrown = errorThrown;
616
+ this._trigger('fail', null, options);
617
+ if (options.recalculateProgress) {
618
+ // Remove the failed (error or abort) file upload from
619
+ // the global progress calculation:
620
+ this._loaded -= options.loaded || options.uploadedBytes || 0;
621
+ this._total -= options.total || this._getTotal(options.files);
622
+ }
623
+ },
624
+
625
+ _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
626
+ this._active -= 1;
627
+ options.textStatus = textStatus;
628
+ if (jqXHRorError && jqXHRorError.always) {
629
+ options.jqXHR = jqXHRorError;
630
+ options.result = jqXHRorResult;
631
+ } else {
632
+ options.jqXHR = jqXHRorResult;
633
+ options.errorThrown = jqXHRorError;
634
+ }
635
+ this._trigger('always', null, options);
636
+ if (this._active === 0) {
637
+ // The stop callback is triggered when all uploads have
638
+ // been completed, equivalent to the global ajaxStop event:
639
+ this._trigger('stop');
640
+ // Reset the global progress values:
641
+ this._loaded = this._total = 0;
642
+ this._bitrateTimer = null;
643
+ }
644
+ },
645
+
646
+ _onSend: function (e, data) {
647
+ var that = this,
648
+ jqXHR,
649
+ aborted,
650
+ slot,
651
+ pipe,
652
+ options = that._getAJAXSettings(data),
653
+ send = function () {
654
+ that._sending += 1;
655
+ // Set timer for bitrate progress calculation:
656
+ options._bitrateTimer = new that._BitrateTimer();
657
+ jqXHR = jqXHR || (
658
+ ((aborted || that._trigger('send', e, options) === false) &&
659
+ that._getXHRPromise(false, options.context, aborted)) ||
660
+ that._chunkedUpload(options) || $.ajax(options)
661
+ ).done(function (result, textStatus, jqXHR) {
662
+ that._onDone(result, textStatus, jqXHR, options);
663
+ }).fail(function (jqXHR, textStatus, errorThrown) {
664
+ that._onFail(jqXHR, textStatus, errorThrown, options);
665
+ }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
666
+ that._sending -= 1;
667
+ that._onAlways(
668
+ jqXHRorResult,
669
+ textStatus,
670
+ jqXHRorError,
671
+ options
672
+ );
673
+ if (options.limitConcurrentUploads &&
674
+ options.limitConcurrentUploads > that._sending) {
675
+ // Start the next queued upload,
676
+ // that has not been aborted:
677
+ var nextSlot = that._slots.shift(),
678
+ isPending;
679
+ while (nextSlot) {
680
+ // jQuery 1.6 doesn't provide .state(),
681
+ // while jQuery 1.8+ removed .isRejected():
682
+ isPending = nextSlot.state ?
683
+ nextSlot.state() === 'pending' :
684
+ !nextSlot.isRejected();
685
+ if (isPending) {
686
+ nextSlot.resolve();
687
+ break;
688
+ }
689
+ nextSlot = that._slots.shift();
690
+ }
691
+ }
692
+ });
693
+ return jqXHR;
694
+ };
695
+ this._beforeSend(e, options);
696
+ if (this.options.sequentialUploads ||
697
+ (this.options.limitConcurrentUploads &&
698
+ this.options.limitConcurrentUploads <= this._sending)) {
699
+ if (this.options.limitConcurrentUploads > 1) {
700
+ slot = $.Deferred();
701
+ this._slots.push(slot);
702
+ pipe = slot.pipe(send);
703
+ } else {
704
+ pipe = (this._sequence = this._sequence.pipe(send, send));
705
+ }
706
+ // Return the piped Promise object, enhanced with an abort method,
707
+ // which is delegated to the jqXHR object of the current upload,
708
+ // and jqXHR callbacks mapped to the equivalent Promise methods:
709
+ pipe.abort = function () {
710
+ aborted = [undefined, 'abort', 'abort'];
711
+ if (!jqXHR) {
712
+ if (slot) {
713
+ slot.rejectWith(options.context, aborted);
714
+ }
715
+ return send();
716
+ }
717
+ return jqXHR.abort();
718
+ };
719
+ return this._enhancePromise(pipe);
720
+ }
721
+ return send();
722
+ },
723
+
724
+ _onAdd: function (e, data) {
725
+ var that = this,
726
+ result = true,
727
+ options = $.extend({}, this.options, data),
728
+ limit = options.limitMultiFileUploads,
729
+ paramName = this._getParamName(options),
730
+ paramNameSet,
731
+ paramNameSlice,
732
+ fileSet,
733
+ i;
734
+ if (!(options.singleFileUploads || limit) ||
735
+ !this._isXHRUpload(options)) {
736
+ fileSet = [data.files];
737
+ paramNameSet = [paramName];
738
+ } else if (!options.singleFileUploads && limit) {
739
+ fileSet = [];
740
+ paramNameSet = [];
741
+ for (i = 0; i < data.files.length; i += limit) {
742
+ fileSet.push(data.files.slice(i, i + limit));
743
+ paramNameSlice = paramName.slice(i, i + limit);
744
+ if (!paramNameSlice.length) {
745
+ paramNameSlice = paramName;
746
+ }
747
+ paramNameSet.push(paramNameSlice);
748
+ }
749
+ } else {
750
+ paramNameSet = paramName;
751
+ }
752
+ data.originalFiles = data.files;
753
+ $.each(fileSet || data.files, function (index, element) {
754
+ var newData = $.extend({}, data);
755
+ newData.files = fileSet ? element : [element];
756
+ newData.paramName = paramNameSet[index];
757
+ newData.submit = function () {
758
+ newData.jqXHR = this.jqXHR =
759
+ (that._trigger('submit', e, this) !== false) &&
760
+ that._onSend(e, this);
761
+ return this.jqXHR;
762
+ };
763
+ result = that._trigger('add', e, newData);
764
+ return result;
765
+ });
766
+ return result;
767
+ },
768
+
769
+ _replaceFileInput: function (input) {
770
+ var inputClone = input.clone(true);
771
+ $('<form></form>').append(inputClone)[0].reset();
772
+ // Detaching allows to insert the fileInput on another form
773
+ // without loosing the file input value:
774
+ input.after(inputClone).detach();
775
+ // Avoid memory leaks with the detached file input:
776
+ $.cleanData(input.unbind('remove'));
777
+ // Replace the original file input element in the fileInput
778
+ // elements set with the clone, which has been copied including
779
+ // event handlers:
780
+ this.options.fileInput = this.options.fileInput.map(function (i, el) {
781
+ if (el === input[0]) {
782
+ return inputClone[0];
783
+ }
784
+ return el;
785
+ });
786
+ // If the widget has been initialized on the file input itself,
787
+ // override this.element with the file input clone:
788
+ if (input[0] === this.element[0]) {
789
+ this.element = inputClone;
790
+ }
791
+ },
792
+
793
+ _handleFileTreeEntry: function (entry, path) {
794
+ var that = this,
795
+ dfd = $.Deferred(),
796
+ errorHandler = function (e) {
797
+ if (e && !e.entry) {
798
+ e.entry = entry;
799
+ }
800
+ // Since $.when returns immediately if one
801
+ // Deferred is rejected, we use resolve instead.
802
+ // This allows valid files and invalid items
803
+ // to be returned together in one set:
804
+ dfd.resolve([e]);
805
+ },
806
+ dirReader;
807
+ path = path || '';
808
+ if (entry.isFile) {
809
+ if (entry._file) {
810
+ // Workaround for Chrome bug #149735
811
+ entry._file.relativePath = path;
812
+ dfd.resolve(entry._file);
813
+ } else {
814
+ entry.file(function (file) {
815
+ file.relativePath = path;
816
+ dfd.resolve(file);
817
+ }, errorHandler);
818
+ }
819
+ } else if (entry.isDirectory) {
820
+ dirReader = entry.createReader();
821
+ dirReader.readEntries(function (entries) {
822
+ that._handleFileTreeEntries(
823
+ entries,
824
+ path + entry.name + '/'
825
+ ).done(function (files) {
826
+ dfd.resolve(files);
827
+ }).fail(errorHandler);
828
+ }, errorHandler);
829
+ } else {
830
+ // Return an empy list for file system items
831
+ // other than files or directories:
832
+ dfd.resolve([]);
833
+ }
834
+ return dfd.promise();
835
+ },
836
+
837
+ _handleFileTreeEntries: function (entries, path) {
838
+ var that = this;
839
+ return $.when.apply(
840
+ $,
841
+ $.map(entries, function (entry) {
842
+ return that._handleFileTreeEntry(entry, path);
843
+ })
844
+ ).pipe(function () {
845
+ return Array.prototype.concat.apply(
846
+ [],
847
+ arguments
848
+ );
849
+ });
850
+ },
851
+
852
+ _getDroppedFiles: function (dataTransfer) {
853
+ dataTransfer = dataTransfer || {};
854
+ var items = dataTransfer.items;
855
+ if (items && items.length && (items[0].webkitGetAsEntry ||
856
+ items[0].getAsEntry)) {
857
+ return this._handleFileTreeEntries(
858
+ $.map(items, function (item) {
859
+ var entry;
860
+ if (item.webkitGetAsEntry) {
861
+ entry = item.webkitGetAsEntry();
862
+ if (entry) {
863
+ // Workaround for Chrome bug #149735:
864
+ entry._file = item.getAsFile();
865
+ }
866
+ return entry;
867
+ }
868
+ return item.getAsEntry();
869
+ })
870
+ );
871
+ }
872
+ return $.Deferred().resolve(
873
+ $.makeArray(dataTransfer.files)
874
+ ).promise();
875
+ },
876
+
877
+ _getSingleFileInputFiles: function (fileInput) {
878
+ fileInput = $(fileInput);
879
+ var entries = fileInput.prop('webkitEntries') ||
880
+ fileInput.prop('entries'),
881
+ files,
882
+ value;
883
+ if (entries && entries.length) {
884
+ return this._handleFileTreeEntries(entries);
885
+ }
886
+ files = $.makeArray(fileInput.prop('files'));
887
+ if (!files.length) {
888
+ value = fileInput.prop('value');
889
+ if (!value) {
890
+ return $.Deferred().resolve([]).promise();
891
+ }
892
+ // If the files property is not available, the browser does not
893
+ // support the File API and we add a pseudo File object with
894
+ // the input value as name with path information removed:
895
+ files = [{name: value.replace(/^.*\\/, '')}];
896
+ } else if (files[0].name === undefined && files[0].fileName) {
897
+ // File normalization for Safari 4 and Firefox 3:
898
+ $.each(files, function (index, file) {
899
+ file.name = file.fileName;
900
+ file.size = file.fileSize;
901
+ });
902
+ }
903
+ return $.Deferred().resolve(files).promise();
904
+ },
905
+
906
+ _getFileInputFiles: function (fileInput) {
907
+ if (!(fileInput instanceof $) || fileInput.length === 1) {
908
+ return this._getSingleFileInputFiles(fileInput);
909
+ }
910
+ return $.when.apply(
911
+ $,
912
+ $.map(fileInput, this._getSingleFileInputFiles)
913
+ ).pipe(function () {
914
+ return Array.prototype.concat.apply(
915
+ [],
916
+ arguments
917
+ );
918
+ });
919
+ },
920
+
921
+ _onChange: function (e) {
922
+ var that = this,
923
+ data = {
924
+ fileInput: $(e.target),
925
+ form: $(e.target.form)
926
+ };
927
+ this._getFileInputFiles(data.fileInput).always(function (files) {
928
+ data.files = files;
929
+ if (that.options.replaceFileInput) {
930
+ that._replaceFileInput(data.fileInput);
931
+ }
932
+ if (that._trigger('change', e, data) !== false) {
933
+ that._onAdd(e, data);
934
+ }
935
+ });
936
+ },
937
+
938
+ _onPaste: function (e) {
939
+ var cbd = e.originalEvent.clipboardData,
940
+ items = (cbd && cbd.items) || [],
941
+ data = {files: []};
942
+ $.each(items, function (index, item) {
943
+ var file = item.getAsFile && item.getAsFile();
944
+ if (file) {
945
+ data.files.push(file);
946
+ }
947
+ });
948
+ if (this._trigger('paste', e, data) === false ||
949
+ this._onAdd(e, data) === false) {
950
+ return false;
951
+ }
952
+ },
953
+
954
+ _onDrop: function (e) {
955
+ e.preventDefault();
956
+ var that = this,
957
+ dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
958
+ data = {};
959
+ this._getDroppedFiles(dataTransfer).always(function (files) {
960
+ data.files = files;
961
+ if (that._trigger('drop', e, data) !== false) {
962
+ that._onAdd(e, data);
963
+ }
964
+ });
965
+ },
966
+
967
+ _onDragOver: function (e) {
968
+ var dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
969
+ if (this._trigger('dragover', e) === false) {
970
+ return false;
971
+ }
972
+ if (dataTransfer) {
973
+ dataTransfer.dropEffect = 'copy';
974
+ }
975
+ e.preventDefault();
976
+ },
977
+
978
+ _initEventHandlers: function () {
979
+ if (this._isXHRUpload(this.options)) {
980
+ this._on(this.options.dropZone, {
981
+ dragover: this._onDragOver,
982
+ drop: this._onDrop
983
+ });
984
+ this._on(this.options.pasteZone, {
985
+ paste: this._onPaste
986
+ });
987
+ }
988
+ this._on(this.options.fileInput, {
989
+ change: this._onChange
990
+ });
991
+ },
992
+
993
+ _destroyEventHandlers: function () {
994
+ this._off(this.options.dropZone, 'dragover drop');
995
+ this._off(this.options.pasteZone, 'paste');
996
+ this._off(this.options.fileInput, 'change');
997
+ },
998
+
999
+ _setOption: function (key, value) {
1000
+ var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
1001
+ if (refresh) {
1002
+ this._destroyEventHandlers();
1003
+ }
1004
+ this._super(key, value);
1005
+ if (refresh) {
1006
+ this._initSpecialOptions();
1007
+ this._initEventHandlers();
1008
+ }
1009
+ },
1010
+
1011
+ _initSpecialOptions: function () {
1012
+ var options = this.options;
1013
+ if (options.fileInput === undefined) {
1014
+ options.fileInput = this.element.is('input[type="file"]') ?
1015
+ this.element : this.element.find('input[type="file"]');
1016
+ } else if (!(options.fileInput instanceof $)) {
1017
+ options.fileInput = $(options.fileInput);
1018
+ }
1019
+ if (!(options.dropZone instanceof $)) {
1020
+ options.dropZone = $(options.dropZone);
1021
+ }
1022
+ if (!(options.pasteZone instanceof $)) {
1023
+ options.pasteZone = $(options.pasteZone);
1024
+ }
1025
+ },
1026
+
1027
+ _create: function () {
1028
+ var options = this.options;
1029
+ // Initialize options set via HTML5 data-attributes:
1030
+ $.extend(options, $(this.element[0].cloneNode(false)).data());
1031
+ this._initSpecialOptions();
1032
+ this._slots = [];
1033
+ this._sequence = this._getXHRPromise(true);
1034
+ this._sending = this._active = this._loaded = this._total = 0;
1035
+ this._initEventHandlers();
1036
+ },
1037
+
1038
+ _destroy: function () {
1039
+ this._destroyEventHandlers();
1040
+ },
1041
+
1042
+ // This method is exposed to the widget API and allows adding files
1043
+ // using the fileupload API. The data parameter accepts an object which
1044
+ // must have a files property and can contain additional options:
1045
+ // .fileupload('add', {files: filesList});
1046
+ add: function (data) {
1047
+ var that = this;
1048
+ if (!data || this.options.disabled) {
1049
+ return;
1050
+ }
1051
+ if (data.fileInput && !data.files) {
1052
+ this._getFileInputFiles(data.fileInput).always(function (files) {
1053
+ data.files = files;
1054
+ that._onAdd(null, data);
1055
+ });
1056
+ } else {
1057
+ data.files = $.makeArray(data.files);
1058
+ this._onAdd(null, data);
1059
+ }
1060
+ },
1061
+
1062
+ // This method is exposed to the widget API and allows sending files
1063
+ // using the fileupload API. The data parameter accepts an object which
1064
+ // must have a files or fileInput property and can contain additional options:
1065
+ // .fileupload('send', {files: filesList});
1066
+ // The method returns a Promise object for the file upload call.
1067
+ send: function (data) {
1068
+ if (data && !this.options.disabled) {
1069
+ if (data.fileInput && !data.files) {
1070
+ var that = this,
1071
+ dfd = $.Deferred(),
1072
+ promise = dfd.promise(),
1073
+ jqXHR,
1074
+ aborted;
1075
+ promise.abort = function () {
1076
+ aborted = true;
1077
+ if (jqXHR) {
1078
+ return jqXHR.abort();
1079
+ }
1080
+ dfd.reject(null, 'abort', 'abort');
1081
+ return promise;
1082
+ };
1083
+ this._getFileInputFiles(data.fileInput).always(
1084
+ function (files) {
1085
+ if (aborted) {
1086
+ return;
1087
+ }
1088
+ data.files = files;
1089
+ jqXHR = that._onSend(null, data).then(
1090
+ function (result, textStatus, jqXHR) {
1091
+ dfd.resolve(result, textStatus, jqXHR);
1092
+ },
1093
+ function (jqXHR, textStatus, errorThrown) {
1094
+ dfd.reject(jqXHR, textStatus, errorThrown);
1095
+ }
1096
+ );
1097
+ }
1098
+ );
1099
+ return this._enhancePromise(promise);
1100
+ }
1101
+ data.files = $.makeArray(data.files);
1102
+ if (data.files.length) {
1103
+ return this._onSend(null, data);
1104
+ }
1105
+ }
1106
+ return this._getXHRPromise(false, data && data.context);
1107
+ }
1108
+
1109
+ });
1110
+
1111
+ }));