[MRM-1586] rewrite upload artifact page

importing ui stuff.

git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@1306255 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Olivier Lamy 2012-03-28 11:16:08 +00:00
parent d9308af1bb
commit 5a812b4b63
13 changed files with 2295 additions and 8 deletions

View File

@ -79,7 +79,8 @@
"jquery": "jquery-1.7.2",
"jquery.tmpl": "jquery.tmpl",
"utils": "archiva/utils",
"jquery.ui.widget": "jquery-ui-1.8.16.custom.min",
"jquery.ui": "jquery-ui-1.8.16.custom.min",
"jquery.ui.widget": "jquery.ui.widget-1.8.18",
"jquery.cookie": "jquery.cookie.1.0.0",
"bootstrap": "bootstrap.2.0.2",
"choosen": "chosen.jquery-0.9.7",
@ -88,6 +89,11 @@
"knockout": "knockout-2.0.0.debug",
"knockout.simpleGrid": "knockout.simpleGrid",
"knockout.sortable": "knockout-sortable",
"jquery.iframe.transport": "jquery.iframe-transport-1.4",
"jquery.fileupload": "jquery.fileupload-5.10.0",
"jquery.fileupload.ip":"jquery.fileupload-ip-1.0.6",
"jquery.fileupload.ui":"jquery.fileupload-ui-6.6.3",
"tmpl": "tmpl.min",
"redback": "redback/redback",
"redback.roles": "redback/roles",
"redback.user": "redback/user",
@ -99,6 +105,7 @@
"archiva.network-proxies": "archiva/network-proxies",
"archiva.proxy-connectors": "archiva/proxy-connectors",
"archiva.repository-groups": "archiva/repository-groups",
"archiva.artifacts-management": "archiva/artifacts-management",
"archiva.main": "archiva/main"
}
// file upload dependencies

View File

@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
define("archiva.artifacts-management",["jquery","i18n","order!utils","order!jquery.tmpl","order!knockout",
"order!knockout.simpleGrid","jquery.validate","bootstrap","jquery.fileupload","jquery.fileupload.ui"]
, function() {
displayUploadArtifact=function(){
var mainContent=$("#main-content");
mainContent.html($("#file-upload-tmpl" ).tmpl());
$('#fileupload').fileupload();
}
});

View File

