gecko-dev/browser/components/loop/standalone/content/js/standaloneMozLoop.js

277 lines
9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global loop:true */
/**
* The StandaloneMozLoop implementation reflects that of the mozLoop API for Loop
* in the desktop code. Not all functions are implemented.
*/
var loop = loop || {};
loop.StandaloneMozLoop = (function(mozL10n) {
"use strict";
/**
* The maximum number of clients that we currently support.
*/
var ROOM_MAX_CLIENTS = 2;
/**
* Validates a data object to confirm it has the specified properties.
*
* @param {Object} data The data object to verify
* @param {Array} schema The validation schema
* @return Returns all properties if valid, or an empty object if no properties
* have been specified.
*/
function validate(data, schema) {
if (!schema) {
return {};
}
return new loop.validate.Validator(schema).validate(data);
}
/**
* Generic handler for XHR failures.
*
* @param {Function} callback Callback(err)
* @param jqXHR See jQuery docs
* @param textStatus See jQuery docs
* @param errorThrown See jQuery docs
*/
function failureHandler(callback, jqXHR, textStatus, errorThrown) {
var jsonErr = jqXHR && jqXHR.responseJSON || {};
var message = "HTTP " + jqXHR.status + " " + errorThrown;
// Create an error with server error `errno` code attached as a property
var err = new Error(message + (jsonErr.error ? "; " + jsonErr.error : ""));
err.errno = jsonErr.errno;
callback(err);
}
/**
* StandaloneMozLoopRooms is used as part of StandaloneMozLoop to define
* the rooms sub-object. We do it this way so that we can share the options
* information from the parent.
*/
var StandaloneMozLoopRooms = function(options) {
options = options || {};
if (!options.baseServerUrl) {
throw new Error("missing required baseServerUrl");
}
this._baseServerUrl = options.baseServerUrl;
};
StandaloneMozLoopRooms.prototype = {
/**
* Request information about a specific room from the server.
*
* @param {String} roomToken Room identifier
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`. The second argument will
* be the list of rooms, if it was fetched successfully.
*/
get: function(roomToken, callback) {
var req = $.ajax({
url: this._baseServerUrl + "/rooms/" + roomToken,
method: "GET",
contentType: "application/json",
beforeSend: function(xhr) {
if (this.sessionToken) {
xhr.setRequestHeader("Authorization", "Basic " + btoa(this.sessionToken));
}
}.bind(this)
});
req.done(function(responseData) {
try {
// We currently only require things we need rather than everything possible.
callback(null, validate(responseData, {
roomName: String,
roomOwner: String,
roomUrl: String
}));
} catch (err) {
console.error("Error requesting call info", err.message);
callback(err);
}
}.bind(this));
req.fail(failureHandler.bind(this, callback));
},
/**
* Internal function to actually perform a post to a room.
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The sessionToken for the room if known
* @param {Object} roomData The data to send with the request
* @param {Array} expectedProps The expected properties we should receive from the
* server
* @param {Boolean} async Set to true for an async request, false for sync.
* @param {Function} callback The callback for when the request completes. The
* first parameter is non-null on error, the second parameter
* is the response data.
*/
_postToRoom: function(roomToken, sessionToken, roomData, expectedProps,
async, callback) {
var req = $.ajax({
url: this._baseServerUrl + "/rooms/" + roomToken,
method: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify(roomData),
beforeSend: function(xhr) {
if (sessionToken) {
xhr.setRequestHeader("Authorization", "Basic " + btoa(sessionToken));
}
},
async: async,
success: function(responseData) {
console.log("done");
try {
callback(null, validate(responseData, expectedProps));
} catch (err) {
console.error("Error requesting call info", err.message);
callback(err);
}
}.bind(this),
error: failureHandler.bind(this, callback)
});
},
/**
* Joins a room
*
* @param {String} roomToken The room token.
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
join: function(roomToken, callback) {
function callbackWrapper(err, result) {
// XXX Save the sessionToken for purposes of get.
// When bug 1103331 this can probably be removed.
if (result) {
this.sessionToken = result.sessionToken;
}
callback(err, result);
}
this._postToRoom(roomToken, null, {
action: "join",
displayName: mozL10n.get("rooms_display_name_guest"),
clientMaxSize: ROOM_MAX_CLIENTS
}, {
apiKey: String,
sessionId: String,
sessionToken: String,
expires: Number
}, true, callbackWrapper.bind(this));
},
/**
* Refreshes a room
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
refreshMembership: function(roomToken, sessionToken, callback) {
this._postToRoom(roomToken, sessionToken, {
action: "refresh",
sessionToken: sessionToken
}, {
expires: Number
}, true, callback);
},
/**
* Leaves a room. Although this is an sync function, no data is returned
* from the server.
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Optional. Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
leave: function(roomToken, sessionToken, callback) {
if (!callback) {
callback = function(error) {
if (error) {
console.error(error);
}
};
}
// We do this as a synchronous request in case this is closing the window.
this._postToRoom(roomToken, sessionToken, {
action: "leave",
sessionToken: sessionToken
}, null, false, callback);
},
// Dummy functions to reflect those in the desktop mozLoop.rooms that we
// don't currently use.
on: function() {},
once: function() {},
off: function() {}
};
var StandaloneMozLoop = function(options) {
options = options || {};
if (!options.baseServerUrl) {
throw new Error("missing required baseServerUrl");
}
this._baseServerUrl = options.baseServerUrl;
this.rooms = new StandaloneMozLoopRooms(options);
};
StandaloneMozLoop.prototype = {
/**
* Stores a preference in the local storage for standalone.
* Note: Some prefs are filtered out as they are not applicable
* to the standalone UI.
*
* @param {String} prefName The name of the pref
* @param {String} value The value to set.
*/
setLoopPref: function(prefName, value) {
if (prefName === "seenToS") {
return;
}
localStorage.setItem(prefName, value);
},
/**
* Gets a preference from the local storage for standalone.
*
* @param {String} prefName The name of the pref
* @param {String} value The value to set.
*/
getLoopPref: function(prefName) {
return localStorage.getItem(prefName);
},
// Dummy function to reflect those in the desktop mozLoop that we
// don't currently use.
addConversationContext: function() {}
};
return StandaloneMozLoop;
})(navigator.mozL10n);