/** * @license * Copyright (C) 2013 KO GmbH * * @licstart * The JavaScript code in this page is free software: you can redistribute it * and/or modify it under the terms of the GNU Affero General Public License * (GNU AGPL) as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. The code is distributed * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details. * * As additional permission under GNU AGPL version 3 section 7, you * may distribute non-source (e.g., minimized or compacted) forms of * that code without the copy of the GNU GPL normally required by * section 4, provided you include this license notice and a URL * through which recipients can access the Corresponding Source. * * As a special exception to the AGPL, any HTML file which merely makes function * calls to this code, and for that purpose includes it by reference shall be * deemed a separate work for copyright law purposes. In addition, the copyright * holders of this code give you permission to combine this code with free * software libraries that are released under the GNU LGPL. You may copy and * distribute such a system following the terms of the GNU AGPL for this code * and the LGPL for the libraries. If you modify this code, you may extend this * exception to your version of the code, but you are not obligated to do so. * If you do not wish to do so, delete this exception statement from your * version. * * This license applies to this entire compilation. * @licend * @source: http://www.webodf.org/ * @source: http://gitorious.org/webodf/webodf/ */ /*global XMLHttpRequest, runtime, core, ops*/ define("webodf/editor/server/pullbox/Server", [], function () { "use strict"; runtime.loadClass("core.Base64"); runtime.loadClass("core.ByteArrayWriter"); /** * @constructor * @implements ops.Server * @param {{url:string}} args */ return function PullBoxServer(args) { var self = this, token, base64 = new core.Base64(); args = args || {}; args.url = args.url || "/WSER"; args.sessionStateToFileUrl = args.sessionStateToFileUrl || "/SS2F" this.getGenesisUrl = function (sessionId) { return "/session/" + sessionId + "/genesis"; }; /** * @param {!Object} message * @param {!function(!string)} cb * @return {undefined} */ function call(message, cb) { var xhr = new XMLHttpRequest(), byteArrayWriter = new core.ByteArrayWriter("utf8"), messageString = JSON.stringify(message), data; function handleResult() { if (xhr.readyState === 4) { if ((xhr.status < 200 || xhr.status >= 300) && xhr.status === 0) { // report error runtime.log("Status " + String(xhr.status) + ": " + xhr.responseText || xhr.statusText); } cb(xhr.responseText); } } runtime.log("Sending message to server: "+messageString); // create body data for request from metadata and payload byteArrayWriter.appendString(messageString); // byteArrayWriter.appendByteArray(zipData); data = byteArrayWriter.getByteArray(); // do the request xhr.open('POST', args.url, true); xhr.onreadystatechange = handleResult; // ArrayBufferView will have an ArrayBuffer property, in WebKit, XHR can send() // an ArrayBuffer, In Firefox, one must use sendAsBinary with a string if (data.buffer && !xhr.sendAsBinary) { data = data.buffer; // webkit supports sending an ArrayBuffer } else { // encode into a string, this works in FireFox >= 3 data = runtime.byteArrayToString(data, "binary"); } try { if (xhr.sendAsBinary) { xhr.sendAsBinary(data); } else { xhr.send(data); } } catch (e) { runtime.log("Problem with calling server: " + e + " " + data); cb(e.message); } } this.call = call; this.getToken = function () { return token; }; /** * for pre-authenticated use */ this.setToken = function (a_token) { token = a_token; }; /*jslint unparam: true*/ /** * @param {!number} timeout in milliseconds * @param {!function(!string)} callback * @return {undefined} */ this.connect = function (timeout, callback) { /* var accumulatedWaitingTime = 0; // already tried connecting? if (self.networkStatus() === "ready") { return; } */ callback("ready"); }; /*jslint unparam: false*/ /** * @return {!string} */ this.networkStatus = function () { return "ready"; }; /** * @param {!string} login * @param {!string} password * @param {function(!Object)} successCb * @param {function(!string)} failCb * @return {undefined} */ this.login = function (login, password, successCb, failCb) { call({ command: "login", args: { login: base64.toBase64(login), password: base64.toBase64(password) } }, function(responseData) { var response = /**@type {{token:string}}*/(runtime.fromJson(responseData)); runtime.log("Login reply: " + responseData); if (response.hasOwnProperty("token")) { token = response.token; runtime.log("Caching token: " + self.getToken()); successCb(response); } else { failCb(responseData); } }); }; /** * @param {!string} userId * @param {!string} sessionId * @param {!function(!string)} successCb * @param {function()=} failCb * @return {undefined} */ this.joinSession = function (userId, sessionId, successCb, failCb) { call({ command: "join_session", args: { user_id: userId, es_id: sessionId } }, function(responseData) { var response = /**@type {{success:string, member_id:string}}*/(runtime.fromJson(responseData)); runtime.log("join_session reply: " + responseData); if (response.hasOwnProperty("success") && response.success) { successCb(response.member_id); } else { if (failCb) { failCb(); } } }); }; /** * @param {!string} sessionId * @param {!string} memberId * @param {!function()} successCb * @param {function()=} failCb * @return {undefined} */ this.leaveSession = function (sessionId, memberId, successCb, failCb) { call({ command: "leave_session", args: { es_id: sessionId, member_id: memberId } }, function(responseData) { var response = /**@type {{success:string, member_id:string}}*/(runtime.fromJson(responseData)); runtime.log("leave_session reply: " + responseData); if (response.hasOwnProperty("success") && response.success) { successCb(); } else { if (failCb) { failCb(); } } }); }; /** * @param {!string} sessionId * @param {!string} memberId * @param {!string} seqHead * @param {function()=} callback * @return {undefined} */ this.writeSessionStateToFile = function(sessionId, memberId, seqHead, fileData, callback) { // code copied from BrowserRuntime.writeFile and adapted var xhr = new XMLHttpRequest(); function handleResult() { if (xhr.readyState === 4) { if (xhr.status === 0 && !xhr.responseText) {// TODO: check makes sense here as well? // for local files there is no difference between missing // and empty files, so empty files are considered as errors runtime.log("File " + args.sessionStateToFileUrl + " is empty."); } else if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { // report success runtime.log(null); } else { // report error runtime.log("Status " + String(xhr.status) + ": " + xhr.responseText || xhr.statusText); } callback(); } } // do the request xhr.open('POST', args.sessionStateToFileUrl, true); xhr.setRequestHeader("x-webodf-es_id", sessionId); xhr.setRequestHeader("x-webodf-member_id", memberId); xhr.setRequestHeader("x-webodf-seq_head", seqHead); xhr.onreadystatechange = handleResult; // ArrayBufferView will have an ArrayBuffer property, in WebKit, XHR can send() // an ArrayBuffer, In Firefox, one must use sendAsBinary with a string if (fileData.buffer && !xhr.sendAsBinary) { fileData = fileData.buffer; // webkit supports sending an ArrayBuffer } else { // encode into a string, this works in FireFox >= 3 fileData = runtime.byteArrayToString(fileData, "binary"); } try { if (xhr.sendAsBinary) { xhr.sendAsBinary(fileData); } else { xhr.send(fileData); } } catch (e) { runtime.log("Problem with calling \"writeSessionStateToFile\" on server"); callback(e.message); } }; }; });