@ -26,9 +26,10 @@ define("archiva.templates",["text!templates/archiva/menu.html",
"text!templates/archiva/repository-groups.html",
"text!templates/archiva/search.html",
"text!templates/archiva/general-admin.html",
"text!templates/archiva/artifacts-management.html",
"jquery.tmpl","utils"],
function(menu,message,modal,grids_generics,repositories,network_proxies,proxies_connectors,
repository_groups,search,general_admin) {
repository_groups,search,general_admin,artifacts_management) {
var htmlFragment=$("#html-fragments");
// template loading
@ -42,6 +43,7 @@ define("archiva.templates",["text!templates/archiva/menu.html",
htmlFragment.append(repository_groups);
htmlFragment.append(search);
htmlFragment.append(general_admin);
htmlFragment.append(artifacts_management);
$.log("main-tmpl.js loaded");
}
);

View File

@ -16,12 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
define("archiva.main",["order!jquery","jquery.ui.widget","jquery.cookie","bootstrap","order!archiva/search",
define("archiva.main",["order!jquery","jquery.ui","jquery.cookie","bootstrap","order!archiva/search",
"jquery.validate","jquery.json","order!knockout","order!redback.templates","order!archiva.templates",
"order!redback.roles",
"order!redback","archiva.general-admin","archiva.repositories","archiva.network-proxies","archiva.proxy-connectors",
"archiva.repository-groups",
"order!knockout.simpleGrid"],
"order!redback.roles","order!redback","archiva.general-admin","archiva.repositories",
"archiva.network-proxies","archiva.proxy-connectors","archiva.repository-groups","archiva.artifacts-management"],
function() {
/**

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
define("archiva.repository-groups",["jquery","i18n","jquery.tmpl","bootstrap","jquery.validate","jquery.ui.widget","order!knockout"
define("archiva.repository-groups",["jquery","i18n","jquery.tmpl","bootstrap","jquery.validate","jquery.ui","order!knockout"
,"order!knockout.simpleGrid","knockout.sortable"],
function() {

View File

@ -0,0 +1,905 @@
/*
* jQuery File Upload Plugin 5.10.0
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/*jslint nomen: true, unparam: true, regexp: true */
/*global define, window, document, Blob, FormData, location */
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'jquery',
'jquery.ui.widget'
], factory);
} else {
// Browser globals:
factory(window.jQuery);
}
}(function ($) {
'use strict';
// The FileReader API is not actually used, but works as feature detection,
// as e.g. Safari supports XHR file uploads via the FormData API,
// but not non-multipart XHR file uploads:
$.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
$.support.xhrFormDataFileUpload = !!window.FormData;
// The fileupload widget listens for change events on file input fields defined
// via fileInput setting and paste or drop events of the given dropZone.
// In addition to the default jQuery Widget methods, the fileupload widget
// exposes the "add" and "send" methods, to add or directly send files using
// the fileupload API.
// By default, files added via file input selection, paste, drag & drop or
// "add" method are uploaded immediately, but it is possible to override
// the "add" callback option to queue file uploads.
$.widget('blueimp.fileupload', {
options: {
// The namespace used for event handler binding on the dropZone and
// fileInput collections.
// If not set, the name of the widget ("fileupload") is used.
namespace: undefined,
// The drop target collection, by the default the complete document.
// Set to null or an empty collection to disable drag & drop support:
dropZone: $(document),
// The file input field collection, that is listened for change events.
// If undefined, it is set to the file input fields inside
// of the widget element on plugin initialization.
// Set to null or an empty collection to disable the change listener.
fileInput: undefined,
// By default, the file input field is replaced with a clone after
// each input field change event. This is required for iframe transport
// queues and allows change events to be fired for the same file
// selection, but can be disabled by setting the following option to false:
replaceFileInput: true,
// The parameter name for the file form data (the request argument name).
// If undefined or empty, the name property of the file input field is
// used, or "files[]" if the file input name property is also empty,
// can be a string or an array of strings:
paramName: undefined,
// By default, each file of a selection is uploaded using an individual
// request for XHR type uploads. Set to false to upload file
// selections in one request each:
singleFileUploads: true,
// To limit the number of files uploaded with one XHR request,
// set the following option to an integer greater than 0:
limitMultiFileUploads: undefined,
// Set the following option to true to issue all file upload requests
// in a sequential order:
sequentialUploads: false,
// To limit the number of concurrent uploads,
// set the following option to an integer greater than 0:
limitConcurrentUploads: undefined,
// Set the following option to true to force iframe transport uploads:
forceIframeTransport: false,
// Set the following option to the location of a redirect url on the
// origin server, for cross-domain iframe transport uploads:
redirect: undefined,
// The parameter name for the redirect url, sent as part of the form
// data and set to 'redirect' if this option is empty:
redirectParamName: undefined,
// Set the following option to the location of a postMessage window,
// to enable postMessage transport uploads:
postMessage: undefined,
// By default, XHR file uploads are sent as multipart/form-data.
// The iframe transport is always using multipart/form-data.
// Set to false to enable non-multipart XHR uploads:
multipart: true,
// To upload large files in smaller chunks, set the following option
// to a preferred maximum chunk size. If set to 0, null or undefined,
// or the browser does not support the required Blob API, files will
// be uploaded as a whole.
maxChunkSize: undefined,
// When a non-multipart upload or a chunked multipart upload has been
// aborted, this option can be used to resume the upload by setting
// it to the size of the already uploaded bytes. This option is most
// useful when modifying the options object inside of the "add" or
// "send" callbacks, as the options are cloned for each file upload.
uploadedBytes: undefined,
// By default, failed (abort or error) file uploads are removed from the
// global progress calculation. Set the following option to false to
// prevent recalculating the global progress data:
recalculateProgress: true,
// Additional form data to be sent along with the file uploads can be set
// using this option, which accepts an array of objects with name and
// value properties, a function returning such an array, a FormData
// object (for XHR file uploads), or a simple object.
// The form of the first fileInput is given as parameter to the function:
formData: function (form) {
return form.serializeArray();
},
// The add callback is invoked as soon as files are added to the fileupload
// widget (via file input selection, drag & drop, paste or add API call).
// If the singleFileUploads option is enabled, this callback will be
// called once for each file in the selection for XHR file uplaods, else
// once for each file selection.
// The upload starts when the submit method is invoked on the data parameter.
// The data object contains a files property holding the added files
// and allows to override plugin options as well as define ajax settings.
// Listeners for this callback can also be bound the following way:
// .bind('fileuploadadd', func);
// data.submit() returns a Promise object and allows to attach additional
// handlers using jQuery's Deferred callbacks:
// data.submit().done(func).fail(func).always(func);
add: function (e, data) {
data.submit();
},
// Other callbacks:
// Callback for the submit event of each file upload:
// submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
// Callback for the start of each file upload request:
// send: function (e, data) {}, // .bind('fileuploadsend', func);
// Callback for successful uploads:
// done: function (e, data) {}, // .bind('fileuploaddone', func);
// Callback for failed (abort or error) uploads:
// fail: function (e, data) {}, // .bind('fileuploadfail', func);
// Callback for completed (success, abort or error) requests:
// always: function (e, data) {}, // .bind('fileuploadalways', func);
// Callback for upload progress events:
// progress: function (e, data) {}, // .bind('fileuploadprogress', func);
// Callback for global upload progress events:
// progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
// Callback for uploads start, equivalent to the global ajaxStart event:
// start: function (e) {}, // .bind('fileuploadstart', func);
// Callback for uploads stop, equivalent to the global ajaxStop event:
// stop: function (e) {}, // .bind('fileuploadstop', func);
// Callback for change events of the fileInput collection:
// change: function (e, data) {}, // .bind('fileuploadchange', func);
// Callback for paste events to the dropZone collection:
// paste: function (e, data) {}, // .bind('fileuploadpaste', func);
// Callback for drop events of the dropZone collection:
// drop: function (e, data) {}, // .bind('fileuploaddrop', func);
// Callback for dragover events of the dropZone collection:
// dragover: function (e) {}, // .bind('fileuploaddragover', func);
// The plugin options are used as settings object for the ajax calls.
// The following are jQuery ajax settings required for the file uploads:
processData: false,
contentType: false,
cache: false
},
// A list of options that require a refresh after assigning a new value:
_refreshOptionsList: [
'namespace',
'dropZone',
'fileInput',
'multipart',
'forceIframeTransport'
],
_isXHRUpload: function (options) {
return !options.forceIframeTransport &&
((!options.multipart && $.support.xhrFileUpload) ||
$.support.xhrFormDataFileUpload);
},
_getFormData: function (options) {
var formData;
if (typeof options.formData === 'function') {
return options.formData(options.form);
} else if ($.isArray(options.formData)) {
return options.formData;
} else if (options.formData) {
formData = [];
$.each(options.formData, function (name, value) {
formData.push({name: name, value: value});
});
return formData;
}
return [];
},
_getTotal: function (files) {
var total = 0;
$.each(files, function (index, file) {
total += file.size || 1;
});
return total;
},
_onProgress: function (e, data) {
if (e.lengthComputable) {
var total = data.total || this._getTotal(data.files),
loaded = parseInt(
e.loaded / e.total * (data.chunkSize || total),
10
) + (data.uploadedBytes || 0);
this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
data.lengthComputable = true;
data.loaded = loaded;
data.total = total;
// Trigger a custom progress event with a total data property set
// to the file size(s) of the current upload and a loaded data
// property calculated accordingly:
this._trigger('progress', e, data);
// Trigger a global progress event for all current file uploads,
// including ajax calls queued for sequential file uploads:
this._trigger('progressall', e, {
lengthComputable: true,
loaded: this._loaded,
total: this._total
});
}
},
_initProgressListener: function (options) {
var that = this,
xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
// Accesss to the native XHR object is required to add event listeners
// for the upload progress event:
if (xhr.upload) {
$(xhr.upload).bind('progress', function (e) {
var oe = e.originalEvent;
// Make sure the progress event properties get copied over:
e.lengthComputable = oe.lengthComputable;
e.loaded = oe.loaded;
e.total = oe.total;
that._onProgress(e, options);
});
options.xhr = function () {
return xhr;
};
}
},
_initXHRData: function (options) {
var formData,
file = options.files[0],
// Ignore non-multipart setting if not supported:
multipart = options.multipart || !$.support.xhrFileUpload,
paramName = options.paramName[0];
if (!multipart || options.blob) {
// For non-multipart uploads and chunked uploads,
// file meta data is not part of the request body,
// so we transmit this data as part of the HTTP headers.
// For cross domain requests, these headers must be allowed
// via Access-Control-Allow-Headers or removed using
// the beforeSend callback:
options.headers = $.extend(options.headers, {
'X-File-Name': file.name,
'X-File-Type': file.type,
'X-File-Size': file.size
});
if (!options.blob) {
// Non-chunked non-multipart upload:
options.contentType = file.type;
options.data = file;
} else if (!multipart) {
// Chunked non-multipart upload:
options.contentType = 'application/octet-stream';
options.data = options.blob;
}
}
if (multipart && $.support.xhrFormDataFileUpload) {
if (options.postMessage) {
// window.postMessage does not allow sending FormData
// objects, so we just add the File/Blob objects to
// the formData array and let the postMessage window
// create the FormData object out of this array:
formData = this._getFormData(options);
if (options.blob) {
formData.push({
name: paramName,
value: options.blob
});
} else {
$.each(options.files, function (index, file) {
formData.push({
name: options.paramName[index] || paramName,
value: file
});
});
}
} else {
if (options.formData instanceof FormData) {
formData = options.formData;
} else {
formData = new FormData();
$.each(this._getFormData(options), function (index, field) {
formData.append(field.name, field.value);
});
}
if (options.blob) {
formData.append(paramName, options.blob, file.name);
} else {
$.each(options.files, function (index, file) {
// File objects are also Blob instances.
// This check allows the tests to run with
// dummy objects:
if (file instanceof Blob) {
formData.append(
options.paramName[index] || paramName,
file,
file.name
);
}
});
}
}
options.data = formData;
}
// Blob reference is not needed anymore, free memory:
options.blob = null;
},
_initIframeSettings: function (options) {
// Setting the dataType to iframe enables the iframe transport:
options.dataType = 'iframe ' + (options.dataType || '');
// The iframe transport accepts a serialized array as form data:
options.formData = this._getFormData(options);
// Add redirect url to form data on cross-domain uploads:
if (options.redirect && $('<a></a>').prop('href', options.url)
.prop('host') !== location.host) {
options.formData.push({
name: options.redirectParamName || 'redirect',
value: options.redirect
});
}
},
_initDataSettings: function (options) {
if (this._isXHRUpload(options)) {
if (!this._chunkedUpload(options, true)) {
if (!options.data) {
this._initXHRData(options);
}
this._initProgressListener(options);
}
if (options.postMessage) {
// Setting the dataType to postmessage enables the
// postMessage transport:
options.dataType = 'postmessage ' + (options.dataType || '');
}
} else {
this._initIframeSettings(options, 'iframe');
}
},
_getParamName: function (options) {
var fileInput = $(options.fileInput),
paramName = options.paramName;
if (!paramName) {
paramName = [];
fileInput.each(function () {
var input = $(this),
name = input.prop('name') || 'files[]',
i = (input.prop('files') || [1]).length;
while (i) {
paramName.push(name);
i -= 1;
}
});
if (!paramName.length) {
paramName = [fileInput.prop('name') || 'files[]'];
}
} else if (!$.isArray(paramName)) {
paramName = [paramName];
}
return paramName;
},
_initFormSettings: function (options) {
// Retrieve missing options from the input field and the
// associated form, if available:
if (!options.form || !options.form.length) {
options.form = $(options.fileInput.prop('form'));
}
options.paramName = this._getParamName(options);
if (!options.url) {
options.url = options.form.prop('action') || location.href;
}
// The HTTP request method must be "POST" or "PUT":
options.type = (options.type || options.form.prop('method') || '')
.toUpperCase();
if (options.type !== 'POST' && options.type !== 'PUT') {
options.type = 'POST';
}
},
_getAJAXSettings: function (data) {
var options = $.extend({}, this.options, data);
this._initFormSettings(options);
this._initDataSettings(options);
return options;
},
// Maps jqXHR callbacks to the equivalent
// methods of the given Promise object:
_enhancePromise: function (promise) {
promise.success = promise.done;
promise.error = promise.fail;
promise.complete = promise.always;
return promise;
},
// Creates and returns a Promise object enhanced with
// the jqXHR methods abort, success, error and complete:
_getXHRPromise: function (resolveOrReject, context, args) {
var dfd = $.Deferred(),
promise = dfd.promise();
context = context || this.options.context || promise;
if (resolveOrReject === true) {
dfd.resolveWith(context, args);
} else if (resolveOrReject === false) {
dfd.rejectWith(context, args);
}
promise.abort = dfd.promise;
return this._enhancePromise(promise);
},
// Uploads a file in multiple, sequential requests
// by splitting the file up in multiple blob chunks.
// If the second parameter is true, only tests if the file
// should be uploaded in chunks, but does not invoke any
// upload requests:
_chunkedUpload: function (options, testOnly) {
var that = this,
file = options.files[0],
fs = file.size,
ub = options.uploadedBytes = options.uploadedBytes || 0,
mcs = options.maxChunkSize || fs,
// Use the Blob methods with the slice implementation
// according to the W3C Blob API specification:
slice = file.webkitSlice || file.mozSlice || file.slice,
upload,
n,
jqXHR,
pipe;
if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
options.data) {
return false;
}
if (testOnly) {
return true;
}
if (ub >= fs) {
file.error = 'uploadedBytes';
return this._getXHRPromise(
false,
options.context,
[null, 'error', file.error]
);
}
// n is the number of blobs to upload,
// calculated via filesize, uploaded bytes and max chunk size:
n = Math.ceil((fs - ub) / mcs);
// The chunk upload method accepting the chunk number as parameter:
upload = function (i) {
if (!i) {
return that._getXHRPromise(true, options.context);
}
// Upload the blobs in sequential order:
return upload(i -= 1).pipe(function () {
// Clone the options object for each chunk upload:
var o = $.extend({}, options);
o.blob = slice.call(
file,
ub + i * mcs,
ub + (i + 1) * mcs
);
// Store the current chunk size, as the blob itself
// will be dereferenced after data processing:
o.chunkSize = o.blob.size;
// Process the upload data (the blob and potential form data):
that._initXHRData(o);
// Add progress listeners for this chunk upload:
that._initProgressListener(o);
jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
.done(function () {
// Create a progress event if upload is done and
// no progress event has been invoked for this chunk:
if (!o.loaded) {
that._onProgress($.Event('progress', {
lengthComputable: true,
loaded: o.chunkSize,
total: o.chunkSize
}), o);
}
options.uploadedBytes = o.uploadedBytes +=
o.chunkSize;
});
return jqXHR;
});
};
// Return the piped Promise object, enhanced with an abort method,
// which is delegated to the jqXHR object of the current upload,
// and jqXHR callbacks mapped to the equivalent Promise methods:
pipe = upload(n);
pipe.abort = function () {
return jqXHR.abort();
};
return this._enhancePromise(pipe);
},
_beforeSend: function (e, data) {
if (this._active === 0) {
// the start callback is triggered when an upload starts
// and no other uploads are currently running,
// equivalent to the global ajaxStart event:
this._trigger('start');
}
this._active += 1;
// Initialize the global progress values:
this._loaded += data.uploadedBytes || 0;
this._total += this._getTotal(data.files);
},
_onDone: function (result, textStatus, jqXHR, options) {
if (!this._isXHRUpload(options)) {
// Create a progress event for each iframe load:
this._onProgress($.Event('progress', {
lengthComputable: true,
loaded: 1,
total: 1
}), options);
}
options.result = result;
options.textStatus = textStatus;
options.jqXHR = jqXHR;
this._trigger('done', null, options);
},
_onFail: function (jqXHR, textStatus, errorThrown, options) {
options.jqXHR = jqXHR;
options.textStatus = textStatus;
options.errorThrown = errorThrown;
this._trigger('fail', null, options);
if (options.recalculateProgress) {
// Remove the failed (error or abort) file upload from
// the global progress calculation:
this._loaded -= options.loaded || options.uploadedBytes || 0;
this._total -= options.total || this._getTotal(options.files);
}
},
_onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
this._active -= 1;
options.textStatus = textStatus;
if (jqXHRorError && jqXHRorError.always) {
options.jqXHR = jqXHRorError;
options.result = jqXHRorResult;
} else {
options.jqXHR = jqXHRorResult;
options.errorThrown = jqXHRorError;
}
this._trigger('always', null, options);
if (this._active === 0) {
// The stop callback is triggered when all uploads have
// been completed, equivalent to the global ajaxStop event:
this._trigger('stop');
// Reset the global progress values:
this._loaded = this._total = 0;
}
},
_onSend: function (e, data) {
var that = this,
jqXHR,
slot,
pipe,
options = that._getAJAXSettings(data),
send = function (resolve, args) {
that._sending += 1;
jqXHR = jqXHR || (
(resolve !== false &&
that._trigger('send', e, options) !== false &&
(that._chunkedUpload(options) || $.ajax(options))) ||
that._getXHRPromise(false, options.context, args)
).done(function (result, textStatus, jqXHR) {
that._onDone(result, textStatus, jqXHR, options);
}).fail(function (jqXHR, textStatus, errorThrown) {
that._onFail(jqXHR, textStatus, errorThrown, options);
}).always(function (jqXHRorResult, textStatus, jqXHRorError) {
that._sending -= 1;
that._onAlways(
jqXHRorResult,
textStatus,
jqXHRorError,
options
);
if (options.limitConcurrentUploads &&
options.limitConcurrentUploads > that._sending) {
// Start the next queued upload,
// that has not been aborted:
var nextSlot = that._slots.shift();
while (nextSlot) {
if (!nextSlot.isRejected()) {
nextSlot.resolve();
break;
}
nextSlot = that._slots.shift();
}
}
});
return jqXHR;
};
this._beforeSend(e, options);
if (this.options.sequentialUploads ||
(this.options.limitConcurrentUploads &&
this.options.limitConcurrentUploads <= this._sending)) {
if (this.options.limitConcurrentUploads > 1) {
slot = $.Deferred();
this._slots.push(slot);
pipe = slot.pipe(send);
} else {
pipe = (this._sequence = this._sequence.pipe(send, send));
}
// Return the piped Promise object, enhanced with an abort method,
// which is delegated to the jqXHR object of the current upload,
// and jqXHR callbacks mapped to the equivalent Promise methods:
pipe.abort = function () {
var args = [undefined, 'abort', 'abort'];
if (!jqXHR) {
if (slot) {
slot.rejectWith(args);
}
return send(false, args);
}
return jqXHR.abort();
};
return this._enhancePromise(pipe);
}
return send();
},
_onAdd: function (e, data) {
var that = this,
result = true,
options = $.extend({}, this.options, data),
limit = options.limitMultiFileUploads,
paramName = this._getParamName(options),
paramNameSet,
paramNameSlice,
fileSet,
i;
if (!(options.singleFileUploads || limit) ||
!this._isXHRUpload(options)) {
fileSet = [data.files];
paramNameSet = [paramName];
} else if (!options.singleFileUploads && limit) {
fileSet = [];
paramNameSet = [];
for (i = 0; i < data.files.length; i += limit) {
fileSet.push(data.files.slice(i, i + limit));
paramNameSlice = paramName.slice(i, i + limit);
if (!paramNameSlice.length) {
paramNameSlice = paramName;
}
paramNameSet.push(paramNameSlice);
}
} else {
paramNameSet = paramName;
}
data.originalFiles = data.files;
$.each(fileSet || data.files, function (index, element) {
var newData = $.extend({}, data);
newData.files = fileSet ? element : [element];
newData.paramName = paramNameSet[index];
newData.submit = function () {
newData.jqXHR = this.jqXHR =
(that._trigger('submit', e, this) !== false) &&
that._onSend(e, this);
return this.jqXHR;
};
return (result = that._trigger('add', e, newData));
});
return result;
},
// File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
_normalizeFile: function (index, file) {
if (file.name === undefined && file.size === undefined) {
file.name = file.fileName;
file.size = file.fileSize;
}
},
_replaceFileInput: function (input) {
var inputClone = input.clone(true);
$('<form></form>').append(inputClone)[0].reset();
// Detaching allows to insert the fileInput on another form
// without loosing the file input value:
input.after(inputClone).detach();
// Avoid memory leaks with the detached file input:
$.cleanData(input.unbind('remove'));
// Replace the original file input element in the fileInput
// collection with the clone, which has been copied including
// event handlers:
this.options.fileInput = this.options.fileInput.map(function (i, el) {
if (el === input[0]) {
return inputClone[0];
}
return el;
});
// If the widget has been initialized on the file input itself,
// override this.element with the file input clone:
if (input[0] === this.element[0]) {
this.element = inputClone;
}
},
_onChange: function (e) {
var that = e.data.fileupload,
data = {
files: $.each($.makeArray(e.target.files), that._normalizeFile),
fileInput: $(e.target),
form: $(e.target.form)
};
if (!data.files.length) {
// If the files property is not available, the browser does not
// support the File API and we add a pseudo File object with
// the input value as name with path information removed:
data.files = [{name: e.target.value.replace(/^.*\\/, '')}];
}
if (that.options.replaceFileInput) {
that._replaceFileInput(data.fileInput);
}
if (that._trigger('change', e, data) === false ||
that._onAdd(e, data) === false) {
return false;
}
},
_onPaste: function (e) {
var that = e.data.fileupload,
cbd = e.originalEvent.clipboardData,
items = (cbd && cbd.items) || [],
data = {files: []};
$.each(items, function (index, item) {
var file = item.getAsFile && item.getAsFile();
if (file) {
data.files.push(file);
}
});
if (that._trigger('paste', e, data) === false ||
that._onAdd(e, data) === false) {
return false;
}
},
_onDrop: function (e) {
var that = e.data.fileupload,
dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
data = {
files: $.each(
$.makeArray(dataTransfer && dataTransfer.files),
that._normalizeFile
)
};
if (that._trigger('drop', e, data) === false ||
that._onAdd(e, data) === false) {
return false;
}
e.preventDefault();
},
_onDragOver: function (e) {
var that = e.data.fileupload,
dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
if (that._trigger('dragover', e) === false) {
return false;
}
if (dataTransfer) {
dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
}
e.preventDefault();
},
_initEventHandlers: function () {
var ns = this.options.namespace;
if (this._isXHRUpload(this.options)) {
this.options.dropZone
.bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
.bind('drop.' + ns, {fileupload: this}, this._onDrop)
.bind('paste.' + ns, {fileupload: this}, this._onPaste);
}
this.options.fileInput
.bind('change.' + ns, {fileupload: this}, this._onChange);
},
_destroyEventHandlers: function () {
var ns = this.options.namespace;
this.options.dropZone
.unbind('dragover.' + ns, this._onDragOver)
.unbind('drop.' + ns, this._onDrop)
.unbind('paste.' + ns, this._onPaste);
this.options.fileInput
.unbind('change.' + ns, this._onChange);
},
_setOption: function (key, value) {
var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
if (refresh) {
this._destroyEventHandlers();
}
$.Widget.prototype._setOption.call(this, key, value);
if (refresh) {
this._initSpecialOptions();
this._initEventHandlers();
}
},
_initSpecialOptions: function () {
var options = this.options;
if (options.fileInput === undefined) {
options.fileInput = this.element.is('input:file') ?
this.element : this.element.find('input:file');
} else if (!(options.fileInput instanceof $)) {
options.fileInput = $(options.fileInput);
}
if (!(options.dropZone instanceof $)) {
options.dropZone = $(options.dropZone);
}
},
_create: function () {
var options = this.options,
dataOpts = $.extend({}, this.element.data());
dataOpts[this.widgetName] = undefined;
$.extend(options, dataOpts);
options.namespace = options.namespace || this.widgetName;
this._initSpecialOptions();
this._slots = [];
this._sequence = this._getXHRPromise(true);
this._sending = this._active = this._loaded = this._total = 0;
this._initEventHandlers();
},
destroy: function () {
this._destroyEventHandlers();
$.Widget.prototype.destroy.call(this);
},
enable: function () {
$.Widget.prototype.enable.call(this);
this._initEventHandlers();
},
disable: function () {
this._destroyEventHandlers();
$.Widget.prototype.disable.call(this);
},
// This method is exposed to the widget API and allows adding files
// using the fileupload API. The data parameter accepts an object which
// must have a files property and can contain additional options:
// .fileupload('add', {files: filesList});
add: function (data) {
if (!data || this.options.disabled) {
return;
}
data.files = $.each($.makeArray(data.files), this._normalizeFile);
this._onAdd(null, data);
},
// This method is exposed to the widget API and allows sending files
// using the fileupload API. The data parameter accepts an object which
// must have a files property and can contain additional options:
// .fileupload('send', {files: filesList});
// The method returns a Promise object for the file upload call.
send: function (data) {
if (data && !this.options.disabled) {
data.files = $.each($.makeArray(data.files), this._normalizeFile);
if (data.files.length) {
return this._onSend(null, data);
}
}
return this._getXHRPromise(false, data && data.context);
}
});
}));

