From 5a77568a2c4996c617ced3fd1e774aeeee910ac5 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Mon, 25 Nov 2019 20:53:09 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E6=9C=AC=E5=9C=B0=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/dev/index.html | 64 - public/explore/result.html | 47 - public/favicon.ico | Bin 3870 -> 0 bytes public/fileShare.html | 60 - public/folderShare.html | 60 - public/home/download.html | 22 - public/home/home.html | 44 - public/home/navbar_home.html | 161 - public/home/quota.html | 26 - public/index.html | 64 +- public/lock.html | 60 - public/manifest.json | 15 - public/markdown.html | 60 - public/myShare.html | 60 - public/profile.html | 60 - public/profile/profile.html | 79 - public/setting.html | 60 - public/share/share_dir.html | 48 - public/share/share_home.html | 22 - public/share/share_lock.html | 28 - public/share/share_single.html | 48 - public/static/js/uploader/i18n/zh_CN.js | 2 + public/static/js/uploader/main.js | 274 + public/static/js/uploader/moxie.js | 11509 ++++++++++++++++ public/static/js/uploader/plupload.dev.js | 2491 ++++ .../static/js/uploader/tempCodeRunnerFile.js | 1 + public/static/js/uploader/ui.js | 278 + public/static/js/uploader/uploader.js | 1856 +++ public/video.html | 60 - public/viewer/markdown.html | 74 - public/viewer/video.html | 70 - src/App.js | 2 +- src/component/FileManager/FileIcon.js | 10 +- src/component/FileManager/Folder.js | 10 +- src/component/FileManager/Navigator.js | 2 +- src/component/FileManager/SmallIcon.js | 15 +- src/component/FileManager/TableRow.js | 8 +- src/component/Upload/FileList.js | 3 +- src/component/Uploader.js | 15 +- src/index.js | 2 +- src/reducers/index.js | 32 + src/setupProxy.js | 35 - 42 files changed, 16510 insertions(+), 1327 deletions(-) delete mode 100644 public/dev/index.html delete mode 100644 public/explore/result.html delete mode 100644 public/favicon.ico delete mode 100644 public/fileShare.html delete mode 100644 public/folderShare.html delete mode 100644 public/home/download.html delete mode 100644 public/home/home.html delete mode 100644 public/home/navbar_home.html delete mode 100644 public/home/quota.html delete mode 100644 public/lock.html delete mode 100644 public/manifest.json delete mode 100644 public/markdown.html delete mode 100644 public/myShare.html delete mode 100644 public/profile.html delete mode 100644 public/profile/profile.html delete mode 100644 public/setting.html delete mode 100644 public/share/share_dir.html delete mode 100644 public/share/share_home.html delete mode 100644 public/share/share_lock.html delete mode 100644 public/share/share_single.html create mode 100644 public/static/js/uploader/i18n/zh_CN.js create mode 100644 public/static/js/uploader/main.js create mode 100644 public/static/js/uploader/moxie.js create mode 100644 public/static/js/uploader/plupload.dev.js create mode 100644 public/static/js/uploader/tempCodeRunnerFile.js create mode 100644 public/static/js/uploader/ui.js create mode 100644 public/static/js/uploader/uploader.js delete mode 100644 public/video.html delete mode 100644 public/viewer/markdown.html delete mode 100644 public/viewer/video.html delete mode 100644 src/setupProxy.js diff --git a/public/dev/index.html b/public/dev/index.html deleted file mode 100644 index be126e8..0000000 --- a/public/dev/index.html +++ /dev/null @@ -1,64 +0,0 @@ - - - -
- - - - - -..tags that might be enclosing the response + if (Basic.typeOf(_response) === 'string' && !!window.JSON) { + try { + return JSON.parse(_response.replace(/^\s*
]*>/, '').replace(/<\/pre>\s*$/, ''));
+ } catch (ex) {
+ return null;
+ }
+ }
+ } else if ('document' === responseType) {
+
+ }
+ return _response;
+ },
+
+ abort: function() {
+ var target = this;
+
+ if (_iframe && _iframe.contentWindow) {
+ if (_iframe.contentWindow.stop) { // FireFox/Safari/Chrome
+ _iframe.contentWindow.stop();
+ } else if (_iframe.contentWindow.document.execCommand) { // IE
+ _iframe.contentWindow.document.execCommand('Stop');
+ } else {
+ _iframe.src = "about:blank";
+ }
+ }
+
+ cleanup.call(this, function() {
+ // target.dispatchEvent('readystatechange');
+ target.dispatchEvent('abort');
+ });
+ }
+ });
+ }
+
+ return (extensions.XMLHttpRequest = XMLHttpRequest);
+});
+
+// Included from: src/javascript/runtime/html4/image/Image.js
+
+/**
+ * Image.js
+ *
+ * Copyright 2013, Moxiecode Systems AB
+ * Released under GPL License.
+ *
+ * License: http://www.plupload.com/license
+ * Contributing: http://www.plupload.com/contributing
+ */
+
+/**
+@class moxie/runtime/html4/image/Image
+@private
+*/
+define("moxie/runtime/html4/image/Image", [
+ "moxie/runtime/html4/Runtime",
+ "moxie/runtime/html5/image/Image"
+], function(extensions, Image) {
+ return (extensions.Image = Image);
+});
+
+expose(["moxie/core/utils/Basic","moxie/core/utils/Encode","moxie/core/utils/Env","moxie/core/Exceptions","moxie/core/utils/Dom","moxie/core/EventTarget","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/core/I18n","moxie/core/utils/Mime","moxie/file/FileInput","moxie/file/File","moxie/file/FileDrop","moxie/file/FileReader","moxie/core/utils/Url","moxie/runtime/RuntimeTarget","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events","moxie/runtime/html5/image/ResizerCanvas"]);
+})(this);
+}));
\ No newline at end of file
diff --git a/public/static/js/uploader/plupload.dev.js b/public/static/js/uploader/plupload.dev.js
new file mode 100644
index 0000000..a5bacfc
--- /dev/null
+++ b/public/static/js/uploader/plupload.dev.js
@@ -0,0 +1,2491 @@
+/**
+ * Plupload - multi-runtime File Uploader
+ * v2.3.1
+ *
+ * Copyright 2013, Moxiecode Systems AB
+ * Released under GPL License.
+ *
+ * License: http://www.plupload.com/license
+ * Contributing: http://www.plupload.com/contributing
+ *
+ * Date: 2017-02-06
+ */
+;(function (global, factory) {
+ var extract = function() {
+ var ctx = {};
+ factory.apply(ctx, arguments);
+ return ctx.plupload;
+ };
+
+ if (typeof define === "function" && define.amd) {
+ define("plupload", ['./moxie'], extract);
+ } else if (typeof module === "object" && module.exports) {
+ module.exports = extract(require('../../../js-sdk-master 2/src/moxie'));
+ } else {
+ global.plupload = extract(global.moxie);
+ }
+}(this || window, function(moxie) {
+/**
+ * Plupload.js
+ *
+ * Copyright 2013, Moxiecode Systems AB
+ * Released under GPL License.
+ *
+ * License: http://www.plupload.com/license
+ * Contributing: http://www.plupload.com/contributing
+ */
+
+;(function(exports, o, undef) {
+
+var delay = window.setTimeout;
+var fileFilters = {};
+var u = o.core.utils;
+var Runtime = o.runtime.Runtime;
+
+// convert plupload features to caps acceptable by mOxie
+function normalizeCaps(settings) {
+ var features = settings.required_features, caps = {};
+
+ function resolve(feature, value, strict) {
+ // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
+ var map = {
+ chunks: 'slice_blob',
+ jpgresize: 'send_binary_string',
+ pngresize: 'send_binary_string',
+ progress: 'report_upload_progress',
+ multi_selection: 'select_multiple',
+ dragdrop: 'drag_and_drop',
+ drop_element: 'drag_and_drop',
+ headers: 'send_custom_headers',
+ urlstream_upload: 'send_binary_string',
+ canSendBinary: 'send_binary',
+ triggerDialog: 'summon_file_dialog'
+ };
+
+ if (map[feature]) {
+ caps[map[feature]] = value;
+ } else if (!strict) {
+ caps[feature] = value;
+ }
+ }
+
+ if (typeof(features) === 'string') {
+ plupload.each(features.split(/\s*,\s*/), function(feature) {
+ resolve(feature, true);
+ });
+ } else if (typeof(features) === 'object') {
+ plupload.each(features, function(value, feature) {
+ resolve(feature, value);
+ });
+ } else if (features === true) {
+ // check settings for required features
+ if (settings.chunk_size && settings.chunk_size > 0) {
+ caps.slice_blob = true;
+ }
+
+ if (!plupload.isEmptyObj(settings.resize) || settings.multipart === false) {
+ caps.send_binary_string = true;
+ }
+
+ if (settings.http_method) {
+ caps.use_http_method = settings.http_method;
+ }
+
+ plupload.each(settings, function(value, feature) {
+ resolve(feature, !!value, true); // strict check
+ });
+ }
+
+ return caps;
+}
+
+/**
+ * @module plupload
+ * @static
+ */
+var plupload = {
+ /**
+ * Plupload version will be replaced on build.
+ *
+ * @property VERSION
+ * @for Plupload
+ * @static
+ * @final
+ */
+ VERSION : '2.3.1',
+
+ /**
+ * The state of the queue before it has started and after it has finished
+ *
+ * @property STOPPED
+ * @static
+ * @final
+ */
+ STOPPED : 1,
+
+ /**
+ * Upload process is running
+ *
+ * @property STARTED
+ * @static
+ * @final
+ */
+ STARTED : 2,
+
+ /**
+ * File is queued for upload
+ *
+ * @property QUEUED
+ * @static
+ * @final
+ */
+ QUEUED : 1,
+
+ /**
+ * File is being uploaded
+ *
+ * @property UPLOADING
+ * @static
+ * @final
+ */
+ UPLOADING : 2,
+
+ /**
+ * File has failed to be uploaded
+ *
+ * @property FAILED
+ * @static
+ * @final
+ */
+ FAILED : 4,
+
+ /**
+ * File has been uploaded successfully
+ *
+ * @property DONE
+ * @static
+ * @final
+ */
+ DONE : 5,
+
+ // Error constants used by the Error event
+
+ /**
+ * Generic error for example if an exception is thrown inside Silverlight.
+ *
+ * @property GENERIC_ERROR
+ * @static
+ * @final
+ */
+ GENERIC_ERROR : -100,
+
+ /**
+ * HTTP transport error. For example if the server produces a HTTP status other than 200.
+ *
+ * @property HTTP_ERROR
+ * @static
+ * @final
+ */
+ HTTP_ERROR : -200,
+
+ /**
+ * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
+ *
+ * @property IO_ERROR
+ * @static
+ * @final
+ */
+ IO_ERROR : -300,
+
+ /**
+ * @property SECURITY_ERROR
+ * @static
+ * @final
+ */
+ SECURITY_ERROR : -400,
+
+ /**
+ * Initialization error. Will be triggered if no runtime was initialized.
+ *
+ * @property INIT_ERROR
+ * @static
+ * @final
+ */
+ INIT_ERROR : -500,
+
+ /**
+ * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
+ *
+ * @property FILE_SIZE_ERROR
+ * @static
+ * @final
+ */
+ FILE_SIZE_ERROR : -600,
+
+ /**
+ * File extension error. If the user selects a file that isn't valid according to the filters setting.
+ *
+ * @property FILE_EXTENSION_ERROR
+ * @static
+ * @final
+ */
+ FILE_EXTENSION_ERROR : -601,
+
+ /**
+ * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
+ *
+ * @property FILE_DUPLICATE_ERROR
+ * @static
+ * @final
+ */
+ FILE_DUPLICATE_ERROR : -602,
+
+ /**
+ * Runtime will try to detect if image is proper one. Otherwise will throw this error.
+ *
+ * @property IMAGE_FORMAT_ERROR
+ * @static
+ * @final
+ */
+ IMAGE_FORMAT_ERROR : -700,
+
+ /**
+ * While working on files runtime may run out of memory and will throw this error.
+ *
+ * @since 2.1.2
+ * @property MEMORY_ERROR
+ * @static
+ * @final
+ */
+ MEMORY_ERROR : -701,
+
+ /**
+ * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
+ *
+ * @property IMAGE_DIMENSIONS_ERROR
+ * @static
+ * @final
+ */
+ IMAGE_DIMENSIONS_ERROR : -702,
+
+ /**
+ * Mime type lookup table.
+ *
+ * @property mimeTypes
+ * @type Object
+ * @final
+ */
+ mimeTypes : u.Mime.mimes,
+
+ /**
+ * In some cases sniffing is the only way around :(
+ */
+ ua: u.Env,
+
+ /**
+ * Gets the true type of the built-in object (better version of typeof).
+ * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
+ *
+ * @method typeOf
+ * @static
+ * @param {Object} o Object to check.
+ * @return {String} Object [[Class]]
+ */
+ typeOf: u.Basic.typeOf,
+
+ /**
+ * Extends the specified object with another object.
+ *
+ * @method extend
+ * @static
+ * @param {Object} target Object to extend.
+ * @param {Object..} obj Multiple objects to extend with.
+ * @return {Object} Same as target, the extended object.
+ */
+ extend : u.Basic.extend,
+
+ /**
+ * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
+ * The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages
+ * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
+ * It's more probable for the earth to be hit with an asteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
+ * to an user unique key.
+ *
+ * @method guid
+ * @static
+ * @return {String} Virtually unique id.
+ */
+ guid : u.Basic.guid,
+
+ /**
+ * Get array of DOM Elements by their ids.
+ *
+ * @method get
+ * @param {String} id Identifier of the DOM Element
+ * @return {Array}
+ */
+ getAll : function get(ids) {
+ var els = [], el;
+
+ if (plupload.typeOf(ids) !== 'array') {
+ ids = [ids];
+ }
+
+ var i = ids.length;
+ while (i--) {
+ el = plupload.get(ids[i]);
+ if (el) {
+ els.push(el);
+ }
+ }
+
+ return els.length ? els : null;
+ },
+
+ /**
+ Get DOM element by id
+
+ @method get
+ @param {String} id Identifier of the DOM Element
+ @return {Node}
+ */
+ get: u.Dom.get,
+
+ /**
+ * Executes the callback function for each item in array/object. If you return false in the
+ * callback it will break the loop.
+ *
+ * @method each
+ * @static
+ * @param {Object} obj Object to iterate.
+ * @param {function} callback Callback function to execute for each item.
+ */
+ each : u.Basic.each,
+
+ /**
+ * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
+ *
+ * @method getPos
+ * @static
+ * @param {Element} node HTML element or element id to get x, y position from.
+ * @param {Element} root Optional root element to stop calculations at.
+ * @return {object} Absolute position of the specified element object with x, y fields.
+ */
+ getPos : u.Dom.getPos,
+
+ /**
+ * Returns the size of the specified node in pixels.
+ *
+ * @method getSize
+ * @static
+ * @param {Node} node Node to get the size of.
+ * @return {Object} Object with a w and h property.
+ */
+ getSize : u.Dom.getSize,
+
+ /**
+ * Encodes the specified string.
+ *
+ * @method xmlEncode
+ * @static
+ * @param {String} s String to encode.
+ * @return {String} Encoded string.
+ */
+ xmlEncode : function(str) {
+ var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
+
+ return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
+ return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
+ }) : str;
+ },
+
+ /**
+ * Forces anything into an array.
+ *
+ * @method toArray
+ * @static
+ * @param {Object} obj Object with length field.
+ * @return {Array} Array object containing all items.
+ */
+ toArray : u.Basic.toArray,
+
+ /**
+ * Find an element in array and return its index if present, otherwise return -1.
+ *
+ * @method inArray
+ * @static
+ * @param {mixed} needle Element to find
+ * @param {Array} array
+ * @return {Int} Index of the element, or -1 if not found
+ */
+ inArray : u.Basic.inArray,
+
+ /**
+ Recieve an array of functions (usually async) to call in sequence, each function
+ receives a callback as first argument that it should call, when it completes. Finally,
+ after everything is complete, main callback is called. Passing truthy value to the
+ callback as a first argument will interrupt the sequence and invoke main callback
+ immediately.
+
+ @method inSeries
+ @static
+ @param {Array} queue Array of functions to call in sequence
+ @param {Function} cb Main callback that is called in the end, or in case of error
+ */
+ inSeries: u.Basic.inSeries,
+
+ /**
+ * Extends the language pack object with new items.
+ *
+ * @method addI18n
+ * @static
+ * @param {Object} pack Language pack items to add.
+ * @return {Object} Extended language pack object.
+ */
+ addI18n : o.core.I18n.addI18n,
+
+ /**
+ * Translates the specified string by checking for the english string in the language pack lookup.
+ *
+ * @method translate
+ * @static
+ * @param {String} str String to look for.
+ * @return {String} Translated string or the input string if it wasn't found.
+ */
+ translate : o.core.I18n.translate,
+
+ /**
+ * Pseudo sprintf implementation - simple way to replace tokens with specified values.
+ *
+ * @param {String} str String with tokens
+ * @return {String} String with replaced tokens
+ */
+ sprintf : u.Basic.sprintf,
+
+ /**
+ * Checks if object is empty.
+ *
+ * @method isEmptyObj
+ * @static
+ * @param {Object} obj Object to check.
+ * @return {Boolean}
+ */
+ isEmptyObj : u.Basic.isEmptyObj,
+
+ /**
+ * Checks if specified DOM element has specified class.
+ *
+ * @method hasClass
+ * @static
+ * @param {Object} obj DOM element like object to add handler to.
+ * @param {String} name Class name
+ */
+ hasClass : u.Dom.hasClass,
+
+ /**
+ * Adds specified className to specified DOM element.
+ *
+ * @method addClass
+ * @static
+ * @param {Object} obj DOM element like object to add handler to.
+ * @param {String} name Class name
+ */
+ addClass : u.Dom.addClass,
+
+ /**
+ * Removes specified className from specified DOM element.
+ *
+ * @method removeClass
+ * @static
+ * @param {Object} obj DOM element like object to add handler to.
+ * @param {String} name Class name
+ */
+ removeClass : u.Dom.removeClass,
+
+ /**
+ * Returns a given computed style of a DOM element.
+ *
+ * @method getStyle
+ * @static
+ * @param {Object} obj DOM element like object.
+ * @param {String} name Style you want to get from the DOM element
+ */
+ getStyle : u.Dom.getStyle,
+
+ /**
+ * Adds an event handler to the specified object and store reference to the handler
+ * in objects internal Plupload registry (@see removeEvent).
+ *
+ * @method addEvent
+ * @static
+ * @param {Object} obj DOM element like object to add handler to.
+ * @param {String} name Name to add event listener to.
+ * @param {Function} callback Function to call when event occurs.
+ * @param {String} (optional) key that might be used to add specifity to the event record.
+ */
+ addEvent : u.Events.addEvent,
+
+ /**
+ * Remove event handler from the specified object. If third argument (callback)
+ * is not specified remove all events with the specified name.
+ *
+ * @method removeEvent
+ * @static
+ * @param {Object} obj DOM element to remove event listener(s) from.
+ * @param {String} name Name of event listener to remove.
+ * @param {Function|String} (optional) might be a callback or unique key to match.
+ */
+ removeEvent: u.Events.removeEvent,
+
+ /**
+ * Remove all kind of events from the specified object
+ *
+ * @method removeAllEvents
+ * @static
+ * @param {Object} obj DOM element to remove event listeners from.
+ * @param {String} (optional) unique key to match, when removing events.
+ */
+ removeAllEvents: u.Events.removeAllEvents,
+
+ /**
+ * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
+ *
+ * @method cleanName
+ * @static
+ * @param {String} s String to clean up.
+ * @return {String} Cleaned string.
+ */
+ cleanName : function(name) {
+ var i, lookup;
+
+ // Replace diacritics
+ lookup = [
+ /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
+ /\307/g, 'C', /\347/g, 'c',
+ /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
+ /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
+ /\321/g, 'N', /\361/g, 'n',
+ /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
+ /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
+ ];
+
+ for (i = 0; i < lookup.length; i += 2) {
+ name = name.replace(lookup[i], lookup[i + 1]);
+ }
+
+ // Replace whitespace
+ name = name.replace(/\s+/g, '_');
+
+ // Remove anything else
+ name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
+
+ return name;
+ },
+
+ /**
+ * Builds a full url out of a base URL and an object with items to append as query string items.
+ *
+ * @method buildUrl
+ * @static
+ * @param {String} url Base URL to append query string items to.
+ * @param {Object} items Name/value object to serialize as a querystring.
+ * @return {String} String with url + serialized query string items.
+ */
+ buildUrl: function(url, items) {
+ var query = '';
+
+ plupload.each(items, function(value, name) {
+ query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
+ });
+
+ if (query) {
+ url += (url.indexOf('?') > 0 ? '&' : '?') + query;
+ }
+
+ return url;
+ },
+
+ /**
+ * Formats the specified number as a size string for example 1024 becomes 1 KB.
+ *
+ * @method formatSize
+ * @static
+ * @param {Number} size Size to format as string.
+ * @return {String} Formatted size string.
+ */
+ formatSize : function(size) {
+
+ if (size === undef || /\D/.test(size)) {
+ return plupload.translate('N/A');
+ }
+
+ function round(num, precision) {
+ return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
+ }
+
+ var boundary = Math.pow(1024, 4);
+
+ // TB
+ if (size > boundary) {
+ return round(size / boundary, 1) + " " + plupload.translate('tb');
+ }
+
+ // GB
+ if (size > (boundary/=1024)) {
+ return round(size / boundary, 1) + " " + plupload.translate('gb');
+ }
+
+ // MB
+ if (size > (boundary/=1024)) {
+ return round(size / boundary, 1) + " " + plupload.translate('mb');
+ }
+
+ // KB
+ if (size > 1024) {
+ return Math.round(size / 1024) + " " + plupload.translate('kb');
+ }
+
+ return size + " " + plupload.translate('b');
+ },
+
+
+ /**
+ * Parses the specified size string into a byte value. For example 10kb becomes 10240.
+ *
+ * @method parseSize
+ * @static
+ * @param {String|Number} size String to parse or number to just pass through.
+ * @return {Number} Size in bytes.
+ */
+ parseSize : u.Basic.parseSizeStr,
+
+
+ /**
+ * A way to predict what runtime will be choosen in the current environment with the
+ * specified settings.
+ *
+ * @method predictRuntime
+ * @static
+ * @param {Object|String} config Plupload settings to check
+ * @param {String} [runtimes] Comma-separated list of runtimes to check against
+ * @return {String} Type of compatible runtime
+ */
+ predictRuntime : function(config, runtimes) {
+ var up, runtime;
+
+ up = new plupload.Uploader(config);
+ runtime = Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
+ up.destroy();
+ return runtime;
+ },
+
+ /**
+ * Registers a filter that will be executed for each file added to the queue.
+ * If callback returns false, file will not be added.
+ *
+ * Callback receives two arguments: a value for the filter as it was specified in settings.filters
+ * and a file to be filtered. Callback is executed in the context of uploader instance.
+ *
+ * @method addFileFilter
+ * @static
+ * @param {String} name Name of the filter by which it can be referenced in settings.filters
+ * @param {String} cb Callback - the actual routine that every added file must pass
+ */
+ addFileFilter: function(name, cb) {
+ fileFilters[name] = cb;
+ }
+};
+
+
+plupload.addFileFilter('mime_types', function(filters, file, cb) {
+ if (filters.length && !filters.regexp.test(file.name)) {
+ this.trigger('Error', {
+ code : plupload.FILE_EXTENSION_ERROR,
+ message : plupload.translate('File extension error.'),
+ file : file
+ });
+ cb(false);
+ } else {
+ cb(true);
+ }
+});
+
+
+plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
+ var undef;
+
+ maxSize = plupload.parseSize(maxSize);
+
+ // Invalid file size
+ if (file.size !== undef && maxSize && file.size > maxSize) {
+ this.trigger('Error', {
+ code : plupload.FILE_SIZE_ERROR,
+ message : plupload.translate('File size error.'),
+ file : file
+ });
+ cb(false);
+ } else {
+ cb(true);
+ }
+});
+
+
+plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
+ if (value) {
+ var ii = this.files.length;
+ while (ii--) {
+ // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
+ if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
+ this.trigger('Error', {
+ code : plupload.FILE_DUPLICATE_ERROR,
+ message : plupload.translate('Duplicate file error.'),
+ file : file
+ });
+ cb(false);
+ return;
+ }
+ }
+ }
+ cb(true);
+});
+
+
+/**
+@class Uploader
+@constructor
+
+@param {Object} settings For detailed information about each option check documentation.
+ @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
+ @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
+ @param {String|DOMElement} [settings.container] id of the DOM element or DOM element itself that will be used to wrap uploader structures. Defaults to immediate parent of the `browse_button` element.
+ @param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
+ @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
+ @param {Object} [settings.filters={}] Set of file type filters.
+ @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
+ @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
+ @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
+ @param {String} [settings.flash_swf_url] URL of the Flash swf.
+ @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
+ @param {String} [settings.http_method="POST"] HTTP method to use during upload (only PUT or POST allowed).
+ @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
+ @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
+ @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
+ @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
+ @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
+ @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
+ @param {Number} [settings.resize.width] If image is bigger, it will be resized.
+ @param {Number} [settings.resize.height] If image is bigger, it will be resized.
+ @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
+ @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
+ @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
+ @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
+ @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
+ @param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
+ @param {String} settings.url URL of the server-side upload handler.
+ @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
+
+*/
+plupload.Uploader = function(options) {
+ /**
+ Fires when the current RunTime has been initialized.
+
+ @event Init
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires after the init event incase you need to perform actions there.
+
+ @event PostInit
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires when the option is changed in via uploader.setOption().
+
+ @event OptionChanged
+ @since 2.1
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {String} name Name of the option that was changed
+ @param {Mixed} value New value for the specified option
+ @param {Mixed} oldValue Previous value of the option
+ */
+
+ /**
+ Fires when the silverlight/flash or other shim needs to move.
+
+ @event Refresh
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires when the overall state is being changed for the upload queue.
+
+ @event StateChanged
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires when browse_button is clicked and browse dialog shows.
+
+ @event Browse
+ @since 2.1.2
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires for every filtered file before it is added to the queue.
+
+ @event FileFiltered
+ @since 2.1
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file Another file that has to be added to the queue.
+ */
+
+ /**
+ Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
+
+ @event QueueChanged
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+
+ /**
+ Fires after files were filtered and added to the queue.
+
+ @event FilesAdded
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {Array} files Array of file objects that were added to queue by the user.
+ */
+
+ /**
+ Fires when file is removed from the queue.
+
+ @event FilesRemoved
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {Array} files Array of files that got removed.
+ */
+
+ /**
+ Fires just before a file is uploaded. Can be used to cancel the upload for the specified file
+ by returning false from the handler.
+
+ @event BeforeUpload
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file File to be uploaded.
+ */
+
+ /**
+ Fires when a file is to be uploaded by the runtime.
+
+ @event UploadFile
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file File to be uploaded.
+ */
+
+ /**
+ Fires while a file is being uploaded. Use this event to update the current file upload progress.
+
+ @event UploadProgress
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file File that is currently being uploaded.
+ */
+
+ /**
+ * Fires just before a chunk is uploaded. This event enables you to override settings
+ * on the uploader instance before the chunk is uploaded.
+ *
+ * @event BeforeChunkUpload
+ * @param {plupload.Uploader} uploader Uploader instance sending the event.
+ * @param {plupload.File} file File to be uploaded.
+ * @param {Object} args POST params to be sent.
+ * @param {Blob} chunkBlob Current blob.
+ * @param {offset} offset Current offset.
+ */
+
+ /**
+ Fires when file chunk is uploaded.
+
+ @event ChunkUploaded
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file File that the chunk was uploaded for.
+ @param {Object} result Object with response properties.
+ @param {Number} result.offset The amount of bytes the server has received so far, including this chunk.
+ @param {Number} result.total The size of the file.
+ @param {String} result.response The response body sent by the server.
+ @param {Number} result.status The HTTP status code sent by the server.
+ @param {String} result.responseHeaders All the response headers as a single string.
+ */
+
+ /**
+ Fires when a file is successfully uploaded.
+
+ @event FileUploaded
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {plupload.File} file File that was uploaded.
+ @param {Object} result Object with response properties.
+ @param {String} result.response The response body sent by the server.
+ @param {Number} result.status The HTTP status code sent by the server.
+ @param {String} result.responseHeaders All the response headers as a single string.
+ */
+
+ /**
+ Fires when all files in a queue are uploaded.
+
+ @event UploadComplete
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {Array} files Array of file objects that was added to queue/selected by the user.
+ */
+
+ /**
+ Fires when a error occurs.
+
+ @event Error
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ @param {Object} error Contains code, message and sometimes file and other details.
+ @param {Number} error.code The plupload error code.
+ @param {String} error.message Description of the error (uses i18n).
+ */
+
+ /**
+ Fires when destroy method is called.
+
+ @event Destroy
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
+ */
+ var uid = plupload.guid()
+ , settings
+ , files = []
+ , preferred_caps = {}
+ , fileInputs = []
+ , fileDrops = []
+ , startTime
+ , total
+ , disabled = false
+ , xhr
+ ;
+
+
+ // Private methods
+ function uploadNext() {
+ var file, count = 0, i;
+
+ if (this.state == plupload.STARTED) {
+ // Find first QUEUED file
+ for (i = 0; i < files.length; i++) {
+ if (!file && files[i].status == plupload.QUEUED) {
+ file = files[i];
+ if (this.trigger("BeforeUpload", file)) {
+ file.status = plupload.UPLOADING;
+ this.trigger("UploadFile", file);
+ }
+ } else {
+ count++;
+ }
+ }
+
+ // All files are DONE or FAILED
+ if (count == files.length) {
+ if (this.state !== plupload.STOPPED) {
+ this.state = plupload.STOPPED;
+ this.trigger("StateChanged");
+ }
+ this.trigger("UploadComplete", files);
+ }
+ }
+ }
+
+
+ function calcFile(file) {
+ file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
+ calc();
+ }
+
+
+ function calc() {
+ var i, file;
+ var loaded;
+ var loadedDuringCurrentSession = 0;
+
+ // Reset stats
+ total.reset();
+
+ // Check status, size, loaded etc on all files
+ for (i = 0; i < files.length; i++) {
+ file = files[i];
+
+ if (file.size !== undef) {
+ // We calculate totals based on original file size
+ total.size += file.origSize;
+
+ // Since we cannot predict file size after resize, we do opposite and
+ // interpolate loaded amount to match magnitude of total
+ loaded = file.loaded * file.origSize / file.size;
+
+ if (!file.completeTimestamp || file.completeTimestamp > startTime) {
+ loadedDuringCurrentSession += loaded;
+ }
+
+ total.loaded += loaded;
+ } else {
+ total.size = undef;
+ }
+
+ if (file.status == plupload.DONE) {
+ total.uploaded++;
+ } else if (file.status == plupload.FAILED) {
+ total.failed++;
+ } else {
+ total.queued++;
+ }
+ }
+
+ // If we couldn't calculate a total file size then use the number of files to calc percent
+ if (total.size === undef) {
+ total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
+ } else {
+ total.bytesPerSec = Math.ceil(loadedDuringCurrentSession / ((+new Date() - startTime || 1) / 1000.0));
+ total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
+ }
+ }
+
+
+ function getRUID() {
+ var ctrl = fileInputs[0] || fileDrops[0];
+ if (ctrl) {
+ return ctrl.getRuntime().uid;
+ }
+ return false;
+ }
+
+
+ function runtimeCan(file, cap) {
+ if (file.ruid) {
+ var info = Runtime.getInfo(file.ruid);
+ if (info) {
+ return info.can(cap);
+ }
+ }
+ return false;
+ }
+
+
+ function bindEventListeners() {
+ this.bind('FilesAdded FilesRemoved', function(up) {
+ up.trigger('QueueChanged');
+ up.refresh();
+ });
+
+ this.bind('CancelUpload', onCancelUpload);
+
+ this.bind('BeforeUpload', onBeforeUpload);
+
+ this.bind('UploadFile', onUploadFile);
+
+ this.bind('UploadProgress', onUploadProgress);
+
+ this.bind('StateChanged', onStateChanged);
+
+ this.bind('QueueChanged', calc);
+
+ this.bind('Error', onError);
+
+ this.bind('FileUploaded', onFileUploaded);
+
+ this.bind('Destroy', onDestroy);
+ }
+
+
+ function initControls(settings, cb) {
+ var self = this, inited = 0, queue = [];
+
+ // common settings
+ var options = {
+ runtime_order: settings.runtimes,
+ required_caps: settings.required_features,
+ preferred_caps: preferred_caps,
+ swf_url: settings.flash_swf_url,
+ xap_url: settings.silverlight_xap_url
+ };
+
+ // add runtime specific options if any
+ plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
+ if (settings[runtime]) {
+ options[runtime] = settings[runtime];
+ }
+ });
+
+ // initialize file pickers - there can be many
+ if (settings.browse_button) {
+ plupload.each(settings.browse_button, function(el) {
+ queue.push(function(cb) {
+ var fileInput = new o.file.FileInput(plupload.extend({}, options, {
+ accept: settings.filters.mime_types,
+ name: settings.file_data_name,
+ multiple: settings.multi_selection,
+ container: settings.container,
+ browse_button: el
+ }));
+
+ fileInput.onready = function() {
+ var info = Runtime.getInfo(this.ruid);
+
+ // for backward compatibility
+ plupload.extend(self.features, {
+ chunks: info.can('slice_blob'),
+ multipart: info.can('send_multipart'),
+ multi_selection: info.can('select_multiple')
+ });
+
+ inited++;
+ fileInputs.push(this);
+ cb();
+ };
+
+ fileInput.onchange = function() {
+ self.addFile(this.files);
+ };
+
+ fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
+ if (!disabled) {
+ if (settings.browse_button_hover) {
+ if ('mouseenter' === e.type) {
+ plupload.addClass(el, settings.browse_button_hover);
+ } else if ('mouseleave' === e.type) {
+ plupload.removeClass(el, settings.browse_button_hover);
+ }
+ }
+
+ if (settings.browse_button_active) {
+ if ('mousedown' === e.type) {
+ plupload.addClass(el, settings.browse_button_active);
+ } else if ('mouseup' === e.type) {
+ plupload.removeClass(el, settings.browse_button_active);
+ }
+ }
+ }
+ });
+
+ fileInput.bind('mousedown', function() {
+ self.trigger('Browse');
+ });
+
+ fileInput.bind('error runtimeerror', function() {
+ fileInput = null;
+ cb();
+ });
+
+ fileInput.init();
+ });
+ });
+ }
+
+ // initialize drop zones
+ if (settings.drop_element) {
+ plupload.each(settings.drop_element, function(el) {
+ queue.push(function(cb) {
+ var fileDrop = new o.file.FileDrop(plupload.extend({}, options, {
+ drop_zone: el
+ }));
+
+ fileDrop.onready = function() {
+ var info = Runtime.getInfo(this.ruid);
+
+ // for backward compatibility
+ plupload.extend(self.features, {
+ chunks: info.can('slice_blob'),
+ multipart: info.can('send_multipart'),
+ dragdrop: info.can('drag_and_drop')
+ });
+
+ inited++;
+ fileDrops.push(this);
+ cb();
+ };
+
+ fileDrop.ondrop = function() {
+ self.addFile(this.files);
+ };
+
+ fileDrop.bind('error runtimeerror', function() {
+ fileDrop = null;
+ cb();
+ });
+
+ fileDrop.init();
+ });
+ });
+ }
+
+
+ plupload.inSeries(queue, function() {
+ if (typeof(cb) === 'function') {
+ cb(inited);
+ }
+ });
+ }
+
+
+ function resizeImage(blob, params, cb) {
+ var img = new o.image.Image();
+
+ try {
+ img.onload = function() {
+ // no manipulation required if...
+ if (params.width > this.width &&
+ params.height > this.height &&
+ params.quality === undef &&
+ params.preserve_headers &&
+ !params.crop
+ ) {
+ this.destroy();
+ return cb(blob);
+ }
+ // otherwise downsize
+ img.downsize(params.width, params.height, params.crop, params.preserve_headers);
+ };
+
+ img.onresize = function() {
+ cb(this.getAsBlob(blob.type, params.quality));
+ this.destroy();
+ };
+
+ img.onerror = function() {
+ cb(blob);
+ };
+
+ img.load(blob);
+ } catch(ex) {
+ cb(blob);
+ }
+ }
+
+
+ function setOption(option, value, init) {
+ var self = this, reinitRequired = false;
+
+ function _setOption(option, value, init) {
+ var oldValue = settings[option];
+
+ switch (option) {
+ case 'max_file_size':
+ if (option === 'max_file_size') {
+ settings.max_file_size = settings.filters.max_file_size = value;
+ }
+ break;
+
+ case 'chunk_size':
+ if (value = plupload.parseSize(value)) {
+ settings[option] = value;
+ settings.send_file_name = true;
+ }
+ break;
+
+ case 'multipart':
+ settings[option] = value;
+ // if (!value) {
+ // settings.send_file_name = true;
+ // }
+ break;
+
+ case 'http_method':
+ settings[option] = value.toUpperCase() === 'PUT' ? 'PUT' : 'POST';
+ break;
+
+ case 'unique_names':
+ settings[option] = value;
+ if (value) {
+ settings.send_file_name = true;
+ }
+ break;
+
+ case 'filters':
+ // for sake of backward compatibility
+ if (plupload.typeOf(value) === 'array') {
+ value = {
+ mime_types: value
+ };
+ }
+
+ if (init) {
+ plupload.extend(settings.filters, value);
+ } else {
+ settings.filters = value;
+ }
+
+ // if file format filters are being updated, regenerate the matching expressions
+ if (value.mime_types) {
+ if (plupload.typeOf(value.mime_types) === 'string') {
+ value.mime_types = o.core.utils.Mime.mimes2extList(value.mime_types);
+ }
+
+ value.mime_types.regexp = (function(filters) {
+ var extensionsRegExp = [];
+
+ plupload.each(filters, function(filter) {
+ plupload.each(filter.extensions.split(/,/), function(ext) {
+ if (/^\s*\*\s*$/.test(ext)) {
+ extensionsRegExp.push('\\.*');
+ } else {
+ extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
+ }
+ });
+ });
+
+ return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
+ }(value.mime_types));
+
+ settings.filters.mime_types = value.mime_types;
+ }
+ break;
+
+ case 'resize':
+ if (value) {
+ settings.resize = plupload.extend({
+ preserve_headers: true,
+ crop: false
+ }, value);
+ } else {
+ settings.resize = false;
+ }
+ break;
+
+ case 'prevent_duplicates':
+ settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
+ break;
+
+ // options that require reinitialisation
+ case 'container':
+ case 'browse_button':
+ case 'drop_element':
+ value = 'container' === option
+ ? plupload.get(value)
+ : plupload.getAll(value)
+ ;
+
+ case 'runtimes':
+ case 'multi_selection':
+ case 'flash_swf_url':
+ case 'silverlight_xap_url':
+ settings[option] = value;
+ if (!init) {
+ reinitRequired = true;
+ }
+ break;
+
+ default:
+ settings[option] = value;
+ }
+
+ if (!init) {
+ self.trigger('OptionChanged', option, value, oldValue);
+ }
+ }
+
+ if (typeof(option) === 'object') {
+ plupload.each(option, function(value, option) {
+ _setOption(option, value, init);
+ });
+ } else {
+ _setOption(option, value, init);
+ }
+
+ if (init) {
+ // Normalize the list of required capabilities
+ settings.required_features = normalizeCaps(plupload.extend({}, settings));
+
+ // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
+ preferred_caps = normalizeCaps(plupload.extend({}, settings, {
+ required_features: true
+ }));
+ } else if (reinitRequired) {
+ self.trigger('Destroy');
+
+ initControls.call(self, settings, function(inited) {
+ if (inited) {
+ self.runtime = Runtime.getInfo(getRUID()).type;
+ self.trigger('Init', { runtime: self.runtime });
+ self.trigger('PostInit');
+ } else {
+ self.trigger('Error', {
+ code : plupload.INIT_ERROR,
+ message : plupload.translate('Init error.')
+ });
+ }
+ });
+ }
+ }
+
+
+ // Internal event handlers
+ function onBeforeUpload(up, file) {
+ // Generate unique target filenames
+ if (up.settings.unique_names) {
+ var matches = file.name.match(/\.([^.]+)$/), ext = "part";
+ if (matches) {
+ ext = matches[1];
+ }
+ file.target_name = file.id + '.' + ext;
+ }
+ }
+
+
+ function onUploadFile(up, file) {
+ var url = up.settings.url
+ , chunkSize = up.settings.chunk_size
+ , retries = up.settings.max_retries
+ , features = up.features
+ , offset = 0
+ , blob
+ ;
+
+ // make sure we start at a predictable offset
+ if (file.loaded) {
+ offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
+ }
+
+ function handleError(err=null) {
+ if (retries-- > 0) {
+ delay(uploadNextChunk, 1000);
+ } else {
+ file.loaded = offset; // reset all progress
+
+ up.trigger('Error', {
+ code : plupload.HTTP_ERROR,
+ message : err == null ? plupload.translate('HTTP Error.') : err,
+ file : file,
+ response : xhr.responseText,
+ status : xhr.status,
+ responseHeaders: xhr.getAllResponseHeaders()
+ });
+ }
+ }
+
+ function uploadNextChunk() {
+ var chunkBlob, args = {}, curChunkSize;
+
+ // make sure that file wasn't cancelled and upload is not stopped in general
+ if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
+ return;
+ }
+
+ // send additional 'name' parameter only if required
+ if (up.settings.send_file_name) {
+ args.name = file.target_name || file.name;
+ }
+
+ if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
+ curChunkSize = Math.min(chunkSize, blob.size - offset);
+ chunkBlob = blob.slice(offset, offset + curChunkSize);
+ } else {
+ curChunkSize = blob.size;
+ chunkBlob = blob;
+ }
+
+ // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
+ if (chunkSize && features.chunks) {
+ // Setup query string arguments
+ if (up.settings.send_chunk_number) {
+ args.chunk = Math.ceil(offset / chunkSize);
+ args.chunks = Math.ceil(blob.size / chunkSize);
+ } else { // keep support for experimental chunk format, just in case
+ args.offset = offset;
+ args.total = blob.size;
+ }
+ }
+
+ if (up.trigger('BeforeChunkUpload', file, args, chunkBlob, offset)) {
+ uploadChunk(args, chunkBlob, curChunkSize);
+ }
+ }
+
+ function uploadChunk(args, chunkBlob, curChunkSize) {
+ var formData;
+
+ xhr = new o.xhr.XMLHttpRequest();
+
+ xhr.withCredentials = true;
+
+ // Do we have upload progress support
+ if (xhr.upload) {
+ xhr.upload.onprogress = function(e) {
+ file.loaded = Math.min(file.size, offset + e.loaded);
+ up.trigger('UploadProgress', file);
+ };
+ }
+
+ xhr.onload = function() {
+ // check if upload made itself through
+ if (xhr.status >= 400) {
+ handleError();
+ return;
+ }
+ if (xhr.status == 203) {
+ handleError();
+ return;
+ }
+
+ // 本地策略需要读取错误代码以出发错误
+ if(uploadConfig.saveType == "local"){
+ var res = JSON.parse(xhr.responseText);
+ if(res.code !== 0){
+ handleError(res.msg);
+ return;
+ }
+ }
+
+ retries = up.settings.max_retries; // reset the counter
+
+ // Handle chunk response
+ if (curChunkSize < blob.size) {
+ chunkBlob.destroy();
+
+ offset += curChunkSize;
+ file.loaded = Math.min(offset, blob.size);
+
+ up.trigger('ChunkUploaded', file, {
+ offset : file.loaded,
+ total : blob.size,
+ response : xhr.responseText,
+ status : xhr.status,
+ responseHeaders: xhr.getAllResponseHeaders()
+ });
+
+ // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
+ if (plupload.ua.browser === 'Android Browser') {
+ // doesn't harm in general, but is not required anywhere else
+ up.trigger('UploadProgress', file);
+ }
+ } else {
+ file.loaded = file.size;
+ }
+
+ chunkBlob = formData = null; // Free memory
+
+ // Check if file is uploaded
+ if (!offset || offset >= blob.size) {
+ // If file was modified, destory the copy
+ if (file.size != file.origSize) {
+ blob.destroy();
+ blob = null;
+ }
+
+ up.trigger('UploadProgress', file);
+
+ file.status = plupload.DONE;
+ file.completeTimestamp = +new Date();
+
+ up.trigger('FileUploaded', file, {
+ response : xhr.responseText,
+ status : xhr.status,
+ responseHeaders: xhr.getAllResponseHeaders()
+ });
+ } else {
+ // Still chunks left
+ delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
+ }
+ };
+
+ xhr.onerror = function() {
+ handleError();
+ };
+
+ xhr.onloadend = function() {
+ this.destroy();
+ xhr = null;
+ };
+
+ // Build multipart request
+ if (up.settings.multipart && features.multipart) {
+ xhr.open(up.settings.http_method, url, true);
+
+ // Set custom headers
+ plupload.each(up.settings.headers, function(value, name) {
+ xhr.setRequestHeader(name, value);
+ });
+
+ formData = new o.xhr.FormData();
+
+ // Add multipart params
+ plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
+ formData.append(name, value);
+ });
+
+ // Add file and send it
+ formData.append(up.settings.file_data_name, chunkBlob);
+ xhr.send(formData, {
+ runtime_order: up.settings.runtimes,
+ required_caps: up.settings.required_features,
+ preferred_caps: preferred_caps,
+ swf_url: up.settings.flash_swf_url,
+ xap_url: up.settings.silverlight_xap_url
+ });
+ } else {
+ // if no multipart, send as binary stream
+ url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
+
+ xhr.open(up.settings.http_method, url, true);
+
+ // Set custom headers
+ plupload.each(up.settings.headers, function(value, name) {
+ xhr.setRequestHeader(name, value);
+ });
+
+ // do not set Content-Type, if it was defined previously (see #1203)
+ if (!xhr.hasRequestHeader('Content-Type')) {
+ xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
+ }
+
+ xhr.send(chunkBlob, {
+ runtime_order: up.settings.runtimes,
+ required_caps: up.settings.required_features,
+ preferred_caps: preferred_caps,
+ swf_url: up.settings.flash_swf_url,
+ xap_url: up.settings.silverlight_xap_url
+ });
+ }
+ }
+
+
+ blob = file.getSource();
+
+ // Start uploading chunks
+ if (!plupload.isEmptyObj(up.settings.resize) && runtimeCan(blob, 'send_binary_string') && plupload.inArray(blob.type, ['image/jpeg', 'image/png']) !== -1) {
+ // Resize if required
+ resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
+ blob = resizedBlob;
+ file.size = resizedBlob.size;
+ uploadNextChunk();
+ });
+ } else {
+ uploadNextChunk();
+ }
+ }
+
+
+ function onUploadProgress(up, file) {
+ calcFile(file);
+ }
+
+
+ function onStateChanged(up) {
+ if (up.state == plupload.STARTED) {
+ // Get start time to calculate bps
+ startTime = (+new Date());
+ } else if (up.state == plupload.STOPPED) {
+ // Reset currently uploading files
+ for (var i = up.files.length - 1; i >= 0; i--) {
+ if (up.files[i].status == plupload.UPLOADING) {
+ up.files[i].status = plupload.QUEUED;
+ calc();
+ }
+ }
+ }
+ }
+
+
+ function onCancelUpload() {
+ if (xhr) {
+ xhr.abort();
+ }
+ }
+
+
+ function onFileUploaded(up) {
+ calc();
+
+ // Upload next file but detach it from the error event
+ // since other custom listeners might want to stop the queue
+ delay(function() {
+ uploadNext.call(up);
+ }, 1);
+ }
+
+
+ function onError(up, err) {
+ if (err.code === plupload.INIT_ERROR) {
+ up.destroy();
+ }
+ // Set failed status if an error occured on a file
+ else if (err.code === plupload.HTTP_ERROR) {
+ err.file.status = plupload.FAILED;
+ err.file.completeTimestamp = +new Date();
+ calcFile(err.file);
+
+ // Upload next file but detach it from the error event
+ // since other custom listeners might want to stop the queue
+ if (up.state == plupload.STARTED) { // upload in progress
+ up.trigger('CancelUpload');
+ delay(function() {
+ uploadNext.call(up);
+ }, 1);
+ }
+ }
+ }
+
+
+ function onDestroy(up) {
+ up.stop();
+
+ // Purge the queue
+ plupload.each(files, function(file) {
+ file.destroy();
+ });
+ files = [];
+
+ if (fileInputs.length) {
+ plupload.each(fileInputs, function(fileInput) {
+ fileInput.destroy();
+ });
+ fileInputs = [];
+ }
+
+ if (fileDrops.length) {
+ plupload.each(fileDrops, function(fileDrop) {
+ fileDrop.destroy();
+ });
+ fileDrops = [];
+ }
+
+ preferred_caps = {};
+ disabled = false;
+ startTime = xhr = null;
+ total.reset();
+ }
+
+
+ // Default settings
+ settings = {
+ chunk_size: 0,
+ file_data_name: 'file',
+ filters: {
+ mime_types: [],
+ prevent_duplicates: false,
+ max_file_size: 0
+ },
+ flash_swf_url: 'js/Moxie.swf',
+ http_method: 'POST',
+ max_retries: 0,
+ multipart: true,
+ multi_selection: true,
+ resize: false,
+ runtimes: Runtime.order,
+ send_file_name: true,
+ send_chunk_number: true,
+ silverlight_xap_url: 'js/Moxie.xap'
+ };
+
+
+ setOption.call(this, options, null, true);
+
+ // Inital total state
+ total = new plupload.QueueProgress();
+
+ // Add public methods
+ plupload.extend(this, {
+
+ /**
+ * Unique id for the Uploader instance.
+ *
+ * @property id
+ * @type String
+ */
+ id : uid,
+ uid : uid, // mOxie uses this to differentiate between event targets
+
+ /**
+ * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
+ * These states are controlled by the stop/start methods. The default value is STOPPED.
+ *
+ * @property state
+ * @type Number
+ */
+ state : plupload.STOPPED,
+
+ /**
+ * Map of features that are available for the uploader runtime. Features will be filled
+ * before the init event is called, these features can then be used to alter the UI for the end user.
+ * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
+ *
+ * @property features
+ * @type Object
+ */
+ features : {},
+
+ /**
+ * Current runtime name.
+ *
+ * @property runtime
+ * @type String
+ */
+ runtime : null,
+
+ /**
+ * Current upload queue, an array of File instances.
+ *
+ * @property files
+ * @type Array
+ * @see plupload.File
+ */
+ files : files,
+
+ /**
+ * Object with name/value settings.
+ *
+ * @property settings
+ * @type Object
+ */
+ settings : settings,
+
+ /**
+ * Total progess information. How many files has been uploaded, total percent etc.
+ *
+ * @property total
+ * @type plupload.QueueProgress
+ */
+ total : total,
+
+
+ /**
+ * Initializes the Uploader instance and adds internal event listeners.
+ *
+ * @method init
+ */
+ init : function() {
+ var self = this, opt, preinitOpt, err;
+
+ preinitOpt = self.getOption('preinit');
+ if (typeof(preinitOpt) == "function") {
+ preinitOpt(self);
+ } else {
+ plupload.each(preinitOpt, function(func, name) {
+ self.bind(name, func);
+ });
+ }
+
+ bindEventListeners.call(self);
+
+ // Check for required options
+ plupload.each(['container', 'browse_button', 'drop_element'], function(el) {
+ if (self.getOption(el) === null) {
+ err = {
+ code : plupload.INIT_ERROR,
+ message : plupload.sprintf(plupload.translate("%s specified, but cannot be found."), el)
+ }
+ return false;
+ }
+ });
+
+ if (err) {
+ return self.trigger('Error', err);
+ }
+
+
+ if (!settings.browse_button && !settings.drop_element) {
+ return self.trigger('Error', {
+ code : plupload.INIT_ERROR,
+ message : plupload.translate("You must specify either browse_button or drop_element.")
+ });
+ }
+
+
+ initControls.call(self, settings, function(inited) {
+ var initOpt = self.getOption('init');
+ if (typeof(initOpt) == "function") {
+ initOpt(self);
+ } else {
+ plupload.each(initOpt, function(func, name) {
+ self.bind(name, func);
+ });
+ }
+
+ if (inited) {
+ self.runtime = Runtime.getInfo(getRUID()).type;
+ self.trigger('Init', { runtime: self.runtime });
+ self.trigger('PostInit');
+ } else {
+ self.trigger('Error', {
+ code : plupload.INIT_ERROR,
+ message : plupload.translate('Init error.')
+ });
+ }
+ });
+ },
+
+ /**
+ * Set the value for the specified option(s).
+ *
+ * @method setOption
+ * @since 2.1
+ * @param {String|Object} option Name of the option to change or the set of key/value pairs
+ * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
+ */
+ setOption: function(option, value) {
+ setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
+ },
+
+ /**
+ * Get the value for the specified option or the whole configuration, if not specified.
+ *
+ * @method getOption
+ * @since 2.1
+ * @param {String} [option] Name of the option to get
+ * @return {Mixed} Value for the option or the whole set
+ */
+ getOption: function(option) {
+ if (!option) {
+ return settings;
+ }
+ return settings[option];
+ },
+
+ /**
+ * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
+ * This would for example reposition flash/silverlight shims on the page.
+ *
+ * @method refresh
+ */
+ refresh : function() {
+ if (fileInputs.length) {
+ plupload.each(fileInputs, function(fileInput) {
+ fileInput.trigger('Refresh');
+ });
+ }
+ this.trigger('Refresh');
+ },
+
+ /**
+ * Starts uploading the queued files.
+ *
+ * @method start
+ */
+ start : function() {
+ if (this.state != plupload.STARTED) {
+ this.state = plupload.STARTED;
+ this.trigger('StateChanged');
+
+ uploadNext.call(this);
+ }
+ },
+
+ /**
+ * Stops the upload of the queued files.
+ *
+ * @method stop
+ */
+ stop : function() {
+ if (this.state != plupload.STOPPED) {
+ this.state = plupload.STOPPED;
+ this.trigger('StateChanged');
+ this.trigger('CancelUpload');
+ }
+ },
+
+
+ /**
+ * Disables/enables browse button on request.
+ *
+ * @method disableBrowse
+ * @param {Boolean} disable Whether to disable or enable (default: true)
+ */
+ disableBrowse : function() {
+ disabled = arguments[0] !== undef ? arguments[0] : true;
+
+ if (fileInputs.length) {
+ plupload.each(fileInputs, function(fileInput) {
+ fileInput.disable(disabled);
+ });
+ }
+
+ this.trigger('DisableBrowse', disabled);
+ },
+
+ /**
+ * Returns the specified file object by id.
+ *
+ * @method getFile
+ * @param {String} id File id to look for.
+ * @return {plupload.File} File object or undefined if it wasn't found;
+ */
+ getFile : function(id) {
+ var i;
+ for (i = files.length - 1; i >= 0; i--) {
+ if (files[i].id === id) {
+ return files[i];
+ }
+ }
+ },
+
+ /**
+ * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
+ * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
+ * if any files were added to the queue. Otherwise nothing happens.
+ *
+ * @method addFile
+ * @since 2.0
+ * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
+ * @param {String} [fileName] If specified, will be used as a name for the file
+ */
+ addFile : function(file, fileName) {
+ var self = this
+ , queue = []
+ , filesAdded = []
+ , ruid
+ ;
+
+ function filterFile(file, cb) {
+ var queue = [];
+ plupload.each(self.settings.filters, function(rule, name) {
+ if (fileFilters[name]) {
+ queue.push(function(cb) {
+ fileFilters[name].call(self, rule, file, function(res) {
+ cb(!res);
+ });
+ });
+ }
+ });
+ plupload.inSeries(queue, cb);
+ }
+
+ /**
+ * @method resolveFile
+ * @private
+ * @param {moxie.file.File|moxie.file.Blob|plupload.File|File|Blob|input[type="file"]} file
+ */
+ function resolveFile(file) {
+ var type = plupload.typeOf(file);
+
+ // moxie.file.File
+ if (file instanceof o.file.File) {
+ if (!file.ruid && !file.isDetached()) {
+ if (!ruid) { // weird case
+ return false;
+ }
+ file.ruid = ruid;
+ file.connectRuntime(ruid);
+ }
+ resolveFile(new plupload.File(file));
+ }
+ // moxie.file.Blob
+ else if (file instanceof o.file.Blob) {
+ resolveFile(file.getSource());
+ file.destroy();
+ }
+ // plupload.File - final step for other branches
+ else if (file instanceof plupload.File) {
+ if (fileName) {
+ file.name = fileName;
+ }
+
+ queue.push(function(cb) {
+ // run through the internal and user-defined filters, if any
+ filterFile(file, function(err) {
+ if (!err) {
+ // make files available for the filters by updating the main queue directly
+ files.push(file);
+ // collect the files that will be passed to FilesAdded event
+ filesAdded.push(file);
+
+ self.trigger("FileFiltered", file);
+ }
+ delay(cb, 1); // do not build up recursions or eventually we might hit the limits
+ });
+ });
+ }
+ // native File or blob
+ else if (plupload.inArray(type, ['file', 'blob']) !== -1) {
+ resolveFile(new o.file.File(null, file));
+ }
+ // input[type="file"]
+ else if (type === 'node' && plupload.typeOf(file.files) === 'filelist') {
+ // if we are dealing with input[type="file"]
+ plupload.each(file.files, resolveFile);
+ }
+ // mixed array of any supported types (see above)
+ else if (type === 'array') {
+ fileName = null; // should never happen, but unset anyway to avoid funny situations
+ plupload.each(file, resolveFile);
+ }
+ }
+
+ ruid = getRUID();
+
+ resolveFile(file);
+
+ if (queue.length) {
+ plupload.inSeries(queue, function() {
+ // if any files left after filtration, trigger FilesAdded
+ if (filesAdded.length) {
+ self.trigger("FilesAdded", filesAdded);
+ }
+ });
+ }
+ },
+
+ /**
+ * Removes a specific file.
+ *
+ * @method removeFile
+ * @param {plupload.File|String} file File to remove from queue.
+ */
+ removeFile : function(file) {
+ var id = typeof(file) === 'string' ? file : file.id;
+
+ for (var i = files.length - 1; i >= 0; i--) {
+ if (files[i].id === id) {
+ return this.splice(i, 1)[0];
+ }
+ }
+ },
+
+ /**
+ * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
+ *
+ * @method splice
+ * @param {Number} start (Optional) Start index to remove from.
+ * @param {Number} length (Optional) Lengh of items to remove.
+ * @return {Array} Array of files that was removed.
+ */
+ splice : function(start, length) {
+ // Splice and trigger events
+ var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
+
+ // if upload is in progress we need to stop it and restart after files are removed
+ var restartRequired = false;
+ if (this.state == plupload.STARTED) { // upload in progress
+ plupload.each(removed, function(file) {
+ if (file.status === plupload.UPLOADING) {
+ restartRequired = true; // do not restart, unless file that is being removed is uploading
+ return false;
+ }
+ });
+
+ if (restartRequired) {
+ this.stop();
+ }
+ }
+
+ this.trigger("FilesRemoved", removed);
+
+ // Dispose any resources allocated by those files
+ plupload.each(removed, function(file) {
+ file.destroy();
+ });
+
+ if (restartRequired) {
+ this.start();
+ }
+
+ return removed;
+ },
+
+ /**
+ Dispatches the specified event name and its arguments to all listeners.
+
+ @method trigger
+ @param {String} name Event name to fire.
+ @param {Object..} Multiple arguments to pass along to the listener functions.
+ */
+
+ // override the parent method to match Plupload-like event logic
+ dispatchEvent: function(type) {
+ var list, args, result;
+
+ type = type.toLowerCase();
+
+ list = this.hasEventListener(type);
+
+ if (list) {
+ // sort event list by priority
+ list.sort(function(a, b) { return b.priority - a.priority; });
+
+ // first argument should be current plupload.Uploader instance
+ args = [].slice.call(arguments);
+ args.shift();
+ args.unshift(this);
+
+ for (var i = 0; i < list.length; i++) {
+ // Fire event, break chain if false is returned
+ if (list[i].fn.apply(list[i].scope, args) === false) {
+ return false;
+ }
+ }
+ }
+ return true;
+ },
+
+ /**
+ Check whether uploader has any listeners to the specified event.
+
+ @method hasEventListener
+ @param {String} name Event name to check for.
+ */
+
+
+ /**
+ Adds an event listener by name.
+
+ @method bind
+ @param {String} name Event name to listen for.
+ @param {function} fn Function to call ones the event gets fired.
+ @param {Object} [scope] Optional scope to execute the specified function in.
+ @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
+ */
+ bind: function(name, fn, scope, priority) {
+ // adapt moxie EventTarget style to Plupload-like
+ plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope);
+ },
+
+ /**
+ Removes the specified event listener.
+
+ @method unbind
+ @param {String} name Name of event to remove.
+ @param {function} fn Function to remove from listener.
+ */
+
+ /**
+ Removes all event listeners.
+
+ @method unbindAll
+ */
+
+
+ /**
+ * Destroys Plupload instance and cleans after itself.
+ *
+ * @method destroy
+ */
+ destroy : function() {
+ this.trigger('Destroy');
+ settings = total = null; // purge these exclusively
+ this.unbindAll();
+ }
+ });
+};
+
+plupload.Uploader.prototype = o.core.EventTarget.instance;
+
+/**
+ * Constructs a new file instance.
+ *
+ * @class File
+ * @constructor
+ *
+ * @param {Object} file Object containing file properties
+ * @param {String} file.name Name of the file.
+ * @param {Number} file.size File size.
+ */
+plupload.File = (function() {
+ var filepool = {};
+
+ function PluploadFile(file) {
+
+ plupload.extend(this, {
+
+ /**
+ * File id this is a globally unique id for the specific file.
+ *
+ * @property id
+ * @type String
+ */
+ id: plupload.guid(),
+
+ /**
+ * File name for example "myfile.gif".
+ *
+ * @property name
+ * @type String
+ */
+ name: file.name || file.fileName,
+
+ /**
+ * File type, `e.g image/jpeg`
+ *
+ * @property type
+ * @type String
+ */
+ type: file.type || '',
+
+ /**
+ * File size in bytes (may change after client-side manupilation).
+ *
+ * @property size
+ * @type Number
+ */
+ size: file.size || file.fileSize,
+
+ /**
+ * Original file size in bytes.
+ *
+ * @property origSize
+ * @type Number
+ */
+ origSize: file.size || file.fileSize,
+
+ /**
+ * Number of bytes uploaded of the files total size.
+ *
+ * @property loaded
+ * @type Number
+ */
+ loaded: 0,
+
+ /**
+ * Number of percentage uploaded of the file.
+ *
+ * @property percent
+ * @type Number
+ */
+ percent: 0,
+
+ /**
+ * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
+ *
+ * @property status
+ * @type Number
+ * @see plupload
+ */
+ status: plupload.QUEUED,
+
+ /**
+ * Date of last modification.
+ *
+ * @property lastModifiedDate
+ * @type {String}
+ */
+ lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
+
+
+ /**
+ * Set when file becomes plupload.DONE or plupload.FAILED. Is used to calculate proper plupload.QueueProgress.bytesPerSec.
+ * @private
+ * @property completeTimestamp
+ * @type {Number}
+ */
+ completeTimestamp: 0,
+
+ /**
+ * Returns native window.File object, when it's available.
+ *
+ * @method getNative
+ * @return {window.File} or null, if plupload.File is of different origin
+ */
+ getNative: function() {
+ var file = this.getSource().getSource();
+ return plupload.inArray(plupload.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
+ },
+
+ /**
+ * Returns mOxie.File - unified wrapper object that can be used across runtimes.
+ *
+ * @method getSource
+ * @return {mOxie.File} or null
+ */
+ getSource: function() {
+ if (!filepool[this.id]) {
+ return null;
+ }
+ return filepool[this.id];
+ },
+
+ /**
+ * Destroys plupload.File object.
+ *
+ * @method destroy
+ */
+ destroy: function() {
+ var src = this.getSource();
+ if (src) {
+ src.destroy();
+ delete filepool[this.id];
+ }
+ }
+ });
+
+ filepool[this.id] = file;
+ }
+
+ return PluploadFile;
+}());
+
+
+/**
+ * Constructs a queue progress.
+ *
+ * @class QueueProgress
+ * @constructor
+ */
+ plupload.QueueProgress = function() {
+ var self = this; // Setup alias for self to reduce code size when it's compressed
+
+ /**
+ * Total queue file size.
+ *
+ * @property size
+ * @type Number
+ */
+ self.size = 0;
+
+ /**
+ * Total bytes uploaded.
+ *
+ * @property loaded
+ * @type Number
+ */
+ self.loaded = 0;
+
+ /**
+ * Number of files uploaded.
+ *
+ * @property uploaded
+ * @type Number
+ */
+ self.uploaded = 0;
+
+ /**
+ * Number of files failed to upload.
+ *
+ * @property failed
+ * @type Number
+ */
+ self.failed = 0;
+
+ /**
+ * Number of files yet to be uploaded.
+ *
+ * @property queued
+ * @type Number
+ */
+ self.queued = 0;
+
+ /**
+ * Total percent of the uploaded bytes.
+ *
+ * @property percent
+ * @type Number
+ */
+ self.percent = 0;
+
+ /**
+ * Bytes uploaded per second.
+ *
+ * @property bytesPerSec
+ * @type Number
+ */
+ self.bytesPerSec = 0;
+
+ /**
+ * Resets the progress to its initial values.
+ *
+ * @method reset
+ */
+ self.reset = function() {
+ self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
+ };
+};
+
+exports.plupload = plupload;
+
+}(this, moxie));
+
+}));
\ No newline at end of file
diff --git a/public/static/js/uploader/tempCodeRunnerFile.js b/public/static/js/uploader/tempCodeRunnerFile.js
new file mode 100644
index 0000000..d464c3e
--- /dev/null
+++ b/public/static/js/uploader/tempCodeRunnerFile.js
@@ -0,0 +1 @@
+Path
\ No newline at end of file
diff --git a/public/static/js/uploader/ui.js b/public/static/js/uploader/ui.js
new file mode 100644
index 0000000..9f78b44
--- /dev/null
+++ b/public/static/js/uploader/ui.js
@@ -0,0 +1,278 @@
+/*global plupload */
+/*global qiniu */
+function FileProgress(file, targetID) {
+ this.fileProgressID = file.id;
+ this.file = file;
+
+ this.opacity = 100;
+ this.height = 0;
+ this.fileProgressWrapper = $('#' + this.fileProgressID);
+ if (!this.fileProgressWrapper.length) {
+ //
+ //
+ //
+
+ this.fileProgressWrapper = $(' ');
+ var Wrappeer = this.fileProgressWrapper;
+ Wrappeer.attr('id', this.fileProgressID).addClass('progressContainer');
+
+ var progressText = $(" ");
+ progressText.addClass('progressName').text(file.name);
+
+
+ var fileSize = plupload.formatSize(file.size).toUpperCase();
+ var progressSize = $(" ");
+ progressSize.addClass("progressFileSize").text(fileSize);
+
+ var progressBarTd = $(" ");
+ var progressBarBox = $("");
+ progressBarBox.addClass('info');
+ var progressBarWrapper = $("");
+ progressBarWrapper.addClass("progress progress-striped");
+
+ var progressBar = $("");
+ progressBar.addClass("progress-bar progress-bar-info")
+ .attr('role', 'progressbar')
+ .attr('aria-valuemax', 100)
+ .attr('aria-valuenow', 0)
+ .attr('aria-valuein', 0)
+ .width('0%');
+
+ var progressBarPercent = $('');
+ progressBarPercent.text(fileSize);
+
+ var progressCancel = $('');
+ progressCancel.show().addClass('progressCancel').text('×');
+
+ progressBar.append(progressBarPercent);
+ progressBarWrapper.append(progressBar);
+ progressBarBox.append(progressBarWrapper);
+ progressText.append(progressCancel);
+
+ var progressBarStatus = $('');
+ progressBarBox.append(progressBarStatus);
+ progressBarTd.append(progressBarBox);
+
+ Wrappeer.append(progressText);
+ Wrappeer.append(progressSize);
+ Wrappeer.append(progressBarTd);
+
+ $('#' + targetID).append(Wrappeer);
+ } else {
+ this.reset();
+ }
+
+ this.height = this.fileProgressWrapper.offset().top;
+ this.setTimer(null);
+}
+
+FileProgress.prototype.setTimer = function(timer) {
+ this.fileProgressWrapper.FP_TIMER = timer;
+};
+
+FileProgress.prototype.getTimer = function(timer) {
+ return this.fileProgressWrapper.FP_TIMER || null;
+};
+
+FileProgress.prototype.reset = function() {
+ this.fileProgressWrapper.attr('class', "progressContainer");
+ this.fileProgressWrapper.find('td .progress .progress-bar-info').attr('aria-valuenow', 0).width('0%').find('span').text('');
+ this.appear();
+};
+
+FileProgress.prototype.setChunkProgess = function(chunk_size) {
+ var chunk_amount = Math.ceil(this.file.size / chunk_size);
+ if (chunk_amount === 1) {
+ return false;
+ }
+
+ var viewProgess = $('');
+
+ var progressBarChunkTr = $(' ');
+ var progressBarChunk = $('');
+ for (var i = 1; i <= chunk_amount; i++) {
+ var col = $('');
+ var progressBarWrapper = $('");
+ progressBar.addClass("progress-bar progress-bar-info text-left")
+ .attr('role', 'progressbar')
+ .attr('aria-valuemax', 100)
+ .attr('aria-valuenow', 0)
+ .attr('aria-valuein', 0)
+ .width('0%')
+ .attr('id', this.file.id + '_' + i)
+ .text('');
+
+ var progressBarStatus = $('');
+ progressBarStatus.addClass('chunk-status').text();
+
+ progressBarWrapper.append(progressBar);
+ progressBarWrapper.append(progressBarStatus);
+
+ col.append(progressBarWrapper);
+ progressBarChunk.append(col);
+ }
+
+ if(!this.fileProgressWrapper.find('td:eq(2) .btn-default').length){
+ this.fileProgressWrapper.find('td>div').append(viewProgess);
+ }
+ progressBarChunkTr.hide().find('td').append(progressBarChunk);
+ progressBarChunkTr.insertAfter(this.fileProgressWrapper);
+
+};
+
+FileProgress.prototype.setProgress = function(percentage, speed, chunk_size) {
+ this.fileProgressWrapper.attr('class', "progressContainer green");
+
+ var file = this.file;
+ var uploaded = file.loaded;
+
+ var size = plupload.formatSize(uploaded).toUpperCase();
+ var formatSpeed = plupload.formatSize(speed).toUpperCase();
+ var progressbar = this.fileProgressWrapper.find('td .progress').find('.progress-bar-info');
+ if (this.fileProgressWrapper.find('.status').text() === '取消上传'){
+ return;
+ }
+ this.fileProgressWrapper.find('.status').text("已上传: " + size + " 上传速度: " + formatSpeed + "/s");
+ percentage = parseInt(percentage, 10);
+ if (file.status !== plupload.DONE && percentage === 100) {
+ percentage = 99;
+ }
+
+ progressbar.attr('aria-valuenow', percentage).css('width', percentage + '%');
+
+ if (chunk_size) {
+ var chunk_amount = Math.ceil(file.size / chunk_size);
+ if (chunk_amount === 1) {
+ return false;
+ }
+ var current_uploading_chunk = Math.ceil(uploaded / chunk_size);
+ var pre_chunk, text;
+
+ for (var index = 0; index < current_uploading_chunk; index++) {
+ pre_chunk = $('#' + file.id + "_" + index);
+ pre_chunk.width('100%').removeClass().addClass('alert alert-success').attr('aria-valuenow', 100);
+ text = "块" + index + "上传进度100%";
+ pre_chunk.next().html(text);
+ }
+
+ var currentProgessBar = $('#' + file.id + "_" + current_uploading_chunk);
+ var current_chunk_percent;
+ if (current_uploading_chunk < chunk_amount) {
+ if (uploaded % chunk_size) {
+ current_chunk_percent = ((uploaded % chunk_size) / chunk_size * 100).toFixed(2);
+ } else {
+ current_chunk_percent = 100;
+ currentProgessBar.removeClass().addClass('alert alert-success');
+ }
+ } else {
+ var last_chunk_size = file.size - chunk_size * (chunk_amount - 1);
+ var left_file_size = file.size - uploaded;
+ if (left_file_size % last_chunk_size) {
+ current_chunk_percent = ((uploaded % chunk_size) / last_chunk_size * 100).toFixed(2);
+ } else {
+ current_chunk_percent = 100;
+ currentProgessBar.removeClass().addClass('alert alert-success');
+ }
+ }
+ currentProgessBar.width(current_chunk_percent + '%');
+ currentProgessBar.attr('aria-valuenow', current_chunk_percent);
+ text = "块" + current_uploading_chunk + "上传进度" + current_chunk_percent + '%';
+ currentProgessBar.next().html(text);
+ }
+
+ this.appear();
+};
+
+FileProgress.prototype.setComplete = function(up, info) {
+ var td = this.fileProgressWrapper.find('td:eq(2)'),
+ tdProgress = td.find('.progress');
+
+ var res;
+ var url;
+ if(uploadConfig.saveType == "oss"){
+ url = "oss";
+ str = "上传成功";
+ }else{
+ res = $.parseJSON(info);
+ if (res.url) {
+ url = res.url;
+ str = "上传成功";
+ } else {
+ var domain = up.getOption('domain');
+ url = domain + encodeURI(res.key);
+ var link = domain + res.key;
+ str = "上传成功";
+ }
+ }
+
+ tdProgress.html(str).removeClass().next().next('.status').hide();
+ this.fileProgressWrapper.find('td:eq(0) .progressCancel').hide();
+ td.find('.status').hide();
+ angular.element(document.querySelector('angular-filemanager > div')).scope().fileNavigator.refresh();
+};
+FileProgress.prototype.setError = function() {
+ this.fileProgressWrapper.find('td:eq(2)').attr('class', 'text-warning');
+ this.fileProgressWrapper.find('td:eq(2) .progress').css('width', 0).hide();
+ this.fileProgressWrapper.find('button').hide();
+ this.fileProgressWrapper.next('.chunk-status-tr').hide();
+};
+
+FileProgress.prototype.setCancelled = function(manual) {
+ var progressContainer = 'progressContainer';
+ if (!manual) {
+ progressContainer += ' red';
+ }
+ this.fileProgressWrapper.attr('class', progressContainer);
+ this.fileProgressWrapper.find('td .progress').remove();
+ this.fileProgressWrapper.find('td:eq(2) .btn-default').hide();
+ this.fileProgressWrapper.find('td:eq(0) .progressCancel').hide();
+};
+
+FileProgress.prototype.setStatus = function(status, isUploading) {
+ if (!isUploading) {
+ this.fileProgressWrapper.find('.status').text(status).attr('class', 'status text-left');
+ }
+};
+
+// 绑定取消上传事件
+FileProgress.prototype.bindUploadCancel = function(up) {
+ var self = this;
+ if (up) {
+ self.fileProgressWrapper.find('td:eq(0) .progressCancel').on('click', function(){
+ self.setCancelled(false);
+ self.setStatus("取消上传");
+ self.fileProgressWrapper.find('.status').css('left', '0');
+ up.removeFile(self.file);
+ });
+ }
+
+};
+
+FileProgress.prototype.appear = function() {
+ if (this.getTimer() !== null) {
+ clearTimeout(this.getTimer());
+ this.setTimer(null);
+ }
+
+ if (this.fileProgressWrapper[0].filters) {
+ try {
+ this.fileProgressWrapper[0].filters.item("DXImageTransform.Microsoft.Alpha").opacity = 100;
+ } catch (e) {
+ // If it is not set initially, the browser will throw an error. This will set it if it is not set yet.
+ this.fileProgressWrapper.css('filter', "progid:DXImageTransform.Microsoft.Alpha(opacity=100)");
+ }
+ } else {
+ this.fileProgressWrapper.css('opacity', 1);
+ }
+
+ this.fileProgressWrapper.css('height', '');
+
+ this.height = this.fileProgressWrapper.offset().top;
+ this.opacity = 100;
+ this.fileProgressWrapper.show();
+
+};
diff --git a/public/static/js/uploader/uploader.js b/public/static/js/uploader/uploader.js
new file mode 100644
index 0000000..8ca5a8e
--- /dev/null
+++ b/public/static/js/uploader/uploader.js
@@ -0,0 +1,1856 @@
+/*!
+ * qiniu-js-sdk v@VERSION
+ *
+ * Copyright 2015 by Qiniu
+ * Released under GPL V2 License.
+ *
+ * GitHub: http://github.com/qiniu/js-sdk
+ *
+ * Date: @DATE
+*/
+/*!
+ *
+ * Rebuild By Aaron@2018
+ *
+*/
+/*global plupload ,mOxie*/
+/*global ActiveXObject */
+/*exported Qiniu */
+/*exported QiniuJsSDK */
+
+function getCookieByString(cookieName){
+ var start = document.cookie.indexOf(cookieName+'=');
+ if (start == -1) return false;
+ start = start+cookieName.length+1;
+ var end = document.cookie.indexOf(';', start);
+ if (end == -1) end=document.cookie.length;
+ return document.cookie.substring(start, end);
+}
+;(function( global ){
+
+/**
+ * Creates new cookie or removes cookie with negative expiration
+ * @param key The key or identifier for the store
+ * @param value Contents of the store
+ * @param exp Expiration - creation defaults to 30 days
+ */
+function createCookie(key, value, exp) {
+ var date = new Date();
+ date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000));
+ var expires = "; expires=" + date.toGMTString();
+ document.cookie = key + "=" + value + expires + "; path=/";
+}
+
+/**
+ * Returns contents of cookie
+ * @param key The key or identifier for the store
+ */
+function readCookie(key) {
+ var nameEQ = key + "=";
+ var ca = document.cookie.split(';');
+ for (var i = 0, max = ca.length; i < max; i++) {
+ var c = ca[i];
+ while (c.charAt(0) === ' ') {
+ c = c.substring(1, c.length);
+ }
+ if (c.indexOf(nameEQ) === 0) {
+ return c.substring(nameEQ.length, c.length);
+ }
+ }
+ return null;
+}
+
+// if current browser is not support localStorage
+// use cookie to make a polyfill
+if ( !window.localStorage ) {
+ window.localStorage = {
+ setItem: function (key, value) {
+ createCookie(key, value, 30);
+ },
+ getItem: function (key) {
+ return readCookie(key);
+ },
+ removeItem: function (key) {
+ createCookie(key, '', -1);
+ }
+ };
+}
+
+function QiniuJsSDK() {
+
+ var that = this;
+
+ /**
+ * detect IE version
+ * if current browser is not IE
+ * it will return false
+ * else
+ * it will return version of current IE browser
+ * @return {Number|Boolean} IE version or false
+ */
+ this.detectIEVersion = function() {
+ var v = 4,
+ div = document.createElement('div'),
+ all = div.getElementsByTagName('i');
+ while (
+ div.innerHTML = '',
+ all[0]
+ ) {
+ v++;
+ }
+ return v > 4 ? v : false;
+ };
+
+ var logger = {
+ MUTE: 0,
+ FATA: 1,
+ ERROR: 2,
+ WARN: 3,
+ INFO: 4,
+ DEBUG: 5,
+ TRACE: 6,
+ level: 0
+ };
+
+ function log(type, args){
+ var header = "[Cloudreve-uploader]["+type+"]";
+ var msg = header;
+ for (var i = 0; i < args.length; i++) {
+ if (typeof args[i] === "string") {
+ msg += " " + args[i];
+ } else {
+ msg += " " + that.stringifyJSON(args[i]);
+ }
+ }
+ if (that.detectIEVersion()) {
+ // http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9
+ //var log = Function.prototype.bind.call(console.log, console);
+ //log.apply(console, args);
+ console.log(msg);
+ }else{
+ args.unshift(header);
+ console.log.apply(console, args);
+ }
+ if (document.getElementById('qiniu-js-sdk-log')) {
+ document.getElementById('qiniu-js-sdk-log').innerHTML += ''+msg+'
';
+ }
+ }
+
+ function makeLogFunc(code){
+ var func = code.toLowerCase();
+ logger[func] = function(){
+ // logger[func].history = logger[func].history || [];
+ // logger[func].history.push(arguments);
+ if(window.console && window.console.log && logger.level>=logger[code]){
+ var args = Array.prototype.slice.call(arguments);
+ log(func,args);
+ }
+ };
+ }
+
+ for (var property in logger){
+ if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) {
+ makeLogFunc(property);
+ }
+ }
+
+
+ var qiniuUploadUrl;
+
+
+ /**
+ * qiniu upload urls
+ * 'qiniuUploadUrls' is used to change target when current url is not avaliable
+ * @type {Array}
+ */
+ if(uploadConfig.saveType == "qiniu"){
+ if (window.location.protocol === 'https:') {
+ qiniuUploadUrl = 'https://up.qbox.me';
+ } else {
+ qiniuUploadUrl = 'http://upload.qiniu.com';
+ }
+ var qiniuUploadUrls = [
+ "http://upload.qiniu.com",
+ "http://up.qiniu.com"
+ ];
+
+ var qiniuUpHosts = {
+ "http": [
+ "http://upload.qiniu.com",
+ "http://up.qiniu.com"
+ ],
+ "https": [
+ "https://up.qbox.me"
+ ]
+ };
+ //TODO 优化写法
+}else if(uploadConfig.saveType == "local" || uploadConfig.saveType == "oss" ||uploadConfig.saveType == "upyun"||uploadConfig.saveType == "s3"|| uploadConfig.saveType == "remote"||uploadConfig.saveType == "onedrive"){
+ qiniuUploadUrl = uploadConfig.upUrl;
+ var qiniuUploadUrls = [uploadConfig.upUrl,];
+ var qiniuUpHosts = {
+ "http": [uploadConfig.upUrl,],
+ "https": [uploadConfig.upUrl,]
+ }
+}
+
+ var changeUrlTimes = 0;
+
+ /**
+ * reset upload url
+ * if current page protocal is https
+ * it will always return 'https://up.qbox.me'
+ * else
+ * it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply
+ */
+ this.resetUploadUrl = function(){
+ var hosts = window.location.protocol === 'https:' ? qiniuUpHosts.https : qiniuUpHosts.http;
+ var i = changeUrlTimes % hosts.length;
+ qiniuUploadUrl = hosts[i];
+ changeUrlTimes++;
+ logger.debug('resetUploadUrl: '+qiniuUploadUrl);
+ };
+
+ // this.resetUploadUrl();
+
+
+ /**
+ * is image
+ * @param {String} url of a file
+ * @return {Boolean} file is a image or not
+ */
+ this.isImage = function(url) {
+ url = url.split(/[?#]/)[0];
+ return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url);
+ };
+
+ /**
+ * get file extension
+ * @param {String} filename
+ * @return {String} file extension
+ * @example
+ * input: test.txt
+ * output: txt
+ */
+ this.getFileExtension = function(filename) {
+ var tempArr = filename.split(".");
+ var ext;
+ if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) {
+ ext = "";
+ } else {
+ ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case
+ }
+ return ext;
+ };
+
+ /**
+ * encode string by utf8
+ * @param {String} string to encode
+ * @return {String} encoded string
+ */
+ this.utf8_encode = function(argString) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + improved by: sowberry
+ // + tweaked by: Jack
+ // + bugfixed by: Onno Marsman
+ // + improved by: Yves Sucaet
+ // + bugfixed by: Onno Marsman
+ // + bugfixed by: Ulrich
+ // + bugfixed by: Rafal Kukawski
+ // + improved by: kirilloid
+ // + bugfixed by: kirilloid
+ // * example 1: this.utf8_encode('Kevin van Zonneveld');
+ // * returns 1: 'Kevin van Zonneveld'
+
+ if (argString === null || typeof argString === 'undefined') {
+ return '';
+ }
+
+ var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+ var utftext = '',
+ start, end, stringl = 0;
+
+ start = end = 0;
+ stringl = string.length;
+ for (var n = 0; n < stringl; n++) {
+ var c1 = string.charCodeAt(n);
+ var enc = null;
+
+ if (c1 < 128) {
+ end++;
+ } else if (c1 > 127 && c1 < 2048) {
+ enc = String.fromCharCode(
+ (c1 >> 6) | 192, (c1 & 63) | 128
+ );
+ } else if (c1 & 0xF800 ^ 0xD800 > 0) {
+ enc = String.fromCharCode(
+ (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
+ );
+ } else { // surrogate pairs
+ if (c1 & 0xFC00 ^ 0xD800 > 0) {
+ throw new RangeError('Unmatched trail surrogate at ' + n);
+ }
+ var c2 = string.charCodeAt(++n);
+ if (c2 & 0xFC00 ^ 0xDC00 > 0) {
+ throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
+ }
+ c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
+ enc = String.fromCharCode(
+ (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
+ );
+ }
+ if (enc !== null) {
+ if (end > start) {
+ utftext += string.slice(start, end);
+ }
+ utftext += enc;
+ start = end = n + 1;
+ }
+ }
+
+ if (end > start) {
+ utftext += string.slice(start, stringl);
+ }
+
+ return utftext;
+ };
+
+ this.base64_decode = function (data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Thunder.m
+ // + input by: Aman Gupta
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Onno Marsman
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + input by: Brett Zamir (http://brett-zamir.me)
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
+ // * returns 1: 'Kevin van Zonneveld'
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['atob'] == 'function') {
+ // return atob(data);
+ //}
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
+ ac = 0,
+ dec = "",
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ data += '';
+
+ do { // unpack four hexets into three octets using index points in b64
+ h1 = b64.indexOf(data.charAt(i++));
+ h2 = b64.indexOf(data.charAt(i++));
+ h3 = b64.indexOf(data.charAt(i++));
+ h4 = b64.indexOf(data.charAt(i++));
+
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+
+ if (h3 === 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1);
+ } else if (h4 === 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
+ } else {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < data.length);
+
+ dec = tmp_arr.join('');
+
+ return dec;
+ };
+
+ /**
+ * encode data by base64
+ * @param {String} data to encode
+ * @return {String} encoded data
+ */
+ this.base64_encode = function(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Bayron Guevara
+ // + improved by: Thunder.m
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // - depends on: this.utf8_encode
+ // * example 1: this.base64_encode('Kevin van Zonneveld');
+ // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['atob'] == 'function') {
+ // return atob(data);
+ //}
+ var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
+ ac = 0,
+ enc = '',
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ data = this.utf8_encode(data + '');
+
+ do { // pack three octets into four hexets
+ o1 = data.charCodeAt(i++);
+ o2 = data.charCodeAt(i++);
+ o3 = data.charCodeAt(i++);
+
+ bits = o1 << 16 | o2 << 8 | o3;
+
+ h1 = bits >> 18 & 0x3f;
+ h2 = bits >> 12 & 0x3f;
+ h3 = bits >> 6 & 0x3f;
+ h4 = bits & 0x3f;
+
+ // use hexets to index into b64, and append result to encoded string
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+
+ enc = tmp_arr.join('');
+
+ switch (data.length % 3) {
+ case 1:
+ enc = enc.slice(0, -2) + '==';
+ break;
+ case 2:
+ enc = enc.slice(0, -1) + '=';
+ break;
+ }
+
+ return enc;
+ };
+
+ /**
+ * encode string in url by base64
+ * @param {String} string in url
+ * @return {String} encoded string
+ */
+ this.URLSafeBase64Encode = function(v) {
+ v = this.base64_encode(v);
+ return v.replace(/\//g, '_').replace(/\+/g, '-');
+ };
+
+ this.URLSafeBase64Decode = function(v) {
+ v = v.replace(/_/g, '/').replace(/-/g, '+');
+ return this.base64_decode(v);
+ };
+
+ // TODO: use mOxie
+ /**
+ * craete object used to AJAX
+ * @return {Object}
+ */
+ this.createAjax = function(argument) {
+ var xmlhttp = {};
+ if (window.XMLHttpRequest) {
+ xmlhttp = new XMLHttpRequest();
+ } else {
+ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ return xmlhttp;
+ };
+
+ // TODO: enhance IE compatibility
+ /**
+ * parse json string to javascript object
+ * @param {String} json string
+ * @return {Object} object
+ */
+ this.parseJSON = function(data) {
+ // Attempt to parse using the native JSON parser first
+ if (window.JSON && window.JSON.parse) {
+ return window.JSON.parse(data);
+ }
+
+ //var rx_one = /^[\],:{}\s]*$/,
+ // rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ // rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ // rx_four = /(?:^|:|,)(?:\s*\[)+/g,
+ var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+ //var json;
+
+ var text = String(data);
+ rx_dangerous.lastIndex = 0;
+ if(rx_dangerous.test(text)){
+ text = text.replace(rx_dangerous, function(a){
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+ // todo 使用一下判断,增加安全性
+ //if (
+ // rx_one.test(
+ // text
+ // .replace(rx_two, '@')
+ // .replace(rx_three, ']')
+ // .replace(rx_four, '')
+ // )
+ //) {
+ // return eval('(' + text + ')');
+ //}
+
+ return eval('('+text+')');
+ };
+
+ /**
+ * parse javascript object to json string
+ * @param {Object} object
+ * @return {String} json string
+ */
+ this.stringifyJSON = function(obj) {
+ // Attempt to parse using the native JSON parser first
+ if (window.JSON && window.JSON.stringify) {
+ return window.JSON.stringify(obj);
+ }
+ switch (typeof (obj)) {
+ case 'string':
+ return '"' + obj.replace(/(["\\])/g, '\\$1') + '"';
+ case 'array':
+ return '[' + obj.map(that.stringifyJSON).join(',') + ']';
+ case 'object':
+ if (obj instanceof Array) {
+ var strArr = [];
+ var len = obj.length;
+ for (var i = 0; i < len; i++) {
+ strArr.push(that.stringifyJSON(obj[i]));
+ }
+ return '[' + strArr.join(',') + ']';
+ } else if (obj === null) {
+ return 'null';
+ } else {
+ var string = [];
+ for (var property in obj) {
+ if (obj.hasOwnProperty(property)) {
+ string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property]));
+ }
+ }
+ return '{' + string.join(',') + '}';
+ }
+ break;
+ case 'number':
+ return obj;
+ case false:
+ return obj;
+ case 'boolean':
+ return obj;
+ }
+ };
+
+ /**
+ * trim space beside text
+ * @param {String} untrimed string
+ * @return {String} trimed string
+ */
+ this.trim = function(text) {
+ return text === null ? "" : text.replace(/^\s+|\s+$/g, '');
+ };
+
+ /**
+ * create a uploader by QiniuJsSDK
+ * @param {object} options to create a new uploader
+ * @return {object} uploader
+ */
+ this.uploader = function(op) {
+
+ /********** inner function define start **********/
+
+ // according the different condition to reset chunk size
+ // and the upload strategy according with the chunk size
+ // when chunk size is zero will cause to direct upload
+ // see the statement binded on 'BeforeUpload' event
+ var reset_chunk_size = function() {
+ var ie = that.detectIEVersion();
+ var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size;
+ // case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true
+ var isSpecialSafari = (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.version <= 5 && moxie.core.utils.Env.os === "Windows" && moxie.core.utils.Env.osVersion === "7") || (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.os === "iOS" && moxie.core.utils.Env.osVersion === "7");
+ // case IE 9-,chunk_size is not empty and flash is included in runtimes
+ // set op.chunk_size to zero
+ //if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
+ if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
+ // link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not
+ // when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) .
+ op.chunk_size = 0;
+ } else if (isSpecialSafari) {
+ // win7 safari / iOS7 safari have bug when in chunk upload mode
+ // reset chunk_size to 0
+ // disable chunk in special version safari
+ op.chunk_size = 0;
+ } else {
+ BLOCK_BITS = 20;
+ MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M
+
+ chunk_size = plupload.parseSize(op.chunk_size);
+
+ // qiniu service max_chunk_size is 4m
+ // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m
+ }
+ // if op.chunk_size set 0 will be cause to direct upload
+ };
+
+ var getHosts = function(hosts) {
+ var result = [];
+ for (var i = 0; i < hosts.length; i++) {
+ var host = hosts[i];
+ if (host.indexOf('-H') === 0) {
+ result.push(host.split(' ')[2]);
+ } else {
+ result.push(host);
+ }
+ }
+ return result;
+ };
+
+ var getPutPolicy = function (uptoken) {
+ if(uploadConfig.saveType =="local"||uploadConfig.saveType =="oss" || uploadConfig.saveType =="upyun"||uploadConfig.saveType =="s3"||uploadConfig.saveType =="remote"){
+ return "oss";
+ }else{
+ var segments = uptoken.split(":");
+ var ak = segments[0];
+ var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2]));
+ putPolicy.ak = ak;
+ if (putPolicy.scope.indexOf(":") >= 0) {
+ putPolicy.bucket = putPolicy.scope.split(":")[0];
+ putPolicy.key = putPolicy.scope.split(":")[1];
+ } else {
+ putPolicy.bucket = putPolicy.scope;
+ }
+ return putPolicy;
+ }
+ };
+
+ var getUpHosts = function(uptoken) {
+ var putPolicy = getPutPolicy(uptoken);
+ // var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope;
+ // IE 9- is not support protocal relative url
+ var uphosts_url = window.location.protocol + "//uc.qbox.me/v1/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket;
+ logger.debug("putPolicy: ", putPolicy);
+ logger.debug("get uphosts from: ", uphosts_url);
+ var ie = that.detectIEVersion();
+ var ajax;
+ if (ie && ie <= 9) {
+ ajax = new moxie.XMLHttpRequest();
+ moxie.core.utils.Env.swf_url = op.flash_swf_url;
+ }else{
+ ajax = that.createAjax();
+ }
+ if(uploadConfig.saveType != "qiniu"){
+ qiniuUpHosts.http = [uploadConfig.upUrl];
+ qiniuUpHosts.http = [uploadConfig.upUrl];
+ that.resetUploadUrl();
+ }else{
+ ajax.open('GET', uphosts_url, false);
+ var onreadystatechange = function(){
+ logger.debug("ajax.readyState: ", ajax.readyState);
+ if (ajax.readyState === 4) {
+ logger.debug("ajax.status: ", ajax.status);
+ if (ajax.status < 400) {
+ var res = that.parseJSON(ajax.responseText);
+ qiniuUpHosts.http = getHosts(res.http.up);
+ qiniuUpHosts.https = getHosts(res.https.up);
+ logger.debug("get new uphosts: ", qiniuUpHosts);
+ that.resetUploadUrl();
+ } else {
+ logger.error("get uphosts error: ", ajax.responseText);
+ }
+ }
+ };
+ if (ie && ie <= 9) {
+ ajax.bind('readystatechange', onreadystatechange);
+ }else{
+ ajax.onreadystatechange = onreadystatechange;
+ }
+ ajax.send();
+ // ajax.send();
+ // if (ajax.status < 400) {
+ // var res = that.parseJSON(ajax.responseText);
+ // qiniuUpHosts.http = getHosts(res.http.up);
+ // qiniuUpHosts.https = getHosts(res.https.up);
+ // logger.debug("get new uphosts: ", qiniuUpHosts);
+ // that.resetUploadUrl();
+ // } else {
+ // logger.error("get uphosts error: ", ajax.responseText);
+ // }
+ }
+ return;
+ };
+
+ var getUptoken = function(file) {
+ if(uploadConfig.saveType == "remote"){
+ return that.token;
+ }
+ if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) {
+ return getNewUpToken(file);
+ } else {
+ return that.token;
+ }
+ };
+
+ // getNewUptoken maybe called at Init Event or BeforeUpload Event
+ // case Init Event, the file param of getUptken will be set a null value
+ // if op.uptoken has value, set uptoken with op.uptoken
+ // else if op.uptoken_url has value, set uptoken from op.uptoken_url
+ // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func
+ var getNewUpToken = function(file) {
+ if (op.uptoken) {
+ that.token = op.uptoken;
+ } else if (op.uptoken_url) {
+ logger.debug("get uptoken from: ", that.uptoken_url);
+ // TODO: use mOxie
+ var ajax = that.createAjax();
+ ajax.open('GET', that.uptoken_url+"?path="+encodeURIComponent(window.pathCache[file.id])+"&length="+file.size,false);
+ ajax.setRequestHeader("If-Modified-Since", "0");
+ ajax.send();
+ // var ajax = new Promise(function(resolve, reject) {
+ // var xhr = new XMLHttpRequest();
+ // xhr.onload = function() {
+ // resolve(xhr);
+ // };
+ // xhr.onerror = reject;
+ // xhr.open('GET', that.uptoken_url+"?path="+encodeURIComponent(window.pathCache[file.id]));
+ // xhr.send();
+ // });
+ if (ajax.status === 200) {
+ var res = that.parseJSON(ajax.responseText);
+ that.token = res.uptoken;
+ if (uploadConfig.saveType == "oss"){
+ var putPolicy = that.token;
+ that.sign = res.sign;
+ that.access = res.id;
+ that.file_name = res.key;
+ that.callback = res.callback;
+ }else if(uploadConfig.saveType == "s3"){
+ var putPolicy = that.token;
+ that.sign = res.sign;
+ that.policy = res.policy;
+ that.file_name = res.key;
+ that.credential = res.credential;
+ that.x_amz_date = res.x_amz_date;
+ that.surl = res.siteUrl;
+ that.callbackKey = res.callBackKey;
+ }else if(uploadConfig.saveType == "upyun"){
+ var putPolicy = that.token;
+ that.token = res.token;
+ that.policy = res.policy;
+ }else if(uploadConfig.saveType == "remote"){
+ var putPolicy = that.token;
+ that.policy = res.uptoken;
+ }else{
+ var segments = that.token.split(":");
+ var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2]));
+ if (!that.tokenMap) {
+ that.tokenMap = {};
+ }
+ var getTimestamp = function(time) {
+ return Math.ceil(time.getTime()/1000);
+ };
+ var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date")));
+ var clientTime = getTimestamp(new Date());
+ that.tokenInfo = {
+ serverDelay: clientTime - serverTime,
+ deadline: putPolicy.deadline,
+ isExpired: function() {
+ var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay;
+ return leftTime < 600;
+ }
+ };
+ logger.debug("get token info: ", that.tokenInfo);
+ }
+ }
+ // ajax.onload = function (e){
+ // if (ajax.status === 200) {
+
+
+ // logger.debug("get new uptoken: ", that.token);
+
+ // } else {
+ // logger.error("get uptoken error: ", ajax.responseText);
+ // }
+ // }
+ // ajax.onerror = function (e){
+ // logger.error("get uptoken error: ", ajax.responseText);
+ // }
+
+ } else if (op.uptoken_func) {
+ logger.debug("get uptoken from uptoken_func");
+ that.token = op.uptoken_func(file);
+ logger.debug("get new uptoken: ", that.token);
+ } else {
+ logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!");
+ }
+ if (that.token) {
+ getUpHosts(that.token);
+ }
+ return that.token;
+ };
+
+ // get file key according with the user passed options
+ var getFileKey = function(up, file, func) {
+ // WARNING
+ // When you set the key in putPolicy by "scope": "bucket:key"
+ // You should understand the risk of override a file in the bucket
+ // So the code below that automatically get key from uptoken has been commented
+ // var putPolicy = getPutPolicy(that.token)
+ // if (putPolicy.key) {
+ // logger.debug("key is defined in putPolicy.scope: ", putPolicy.key)
+ // return putPolicy.key
+ // }
+ var key = '',
+ unique_names = false;
+ if (!op.save_key) {
+ unique_names = up.getOption && up.getOption('unique_names');
+ unique_names = unique_names || (up.settings && up.settings.unique_names);
+ if (unique_names) {
+ var ext = that.getFileExtension(file.name);
+ key = ext ? file.id + '.' + ext : file.id;
+ } else if (typeof func === 'function') {
+ key = func(up, file);
+ } else {
+ key = file.name;
+ }
+ }
+ if(uploadConfig.saveType == "qiniu"){
+ return "";
+ }
+ return key;
+ };
+
+ /********** inner function define end **********/
+
+ if (op.log_level) {
+ logger.level = op.log_level;
+ }
+
+ if (!op.domain) {
+ throw 'domain setting in options is required!';
+ }
+
+ if (!op.browse_button) {
+ throw 'browse_button setting in options is required!';
+ }
+
+ if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) {
+ throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!';
+ }
+
+ logger.debug("init uploader start");
+
+ logger.debug("environment: ", moxie.core.utils.Env);
+
+ logger.debug("userAgent: ", navigator.userAgent);
+
+ var option = {};
+
+ // hold the handler from user passed options
+ var _Error_Handler = op.init && op.init.Error;
+ var _FileUploaded_Handler = op.init && op.init.FileUploaded;
+
+ // replace the handler for intercept
+ op.init.Error = function() {};
+ op.init.FileUploaded = function() {};
+
+ that.uptoken_url = op.uptoken_url;
+ that.token = '';
+ that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : '';
+ this.domain = op.domain;
+ // TODO: ctx is global in scope of a uploader instance
+ // this maybe cause error
+ var ctx = '';
+ var speedCalInfo = {
+ isResumeUpload: false,
+ resumeFilesize: 0,
+ startTime: '',
+ currentTime: ''
+ };
+
+ reset_chunk_size();
+ logger.debug("invoke reset_chunk_size()");
+ logger.debug("op.chunk_size: ", op.chunk_size);
+
+ var defaultSetting = {
+ url: qiniuUploadUrl,
+ multipart_params: {
+ }
+ };
+ var ie = that.detectIEVersion();
+ // case IE 9-
+ // add accept in multipart params
+ if (ie && ie <= 9) {
+ defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8';
+ logger.debug("add accept text/plain in multipart params");
+ }
+
+ // compose options with user passed options and default setting
+ plupload.extend(option, op, defaultSetting);
+
+ logger.debug("option: ", option);
+
+ // create a new uploader with composed options
+ var uploader = new plupload.Uploader(option);
+
+ logger.debug("new plupload.Uploader(option)");
+
+ // bind getNewUpToken to 'Init' event
+ uploader.bind('Init', function(up, params) {
+ logger.debug("Init event activated");
+ // if op.get_new_uptoken is not true
+ // invoke getNewUptoken when uploader init
+ // else
+ // getNewUptoken everytime before a new file upload
+ if(!op.get_new_uptoken){
+ getNewUpToken(null);
+ }
+ //getNewUpToken(null);
+ });
+
+ logger.debug("bind Init event");
+
+ // bind 'FilesAdded' event
+ // when file be added and auto_start has set value
+ // uploader will auto start upload the file
+ uploader.bind('FilesAdded', function(up, files) {
+ logger.debug("FilesAdded event activated");
+ var auto_start = up.getOption && up.getOption('auto_start');
+ auto_start = auto_start || (up.settings && up.settings.auto_start);
+ logger.debug("auto_start: ", auto_start);
+ logger.debug("files: ", files);
+
+
+ for (var i = 0; i < files.length; i++) {
+ var file = files[i];
+ file.path = decodeURIComponent(getCookieByString("path_tmp"));
+
+ }
+
+ // detect is iOS
+ var is_ios = function (){
+ if(moxie.core.utils.Env.OS.toLowerCase()==="ios") {
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ // if current env os is iOS change file name to [time].[ext]
+ if (is_ios()) {
+ for (var i = 0; i < files.length; i++) {
+ var file = files[i];
+ var ext = that.getFileExtension(file.name);
+ file.name = file.id + "." + ext;
+ }
+ }
+
+ if (auto_start) {
+ setTimeout(function(){
+ up.start();
+ logger.debug("invoke up.start()");
+ }, 0);
+ // up.start();
+ // plupload.each(files, function(i, file) {
+ // up.start();
+ // logger.debug("invoke up.start()")
+ // logger.debug("file: ", file);
+ // });
+ }
+ up.refresh(); // Reposition Flash/Silverlight
+ });
+
+ logger.debug("bind FilesAdded event");
+
+ // bind 'BeforeUpload' event
+ // intercept the process of upload
+ // - prepare uptoken
+ // - according the chunk size to make differnt upload strategy
+ // - resume upload with the last breakpoint of file
+ uploader.bind('BeforeUpload', function(up, file) {
+ logger.debug("BeforeUpload event activated");
+ getNewUpToken(file);
+ // add a key named speed for file object
+ file.speed = file.speed || 0;
+ ctx = '';
+
+ var directUpload = function(up, file, func) {
+ speedCalInfo.startTime = new Date().getTime();
+ var multipart_params_obj;
+ if (op.save_key) {
+ multipart_params_obj = {
+ 'token': that.token
+ };
+ } else {
+ multipart_params_obj = {
+ 'key': getFileKey(up, file, func),
+ 'token': that.token
+ };
+ if(uploadConfig.saveType == "qiniu"){
+ multipart_params_obj = {
+ 'token': that.token,
+ 'x:path': file.path
+ };
+ }else if(uploadConfig.saveType == "remote"){
+ multipart_params_obj = {
+ 'path': file.path,
+ "token" :that.policy,
+ "MAX_FILE_SIZE":4194304,
+ };
+ }else if(uploadConfig.saveType == "oss"){
+ multipart_params_obj = {
+ 'policy': that.token,
+ 'x:path': file.path,
+ "signature":that.sign,
+ "OSSAccessKeyId":that.access,
+ 'x:fname':file.name,
+ "key" :that.file_name.replace("${filename}", file.name),
+ "callback":that.callback,
+ };
+ }else if(uploadConfig.saveType == "s3"){
+ multipart_params_obj = {
+ 'policy': that.policy,
+ "key" :that.file_name+"/"+(file.path.replace(",","/") == "" ? "" : file.path.replace(",","/")+"/")+file.name,
+ "success_action_redirect" :that.surl+"Callback/S3/key/"+that.callbackKey,
+ "x-amz-algorithm":"AWS4-HMAC-SHA256",
+ "x-amz-credential":that.credential,
+ "x-amz-date":that.x_amz_date,
+ "x-amz-signature":that.sign,
+ "Content-Type":file.type,
+ };
+ }else if(uploadConfig.saveType == "upyun"){
+ multipart_params_obj = {
+ 'authorization': that.token,
+ 'policy': that.policy,
+ };
+ }
+ }
+ var ie = that.detectIEVersion();
+ // case IE 9-
+ // add accept in multipart params
+ if (ie && ie <= 9) {
+ multipart_params_obj.accept = 'text/plain; charset=utf-8';
+ logger.debug("add accept text/plain in multipart params");
+ }
+
+ logger.debug("directUpload multipart_params_obj: ", multipart_params_obj);
+
+ var x_vars = op.x_vars;
+ if (x_vars !== undefined && typeof x_vars === 'object') {
+ for (var x_key in x_vars) {
+ if (x_vars.hasOwnProperty(x_key)) {
+ if (typeof x_vars[x_key] === 'function') {
+ multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file);
+ } else if (typeof x_vars[x_key] !== 'object') {
+ multipart_params_obj['x:' + x_key] = x_vars[x_key];
+ }
+ }
+ }
+ }
+
+ // 本地策略时不用multipart
+ if(uploadConfig.saveType == "local"){
+ up.setOption({
+ 'url': qiniuUploadUrl,
+ 'multipart': false,
+ "send_file_name":false,
+ 'headers': {
+ 'X-Path': encodeURIComponent(file.path),
+ 'X-FileName': encodeURIComponent(getFileKey(up, file, func)),
+ },
+ });
+ }else{
+ up.setOption({
+ 'url': qiniuUploadUrl,
+ 'multipart': true,
+ 'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined,
+ 'multipart_params': multipart_params_obj
+ });
+ }
+
+
+ };
+
+ // detect is weixin or qq inner browser
+ var is_android_weixin_or_qq = function (){
+ var ua = navigator.userAgent.toLowerCase();
+ if((ua.match(/MicroMessenger/i) || moxie.core.utils.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && moxie.core.utils.Env.OS.toLowerCase()==="android") {
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ var chunk_size = up.getOption && up.getOption('chunk_size');
+ chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
+
+ logger.debug("uploader.runtime: ",uploader.runtime);
+ logger.debug("chunk_size: ",chunk_size);
+
+ // TODO: flash support chunk upload
+ if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) {
+ if (file.size < chunk_size || is_android_weixin_or_qq()) {
+ logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()");
+ // direct upload if file size is less then the chunk size
+ directUpload(up, file, that.key_handler);
+ } else {
+ // TODO: need a polifill to make it work in IE 9-
+ // ISSUE: if file.name is existed in localStorage
+ // but not the same file maybe cause error
+ var localFileInfo = localStorage.getItem(file.name);
+ var blockSize = chunk_size;
+ if (localFileInfo) {
+ // TODO: although only the html5 runtime will enter this statement
+ // but need uniform way to make convertion between string and json
+ localFileInfo = that.parseJSON(localFileInfo);
+ var now = (new Date()).getTime();
+ var before = localFileInfo.time || 0;
+ var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day
+ // if the last upload time is within one day
+ // will upload continuously follow the last breakpoint
+ // else
+ // will reupload entire file
+ if (now - before < aDay) {
+
+ if (localFileInfo.percent !== 100) {
+ if (file.size === localFileInfo.total) {
+ // TODO: if file.name and file.size is the same
+ // but not the same file will cause error
+ file.percent = localFileInfo.percent;
+ file.loaded = localFileInfo.offset;
+ ctx = localFileInfo.ctx;
+
+ // set speed info
+ speedCalInfo.isResumeUpload = true;
+ speedCalInfo.resumeFilesize = localFileInfo.offset;
+
+ // set block size
+ if (localFileInfo.offset + blockSize > file.size) {
+ blockSize = file.size - localFileInfo.offset;
+ }
+ } else {
+ // remove file info when file.size is conflict with file info
+ localStorage.removeItem(file.name);
+ }
+
+ } else {
+ // remove file info when upload percent is 100%
+ // avoid 499 bug
+ localStorage.removeItem(file.name);
+ }
+ } else {
+ // remove file info when last upload time is over one day
+ localStorage.removeItem(file.name);
+ }
+ }
+ speedCalInfo.startTime = new Date().getTime();
+ var multipart_params_obj = {};
+ var ie = that.detectIEVersion();
+ // case IE 9-
+ // add accept in multipart params
+ if (ie && ie <= 9) {
+ multipart_params_obj.accept = 'text/plain; charset=utf-8';
+ logger.debug("add accept text/plain in multipart params");
+ }
+ // TODO: to support bput
+ // http://developer.qiniu.com/docs/v6/api/reference/up/bput.html
+ if(uploadConfig.saveType == "remote"){
+ up.setOption({
+ 'url': qiniuUploadUrl + 'chunk.php',
+ 'multipart': false,
+ 'chunk_size': chunk_size,
+ 'required_features': "chunks",
+ 'headers': {
+ 'Authorization': getUptoken(file)
+ },
+ 'multipart_params': multipart_params_obj
+ });
+ }else{
+ up.setOption({
+ 'url': qiniuUploadUrl + '/mkblk/' + blockSize,
+ 'multipart': false,
+ 'chunk_size': chunk_size,
+ 'required_features': "chunks",
+ 'headers': {
+ 'Authorization': 'UpToken ' + getUptoken(file)
+ },
+ 'multipart_params': multipart_params_obj
+ });
+ }
+ }
+ } else {
+ logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size");
+ // direct upload if runtime is not html5
+ directUpload(up, file, that.key_handler);
+ }
+ });
+
+ logger.debug("bind BeforeUpload event");
+
+ // bind 'UploadProgress' event
+ // calculate upload speed
+ uploader.bind('UploadProgress', function(up, file) {
+ logger.trace("UploadProgress event activated");
+ speedCalInfo.currentTime = new Date().getTime();
+ var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms
+ var fileUploaded = file.loaded || 0;
+ if (speedCalInfo.isResumeUpload) {
+ fileUploaded = file.loaded - speedCalInfo.resumeFilesize;
+ }
+ file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s
+ });
+
+ logger.debug("bind UploadProgress event");
+
+ // bind 'ChunkUploaded' event
+ // store the chunk upload info and set next chunk upload url
+ uploader.bind('ChunkUploaded', function(up, file, info) {
+ logger.debug("ChunkUploaded event activated");
+ logger.debug("file: ", file);
+ logger.debug("info: ", info);
+ var res = that.parseJSON(info.response);
+ logger.debug("res: ", res);
+ // ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...'
+ ctx = ctx ? ctx + ',' + res.ctx : res.ctx;
+ var leftSize = info.total - info.offset;
+ var chunk_size = up.getOption && up.getOption('chunk_size');
+ chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
+ if (leftSize < chunk_size) {
+ up.setOption({
+ 'url': qiniuUploadUrl + '/mkblk/' + leftSize
+ });
+ if(uploadConfig.saveType == "remote"){
+ up.setOption({
+ 'url': qiniuUploadUrl + 'chunk.php'
+ });
+ }
+ logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize);
+ }
+ if(uploadConfig.saveType == "remote"){
+ up.setOption({
+ 'headers': {
+ 'Authorization': getUptoken(file)
+ }
+ });
+ }else{
+ up.setOption({
+ 'headers': {
+ 'Authorization': 'UpToken ' + getUptoken(file)
+ }
+ });
+ }
+ localStorage.setItem(file.name, that.stringifyJSON({
+ ctx: ctx,
+ percent: file.percent,
+ total: info.total,
+ offset: info.offset,
+ time: (new Date()).getTime()
+ }));
+ });
+
+ logger.debug("bind ChunkUploaded event");
+
+ var retries = qiniuUploadUrls.length;
+
+ // if error is unkown switch upload url and retry
+ var unknow_error_retry = function(file){
+ if (retries-- > 0) {
+ setTimeout(function(){
+ that.resetUploadUrl();
+ file.status = plupload.QUEUED;
+ uploader.stop();
+ uploader.start();
+ }, 0);
+ return true;
+ }else{
+ retries = qiniuUploadUrls.length;
+ return false;
+ }
+ };
+
+ // bind 'Error' event
+ // check the err.code and return the errTip
+ uploader.bind('Error', (function(_Error_Handler) {
+ return function(up, err) {
+ logger.error("Error event activated");
+ logger.error("err: ", err);
+ var errTip = '';
+ var file = err.file;
+ if (file) {
+ switch (err.code) {
+ case plupload.FAILED:
+ errTip = '上传失败。请稍后再试。';
+ break;
+ case plupload.FILE_SIZE_ERROR:
+ var max_file_size = up.getOption && up.getOption('max_file_size');
+ max_file_size = max_file_size || (up.settings && up.settings.max_file_size);
+ errTip = '文件过大,您当前用户组最多可上传' + max_file_size + '的文件';
+ break;
+ case plupload.FILE_EXTENSION_ERROR:
+ errTip = '您当前的用户组不可上传此文件';
+ break;
+ case plupload.HTTP_ERROR:
+ if (err.response === '') {
+ // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE
+ errTip = err.message || '未知网络错误。';
+ if (!unknow_error_retry(file)) {
+ return;
+ }
+ break;
+ }
+ if(uploadConfig.saveType == "oss"){
+ var str = err.response
+ try {
+ parser=new DOMParser();
+ xmlDoc=parser.parseFromString(str,"text/xml");
+ }catch(e) {
+ errTip = "未知错误";
+ var errorText = "Error";
+ }
+ errTip = "回调失败";
+ var errorText = xmlDoc.getElementsByTagName("Message")[0].innerHTML;
+ }else if(uploadConfig.saveType == "s3" && err.status!=401){
+ var str = err.response
+ parser = new DOMParser();
+ xmlDoc = parser.parseFromString(str, "text/xml");
+ errTip = xmlDoc.getElementsByTagName("Message")[0].childNodes[0].nodeValue;
+ var errorText = "Error";
+
+ }else{
+ var errorObj = that.parseJSON(err.response);
+ var errorText = errorObj.error;
+ if (err.status==579){
+ var errorObj2 = that.parseJSON(errorText);
+ errorText=errorObj2.error;
+ }
+ switch (err.status) {
+ case 400:
+ errTip = "请求报文格式错误。";
+ break;
+ case 401:
+ errTip = "客户端认证授权失败。请重试或提交反馈。";
+ break;
+ case 405:
+ errTip = "客户端请求错误。请重试或提交反馈。";
+ break;
+ case 579:
+ errTip = "资源上传成功,但回调失败。";
+ break;
+ case 599:
+ errTip = "网络连接异常。请重试或提交反馈。";
+ if (!unknow_error_retry(file)) {
+ return;
+ }
+ break;
+ case 614:
+ errTip = "文件已存在。";
+ try {
+ errorObj = that.parseJSON(errorObj.error);
+ errorText = errorObj.error || 'file exists';
+ } catch (e) {
+ errorText = errorObj.error || 'file exists';
+ }
+ break;
+ case 631:
+ errTip = "指定空间不存在。";
+ break;
+ case 701:
+ errTip = "上传数据块校验出错。请重试或提交反馈。";
+ break;
+ default:
+ if (err.message){
+ errTip = err.message;
+ }else{
+ errTip = "未知错误";
+ }
+ if (!unknow_error_retry(file)) {
+ return;
+ }
+ break;
+ }
+
+ }
+ if (uploadConfig.saveType != "local"){
+ errTip = errTip + '(' + err.status + ':' + errorText + ')';
+ }
+ break;
+ case plupload.SECURITY_ERROR:
+ errTip = '安全配置错误。请联系网站管理员。';
+ break;
+ case plupload.GENERIC_ERROR:
+ errTip = '上传失败。请稍后再试。';
+ break;
+ case plupload.IO_ERROR:
+ errTip = '上传失败。请稍后再试。';
+ break;
+ case plupload.INIT_ERROR:
+ errTip = '网站配置错误。请联系网站管理员。';
+ uploader.destroy();
+ break;
+ default:
+ errTip = err.message + err.details;
+ if (!unknow_error_retry(file)) {
+ return;
+ }
+ break;
+ }
+ if (_Error_Handler) {
+ _Error_Handler(up, err, errTip);
+ }
+ }
+ up.refresh(); // Reposition Flash/Silverlight
+ };
+ })(_Error_Handler));
+
+ logger.debug("bind Error event");
+
+ // bind 'FileUploaded' event
+ // intercept the complete of upload
+ // - get downtoken from downtoken_url if bucket is private
+ // - invoke mkfile api to compose chunks if upload strategy is chunk upload
+ uploader.bind('FileUploaded', (function(_FileUploaded_Handler) {
+ return function(up, file, info) {
+ logger.debug("FileUploaded event activated");
+ logger.debug("file: ", file);
+ logger.debug("info: ", info);
+ if(uploadConfig.saveType == "s3"){
+ }
+ var last_step = function(up, file, info) {
+ if (op.downtoken_url) {
+ // if op.dowontoken_url is not empty
+ // need get downtoken before invoke the _FileUploaded_Handler
+ var ajax_downtoken = that.createAjax();
+ ajax_downtoken.open('POST', op.downtoken_url, true);
+ ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ ajax_downtoken.onreadystatechange = function() {
+ if (ajax_downtoken.readyState === 4) {
+
+ if (ajax_downtoken.status === 200 || ajax_downtoken.status === 204||ajax_downtoken.status === 303) {
+
+ var res_downtoken;
+ try {
+ res_downtoken = that.parseJSON(ajax_downtoken.responseText);
+ } catch (e) {
+ throw ('invalid json format');
+ }
+ var info_extended = {};
+ plupload.extend(info_extended, that.parseJSON(info), res_downtoken);
+ if (_FileUploaded_Handler) {
+ _FileUploaded_Handler(up, file, that.stringifyJSON(info_extended));
+ }
+
+ } else {
+ uploader.trigger('Error', {
+ status: ajax_downtoken.status,
+ response: ajax_downtoken.responseText,
+ file: file,
+ code: plupload.HTTP_ERROR
+ });
+ }
+ }
+ };
+ ajax_downtoken.send('key=' + that.parseJSON(info).key + '&domain=' + op.domain);
+ } else if (_FileUploaded_Handler) {
+ _FileUploaded_Handler(up, file, info);
+ }
+ };
+ if(uploadConfig.saveType == "oss" || uploadConfig.saveType == "upyun"){
+ ctx = 0;
+ }else{
+ var res = that.parseJSON(info.response);
+ ctx = ctx ? ctx : res.ctx;
+ }
+ // if ctx is not empty
+ // that means the upload strategy is chunk upload
+ // befroe the invoke the last_step
+ // we need request the mkfile to compose all uploaded chunks
+ // else
+ // invalke the last_step
+ logger.debug("ctx: ", ctx);
+ if (ctx) {
+ var key = '';
+ logger.debug("save_key: ", op.save_key);
+ if (!op.save_key) {
+ key = getFileKey(up, file, that.key_handler);
+ key = key ? '/key/' + that.URLSafeBase64Encode(key) : '';
+ }
+
+ var fname = '/fname/' + that.URLSafeBase64Encode(file.name);
+ if(uploadConfig.saveType=="remote"){
+ if (!op.save_key) {
+ key = getFileKey(up, file, that.key_handler);
+ key = key ? that.URLSafeBase64Encode(key) : '';
+ }
+ fname = '' + that.URLSafeBase64Encode(file.name);
+ op.x_vars= {
+ 'path': file.path,
+ };
+ }
+ logger.debug("op.x_vars: ", op.x_vars);
+ if(uploadConfig.saveType == "qiniu"){
+ op.x_vars= {
+ 'path': file.path,
+ };
+ }
+ var x_vars = op.x_vars,
+ x_val = '',
+ x_vars_url = '';
+ if (x_vars !== undefined && typeof x_vars === 'object') {
+ for (var x_key in x_vars) {
+ if (x_vars.hasOwnProperty(x_key)) {
+ if (typeof x_vars[x_key] === 'function') {
+ x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file));
+ } else if (typeof x_vars[x_key] !== 'object') {
+ x_val = that.URLSafeBase64Encode(x_vars[x_key]);
+ }
+ x_vars_url += '/x:' + x_key + '/' + x_val;
+ }
+ }
+ }
+ local_path = "";
+ if(uploadConfig.saveType == "local"||uploadConfig.saveType == "onedrive"){
+ pathTmp = file.path;
+ if(file.path == ""){
+ pathTmp = "ROOTDIR";
+ }
+ local_path = '/path/'+that.URLSafeBase64Encode(pathTmp);
+ }
+ if(uploadConfig.saveType == "remote"){
+ pathTmp = file.path;
+ local_path = that.URLSafeBase64Encode(pathTmp);
+ var url = qiniuUploadUrl + 'mkfile.php?size=' + file.size +"&key="+ key+"&fname="+ fname +"&path="+local_path;
+ }else{
+ var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url+local_path;
+ }
+ var ie = that.detectIEVersion();
+ var ajax;
+ if (ie && ie <= 9) {
+ ajax = new moxie.xhr.XMLHttpRequest();
+ moxie.core.utils.Env.swf_url = op.flash_swf_url;
+ }else{
+ ajax = that.createAjax();
+ }
+ ajax.open('POST', url, true);
+ ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
+ if(uploadConfig.saveType == "remote"){
+ ajax.setRequestHeader('Authorization',that.token);
+ }else{
+ ajax.setRequestHeader('Authorization', 'UpToken ' + that.token);
+ }
+ var onreadystatechange = function(){
+ logger.debug("ajax.readyState: ", ajax.readyState);
+ if (ajax.readyState === 4) {
+ localStorage.removeItem(file.name);
+ var info;
+ if (ajax.status === 200) {
+ info = ajax.responseText;
+ logger.debug("mkfile is success: ", info);
+ last_step(up, file, info);
+ } else {
+ info = {
+ status: ajax.status,
+ response: ajax.responseText,
+ file: file,
+ code: -200,
+ responseHeaders: ajax.getAllResponseHeaders()
+ };
+ logger.debug("mkfile is error: ", info);
+ uploader.trigger('Error', info);
+ }
+ }
+ };
+ if (ie && ie <= 9) {
+ ajax.bind('readystatechange', onreadystatechange);
+ }else{
+ ajax.onreadystatechange = onreadystatechange;
+ }
+ ajax.send(ctx);
+ logger.debug("mkfile: ", url);
+ } else {
+ last_step(up, file, info.response);
+ }
+
+ };
+ })(_FileUploaded_Handler));
+
+ logger.debug("bind FileUploaded event");
+
+ // init uploader
+ uploader.init();
+
+ logger.debug("invoke uploader.init()");
+
+ logger.debug("init uploader end");
+
+ return uploader;
+ };
+
+ /**
+ * get url by key
+ * @param {String} key of file
+ * @return {String} url of file
+ */
+ this.getUrl = function(key) {
+ if (!key) {
+ return false;
+ }
+ key = encodeURI(key);
+ var domain = this.domain;
+ if (domain.slice(domain.length - 1) !== '/') {
+ domain = domain + '/';
+ }
+ return domain + key;
+ };
+
+ /**
+ * invoke the imageView2 api of Qiniu
+ * @param {Object} api params
+ * @param {String} key of file
+ * @return {String} url of processed image
+ */
+ this.imageView2 = function(op, key) {
+
+ if (!/^\d$/.test(op.mode)) {
+ return false;
+ }
+
+ var mode = op.mode,
+ w = op.w || '',
+ h = op.h || '',
+ q = op.q || '',
+ format = op.format || '';
+
+ if (!w && !h) {
+ return false;
+ }
+
+ var imageUrl = 'imageView2/' + mode;
+ imageUrl += w ? '/w/' + w : '';
+ imageUrl += h ? '/h/' + h : '';
+ imageUrl += q ? '/q/' + q : '';
+ imageUrl += format ? '/format/' + format : '';
+ if (key) {
+ imageUrl = this.getUrl(key) + '?' + imageUrl;
+ }
+ return imageUrl;
+ };
+
+ /**
+ * invoke the imageMogr2 api of Qiniu
+ * @param {Object} api params
+ * @param {String} key of file
+ * @return {String} url of processed image
+ */
+ this.imageMogr2 = function(op, key) {
+ var auto_orient = op['auto-orient'] || '',
+ thumbnail = op.thumbnail || '',
+ strip = op.strip || '',
+ gravity = op.gravity || '',
+ crop = op.crop || '',
+ quality = op.quality || '',
+ rotate = op.rotate || '',
+ format = op.format || '',
+ blur = op.blur || '';
+ //Todo check option
+
+ var imageUrl = 'imageMogr2';
+
+ imageUrl += auto_orient ? '/auto-orient' : '';
+ imageUrl += thumbnail ? '/thumbnail/' + thumbnail : '';
+ imageUrl += strip ? '/strip' : '';
+ imageUrl += gravity ? '/gravity/' + gravity : '';
+ imageUrl += quality ? '/quality/' + quality : '';
+ imageUrl += crop ? '/crop/' + crop : '';
+ imageUrl += rotate ? '/rotate/' + rotate : '';
+ imageUrl += format ? '/format/' + format : '';
+ imageUrl += blur ? '/blur/' + blur : '';
+
+ if (key) {
+ imageUrl = this.getUrl(key) + '?' + imageUrl;
+ }
+ return imageUrl;
+ };
+
+ /**
+ * invoke the watermark api of Qiniu
+ * @param {Object} api params
+ * @param {String} key of file
+ * @return {String} url of processed image
+ */
+ this.watermark = function(op, key) {
+ var mode = op.mode;
+ if (!mode) {
+ return false;
+ }
+
+ var imageUrl = 'watermark/' + mode;
+
+ if (mode === 1) {
+ var image = op.image || '';
+ if (!image) {
+ return false;
+ }
+ imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : '';
+ } else if (mode === 2) {
+ var text = op.text ? op.text : '',
+ font = op.font ? op.font : '',
+ fontsize = op.fontsize ? op.fontsize : '',
+ fill = op.fill ? op.fill : '';
+ if (!text) {
+ return false;
+ }
+ imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : '';
+ imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : '';
+ imageUrl += fontsize ? '/fontsize/' + fontsize : '';
+ imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : '';
+ } else {
+ // Todo mode3
+ return false;
+ }
+
+ var dissolve = op.dissolve || '',
+ gravity = op.gravity || '',
+ dx = op.dx || '',
+ dy = op.dy || '';
+
+ imageUrl += dissolve ? '/dissolve/' + dissolve : '';
+ imageUrl += gravity ? '/gravity/' + gravity : '';
+ imageUrl += dx ? '/dx/' + dx : '';
+ imageUrl += dy ? '/dy/' + dy : '';
+
+ if (key) {
+ imageUrl = this.getUrl(key) + '?' + imageUrl;
+ }
+ return imageUrl;
+ };
+
+ /**
+ * invoke the imageInfo api of Qiniu
+ * @param {String} key of file
+ * @return {Object} image info
+ */
+ this.imageInfo = function(key) {
+ if (!key) {
+ return false;
+ }
+ var url = this.getUrl(key) + '?imageInfo';
+ var xhr = this.createAjax();
+ var info;
+ var that = this;
+ xhr.open('GET', url, false);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ info = that.parseJSON(xhr.responseText);
+ }
+ };
+ xhr.send();
+ return info;
+ };
+
+ /**
+ * invoke the exif api of Qiniu
+ * @param {String} key of file
+ * @return {Object} image exif
+ */
+ this.exif = function(key) {
+ if (!key) {
+ return false;
+ }
+ var url = this.getUrl(key) + '?exif';
+ var xhr = this.createAjax();
+ var info;
+ var that = this;
+ xhr.open('GET', url, false);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ info = that.parseJSON(xhr.responseText);
+ }
+ };
+ xhr.send();
+ return info;
+ };
+
+ /**
+ * invoke the exif or imageInfo api of Qiniu
+ * according with type param
+ * @param {String} ['exif'|'imageInfo']type of info
+ * @param {String} key of file
+ * @return {Object} image exif or info
+ */
+ this.get = function(type, key) {
+ if (!key || !type) {
+ return false;
+ }
+ if (type === 'exif') {
+ return this.exif(key);
+ } else if (type === 'imageInfo') {
+ return this.imageInfo(key);
+ }
+ return false;
+ };
+
+ /**
+ * invoke api of Qiniu like a pipeline
+ * @param {Array of Object} params of a series api call
+ * each object in array is options of api which name is set as 'fop' property
+ * each api's output will be next api's input
+ * @param {String} key of file
+ * @return {String|Boolean} url of processed image
+ */
+ this.pipeline = function(arr, key) {
+ var isArray = Object.prototype.toString.call(arr) === '[object Array]';
+ var option, errOp, imageUrl = '';
+ if (isArray) {
+ for (var i = 0, len = arr.length; i < len; i++) {
+ option = arr[i];
+ if (!option.fop) {
+ return false;
+ }
+ switch (option.fop) {
+ case 'watermark':
+ imageUrl += this.watermark(option) + '|';
+ break;
+ case 'imageView2':
+ imageUrl += this.imageView2(option) + '|';
+ break;
+ case 'imageMogr2':
+ imageUrl += this.imageMogr2(option) + '|';
+ break;
+ default:
+ errOp = true;
+ break;
+ }
+ if (errOp) {
+ return false;
+ }
+ }
+ if (key) {
+ imageUrl = this.getUrl(key) + '?' + imageUrl;
+ var length = imageUrl.length;
+ if (imageUrl.slice(length - 1) === '|') {
+ imageUrl = imageUrl.slice(0, length - 1);
+ }
+ }
+ return imageUrl;
+ }
+ return false;
+ };
+}
+
+var Qiniu = new QiniuJsSDK();
+
+global.Qiniu = Qiniu;
+
+global.QiniuJsSDK = QiniuJsSDK;
+
+})( window );
\ No newline at end of file
diff --git a/public/video.html b/public/video.html
deleted file mode 100644
index 6db3386..0000000
--- a/public/video.html
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
- React App
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/viewer/markdown.html b/public/viewer/markdown.html
deleted file mode 100644
index 1552146..0000000
--- a/public/viewer/markdown.html
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {$fileName} - {$options.siteName}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/viewer/video.html b/public/viewer/video.html
deleted file mode 100644
index 3d7546d..0000000
--- a/public/viewer/video.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- {$fileName} - {$options.siteName}
-
-
-
-
-
-
-
-
-
diff --git a/src/App.js b/src/App.js
index abdf339..cc36051 100644
--- a/src/App.js
+++ b/src/App.js
@@ -41,7 +41,7 @@ export default function App() {
-
+
diff --git a/src/component/FileManager/FileIcon.js b/src/component/FileManager/FileIcon.js
index 5b9f5cd..fb66c45 100644
--- a/src/component/FileManager/FileIcon.js
+++ b/src/component/FileManager/FileIcon.js
@@ -24,20 +24,20 @@ const styles = theme => ({
"&:hover": {
border: "1px solid #d0d0d0",
},
- backgroundColor: theme.palette.explorer.bgSelected,
+ backgroundColor: theme.palette.type == "dark" ? "#fff" : theme.palette.explorer.bgSelected,
},
notSelected: {
"&:hover": {
- backgroundColor: "#f9f9f9",
+ backgroundColor: theme.palette.background.default,
border: "1px solid #d0d0d0",
},
backgroundColor: theme.palette.background.paper,
},
button: {
- border: "1px solid #dadce0",
+ border: "1px solid "+theme.palette.divider,
width: "100%",
borderRadius: "6px",
boxSizing: "border-box",
@@ -46,11 +46,11 @@ const styles = theme => ({
display: "initial",
},
folderNameSelected: {
- color: theme.palette.primary.dark,
+ color: theme.palette.type == "dark" ? theme.palette.background.paper : theme.palette.primary.dark,
fontWeight: "500",
},
folderNameNotSelected: {
- color: theme.palette.explorer.filename,
+ color: theme.palette.text.secondary,
},
folderName: {
marginTop: "15px",
diff --git a/src/component/FileManager/Folder.js b/src/component/FileManager/Folder.js
index 55b8bde..effccd5 100644
--- a/src/component/FileManager/Folder.js
+++ b/src/component/FileManager/Folder.js
@@ -15,13 +15,13 @@ const styles = theme => ({
"&:hover": {
border: "1px solid #d0d0d0",
},
- backgroundColor: theme.palette.explorer.bgSelected,
+ backgroundColor: theme.palette.type == "dark" ? "#fff" : theme.palette.explorer.bgSelected,
},
notSelected: {
"&:hover": {
- backgroundColor: "#f9f9f9",
+ backgroundColor: theme.palette.background.default,
border: "1px solid #d0d0d0",
},
backgroundColor: theme.palette.background.paper,
@@ -29,7 +29,7 @@ const styles = theme => ({
button: {
height: "50px",
- border: "1px solid #dadce0",
+ border: "1px solid "+theme.palette.divider,
width: "100%",
borderRadius: "6px",
boxSizing: "border-box",
@@ -48,11 +48,11 @@ const styles = theme => ({
color: theme.palette.explorer.icon,
},
folderNameSelected: {
- color: theme.palette.primary.dark,
+ color: theme.palette.type == "dark" ? theme.palette.background.paper : theme.palette.primary.dark,
fontWeight: "500",
},
folderNameNotSelected: {
- color: theme.palette.explorer.filename,
+ color: theme.palette.text.secondary,
},
folderName: {
marginTop: "15px",
diff --git a/src/component/FileManager/Navigator.js b/src/component/FileManager/Navigator.js
index 450b4ea..887e9fc 100644
--- a/src/component/FileManager/Navigator.js
+++ b/src/component/FileManager/Navigator.js
@@ -112,7 +112,7 @@ const styles = theme => ({
},
height:"48px",
overflow:"hidden",
- backgroundColor:"#fff",
+ backgroundColor:theme.palette.background.paper,
},
navigatorContainer:{
"display": "flex",
diff --git a/src/component/FileManager/SmallIcon.js b/src/component/FileManager/SmallIcon.js
index 906f547..32df8eb 100644
--- a/src/component/FileManager/SmallIcon.js
+++ b/src/component/FileManager/SmallIcon.js
@@ -22,13 +22,12 @@ const styles = theme => ({
"&:hover": {
border: "1px solid #d0d0d0",
},
- backgroundColor: theme.palette.explorer.bgSelected,
-
+ backgroundColor: theme.palette.type == "dark" ? "#fff" : theme.palette.explorer.bgSelected,
},
notSelected: {
"&:hover": {
- backgroundColor: "#f9f9f9",
+ backgroundColor: theme.palette.background.default,
border: "1px solid #d0d0d0",
},
backgroundColor: theme.palette.background.paper,
@@ -36,7 +35,7 @@ const styles = theme => ({
button: {
height: "50px",
- border: "1px solid #dadce0",
+ border: "1px solid "+theme.palette.divider,
width: "100%",
borderRadius: "6px",
boxSizing: "border-box",
@@ -55,11 +54,11 @@ const styles = theme => ({
color: theme.palette.explorer.icon,
},
folderNameSelected: {
- color: theme.palette.primary.dark,
+ color: theme.palette.type == "dark" ? theme.palette.background.paper : theme.palette.primary.dark,
fontWeight: "500",
},
folderNameNotSelected: {
- color: theme.palette.explorer.filename,
+ color: theme.palette.text.secondary,
},
folderName: {
marginTop: "15px",
@@ -233,7 +232,9 @@ class SmallIconCompoment extends Component {
{this.props.file.name}
+ })}
+ variant="body2"
+ >{this.props.file.name}
);
diff --git a/src/component/FileManager/TableRow.js b/src/component/FileManager/TableRow.js
index ba5b6a4..5e07ded 100644
--- a/src/component/FileManager/TableRow.js
+++ b/src/component/FileManager/TableRow.js
@@ -27,12 +27,12 @@ import { withStyles, TableCell, TableRow, Typography } from "@material-ui/core";
const styles = theme => ({
selected: {
"&:hover": {},
- backgroundColor: theme.palette.explorer.bgSelected
+ backgroundColor: theme.palette.type == "dark" ? theme.palette.background.paper : theme.palette.explorer.bgSelected,
},
notSelected: {
"&:hover": {
- backgroundColor: "#ececec"
+ backgroundColor: theme.palette.background.default,
}
},
icon: {
@@ -106,12 +106,12 @@ const styles = theme => ({
verticalAlign: "middle"
},
folderNameSelected: {
- color: theme.palette.primary.dark,
+ color: theme.palette.type == "dark" ? "#fff" : theme.palette.primary.dark,
fontWeight: "500",
userSelect: "none"
},
folderNameNotSelected: {
- color: theme.palette.explorer.filename,
+ color: theme.palette.text.secondary,
userSelect: "none"
},
folderName: {
diff --git a/src/component/Upload/FileList.js b/src/component/Upload/FileList.js
index 2db6831..120bfb6 100644
--- a/src/component/Upload/FileList.js
+++ b/src/component/Upload/FileList.js
@@ -93,7 +93,7 @@ class FileList extends Component {
updateStatus(file){
var filesNow = this.state.files;
var fileID = filesNow.findIndex((f) => { return f.id === file.id });
- if (fileID !== -1) {
+ if (!file.errMsg) {
if(filesNow[fileID].status!==4){
filesNow[fileID] = file;
this.setState({
@@ -203,6 +203,7 @@ class FileList extends Component {
}else{
progressItem = ( );
}
+
return (
diff --git a/src/component/Uploader.js b/src/component/Uploader.js
index 9280bc3..d9d1a04 100644
--- a/src/component/Uploader.js
+++ b/src/component/Uploader.js
@@ -3,6 +3,7 @@ import scriptLoader from "../loader/index.js";
import { connect } from "react-redux";
import { refreshFileList, refreshStorage } from "../actions/index";
import FileList from "./Upload/FileList.js";
+import Auth from "../middleware/Auth"
let loaded = false;
@@ -53,21 +54,26 @@ class UploaderCompoment extends Component {
return;
}
loaded = true;
+ var user = Auth.GetUser();
+ window.uploadConfig = user.policy;
+ console.log(window.uploadConfig);
this.uploader = window.Qiniu.uploader({
runtimes: "html5",
browse_button: "pickfiles",
container: "container",
drop_element: "container",
- max_file_size: window.uploadConfig.maxSize,
+ max_file_size: user.policy.maxSize,
dragdrop: true,
- chunk_size: window.ChunkSize,
+ chunk_size: user.policy.saveType == "qiniu" ? 4*1024*1024 : 0,
filters: {
- mime_types: window.uploadConfig.allowedType
+ mime_types: user.policy.allowedType
},
// iOS不能多选?
multi_selection: true,
- uptoken_url: "/Upload/Token",
+ uptoken_url: "/api/v3/file/token",
+ uptoken:user.policy.saveType == "local" ? "token" : null,
domain: "s",
+ max_retries:0,
get_new_uptoken: true,
auto_start: true,
log_level: 5,
@@ -86,6 +92,7 @@ class UploaderCompoment extends Component {
this.setState({ queued: up.total.queued });
},
UploadProgress: (up, file) => {
+ console.log("UploadProgress",file);
this.fileList["updateStatus"](file);
},
UploadComplete: (up, file) => {
diff --git a/src/index.js b/src/index.js
index e166948..fb8820d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -109,7 +109,7 @@ const defaultStatus = InitSiteConfig({
}
});
-let store = createStore(cloureveApp, defaultStatus);
+let store = createStore(cloureveApp, defaultStatus,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
UpdateSiteConfig(store);
ReactDOM.render(
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 6ac418d..4776c1e 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -165,6 +165,38 @@ const cloudreveApp = (state = [], action) => {
});
case 'NAVIGATOR_TO':
return doNavigate(action.path,state);
+ case 'TOGGLE_DAYLIGHT_MODE':
+ const copy = Object.assign({}, state);
+ if (copy.siteConfig.theme.palette.type === undefined || copy.siteConfig.theme.palette.type === "light"){
+ return {
+ ...state,
+ siteConfig: {
+ ...state.siteConfig,
+ theme:{
+ ...state.siteConfig.theme,
+ palette:{
+ ...state.siteConfig.theme.palette,
+ type:"dark",
+ }
+ },
+ },
+ };
+ }
+ return {
+ ...state,
+ siteConfig: {
+ ...state.siteConfig,
+ theme:{
+ ...state.siteConfig.theme,
+ palette:{
+ ...state.siteConfig.theme.palette,
+ type:"light",
+ }
+ },
+ },
+ };
+ return copy
+
case 'APPLY_THEME':
if (state.siteConfig.themes !== null){
let themes = JSON.parse(state.siteConfig.themes);
diff --git a/src/setupProxy.js b/src/setupProxy.js
deleted file mode 100644
index 418d1e1..0000000
--- a/src/setupProxy.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const proxy = require('http-proxy-middleware');
-
-module.exports = function(app) {
- app.use('/File', proxy({
- target: 'https://lite.aoaoao.me',
- changeOrigin: true,
- onProxyReq (proxyReq, req) {
- // 将本地请求的头信息复制一遍给代理。
- // 包含cookie信息,这样就能用登录后的cookie请求相关资源
- Object.keys(req.headers).forEach(function (key) {
- proxyReq.setHeader(key, req.headers[key])
- })
- },
- onProxyRes (proxyRes, req, res) {
- // 将服务器返回的头信息,复制一遍给本地请求的响应。
- // 这样就能实现 执行完登录后,本地的返回请求中也有相关cookie,从而实现登录功能代理。
- Object.keys(proxyRes.headers).forEach(function (key) {
- res.append(key, proxyRes.headers[key])
- })
- }
-
- }));
- app.use('/static/js/uploader', proxy({
- target: 'https://lite.aoaoao.me',
- changeOrigin: true,
- }));
- app.use('/static/js/uploader', proxy({
- target: 'https://lite.aoaoao.me',
- changeOrigin: true,
- }));
- app.use('/Member', proxy({
- target: 'https://lite.aoaoao.me',
- changeOrigin: true,
- }));
-};
\ No newline at end of file