From 85585f34ee1f83f307dd42bb97def777e81cbf1e Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 19 Dec 2013 12:06:42 -0600 Subject: [PATCH] feat($session): #784 - ajax requester, session module, more options --- client/js/ajax.requester.js | 6 +-- client/js/session.ajax.requester.js | 62 +++++++++++++++++++++++++++++ client/js/session.js | 62 +++++++++++++++++++++++++++++ client/js/uploader.basic.api.js | 32 +++++++++++++-- client/js/uploader.basic.js | 6 ++- docs/api/events.jmd | 10 +++++ docs/api/options.jmd | 2 + lib/modules.js | 7 +++- 8 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 client/js/session.ajax.requester.js create mode 100644 client/js/session.js diff --git a/client/js/ajax.requester.js b/client/js/ajax.requester.js index 854cf169d..32889a3f1 100644 --- a/client/js/ajax.requester.js +++ b/client/js/ajax.requester.js @@ -18,7 +18,8 @@ qq.AjaxRequester = function (o) { allowXRequestedWithAndCacheControl: true, successfulResponseCodes: { "DELETE": [200, 202, 204], - "POST": [200, 204] + "POST": [200, 204], + "GET": [200] }, cors: { expected: false, @@ -26,8 +27,7 @@ qq.AjaxRequester = function (o) { }, log: function (str, level) {}, onSend: function (id) {}, - onComplete: function (id, xhrOrXdr, isError) {}, - onCancel: function (id) {} + onComplete: function (id, xhrOrXdr, isError) {} }; qq.extend(options, o); diff --git a/client/js/session.ajax.requester.js b/client/js/session.ajax.requester.js new file mode 100644 index 000000000..85a29760c --- /dev/null +++ b/client/js/session.ajax.requester.js @@ -0,0 +1,62 @@ +/*globals qq, XMLHttpRequest*/ +/** + * Thin module used to send GET requests to the server, expecting information about session + * data used to initialize an uploader instance. + * + * @param spec Various options used to influence the associated request. + * @constructor + */ +qq.SessionAjaxRequester = function(spec) { + "use strict"; + + var requester, + options = { + endpoint: null, + customHeaders: {}, + params: {}, + cors: { + expected: false, + sendCredentials: false + }, + onComplete: function(response, success, xhrOrXdr) {}, + log: function(str, level) {} + }; + + qq.extend(options, spec); + + function onComplete(id, xhrOrXdr, isError) { + var response = null; + + /* jshint eqnull:true */ + if (xhrOrXdr.responseText != null) { + response = qq.parseJson(xhrOrXdr.responseText); + } + + options.onComplete(response, !isError, xhrOrXdr); + } + + requester = new qq.AjaxRequester({ + validMethods: ["GET"], + method: "GET", + endpointStore: { + getEndpoint: function() { + return options.endpoint; + } + }, + customHeaders: options.customHeaders, + log: options.log, + onComplete: onComplete, + cors: options.cors + }); + + + qq.extend(this, { + queryServer: function() { + options.log("Session query request."); + + requester.initTransport("sessionRefresh") + .withParams(options.params) + .send(); + } + }); +}; diff --git a/client/js/session.js b/client/js/session.js new file mode 100644 index 000000000..5206796b0 --- /dev/null +++ b/client/js/session.js @@ -0,0 +1,62 @@ +/* globals qq */ +/** + * Module used to control populating the initial list of files. + * + * @constructor + */ +qq.Session = function(spec) { + "use strict"; + + var options = { + endpoint: null, + params: {}, + customHeaders: {}, + cors: {}, + log: function(message, level) {} + }; + + qq.extend(options, spec, true); + + + function isJsonResponseValid(response) { + if (qq.isArray(response)) { + return true; + } + + options.log("Session response is not an array.", "error"); + } + + function handleFileItems(fileItems, success, xhrOrXdr, promise) { + success = isJsonResponseValid(fileItems); + + if (success) { + qq.each(fileItems, function(idx, fileItem) { + // TODO Create ID for the file item (need to abstract this out of the handlers) + // TODO Need to delegate UUID, size, name retrieval out of handlers to uploadData module + // TODO Populate UUID, size, name, delete endpoint, thumbnail url, delete params + // TODO Add file item to uploadData module + // TODO Ensure file item is rendered in UI mode w/ size, name, delete button, success indicator, preview + }); + } + + promise[success ? "success" : "failure"](fileItems, xhrOrXdr); + } + + // Initiate a call to the server that will be used to populate the initial file list. + // Returns a `qq.Promise`. + this.refresh = function() { + /*jshint indent:false */ + var refreshEffort = new qq.Promise(), + refreshCompleteCallback = function(response, success, xhrOrXdr) { + handleFileItems(response, success, xhrOrXdr, refreshEffort); + }, + requsterOptions = qq.extend({}, options), + requester = new qq.SessionAjaxRequester( + qq.extend(requsterOptions, {onComplete: refreshCompleteCallback}) + ); + + requester.queryServer(); + + return refreshEffort; + }; +}; diff --git a/client/js/uploader.basic.api.js b/client/js/uploader.basic.api.js index cb41715a6..38b392437 100644 --- a/client/js/uploader.basic.api.js +++ b/client/js/uploader.basic.api.js @@ -120,9 +120,8 @@ this._uploadData.reset(); this._buttonIdsForFileIds = []; - if (this._pasteHandler) { - this._pasteHandler.reset(); - } + this._pasteHandler && this._pasteHandler.reset(); + this._refreshSessionData(); }, addFiles: function(filesOrInputs, params, endpoint) { @@ -335,6 +334,33 @@ * Defines the private (internal) API for FineUploaderBasic mode. */ qq.basePrivateApi = { + // Attempts to refresh session data only if the `qq.Session` module exists + // and a session endpoint has been specified. The `onSessionRequestComplete` + // callback will be invoked once the refresh is complete. + _refreshSessionData: function() { + var self = this, + options = this._options.session; + + /* jshint eqnull:true */ + if (qq.Session && this._options.session.endpoint != null) { + if (!this._session) { + qq.extend(options, this._options.cors); + options.log = this.log; + + this._session = new qq.Session(options); + } + + this._session.refresh().then(function(response, xhrOrXdr) { + + self._options.callbacks.onSessionRequestComplete(response, true, xhrOrXdr); + + }, function(response, xhrOrXdr) { + + self._options.callbacks.onSessionRequestComplete(response, false, xhrOrXdr); + }); + } + }, + // Updates internal state when a new file has been received, and adds it along with its ID to a passed array. _handleNewFile: function(file, newFileWrapperList) { var id = this._handler.add(file); diff --git a/client/js/uploader.basic.js b/client/js/uploader.basic.js index 9e809bab5..37f45d096 100644 --- a/client/js/uploader.basic.js +++ b/client/js/uploader.basic.js @@ -58,7 +58,8 @@ onDelete: function(id){}, onDeleteComplete: function(id, xhrOrXdr, isError){}, onPasteReceived: function(blob) {}, - onStatusChange: function(id, oldStatus, newStatus) {} + onStatusChange: function(id, oldStatus, newStatus) {}, + onSessionRequestComplete: function(response, success, xhrOrXdr) {} }, messages: { @@ -163,6 +164,8 @@ // during initialization and optionally after a `reset`. session: { endpoint: null, + params: {}, + customHeaders: {}, refreshOnReset: true } }; @@ -217,6 +220,7 @@ this._preventLeaveInProgress(); this._imageGenerator = qq.ImageGenerator && new qq.ImageGenerator(qq.bind(this.log, this)); + this._refreshSessionData(); }; // Define the private & public API methods. diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 92273205e..19a84009f 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -156,6 +156,16 @@ Called just before a file upload is resumed. See the `uploadChunk` event for mo ---- +onSessionRequestComplete: function(response, success, xhrOrXdr) {} + +#### sessionRequestComplete + +**Parameters**: Object response, Boolean success, XMLHttpRequest || XDomainRequest xhrOrXdr + +Invoked when a session request has completed. + +---- + #### statusChange **Parameters**: Integer id, String oldStatus, String newStatus diff --git a/docs/api/options.jmd b/docs/api/options.jmd index eede01592..6ced7f6a1 100644 --- a/docs/api/options.jmd +++ b/docs/api/options.jmd @@ -238,6 +238,8 @@ See the [Initialize File List feature page](../features/session.html) for more d {{ options_table( ( ("endpoint", "`String`", "`null`", "If non-null, Fine Uploader will send a GET request on startup to this endpoint, expecting a JSON response containing data about the initial file list to populate."), + ("params", "`Object`", "`{}`", "Any parameters you would like passed with the associated GET request to your server."), + ("customHeaders", "`Object`", "`{}`", "Any additional headers you would like included with the GET request sent to your server."), ("refreshOnReset", "`Boolean`", "`true`", "Set this to `false` if you do not want the file list to be retrieved from the server as part of a reset."), ) ) }} diff --git a/lib/modules.js b/lib/modules.js index 0e026e103..71a7d6d5e 100644 --- a/lib/modules.js +++ b/lib/modules.js @@ -46,7 +46,8 @@ var fineUploaderModules = { "@fuDndModule", "@fuDeleteFileModule", "@fuImagePreviewModule", - "@fuImageValidationModule" + "@fuImageValidationModule", + "@fuSessionModule" ], "fuUiModules": [ @@ -133,6 +134,10 @@ var fineUploaderModules = { "client/js/ui.handler.focus.filenameinput.js", "client/js/ui.handler.edit.filename.js" ], + "fuSessionModule": [ + "client/js/session.js", + "client/js/session.ajax.requester.js" + ], "fuIframeXssResponse": [ "client/js/iframe.xss.response.js" ],