View File

@ -0,0 +1,160 @@
/*
* jQuery File Upload Image Processing Plugin 1.0.6
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2012, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/*jslint nomen: true, unparam: true, regexp: true */
/*global define, window, document */
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'jquery',
//'load-image',
//'canvas-to-blob',
'jquery.fileupload'
], factory);
} else {
// Browser globals:
factory(
window.jQuery,
window.loadImage,
window.canvasToBlob
);
}
}(function ($, loadImage, canvasToBlob) {
'use strict';
// The File Upload IP version extends the basic fileupload widget
// with image processing functionality:
$.widget('blueimpIP.fileupload', $.blueimp.fileupload, {
options: {
// The regular expression to define which image files are to be
// resized, given that the browser supports the operation:
resizeSourceFileTypes: /^image\/(gif|jpeg|png)$/,
// The maximum file size of images that are to be resized:
resizeSourceMaxFileSize: 20000000, // 20MB
// The maximum width of the resized images:
resizeMaxWidth: undefined,
// The maximum height of the resized images:
resizeMaxHeight: undefined,
// The minimum width of the resized images:
resizeMinWidth: undefined,
// The minimum height of the resized images:
resizeMinHeight: undefined,
// The add callback is invoked as soon as files are added to the fileupload
// widget (via file input selection, drag & drop or add API call).
// See the basic file upload widget for more information:
add: function (e, data) {
$(this).fileupload('resize', data).done(function () {
data.submit();
});
}
},
// Resizes the image file at the given index and stores the created blob
// at the original position of the files list, returns a Promise object:
_resizeImage: function (files, index, options) {
var that = this,
file = files[index],
deferred = $.Deferred(),
canvas,
blob;
options = options || this.options;
loadImage(
file,
function (img) {
var width = img.width,
height = img.height;
canvas = loadImage.scale(img, {
maxWidth: options.resizeMaxWidth,
maxHeight: options.resizeMaxHeight,
minWidth: options.resizeMinWidth,
minHeight: options.resizeMinHeight,
canvas: true
});
if (width !== canvas.width || height !== canvas.height) {
canvasToBlob(canvas, function (blob) {
if (!blob.name) {
if (file.type === blob.type) {
blob.name = file.name;
} else if (file.name) {
blob.name = file.name.replace(
/\..+$/,
'.' + blob.type.substr(6)
);
}
}
files[index] = blob;
deferred.resolveWith(that);
}, file);
} else {
deferred.resolveWith(that);
}
}
);
return deferred.promise();
},
// Resizes the images given as files property of the data parameter,
// returns a Promise object that allows to bind a done handler, which
// will be invoked after processing all images is done:
resize: function (data) {
var that = this,
options = $.extend({}, this.options, data),
resizeAll = $.type(options.resizeSourceMaxFileSize) !== 'number',
isXHRUpload = this._isXHRUpload(options);
$.each(data.files, function (index, file) {
if (isXHRUpload && that._resizeSupport &&
(options.resizeMaxWidth || options.resizeMaxHeight ||
options.resizeMinWidth || options.resizeMinHeight) &&
(resizeAll || file.size < options.resizeSourceMaxFileSize) &&
options.resizeSourceFileTypes.test(file.type)) {
that._processing += 1;
if (that._processing === 1) {
that.element.addClass('fileupload-processing');
}
that._processingQueue = that._processingQueue.pipe(function () {
var deferred = $.Deferred();
that._resizeImage(
data.files,
index,
options
).done(function () {
that._processing -= 1;
if (that._processing === 0) {
that.element
.removeClass('fileupload-processing');
}
deferred.resolveWith(that);
});
return deferred.promise();
});
}
});
return this._processingQueue;
},
_create: function () {
$.blueimp.fileupload.prototype._create.call(this);
this._processing = 0;
this._processingQueue = $.Deferred().resolveWith(this).promise();
this._resizeSupport = canvasToBlob && canvasToBlob(
document.createElement('canvas'),
$.noop
);
}
});
}));

