Index: polymer_1.0.4/bower_components/cors-upload-sample/upload.js |
diff --git a/polymer_1.0.4/bower_components/cors-upload-sample/upload.js b/polymer_1.0.4/bower_components/cors-upload-sample/upload.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc867a1c51154e90e2b093864ca5b0c7a0ddaba2 |
--- /dev/null |
+++ b/polymer_1.0.4/bower_components/cors-upload-sample/upload.js |
@@ -0,0 +1,267 @@ |
+/** |
+ * Helper for implementing retries with backoff. Initial retry |
+ * delay is 1 second, increasing by 2x (+jitter) for subsequent retries |
+ * |
+ * @constructor |
+ */ |
+var RetryHandler = function() { |
+ this.interval = 1000; // Start at one second |
+ this.maxInterval = 60 * 1000; // Don't wait longer than a minute |
+}; |
+ |
+/** |
+ * Invoke the function after waiting |
+ * |
+ * @param {function} fn Function to invoke |
+ */ |
+RetryHandler.prototype.retry = function(fn) { |
+ setTimeout(fn, this.interval); |
+ this.interval = this.nextInterval_(); |
+}; |
+ |
+/** |
+ * Reset the counter (e.g. after successful request.) |
+ */ |
+RetryHandler.prototype.reset = function() { |
+ this.interval = 1000; |
+}; |
+ |
+/** |
+ * Calculate the next wait time. |
+ * @return {number} Next wait interval, in milliseconds |
+ * |
+ * @private |
+ */ |
+RetryHandler.prototype.nextInterval_ = function() { |
+ var interval = this.interval * 2 + this.getRandomInt_(0, 1000); |
+ return Math.min(interval, this.maxInterval); |
+}; |
+ |
+/** |
+ * Get a random int in the range of min to max. Used to add jitter to wait times. |
+ * |
+ * @param {number} min Lower bounds |
+ * @param {number} max Upper bounds |
+ * @private |
+ */ |
+RetryHandler.prototype.getRandomInt_ = function(min, max) { |
+ return Math.floor(Math.random() * (max - min + 1) + min); |
+}; |
+ |
+ |
+/** |
+ * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether |
+ * files or in-memory constructs. |
+ * |
+ * @example |
+ * var content = new Blob(["Hello world"], {"type": "text/plain"}); |
+ * var uploader = new MediaUploader({ |
+ * file: content, |
+ * token: accessToken, |
+ * onComplete: function(data) { ... } |
+ * onError: function(data) { ... } |
+ * }); |
+ * uploader.upload(); |
+ * |
+ * @constructor |
+ * @param {object} options Hash of options |
+ * @param {string} options.token Access token |
+ * @param {blob} options.file Blob-like item to upload |
+ * @param {string} [options.fileId] ID of file if replacing |
+ * @param {object} [options.params] Additional query parameters |
+ * @param {string} [options.contentType] Content-type, if overriding the type of the blob. |
+ * @param {object} [options.metadata] File metadata |
+ * @param {function} [options.onComplete] Callback for when upload is complete |
+ * @param {function} [options.onProgress] Callback for status for the in-progress upload |
+ * @param {function} [options.onError] Callback if upload fails |
+ */ |
+var MediaUploader = function(options) { |
+ var noop = function() {}; |
+ this.file = options.file; |
+ this.contentType = options.contentType || this.file.type || 'application/octet-stream'; |
+ this.metadata = options.metadata || { |
+ 'title': this.file.name, |
+ 'mimeType': this.contentType |
+ }; |
+ this.token = options.token; |
+ this.onComplete = options.onComplete || noop; |
+ this.onProgress = options.onProgress || noop; |
+ this.onError = options.onError || noop; |
+ this.offset = options.offset || 0; |
+ this.chunkSize = options.chunkSize || 0; |
+ this.retryHandler = new RetryHandler(); |
+ |
+ this.url = options.url; |
+ if (!this.url) { |
+ var params = options.params || {}; |
+ params.uploadType = 'resumable'; |
+ this.url = this.buildUrl_(options.fileId, params, options.baseUrl); |
+ } |
+ this.httpMethod = options.fileId ? 'PUT' : 'POST'; |
+}; |
+ |
+/** |
+ * Initiate the upload. |
+ */ |
+MediaUploader.prototype.upload = function() { |
+ var self = this; |
+ var xhr = new XMLHttpRequest(); |
+ |
+ xhr.open(this.httpMethod, this.url, true); |
+ xhr.setRequestHeader('Authorization', 'Bearer ' + this.token); |
+ xhr.setRequestHeader('Content-Type', 'application/json'); |
+ xhr.setRequestHeader('X-Upload-Content-Length', this.file.size); |
+ xhr.setRequestHeader('X-Upload-Content-Type', this.contentType); |
+ |
+ xhr.onload = function(e) { |
+ if (e.target.status < 400) { |
+ var location = e.target.getResponseHeader('Location'); |
+ this.url = location; |
+ this.sendFile_(); |
+ } else { |
+ this.onUploadError_(e); |
+ } |
+ }.bind(this); |
+ xhr.onerror = this.onUploadError_.bind(this); |
+ xhr.send(JSON.stringify(this.metadata)); |
+}; |
+ |
+/** |
+ * Send the actual file content. |
+ * |
+ * @private |
+ */ |
+MediaUploader.prototype.sendFile_ = function() { |
+ var content = this.file; |
+ var end = this.file.size; |
+ |
+ if (this.offset || this.chunkSize) { |
+ // Only bother to slice the file if we're either resuming or uploading in chunks |
+ if (this.chunkSize) { |
+ end = Math.min(this.offset + this.chunkSize, this.file.size); |
+ } |
+ content = content.slice(this.offset, end); |
+ } |
+ |
+ var xhr = new XMLHttpRequest(); |
+ xhr.open('PUT', this.url, true); |
+ xhr.setRequestHeader('Content-Type', this.contentType); |
+ xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size); |
+ xhr.setRequestHeader('X-Upload-Content-Type', this.file.type); |
+ if (xhr.upload) { |
+ xhr.upload.addEventListener('progress', this.onProgress); |
+ } |
+ xhr.onload = this.onContentUploadSuccess_.bind(this); |
+ xhr.onerror = this.onContentUploadError_.bind(this); |
+ xhr.send(content); |
+}; |
+ |
+/** |
+ * Query for the state of the file for resumption. |
+ * |
+ * @private |
+ */ |
+MediaUploader.prototype.resume_ = function() { |
+ var xhr = new XMLHttpRequest(); |
+ xhr.open('PUT', this.url, true); |
+ xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size); |
+ xhr.setRequestHeader('X-Upload-Content-Type', this.file.type); |
+ if (xhr.upload) { |
+ xhr.upload.addEventListener('progress', this.onProgress); |
+ } |
+ xhr.onload = this.onContentUploadSuccess_.bind(this); |
+ xhr.onerror = this.onContentUploadError_.bind(this); |
+ xhr.send(); |
+}; |
+ |
+/** |
+ * Extract the last saved range if available in the request. |
+ * |
+ * @param {XMLHttpRequest} xhr Request object |
+ */ |
+MediaUploader.prototype.extractRange_ = function(xhr) { |
+ var range = xhr.getResponseHeader('Range'); |
+ if (range) { |
+ this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1; |
+ } |
+}; |
+ |
+/** |
+ * Handle successful responses for uploads. Depending on the context, |
+ * may continue with uploading the next chunk of the file or, if complete, |
+ * invokes the caller's callback. |
+ * |
+ * @private |
+ * @param {object} e XHR event |
+ */ |
+MediaUploader.prototype.onContentUploadSuccess_ = function(e) { |
+ if (e.target.status == 200 || e.target.status == 201) { |
+ this.onComplete(e.target.response); |
+ } else if (e.target.status == 308) { |
+ this.extractRange_(e.target); |
+ this.retryHandler.reset(); |
+ this.sendFile_(); |
+ } |
+}; |
+ |
+/** |
+ * Handles errors for uploads. Either retries or aborts depending |
+ * on the error. |
+ * |
+ * @private |
+ * @param {object} e XHR event |
+ */ |
+MediaUploader.prototype.onContentUploadError_ = function(e) { |
+ if (e.target.status && e.target.status < 500) { |
+ this.onError(e.target.response); |
+ } else { |
+ this.retryHandler.retry(this.resume_.bind(this)); |
+ } |
+}; |
+ |
+/** |
+ * Handles errors for the initial request. |
+ * |
+ * @private |
+ * @param {object} e XHR event |
+ */ |
+MediaUploader.prototype.onUploadError_ = function(e) { |
+ this.onError(e.target.response); // TODO - Retries for initial upload |
+}; |
+ |
+/** |
+ * Construct a query string from a hash/object |
+ * |
+ * @private |
+ * @param {object} [params] Key/value pairs for query string |
+ * @return {string} query string |
+ */ |
+MediaUploader.prototype.buildQuery_ = function(params) { |
+ params = params || {}; |
+ return Object.keys(params).map(function(key) { |
+ return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); |
+ }).join('&'); |
+}; |
+ |
+/** |
+ * Build the drive upload URL |
+ * |
+ * @private |
+ * @param {string} [id] File ID if replacing |
+ * @param {object} [params] Query parameters |
+ * @return {string} URL |
+ */ |
+MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) { |
+ var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/'; |
+ if (id) { |
+ url += id; |
+ } |
+ var query = this.buildQuery_(params); |
+ if (query) { |
+ url += '?' + query; |
+ } |
+ return url; |
+}; |
+ |
+ |
+ |