1 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true,
4 /*global XT:true, XM:true, io:true, Backbone:true, _:true, console:true, enyo:true
5 document:true, setTimeout:true, document:true, RJSON:true */
11 /** @scope XT.Request.prototype */
13 send: function (data) {
14 var details = XT.session.details,
15 sock = XT.dataSource._sock,
16 notify = this._notify,
17 handle = this._handle,
24 if (!notify || !(notify instanceof Function)) {
25 callback = function () {};
27 callback = function (response) {
30 if (response && response.isError) {
31 // notify the user in the case of error.
32 // Wait a second to make sure that whatever the expected callback
33 // function has time to do whatever it has to do. Not pretty,
34 // but works across a broad range of callback errors.
35 errorMessage = response.status ? response.status.message : response.message;
37 XT.log("Error:", errorMessage);
38 setTimeout(function () {
39 XT.app.$.postbooks.notify(null, {
40 type: XM.Model.CRITICAL,
49 // attach the session details to the payload
50 payload = _.extend(payload, details);
52 if (XT.session.config.debugging) {
53 XT.log("Socket sending: %@".replace("%@", handle), payload);
56 sock.json.emit(handle, payload, callback);
61 handle: function (event) {
66 notify: function (method) {
67 var args = Array.prototype.slice.call(arguments).slice(1);
68 this._notify = function (response) {
69 args.unshift(response);
70 method.apply(null, args);
78 //datasourceUrl: DOCUMENT_HOSTNAME,
79 //datasourcePort: 443,
83 Returns the value of the user preference if it exists in the
86 getUserPreference: function (name) {
87 var pref = _.find(XT.session.preferences.attributes,
95 Performs a dispatch call to save the given payload
96 string to the specified user preference.
98 @param {String} stringified payload
99 @param {String} operation string
101 saveUserPreference: function (name, payload, op) {
102 var options = {}, patch = [], param = [],
104 options.error = function (resp) {
105 if (resp.isError) { console.log("uh oh"); }
108 {"op": op, "path": path, "value": payload}
110 // Add the patch array to the parameter array
112 XM.ModelMixin.dispatch('XT.Session',
113 'commitPreferences', param, options);
117 * Decode the server's response to a bona fide Javascript object.
118 * @see node-datasource/routes/data.js#encodeResponse
120 * @param {Object} response the server's response object
121 * @param {Object} options the request options
123 * @return {Object} the server's response as a Javascript object.
125 decodeResponse: function (response, options) {
126 var encoding = options.encoding || XT.session.config.encoding;
131 else if (encoding === "rjson") {
132 return RJSON.unpack(response);
137 status: "Encoding [" + encoding + "] not recognized."
145 @param {Object} model or collection
146 @param {String} method
147 @param {Object} payload
148 @param {Object} options
150 request: function (obj, method, payload, options) {
152 isDispatch = _.isObject(payload.dispatch),
153 complete = function (response) {
160 if (response.isError) {
161 if (options && options.error) {
162 params.error = response.message;
163 error = XT.Error.clone('xt1001', { params: params });
164 options.error.call(that, error);
169 dataHash = that.decodeResponse(response, options).data;
171 // Handle no data on a single record retrieve as error
172 if (method === "get" && options.id &&
173 _.isEmpty(dataHash.data)) {
174 if (options && options.error) {
175 error = XT.Error.clone('xt1007');
176 options.error.call(obj, error);
182 if (options && options.success) {
184 options.success(dataHash, options);
188 // Handle case where an entire collection was saved
189 if (options.collection) {
190 // Destroyed models won't have a response unless they made the whole
191 // request fail. Assume successful destruction.
192 options.collection.each(function (model) {
193 if (model.getStatus() === XM.Model.DESTROYED_DIRTY) {
194 model.trigger("destroy", model, model.collection, options);
198 if (dataHash[0].patches) {
199 _.each(dataHash, function (data) {
202 cModel = _.find(options.collection.models, function (model) {
203 return data.id === model.id;
205 attrs = cModel.toJSON({includeNested: true});
206 XM.jsonpatch.apply(attrs, data.patches);
207 cModel.etag = data.etag;
209 // This is a hack to work around Backbone messing with
210 // attributes when we don't want it to. Parse function
211 // on model handles the other side of this
212 options.fixAttributes = cModel.attributes;
214 options.success.call(that, cModel, attrs, options);
216 options.collection.remove(cModel);
219 // This typically happens when requery option === false
220 // and no patches were found
221 options.success.call(that, options.collection.at(0), true, options);
225 // Handle normal single model case
226 } else if (dataHash.patches) {
228 attrs = obj.toJSON({includeNested: true});
229 XM.jsonpatch.apply(attrs, dataHash.patches);
231 attrs = dataHash.patches;
234 attrs = dataHash.data;
236 if (obj instanceof Backbone.Model) {
237 obj.etag = dataHash.etag;
239 options.success.call(that, obj, attrs, options);
244 encoding: options.encoding || XT.session.config.encoding
254 Generic implementation of AJAX response handler
256 ajaxSuccess: function (inSender, inResponse) {
257 var params = {}, error;
260 if (inResponse.isError) {
261 if (inSender.error) {
262 params.error = inResponse.message;
263 error = XT.Error.clone('xt1001', { params: params });
264 inSender.error.call(this, error);
270 if (inSender.success) {
271 inSender.success.call(this, inResponse.data);
275 Generic implementation of AJAX request
277 callRoute: function (path, payload, options) {
278 var ajax = new enyo.Ajax({
279 url: XT.getOrganizationPath() + "/" + path,
280 success: options ? options.success : undefined,
281 error: options ? options.error : undefined
284 ajax.response(this.ajaxSuccess);
289 Reset a global user's password.
292 @param {Function} options.success callback
293 @param {Function} options.error callback
295 resetPassword: function (id, options) {
298 newPassword: options.newPassword
301 if (options.newUser) {
302 // we don't want to send false at all, because false turns
303 // into "false" over the wire which is truthy.
304 payload.newUser = options.newUser;
306 this.callRoute("reset-password", payload, options);
310 Change a global password.
312 @param {Object} parameters
313 @param {Function} options.success callback
314 @param {Function} options.error callback
316 changePassword: function (params, options) {
318 oldPassword: params.oldPassword,
319 newPassword: params.newPassword
322 this.callRoute("change-password", payload, options);
326 Sends a request to node to send out an email
328 @param {Object} payload
329 @param {String} payload.from
330 @param {String} payload.to
331 @param {String} payload.cc
332 @param {String} payload.bcc
333 @param {String} payload.subject
334 @param {String} payload.text
336 sendEmail: function (payload, options) {
337 if (payload.body && !payload.text) {
338 // be flexible with the inputs. Node-emailer prefers the term text, but
339 // body is fine for us as well.
340 payload.text = payload.body;
342 this.callRoute("email", payload, options);
346 connect: function (callback) {
347 if (this.isConnected) {
348 if (callback && callback instanceof Function) {
354 XT.log("Attempting to connect to the datasource");
356 var host = document.location.host,
358 protocol = document.location.protocol,
359 datasource = "%@//%@/%@".f(protocol, host, path),
361 didConnect = this.sockDidConnect,
362 didError = this.sockDidError;
364 // Attempt to connect and supply the appropriate responders for the connect and error events.
365 this._sock = io.connect(datasource, {secure: true});
366 this._sock.on("connect", function () {
367 //didConnect.call(self, callback);
369 this._sock.on("ok", function () {
370 didConnect.call(self, callback);
372 this._sock.on("error", function (err) {
373 // New express conneciton error doesn't send err message back here, but does call this.
374 XT.log("SERVER ERROR.");
375 didError.call(self, err, callback);
377 this._sock.on("connect_failed", function (err) {
378 // This app has not even started yet. Don't bother with the popup because it won't work.
379 XT.log("AUTHENTICATION INVALID: ", err);
384 this._sock.on("debug", function (msg) {
385 XT.log("SERVER DEBUG => ", msg);
388 this._sock.on("timeout", function (msg) {
389 XT.log("SERVER SAID YOU TIMED OUT");
390 var p = XT.app.createComponent({
397 style: "text-align: center;",
399 {content: "_sessionTimedOut".loc()},
400 {kind: "onyx.Button", content: "_ok".loc(), tap: function () { XT.logout(); }}
406 this._sock.on("disconnect", function () {
407 XT.log("DISCONNECTED FROM SERVER");
412 sockDidError: function (err, callback) {
413 // TODO: need some handling here
415 if (callback && callback instanceof Function) {
421 sockDidConnect: function (callback) {
423 XT.log("Successfully connected to the datasource");
425 this.isConnected = true;
427 // go ahead and create the session object for the
428 // application if it does not already exist
430 XT.session = Object.create(XT.Session);
431 setTimeout(_.bind(XT.session.start, XT.session), 0);
434 if (callback && callback instanceof Function) {
440 if (!this.isConnected) {
444 var sock = this._sock;
447 this.isConnected = false;
455 XT.dataSource = Object.create(XT.DataSource);