View File

@ -0,0 +1,629 @@
/*
* jQuery File Upload User Interface Plugin 6.6.3
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/*jslint nomen: true, unparam: true, regexp: true */
/*global define, window, document, URL, webkitURL, FileReader */
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'jquery',
'tmpl',
//'load-image',
'jquery.fileupload.ip'
], factory);
} else {
// Browser globals:
factory(
window.jQuery,
window.tmpl,
window.loadImage
);
}
}(function ($, tmpl, loadImage) {
'use strict';
// The UI version extends the IP (image processing) version or the basic
// file upload widget and adds complete user interface interaction:
var parentWidget = ($.blueimpIP || $.blueimp).fileupload;
$.widget('blueimpUI.fileupload', parentWidget, {
options: {
// By default, files added to the widget are uploaded as soon
// as the user clicks on the start buttons. To enable automatic
// uploads, set the following option to true:
autoUpload: false,
// The following option limits the number of files that are
// allowed to be uploaded using this widget:
maxNumberOfFiles: undefined,
// The maximum allowed file size:
maxFileSize: undefined,
// The minimum allowed file size:
minFileSize: undefined,
// The regular expression for allowed file types, matches
// against either file type or file name:
acceptFileTypes: /.+$/i,
// The regular expression to define for which files a preview
// image is shown, matched against the file type:
previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
// The maximum file size of images that are to be displayed as preview:
previewSourceMaxFileSize: 5000000, // 5MB
// The maximum width of the preview images:
previewMaxWidth: 80,
// The maximum height of the preview images:
previewMaxHeight: 80,
// By default, preview images are displayed as canvas elements
// if supported by the browser. Set the following option to false
// to always display preview images as img elements:
previewAsCanvas: true,
// The ID of the upload template:
uploadTemplateId: 'template-upload',
// The ID of the download template:
downloadTemplateId: 'template-download',
// The expected data type of the upload response, sets the dataType
// option of the $.ajax upload requests:
dataType: 'json',
// The add callback is invoked as soon as files are added to the fileupload
// widget (via file input selection, drag & drop or add API call).
// See the basic file upload widget for more information:
add: function (e, data) {
var that = $(this).data('fileupload'),
options = that.options,
files = data.files;
$(this).fileupload('resize', data).done(data, function () {
that._adjustMaxNumberOfFiles(-files.length);
data.isAdjusted = true;
data.files.valid = data.isValidated = that._validate(files);
data.context = that._renderUpload(files)
.appendTo(options.filesContainer)
.data('data', data);
that._renderPreviews(files, data.context);
that._forceReflow(data.context);
that._transition(data.context).done(
function () {
if ((that._trigger('added', e, data) !== false) &&
(options.autoUpload || data.autoUpload) &&
data.autoUpload !== false && data.isValidated) {
data.submit();
}
}
);
});
},
// Callback for the start of each file upload request:
send: function (e, data) {
var that = $(this).data('fileupload');
if (!data.isValidated) {
if (!data.isAdjusted) {
that._adjustMaxNumberOfFiles(-data.files.length);
}
if (!that._validate(data.files)) {
return false;
}
}
if (data.context && data.dataType &&
data.dataType.substr(0, 6) === 'iframe') {
// Iframe Transport does not support progress events.
// In lack of an indeterminate progress bar, we set
// the progress to 100%, showing the full animated bar:
data.context
.find('.progress').addClass(
!$.support.transition && 'progress-animated'
)
.find('.bar').css(
'width',
parseInt(100, 10) + '%'
);
}
return that._trigger('sent', e, data);
},
// Callback for successful uploads:
done: function (e, data) {
var that = $(this).data('fileupload'),
template,
preview;
if (data.context) {
data.context.each(function (index) {
var file = ($.isArray(data.result) &&
data.result[index]) || {error: 'emptyResult'};
if (file.error) {
that._adjustMaxNumberOfFiles(1);
}
that._transition($(this)).done(
function () {
var node = $(this);
template = that._renderDownload([file])
.css('height', node.height())
.replaceAll(node);
that._forceReflow(template);
that._transition(template).done(
function () {
data.context = $(this);
that._trigger('completed', e, data);
}
);
}
);
});
} else {
template = that._renderDownload(data.result)
.appendTo(that.options.filesContainer);
that._forceReflow(template);
that._transition(template).done(
function () {
data.context = $(this);
that._trigger('completed', e, data);
}
);
}
},
// Callback for failed (abort or error) uploads:
fail: function (e, data) {
var that = $(this).data('fileupload'),
template;
that._adjustMaxNumberOfFiles(data.files.length);
if (data.context) {
data.context.each(function (index) {
if (data.errorThrown !== 'abort') {
var file = data.files[index];
file.error = file.error || data.errorThrown ||
true;
that._transition($(this)).done(
function () {
var node = $(this);
template = that._renderDownload([file])
.replaceAll(node);
that._forceReflow(template);
that._transition(template).done(
function () {
data.context = $(this);
that._trigger('failed', e, data);
}
);
}
);
} else {
that._transition($(this)).done(
function () {
$(this).remove();
that._trigger('failed', e, data);
}
);
}
});
} else if (data.errorThrown !== 'abort') {
that._adjustMaxNumberOfFiles(-data.files.length);
data.context = that._renderUpload(data.files)
.appendTo(that.options.filesContainer)
.data('data', data);
that._forceReflow(data.context);
that._transition(data.context).done(
function () {
data.context = $(this);
that._trigger('failed', e, data);
}
);
} else {
that._trigger('failed', e, data);
}
},
// Callback for upload progress events:
progress: function (e, data) {
if (data.context) {
data.context.find('.progress .bar').css(
'width',
parseInt(data.loaded / data.total * 100, 10) + '%'
);
}
},
// Callback for global upload progress events:
progressall: function (e, data) {
$(this).find('.fileupload-buttonbar .progress .bar').css(
'width',
parseInt(data.loaded / data.total * 100, 10) + '%'
);
},
// Callback for uploads start, equivalent to the global ajaxStart event:
start: function (e) {
var that = $(this).data('fileupload');
that._transition($(this).find('.fileupload-buttonbar .progress')).done(
function () {
that._trigger('started', e);
}
);
},
// Callback for uploads stop, equivalent to the global ajaxStop event:
stop: function (e) {
var that = $(this).data('fileupload');
that._transition($(this).find('.fileupload-buttonbar .progress')).done(
function () {
$(this).find('.bar').css('width', '0%');
that._trigger('stopped', e);
}
);
},
// Callback for file deletion:
destroy: function (e, data) {
var that = $(this).data('fileupload');
if (data.url) {
$.ajax(data);
}
that._adjustMaxNumberOfFiles(1);
that._transition(data.context).done(
function () {
$(this).remove();
that._trigger('destroyed', e, data);
}
);
}
},
// Link handler, that allows to download files
// by drag & drop of the links to the desktop:
_enableDragToDesktop: function () {
var link = $(this),
url = link.prop('href'),
name = link.prop('download'),
type = 'application/octet-stream';
link.bind('dragstart', function (e) {
try {
e.originalEvent.dataTransfer.setData(
'DownloadURL',
[type, name, url].join(':')
);
} catch (err) {}
});
},
_adjustMaxNumberOfFiles: function (operand) {
if (typeof this.options.maxNumberOfFiles === 'number') {
this.options.maxNumberOfFiles += operand;
if (this.options.maxNumberOfFiles < 1) {
this._disableFileInputButton();
} else {
this._enableFileInputButton();
}
}
},
_formatFileSize: function (bytes) {
if (typeof bytes !== 'number') {
return '';
}
if (bytes >= 1000000000) {
return (bytes / 1000000000).toFixed(2) + ' GB';
}
if (bytes >= 1000000) {
return (bytes / 1000000).toFixed(2) + ' MB';
}
return (bytes / 1000).toFixed(2) + ' KB';
},
_hasError: function (file) {
if (file.error) {
return file.error;
}
// The number of added files is subtracted from
// maxNumberOfFiles before validation, so we check if
// maxNumberOfFiles is below 0 (instead of below 1):
if (this.options.maxNumberOfFiles < 0) {
return 'maxNumberOfFiles';
}
// Files are accepted if either the file type or the file name
// matches against the acceptFileTypes regular expression, as
// only browsers with support for the File API report the type:
if (!(this.options.acceptFileTypes.test(file.type) ||
this.options.acceptFileTypes.test(file.name))) {
return 'acceptFileTypes';
}
if (this.options.maxFileSize &&
file.size > this.options.maxFileSize) {
return 'maxFileSize';
}
if (typeof file.size === 'number' &&
file.size < this.options.minFileSize) {
return 'minFileSize';
}
return null;
},
_validate: function (files) {
var that = this,
valid = !!files.length;
$.each(files, function (index, file) {
file.error = that._hasError(file);
if (file.error) {
valid = false;
}
});
return valid;
},
_renderTemplate: function (func, files) {
if (!func) {
return $();
}
var result = func({
files: files,
formatFileSize: this._formatFileSize,
options: this.options
});
if (result instanceof $) {
return result;
}
return $(this.options.templatesContainer).html(result).children();
},
_renderPreview: function (file, node) {
var that = this,
options = this.options,
deferred = $.Deferred();
return ((loadImage && loadImage(
file,
function (img) {
node.append(img);
that._forceReflow(node);
that._transition(node).done(function () {
deferred.resolveWith(node);
});
},
{
maxWidth: options.previewMaxWidth,
maxHeight: options.previewMaxHeight,
canvas: options.previewAsCanvas
}
)) || deferred.resolveWith(node)) && deferred;
},
_renderPreviews: function (files, nodes) {
var that = this,
options = this.options;
nodes.find('.preview span').each(function (index, element) {
var file = files[index];
if (options.previewSourceFileTypes.test(file.type) &&
($.type(options.previewSourceMaxFileSize) !== 'number' ||
file.size < options.previewSourceMaxFileSize)) {
that._processingQueue = that._processingQueue.pipe(function () {
var deferred = $.Deferred();
that._renderPreview(file, $(element)).done(
function () {
deferred.resolveWith(that);
}
);
return deferred.promise();
});
}
});
return this._processingQueue;
},
_renderUpload: function (files) {
return this._renderTemplate(
this.options.uploadTemplate,
files
);
},
_renderDownload: function (files) {
return this._renderTemplate(
this.options.downloadTemplate,
files
).find('a[download]').each(this._enableDragToDesktop).end();
},
_startHandler: function (e) {
e.preventDefault();
var button = $(this),
template = button.closest('.template-upload'),
data = template.data('data');
if (data && data.submit && !data.jqXHR && data.submit()) {
button.prop('disabled', true);
}
},
_cancelHandler: function (e) {
e.preventDefault();
var template = $(this).closest('.template-upload'),
data = template.data('data') || {};
if (!data.jqXHR) {
data.errorThrown = 'abort';
e.data.fileupload._trigger('fail', e, data);
} else {
data.jqXHR.abort();
}
},
_deleteHandler: function (e) {
e.preventDefault();
var button = $(this);
e.data.fileupload._trigger('destroy', e, {
context: button.closest('.template-download'),
url: button.attr('data-url'),
type: button.attr('data-type') || 'DELETE',
dataType: e.data.fileupload.options.dataType
});
},
_forceReflow: function (node) {
this._reflow = $.support.transition &&
node.length && node[0].offsetWidth;
},
_transition: function (node) {
var that = this,
deferred = $.Deferred();
if ($.support.transition && node.hasClass('fade')) {
node.bind(
$.support.transition.end,
function (e) {
// Make sure we don't respond to other transitions events
// in the container element, e.g. from button elements:
if (e.target === node[0]) {
node.unbind($.support.transition.end);
deferred.resolveWith(node);
}
}
).toggleClass('in');
} else {
node.toggleClass('in');
deferred.resolveWith(node);
}
return deferred;
},
_initButtonBarEventHandlers: function () {
var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
filesList = this.options.filesContainer,
ns = this.options.namespace;
fileUploadButtonBar.find('.start')
.bind('click.' + ns, function (e) {
e.preventDefault();
filesList.find('.start button').click();
});
fileUploadButtonBar.find('.cancel')
.bind('click.' + ns, function (e) {
e.preventDefault();
filesList.find('.cancel button').click();
});
fileUploadButtonBar.find('.delete')
.bind('click.' + ns, function (e) {
e.preventDefault();
filesList.find('.delete input:checked')
.siblings('button').click();
fileUploadButtonBar.find('.toggle')
.prop('checked', false);
});
fileUploadButtonBar.find('.toggle')
.bind('change.' + ns, function (e) {
filesList.find('.delete input').prop(
'checked',
$(this).is(':checked')
);
});
},
_destroyButtonBarEventHandlers: function () {
this.element.find('.fileupload-buttonbar button')
.unbind('click.' + this.options.namespace);
this.element.find('.fileupload-buttonbar .toggle')
.unbind('change.' + this.options.namespace);
},
_initEventHandlers: function () {
parentWidget.prototype._initEventHandlers.call(this);
var eventData = {fileupload: this};
this.options.filesContainer
.delegate(
'.start button',
'click.' + this.options.namespace,
eventData,
this._startHandler
)
.delegate(
'.cancel button',
'click.' + this.options.namespace,
eventData,
this._cancelHandler
)
.delegate(
'.delete button',
'click.' + this.options.namespace,
eventData,
this._deleteHandler
);
this._initButtonBarEventHandlers();
},
_destroyEventHandlers: function () {
var options = this.options;
this._destroyButtonBarEventHandlers();
options.filesContainer
.undelegate('.start button', 'click.' + options.namespace)
.undelegate('.cancel button', 'click.' + options.namespace)
.undelegate('.delete button', 'click.' + options.namespace);
parentWidget.prototype._destroyEventHandlers.call(this);
},
_enableFileInputButton: function () {
this.element.find('.fileinput-button input')
.prop('disabled', false)
.parent().removeClass('disabled');
},
_disableFileInputButton: function () {
this.element.find('.fileinput-button input')
.prop('disabled', true)
.parent().addClass('disabled');
},
_initTemplates: function () {
var options = this.options;
options.templatesContainer = document.createElement(
options.filesContainer.prop('nodeName')
);
if (tmpl) {
if (options.uploadTemplateId) {
options.uploadTemplate = tmpl(options.uploadTemplateId);
}
if (options.downloadTemplateId) {
options.downloadTemplate = tmpl(options.downloadTemplateId);
}
}
},
_initFilesContainer: function () {
var options = this.options;
if (options.filesContainer === undefined) {
options.filesContainer = this.element.find('.files');
} else if (!(options.filesContainer instanceof $)) {
options.filesContainer = $(options.filesContainer);
}
},
_initSpecialOptions: function () {
parentWidget.prototype._initSpecialOptions.call(this);
this._initFilesContainer();
this._initTemplates();
},
_create: function () {
parentWidget.prototype._create.call(this);
this._refreshOptionsList.push(
'filesContainer',
'uploadTemplateId',
'downloadTemplateId'
);
if (!$.blueimpIP) {
this._processingQueue = $.Deferred().resolveWith(this).promise();
this.resize = function () {
return this._processingQueue;
};
}
},
enable: function () {
parentWidget.prototype.enable.call(this);
this.element.find('input, button').prop('disabled', false);
this._enableFileInputButton();
},
disable: function () {
this.element.find('input, button').prop('disabled', true);
this._disableFileInputButton();
parentWidget.prototype.disable.call(this);
}
});
}));

