diff --git a/.bowerrc b/.bowerrc index 67b62e774..6a424c379 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,3 +1,3 @@ { - "directory" : "www/bower" + "directory" : "www/bower_components" } diff --git a/.gitignore b/.gitignore index 1157b14cc..aa661d690 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -www/bower/* +www/bower_components/* node_modules /config.js diff --git a/bower.json b/bower.json index e7f0fc2c4..34511bc3e 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,8 @@ "tests" ], "dependencies": { - "jquery": "~2.1.1", + "jquery.sheet": "master", + "jquery": "~2.1.3", "tweetnacl": "~0.12.2", "ckeditor": "~4.4.5", "requirejs": "~2.1.15", diff --git a/server.js b/server.js index bc5b41c0e..4a1f8e39a 100644 --- a/server.js +++ b/server.js @@ -12,6 +12,12 @@ config.websocketPort = config.websocketPort || config.httpPort; var app = Express(); app.use(Express.static(__dirname + '/www')); +// Bower is broken and does not allow components nested within components... +// And jquery.sheet expects it! +// *Workaround* +app.use("/bower_components/jquery.sheet/bower_components", + Express.static(__dirname + '/www/bower_components')); + var httpsOpts; if (config.privKeyAndCertFiles) { var privKeyAndCerts = ''; diff --git a/www/chainpad.js b/www/common/chainpad.js similarity index 98% rename from www/chainpad.js rename to www/common/chainpad.js index 82e8102b4..b872b17f0 100644 --- a/www/chainpad.js +++ b/www/common/chainpad.js @@ -589,6 +589,18 @@ var unschedule = function (realtime, schedule) { clearTimeout(schedule); }; +var onMessage = function (realtime, message, callback) { + if (!realtime.messageHandlers.length) { + callback("no onMessage() handler registered"); + } + for (var i = 0; i < realtime.messageHandlers.length; i++) { + realtime.messageHandlers[i](message, function () { + callback.apply(null, arguments); + callback = function () { }; + }); + } +}; + var sync = function (realtime) { if (Common.PARANOIA) { check(realtime); } if (realtime.syncSchedule) { @@ -622,7 +634,7 @@ var sync = function (realtime) { var strMsg = Message.toString(msg); - realtime.onMessage(strMsg, function (err) { + onMessage(realtime, strMsg, function (err) { if (err) { debug(realtime, "Posting to server failed [" + err + "]"); } @@ -657,7 +669,7 @@ var getMessages = function (realtime) { realtime.authToken, realtime.channelId, Message.REGISTER); - realtime.onMessage(Message.toString(msg), function (err) { + onMessage(realtime, Message.toString(msg), function (err) { if (err) { throw err; } }); }; @@ -670,7 +682,7 @@ var sendPing = function (realtime) { realtime.channelId, Message.PING, realtime.lastPingTime); - realtime.onMessage(Message.toString(msg), function (err) { + onMessage(realtime, Message.toString(msg), function (err) { if (err) { throw err; } }); }; @@ -706,9 +718,7 @@ var create = ChainPad.create = function (userName, authToken, channelId, initial patchHandlers: [], opHandlers: [], - onMessage: function (message, callback) { - callback("no onMessage() handler registered"); - }, + messageHandlers: [], schedules: [], @@ -1127,7 +1137,8 @@ module.exports.create = function (userName, authToken, channelId, initialState, doOperation(realtime, Operation.create(offset, 0, str)); }), onMessage: enterChainPad(realtime, function (handler) { - realtime.onMessage = handler; + Common.assert(typeof(handler) === 'function'); + realtime.messageHandlers.push(handler); }), message: enterChainPad(realtime, function (message) { handleMessage(realtime, message); diff --git a/www/messages.js b/www/common/messages.js similarity index 100% rename from www/messages.js rename to www/common/messages.js diff --git a/www/otaml.js b/www/common/otaml.js similarity index 100% rename from www/otaml.js rename to www/common/otaml.js diff --git a/www/common/toolbar.js b/www/common/toolbar.js new file mode 100644 index 000000000..3399e804b --- /dev/null +++ b/www/common/toolbar.js @@ -0,0 +1,172 @@ +var Toolbar = function ($, container, Messages, myUserName, realtime) { + + /** Id of the element for getting debug info. */ + var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link'; + + /** Id of the div containing the user list. */ + var USER_LIST_CLS = 'rtwysiwyg-user-list'; + + /** Id of the div containing the lag info. */ + var LAG_ELEM_CLS = 'rtwysiwyg-lag'; + + /** The toolbar class which contains the user list, debug link and lag. */ + var TOOLBAR_CLS = 'rtwysiwyg-toolbar'; + + /** Key in the localStore which indicates realtime activity should be disallowed. */ + var LOCALSTORAGE_DISALLOW = 'rtwysiwyg-disallow'; + + var SPINNER_DISAPPEAR_TIME = 3000; + var SPINNER = [ '-', '\\', '|', '/' ]; + + var uid = function () { + return 'rtwysiwyg-uid-' + String(Math.random()).substring(2); + }; + + var createRealtimeToolbar = function (container) { + var id = uid(); + $(container).prepend( + '