View File

@ -0,0 +1,171 @@
/*
* jQuery Iframe Transport Plugin 1.4
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/*jslint unparam: true, nomen: true */
/*global define, window, document */
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define(['jquery'], factory);
} else {
// Browser globals:
factory(window.jQuery);
}
}(function ($) {
'use strict';
// Helper variable to create unique names for the transport iframes:
var counter = 0;
// The iframe transport accepts three additional options:
// options.fileInput: a jQuery collection of file input fields
// options.paramName: the parameter name for the file form data,
// overrides the name property of the file input field(s),
// can be a string or an array of strings.
// options.formData: an array of objects with name and value properties,
// equivalent to the return data of .serializeArray(), e.g.:
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
$.ajaxTransport('iframe', function (options) {
if (options.async && (options.type === 'POST' || options.type === 'GET')) {
var form,
iframe;
return {
send: function (_, completeCallback) {
form = $('<form style="display:none;"></form>');
// javascript:false as initial iframe src
// prevents warning popups on HTTPS in IE6.
// IE versions below IE8 cannot set the name property of
// elements that have already been added to the DOM,
// so we set the name along with the iframe HTML markup:
iframe = $(
'<iframe src="javascript:false;" name="iframe-transport-' +
(counter += 1) + '"></iframe>'
).bind('load', function () {
var fileInputClones,
paramNames = $.isArray(options.paramName) ?
options.paramName : [options.paramName];
iframe
.unbind('load')
.bind('load', function () {
var response;
// Wrap in a try/catch block to catch exceptions thrown
// when trying to access cross-domain iframe contents:
try {
response = iframe.contents();
// Google Chrome and Firefox do not throw an
// exception when calling iframe.contents() on
// cross-domain requests, so we unify the response:
if (!response.length || !response[0].firstChild) {
throw new Error();
}
} catch (e) {
response = undefined;
}
// The complete callback returns the
// iframe content document as response object:
completeCallback(
200,
'success',
{'iframe': response}
);
// Fix for IE endless progress bar activity bug
// (happens on form submits to iframe targets):
$('<iframe src="javascript:false;"></iframe>')
.appendTo(form);
form.remove();
});
form
.prop('target', iframe.prop('name'))
.prop('action', options.url)
.prop('method', options.type);
if (options.formData) {
$.each(options.formData, function (index, field) {
$('<input type="hidden"/>')
.prop('name', field.name)
.val(field.value)
.appendTo(form);
});
}
if (options.fileInput && options.fileInput.length &&
options.type === 'POST') {
fileInputClones = options.fileInput.clone();
// Insert a clone for each file input field:
options.fileInput.after(function (index) {
return fileInputClones[index];
});
if (options.paramName) {
options.fileInput.each(function (index) {
$(this).prop(
'name',
paramNames[index] || options.paramName
);
});
}
// Appending the file input fields to the hidden form
// removes them from their original location:
form
.append(options.fileInput)
.prop('enctype', 'multipart/form-data')
// enctype must be set as encoding for IE:
.prop('encoding', 'multipart/form-data');
}
form.submit();
// Insert the file input fields at their original location
// by replacing the clones with the originals:
if (fileInputClones && fileInputClones.length) {
options.fileInput.each(function (index, input) {
var clone = $(fileInputClones[index]);
$(input).prop('name', clone.prop('name'));
clone.replaceWith(input);
});
}
});
form.append(iframe).appendTo(document.body);
},
abort: function () {
if (iframe) {
// javascript:false as iframe src aborts the request
// and prevents warning popups on HTTPS in IE6.
// concat is used to avoid the "Script URL" JSLint error:
iframe
.unbind('load')
.prop('src', 'javascript'.concat(':false;'));
}
if (form) {
form.remove();
}
}
};
}
});
// The iframe transport returns the iframe content document as response.
// The following adds converters from iframe to text, json, html, and script:
$.ajaxSetup({
converters: {
'iframe text': function (iframe) {
return $(iframe[0].body).text();
},
'iframe json': function (iframe) {
return $.parseJSON($(iframe[0].body).text());
},
'iframe html': function (iframe) {
return $(iframe[0].body).html();
},
'iframe script': function (iframe) {
return $.globalEval($(iframe[0].body).text());
}
}
});
}));

View File

@ -0,0 +1,282 @@
/*
* jQuery UI Widget 1.8.18+amd
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Widget
*/
(function (factory) {
if (typeof define === "function" && define.amd) {
// Register as an anonymous AMD module:
define(["jquery"], factory);
} else {
// Browser globals:
factory(jQuery);
}
}(function( $, undefined ) {
// jQuery 1.4+
if ( $.cleanData ) {
var _cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
try {
$( elem ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
}
_cleanData( elems );
};
} else {
var _remove = $.fn.remove;
$.fn.remove = function( selector, keepData ) {
return this.each(function() {
if ( !keepData ) {
if ( !selector || $.filter( selector, [ this ] ).length ) {
$( "*", this ).add( [ this ] ).each(function() {
try {
$( this ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
});
}
}
return _remove.call( $(this), selector, keepData );
});
};
}
$.widget = function( name, base, prototype ) {
var namespace = name.split( "." )[ 0 ],
fullName;
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
// create selector for plugin
$.expr[ ":" ][ fullName ] = function( elem ) {
return !!$.data( elem, name );
};
$[ namespace ] = $[ namespace ] || {};
$[ namespace ][ name ] = function( options, element ) {
// allow instantiation without initializing for simple inheritance
if ( arguments.length ) {
this._createWidget( options, element );
}
};
var basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
// $.each( basePrototype, function( key, val ) {
// if ( $.isPlainObject(val) ) {
// basePrototype[ key ] = $.extend( {}, val );
// }
// });
basePrototype.options = $.extend( true, {}, basePrototype.options );
$[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
namespace: namespace,
widgetName: name,
widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
widgetBaseClass: fullName
}, prototype );
$.widget.bridge( name, $[ namespace ][ name ] );
};
$.widget.bridge = function( name, object ) {
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = Array.prototype.slice.call( arguments, 1 ),
returnValue = this;
// allow multiple hashes to be passed on init
options = !isMethodCall && args.length ?
$.extend.apply( null, [ true, options ].concat(args) ) :
options;
// prevent calls to internal methods
if ( isMethodCall && options.charAt( 0 ) === "_" ) {
return returnValue;
}
if ( isMethodCall ) {
this.each(function() {
var instance = $.data( this, name ),
methodValue = instance && $.isFunction( instance[options] ) ?
instance[ options ].apply( instance, args ) :
instance;
// TODO: add this back in 1.9 and use $.error() (see #5972)
// if ( !instance ) {
// throw "cannot call methods on " + name + " prior to initialization; " +
// "attempted to call method '" + options + "'";
// }
// if ( !$.isFunction( instance[options] ) ) {
// throw "no such method '" + options + "' for " + name + " widget instance";
// }
// var methodValue = instance[ options ].apply( instance, args );
if ( methodValue !== instance && methodValue !== undefined ) {
returnValue = methodValue;
return false;
}
});
} else {
this.each(function() {
var instance = $.data( this, name );
if ( instance ) {
instance.option( options || {} )._init();
} else {
$.data( this, name, new object( options, this ) );
}
});
}
return returnValue;
};
};
$.Widget = function( options, element ) {
// allow instantiation without initializing for simple inheritance
if ( arguments.length ) {
this._createWidget( options, element );
}
};
$.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
options: {
disabled: false
},
_createWidget: function( options, element ) {
// $.widget.bridge stores the plugin instance, but we do it anyway
// so that it's stored even before the _create function runs
$.data( element, this.widgetName, this );
this.element = $( element );
this.options = $.extend( true, {},
this.options,
this._getCreateOptions(),
options );
var self = this;
this.element.bind( "remove." + this.widgetName, function() {
self.destroy();
});
this._create();
this._trigger( "create" );
this._init();
},
_getCreateOptions: function() {
return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
},
_create: function() {},
_init: function() {},
destroy: function() {
this.element
.unbind( "." + this.widgetName )
.removeData( this.widgetName );
this.widget()
.unbind( "." + this.widgetName )
.removeAttr( "aria-disabled" )
.removeClass(
this.widgetBaseClass + "-disabled " +
"ui-state-disabled" );
},
widget: function() {
return this.element;
},
option: function( key, value ) {
var options = key;
if ( arguments.length === 0 ) {
// don't return a reference to the internal hash
return $.extend( {}, this.options );
}
if (typeof key === "string" ) {
if ( value === undefined ) {
return this.options[ key ];
}
options = {};
options[ key ] = value;
}
this._setOptions( options );
return this;
},
_setOptions: function( options ) {
var self = this;
$.each( options, function( key, value ) {
self._setOption( key, value );
});
return this;
},
_setOption: function( key, value ) {
this.options[ key ] = value;
if ( key === "disabled" ) {
this.widget()
[ value ? "addClass" : "removeClass"](
this.widgetBaseClass + "-disabled" + " " +
"ui-state-disabled" )
.attr( "aria-disabled", value );
}
return this;
},
enable: function() {
return this._setOption( "disabled", false );
},
disable: function() {
return this._setOption( "disabled", true );
},
_trigger: function( type, event, data ) {
var prop, orig,
callback = this.options[ type ];
data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[ 0 ];
// copy original event properties over to the new event
orig = event.originalEvent;
if ( orig ) {
for ( prop in orig ) {
if ( !( prop in event ) ) {
event[ prop ] = orig[ prop ];
}
}
}
this.element.trigger( event, data );
return !( $.isFunction(callback) &&
callback.call( this.element[0], event, data ) === false ||
event.isDefaultPrevented() );
}
};
}));

View File

@ -0,0 +1,101 @@
<script id="file-upload-tmpl" type="text/html">
<form id="fileupload" action="../server/php/" method="POST" enctype="multipart/form-data">
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
<div class="row fileupload-buttonbar">
<div class="span7">
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="icon-plus icon-white"></i>
<span>Add files...</span>
<input type="file" name="files[]" multiple>
</span>
<button type="submit" class="btn btn-primary start">
<i class="icon-upload icon-white"></i>
<span>Start upload</span>
</button>
<button type="reset" class="btn btn-warning cancel">
<i class="icon-ban-circle icon-white"></i>
<span>Cancel upload</span>
</button>
<button type="button" class="btn btn-danger delete">
<i class="icon-trash icon-white"></i>
<span>Delete</span>
</button>
<input type="checkbox" class="toggle">
</div>
<div class="span5">
<!-- The global progress bar -->
<div class="progress progress-success progress-striped active">
<div class="bar" style="width:0%;"></div>
</div>
</div>
</div>
<!-- The loading indicator is shown during image processing -->
<div class="fileupload-loading"></div>
<br>
<!-- The table listing the files available for upload/download -->
<table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
</form>
</script>
<script id="template-upload" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-upload">
<td class="preview"><span class=""></span></td>
<td class="name"><span>{%=file.name%}</span></td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
{% if (file.error) { %}
<td class="error" colspan="2"><span class="label label-important">{%=$.i18n.prop('locale.fileupload.error')%}</span> {%=$.i18n.prop('locale.fileupload.errors',[file.error]) || file.error%}</td>
{% } else if (o.files.valid && !i) { %}
<td>
<div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
</td>
<td class="start">{% if (!o.options.autoUpload) { %}
<button class="btn btn-primary">
<i class="icon-upload icon-white"></i>
<span>{%=$.i18n.prop('locale.fileupload.start')%}</span>
</button>
{% } %}</td>
{% } else { %}
<td colspan="2"></td>
{% } %}
<td class="cancel">{% if (!i) { %}
<button class="btn btn-warning">
<i class="icon-ban-circle icon-white"></i>
<span>{%=$.i18n.prop('locale.fileupload.cancel')%}</span>
</button>
{% } %}</td>
</tr>
{% } %}
</script>
<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-download">
{% if (file.error) { %}
<td></td>
<td class="name"><span>{%=file.name%}</span></td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
<td class="error" colspan="2"><span class="label label-important">{%=$.i18n.prop('locale.fileupload.error')%}</span> {%=$.i18n.prop('locale.fileupload.errors',[file.error]) || file.error%}</td>
{% } else { %}
<td class="preview">{% if (file.thumbnail_url) { %}
<a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
{% } %}</td>
<td class="name">
<a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
</td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
<td colspan="2"></td>
{% } %}
<td class="delete">
<button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
<i class="icon-trash icon-white"></i>
<span>{%=$.i18n.prop('locale.fileupload.destroy')%}</span>
</button>
<input type="checkbox" name="delete" value="1">
</td>
</tr>
{% } %}
</script>

View File

@ -27,6 +27,9 @@
<li>
<a href="#" id="menu-find-browse-a" onclick="displayBrowse(true)">${$.i18n.prop('menu.artifacts.browse')}</a>
</li>
<li>
<a href="#" id="menu-find-upload-a" onclick="displayUploadArtifact(true)">${$.i18n.prop('menu.artifacts.upload')}</a>
</li>
</ul>
<ul class="nav nav-list" redback-permissions="{permissions: ['archiva-manage-configuration']}">

View File

@ -0,0 +1 @@
(function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?![^%]*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){if(b)return{"\n":"\\n","\r":"\\r","\t":"\\t"," ":" "}[a]||"\\"+a;if(c)return c==="="?"'+_e("+d+")+'":"'+("+d+"||'')+'";if(e)return"';";if(f)return"_s+='"},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&#39;"},b.encode=function(a){return String(a||"").replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e&&(s||'')||_e(s);},include=function(s,d){_s+=tmpl(s,d);}",typeof define=="function"&&define.amd?define(function(){return b}):a.tmpl=b})(this);