Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Removed old javascript files, updated build scripts for python

  • Loading branch information...
commit b9c363f8df90fc98599428f4d46341097818216c 1 parent 285a5ae
@ccotter ccotter authored
Showing with 18 additions and 5,449 deletions.
  1. +4 −0 js/build/build.sh
  2. +0 −51 js/lib/coweb/ConfigInitializer.js
  3. +0 −538 js/lib/coweb/collab/UnmanagedHubCollab.js
  4. +0 −77 js/lib/coweb/ext/CowebWrapper.js
  5. +0 −158 js/lib/coweb/ext/SimpleLoader.js
  6. +0 −191 js/lib/coweb/ext/attendance.js
  7. +0 −76 js/lib/coweb/jsoe/ContextDifference.js
  8. +0 −226 js/lib/coweb/jsoe/ContextVector.js
  9. +0 −198 js/lib/coweb/jsoe/ContextVectorTable.js
  10. +0 −83 js/lib/coweb/jsoe/DeleteOperation.js
  11. +0 −179 js/lib/coweb/jsoe/HistoryBuffer.js
  12. +0 −82 js/lib/coweb/jsoe/InsertOperation.js
  13. +0 −296 js/lib/coweb/jsoe/Operation.js
  14. +0 −438 js/lib/coweb/jsoe/OperationEngine.js
  15. +0 −91 js/lib/coweb/jsoe/UpdateOperation.js
  16. +0 −61 js/lib/coweb/jsoe/factory.js
  17. +0 −759 js/lib/coweb/listener/UnmanagedHubListener.js
  18. +0 −94 js/lib/coweb/main.js
  19. +0 −492 js/lib/coweb/session/BayeuxSession.js
  20. +0 −59 js/lib/coweb/session/bayeux/CowebExtension.js
  21. +0 −568 js/lib/coweb/session/bayeux/ListenerBridge.js
  22. +0 −364 js/lib/coweb/session/bayeux/SessionBridge.js
  23. +0 −53 js/lib/coweb/session/bayeux/cometd.js
  24. +0 −41 js/lib/coweb/topics.js
  25. +0 −158 js/lib/coweb/util/Promise.js
  26. +0 −24 js/lib/coweb/util/lang.js
  27. +0 −84 js/lib/coweb/util/xhr.js
  28. +7 −5 js/setup_js.sh
  29. +4 −0 js/setup_python_js.sh
  30. +1 −1  servers/python/coweb/__init__.py
  31. +2 −2 servers/python/setup.py
View
4 js/build/build.sh
@@ -1,4 +1,8 @@
#!/bin/bash
+
+echo "Don't run this outdated script - see coweb/js/setup_python_js.sh"
+exit
+
rm -r ../release/coweb-latest
./requirejs-*/build/build.sh coweb.build.js
VERSION=`grep VERSION ../lib/coweb/main.js`
View
51 js/lib/coweb/ConfigInitializer.js
@@ -1,51 +0,0 @@
-//
-// config initializer.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define cowebConfig*/
-
-if(typeof cowebConfig === 'undefined') {
- cowebConfig = {};
-}
-
-// mix defaults into coweb config where left undefined
-cowebConfig = {
- sessionImpl : cowebConfig.sessionImpl || undefined,
- listenerImpl : cowebConfig.listenerImpl || undefined,
- collabImpl : cowebConfig.collabImpl || undefined,
- debug : cowebConfig.debug || false,
- baseUrl : cowebConfig.baseUrl || '',
- adminUrl : cowebConfig.adminUrl || '/admin',
- loginUrl : cowebConfig.loginUrl || '/login',
- logoutUrl : cowebConfig.logoutUrl || '/logout',
- cacheState : cowebConfig.cacheState || false
-};
-
-(function () {
- // build up a list of dependencies dynamically
- var deps = [];
- // add require to be used to load configured implementation modules
- deps.push("require");
- if (cowebConfig.sessionImpl) {
- deps.push(cowebConfig.sessionImpl);
- }
- if (cowebConfig.listenerImpl) {
- deps.push(cowebConfig.listenerImpl);
- }
- if (cowebConfig.collabImpl) {
- deps.push(cowebConfig.collabImpl);
- }
-
- define(deps, function(req) {
- var sessionImpl = cowebConfig.sessionImpl ? req(cowebConfig.sessionImpl) : undefined;
- var listenerImpl = cowebConfig.listenerImpl ? req(cowebConfig.listenerImpl) : undefined;
- var collabImpl = cowebConfig.collabImpl ? req(cowebConfig.collabImpl) : undefined;
- return {sessionImpl: sessionImpl, listenerImpl: listenerImpl, collabImpl: collabImpl};
- });
-
-}());
View
538 js/lib/coweb/collab/UnmanagedHubCollab.js
@@ -1,538 +0,0 @@
-//
-// Unmanaged OpenAjax Hub implementation of the CollabInterface.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/topics',
- 'coweb/util/Promise',
- 'org/OpenAjax'
-], function(topics, Promise, OpenAjax) {
- /**
- * @constructor
- */
- var UnmanagedHubCollab = function() {
- this._mutex = false;
- this._serviceId = 0;
- this._tokens = {};
- this.id = undefined;
- };
- // save the finger joints
- var proto = UnmanagedHubCollab.prototype;
-
- /**
- * Stores the collaboration instance ID.
- *
- * @param {String} params.id Unique identifier of this wrapper / widget
- */
- proto.init = function(params) {
- if(!params || params.id === undefined) {
- throw new Error('collab id required');
- }
- this.id = params.id;
- };
-
- /**
- * Subscribes to session ready notifications coweb.site.ready.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeReady = function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.READY;
- var tok = OpenAjax.hub.subscribe(topic, function(topic, params) {
- callback.call(context, params);
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Subscribes to session end notifications coweb.site.end.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeEnd = function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.END;
- var tok = OpenAjax.hub.subscribe(topic, function(topic, params) {
- callback.call(context, params);
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Subscribes to site joining notifications coweb.site.join.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeSiteJoin = function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.SITE_JOIN;
- var tok = OpenAjax.hub.subscribe(topic, function(topic, params) {
- callback.call(context, params);
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Subscribes to site leaving notifications coweb.site.leave.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeSiteLeave = function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.SITE_LEAVE;
- var tok = OpenAjax.hub.subscribe(topic, function(topic, params) {
- callback.call(context, params);
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Sends an incremental state change event coweb.sync.<topic>.<id>.
- * Throws an exception if this instance is not initialized.
- *
- * @param {String} name Cooperative event name
- * @param {Object} value JSON-encodable value for the change
- * @param {String|null} [type='update'] Type of change or null
- * @param {Number} [position=0] Integer position of the change
- */
- proto.sendSync = function(name, value, type, position) {
- if(this.id === undefined) {
- throw new Error('call init() first');
- }
- if(type === undefined) {
- type = 'update';
- }
- if(position === undefined) {
- position = 0;
- }
- var topic = topics.SYNC+name+'.'+this.id;
- var params = {value: value, type: type, position:position};
- this._mutex = true;
- OpenAjax.hub.publish(topic, params);
- this._mutex = false;
- };
-
- /**
- * Subscribes to remote incremental state changes
- * coweb.sync.<topic>.<id>. Throws an exception if this instance is
- * not initialized.
- *
- * @param {String} name Cooperative event name
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeSync = function(name, context, callback) {
- if(this.id === undefined) {
- throw new Error('call init() first');
- }
- if(!name) {
- throw new Error('valid sync name required');
- }
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.SYNC+name+'.'+this.id;
- var ls = topics.SYNC.length,
- le = this.id.length+1;
- var tok = OpenAjax.hub.subscribe(topic, function(tp, params) {
- if(!this._mutex) {
- // compute the actual event name, not what was registered
- // because it may have had wildcards
- params.name = tp.substring(ls, tp.length-le);
- params.topic = tp;
- callback.call(context, params);
- }
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Subscribes to full state requests coweb.state.get.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeStateRequest = function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.GET_STATE;
- var tok = OpenAjax.hub.subscribe(topic, function(topic, params) {
- callback.call(context, params);
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Sends a response to a full state request coweb.state.set.<id>.
- * Throws an exception if this instance is not initialized.
- *
- * @param {Object} state JSON-encodable state data for the response
- * @param {String} token Opaque token from the original state request
- */
- proto.sendStateResponse = function(state, token) {
- if(this.id === undefined) {
- throw new Error('call init() first');
- }
- var params = {state : state, recipient : token};
- this._mutex = true;
- try {
- OpenAjax.hub.publish(topics.SET_STATE+this.id, params);
- } finally {
- this._mutex = false;
- }
- };
-
- /**
- * Subscribes to remote full state responses coweb.state.set.<id>.
- * Throws an exception if this instance is not initialized.
- *
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeStateResponse = function(context, callback) {
- if(this.id === undefined) {
- throw new Error('call init() first');
- }
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var topic = topics.SET_STATE+this.id;
- var tok = OpenAjax.hub.subscribe(topic, function(t, params) {
- if(!this._mutex) {
- callback.call(context, params);
- }
- }, this);
- this._tokens[tok] = null;
- var def = new Promise();
- def._cowebToken = tok;
- def.resolve();
- return def;
- };
-
- /**
- * Subscribes to a service with coweb.service.sub.<service>
- * and responses coweb.service.set.<service>.
- * Throws an exception if this instance is not initialized.
- *
- * @param {String} service Name of the service
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.subscribeService = function(service, context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- // build the service response topic
- var setTopic = topics.SET_SERVICE+service;
-
- // register internal callback for service response
- var subData = {
- callback : callback,
- context : context,
- type : 'subscribe'
- };
- var hubToken = OpenAjax.hub.subscribe(setTopic,
- '_cowebServiceResponse', this, subData);
-
- // add metadata and data to the subscription request
- var msg = {topic : setTopic, service : service};
- // send subscription request
- var subTopic = topics.SUB_SERVICE+service;
- OpenAjax.hub.publish(subTopic, msg);
-
- // save all info needed to unregister
- var cowebToken = {topic : setTopic, service : service,
- hubToken : hubToken};
- this._tokens[hubToken] = cowebToken;
-
- var def = new Promise();
- def.resolve();
- def._cowebToken = cowebToken;
- return def;
- };
-
- /**
- * Requests a single service value with coweb.service.get.<service>
- * and response coweb.service.set.<service>_<request id>.<id>.
- * Throws an exception if this instance is not initialized.
- *
- * @param {String} service Name of the service
- * @param {Object} params JSON-encodable parameters to pass to the service
- * @param {Object|Function} context Context in which to invoke the callback
- * or the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto.postService = function(service, params, context, callback) {
- if(this.id === undefined) {
- throw new Error('call init() first');
- }
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- // subscribe to response event
- var setTopic = topics.SET_SERVICE+service+'_'+this._serviceId+'.'+this.id;
- // use our callback so we can automatically unregister
- var subData = {
- context : context,
- callback : callback,
- type : 'get'
- };
- var hubToken = OpenAjax.hub.subscribe(setTopic,
- '_cowebServiceResponse', this, subData);
- // track token for unsubscribeAll
- this._tokens[hubToken] = null;
- // add the unsubscribe token to the subscriber data so we have it
- // when the callback is invoked
- subData.hubToken = hubToken;
- // add metadata and data to message
- var msg = {topic : setTopic, params : params, service : service};
- // send get request to listener
- var get_topic = topics.GET_SERVICE+service;
- OpenAjax.hub.publish(get_topic, msg);
- // make next request unique
- this._serviceId++;
- var def = new Promise();
- def.resolve();
- def._cowebToken = hubToken;
- return def;
- };
-
- /**
- * Handles a service response. Unsubscribes a postService request after
- * delivering data to its callback.
- *
- * @private
- * @param {String} topic Response topic coweb.service.set.**
- * @param {Object} params Cooperative event
- * @returns {Promise} Always notifies success because this impl is
- * synchronous
- */
- proto._cowebServiceResponse = function(topic, params, subData) {
- var hubToken = subData.hubToken;
- // invoke the real callback
- var args = {value : params.value, error: params.error};
- try {
- subData.callback.call(subData.context, args);
- } finally {
- if(subData.type === 'get') {
- // unsubscribe from hub
- OpenAjax.hub.unsubscribe(subData.hubToken);
- // stop tracking token
- delete this._tokens[hubToken];
- }
- }
- };
-
- /**
- * Unsubscribes any subscription created via this interface.
- *
- * @param {Promise} def Promise returned by the method that created the
- * subscription
- */
- proto.unsubscribe = function(def) {
- var token, i;
- if(!def) {
- return;
- } else if(def._cowebToken && def._cowebToken.hubToken) {
- token = def._cowebToken;
- // don't allow reuse of token
- delete def._cowebToken;
- // unsubscribe from local event
- OpenAjax.hub.unsubscribe(token.hubToken);
- // remove from tracked tokens
- delete this._tokens[token.hubToken];
- // send unsubscribe request to listener
- var topic = topics.UNSUB_SERVICE+token.service;
- // include original topic
- OpenAjax.hub.publish(topic, token);
- } else if(def._cowebToken) {
- token = def._cowebToken;
- // don't allow reuse of token
- delete def._cowebToken;
- // remove from tracked tokens
- delete this._tokens[token];
- OpenAjax.hub.unsubscribe(token);
- }
- };
-
- /**
- * Removes all subscriptions created via this interface.
- */
- proto.unsubscribeAll = function() {
- for(var hubToken in this._tokens) {
- if(this._tokens.hasOwnProperty(hubToken)) {
- var cowebToken = this._tokens[hubToken];
- // unsubscribe
- OpenAjax.hub.unsubscribe(hubToken);
- // stop tracking
- delete this._tokens[hubToken];
- if(cowebToken) {
- // unregister from service too
- var topic = topics.UNSUB_SERVICE+cowebToken.service;
- // include original topic
- OpenAjax.hub.publish(topic, cowebToken);
- }
- }
- }
- };
-
- /**
- * Pause the syncing of incoming operations. The application of any incoming
- * operations will be delayed until `resumeSync` is called.
- */
- proto.pauseSync = function() {
- OpenAjax.hub.publish(topics.PAUSE_TOPIC, true);
- };
-
- /**
- * Resume syncing the incoming operations. Any incoming operations that were
- * delayed while the pause was in effect will now be applied.
- */
- proto.resumeSync = function() {
- OpenAjax.hub.publish(topics.RESUME_TOPIC, true);
- };
-
- return UnmanagedHubCollab;
-});
View
77 js/lib/coweb/ext/CowebWrapper.js
@@ -1,77 +0,0 @@
-//
-// A wrapper to assist in making application widgets cooperative. Subclass it
-// or treat it as a template.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/main'
-], function(coweb) {
- /**
- * @constructor
- * @param {String} [args.id=args.widget.id] Unique identifier of this
- * wrapper / widget
- * @param {Object} args.widget Widget to wrap
- */
- var CowebWrapper = function(args) {
- // widget to wrap
- this.widget = args.widget;
- // id of this instance
- this.id = args.id || this.widget.id;
- // init collab interface
- this.collab = coweb.initCollab({id : this.id});
- // listen to ready and full state events
- this.collab.subscribeReady(this, 'onReady');
- this.collab.subscribeStateRequest(this, 'onStateRequest');
- this.collab.subscribeStateResponse(this, 'onStateRequest');
- };
- var proto = CowebWrapper.prototype;
-
- /**
- * Unsubscribes all callbacks from the CollabInterface instance. Should be
- * invoked on widget destruction.
- */
- proto.uninitialize = function() {
- // invoke this to unsubscribe on widget destruction
- this.collab.unsubscribeAll();
- };
-
- /**
- * Invoked when the CollabInterface reports the application is ready for
- * cooperation in the session.
- *
- * @param {Object} info Session information
- */
- proto.onReady = function(info) {
- // override to handle session ready
- };
-
- /**
- * Invoked when the CollabInterface receives a request for this widget's
- * full state. The implementation should invoke
- * this.collab.sendStateResponse.
- *
- * @param {String} token Token to include in the state response call
- */
- proto.onStateRequest = function(token) {
- // override and invoke this.collab.sendStateResponse
- };
-
- /**
- * Invoked when the CollabInterface receives a copy of the shared state
- * state of this widget. The implementation should set the widget to this
- * state.
- *
- * @param {any} state Widget state
- */
- proto.onStateResponse = function(state) {
- // override to apply state
- };
-
- return CowebWrapper;
-});
View
158 js/lib/coweb/ext/SimpleLoader.js
@@ -1,158 +0,0 @@
-//
-// Wraps the use of the session API in a class with declarative options and
-// callback methods to override. An alternative to using the promise-based
-// API for those that prefer classes and callbacks.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'require',
- 'coweb/main'
-], function(require, coweb) {
- /**
- * @constructor
- * @param {String} id Unique id to assign to the CollabInterface instance
- * in this loader
- */
- var SimpleLoader = function(id) {
- // hard coded conference key to use
- this.cowebKey = undefined;
- // is the conference collaborative or standalone (bots only)?
- this.cowebCollab = true;
-
- // initialize session API
- this.sess = coweb.initSession();
- // initialize collab API
- this.collab = coweb.initCollab({id : id});
- this.collab.subscribeReady(this, 'onCollabReady');
- // place to hang onto the session metadata that comes back from server
- this.prepareMetadata = null;
- };
- var proto = SimpleLoader.prototype;
-
- /**
- * Starts the loader sequence.
- */
- proto.run = function() {
- // invoke initial extension point
- this.onRun();
- // attempt to prepare immediately
- this.prepare();
- };
-
- /**
- * Override to perform work when the loader starts running.
- */
- proto.onRun = function() {
- // extension point
- };
-
- /**
- * Override to handle successful session preparation.
- *
- * @param {Object} info Session information from SessionInterface.prepare
- */
- proto.onSessionPrepared = function(info) {
- // extension point
- };
-
- /**
- * Override to handle successful session joining.
- *
- * @param {Object} info Session information from SessionInterface.join
- */
- proto.onSessionJoined = function(info) {
- // extension point
- };
-
- /**
- * Override to handle successful application updating within the session.
- *
- * @param {Object} info Session information from SessionInterface.update
- */
- proto.onSessionUpdated = function(info) {
- // extension point
- };
-
- /**
- * Override to handle any failure while preparing, joining, or updating
- * in the session.
- *
- * @param {Error} err Error object
- */
- proto.onSessionFailed = function(err) {
- // extension point
- };
-
- /**
- * Override to handle the CollaborationInterface.subscribeReady callback.
- *
- * @param {Object} info Roster info from the callback
- */
- proto.onCollabReady = function(info) {
- // extension point
- };
-
- /**
- * Initiates the prepare, join, and update sequence with empty callbacks
- * subscribed to receive notification in each phase.
- */
- proto.prepare = function() {
- var params = {collab : !!this.cowebCollab};
- if(this.cowebKey) {
- params.key = String(this.cowebKey);
- }
- // loader will do the join and update to ensure all callbacks
- // are invoked
- params.autoJoin = false;
- params.autoUpdate = false;
-
- // invoke prepare chain
- this.sess.prepare(params)
- .then('_onSessionPrepared', null, this)
- .then('_onSessionJoined', null, this)
- .then('_onSessionUpdated', 'onSessionFailed', this);
- };
-
- /**
- * Invokes onSessionPrepared and then SessionInterface.join.
- *
- * @private
- */
- proto._onSessionPrepared = function(info) {
- // store metadata for later app access
- this.prepareMetadata = info;
- // notify the extension point; let exceptions bubble
- this.onSessionPrepared(info);
- // do the join
- return this.sess.join();
- };
-
- /**
- * Invokes onSessionJoined and then SessionInterface.update.
- *
- * @private
- */
- proto._onSessionJoined = function(info) {
- // notify the extension point; let exceptions bubble
- this.onSessionJoined(info);
- // do the update
- return this.sess.update();
- };
-
- /**
- * Invokes onSessionUpdated.
- *
- * @private
- */
- proto._onSessionUpdated = function(info) {
- this.onSessionUpdated(info);
- };
-
- return SimpleLoader;
-});
View
191 js/lib/coweb/ext/attendance.js
@@ -1,191 +0,0 @@
-//
-// Tracks session attendance by roster changes.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/main',
- 'coweb/util/Promise'
-], function(coweb, Promise) {
- var attendance = {
- // all attendees, read-only externally
- users : {},
- // total attendee count, read-only externally
- count : 0,
- // subscriptions
- _subs : {},
- // next subscription id
- _subId : 0,
-
- /**
- * Subscribe for attendance changes.
- * @private
- */
- _subscribe : function(context, callback) {
- if(callback === undefined) {
- callback = context;
- context = this;
- }
- if(typeof callback !== 'function') {
- callback = context[callback];
- if(typeof callback !== 'function') {
- throw new Error('callback must be a function');
- }
- }
- var id = this._subId;
- this._subs[id] = {
- context : context,
- callback : callback
- };
- this._subId++;
- return id;
- },
-
- /**
- * Notifies subscribed listeners of a change.
- * @private
- */
- _notify : function(type, users, count) {
- var subs = this._subs;
- for(var id in subs) {
- if(subs.hasOwnProperty(id)) {
- var s = subs[id];
- // bundle to stay api consistent
- var args = {type : type, users : users, count : count};
- try {
- s.callback.call(s.context, args);
- } catch(e) {
- console.error(e);
- }
- }
- }
- },
-
- /**
- * Subscribes to roster change events.
- *
- * @param {Object|Function} Context in which to invoke the callback or
- * the callback itself
- * @param {Function|String} callback Function to invoke if context
- * specified
- * @return Promise which always notifies success because this impl is
- * synchronous
- */
- subscribeChange: function(context, callback) {
- var tok = this._subscribe(context, callback);
- var promise = new Promise();
- promise._cowebToken = tok;
- promise.resolve();
- return promise;
- },
-
- /**
- * Unsubscribes any subscription created via this interface.
- *
- * @param {Promise} promise Promise returned from subscribe method
- */
- unsubscribe: function(promise) {
- var tok = promise._cowebToken;
- if(tok) {
- delete this._subs[tok];
- }
- },
-
- /**
- * Unsubscribes all listeners.
- */
- unsubscribeAll: function() {
- for(var id in this._subs) {
- if(this._subs.hasOwnProperty(id)) {
- delete this._subs[id];
- }
- }
- },
-
- /**
- * Called when the local application is ready in the session.
- * @private
- */
- _onLocalJoin: function(params) {
- var users = [];
- for(var site in params.roster) {
- if(params.roster.hasOwnProperty(site)) {
- var username = params.roster[site];
- users.push(this._addUser(site, username, false));
- }
- }
- // notify about all existing users all at once
- this._notify('join', users, this.count);
-
- var user = this._addUser(params.site, params.username, true);
- if(user) {
- // notify about local user
- this._notify('join', [user], this.count);
- }
- },
-
- /**
- * Called when a remote application is ready in the session.
- * @private
- */
- _onRemoteJoin: function(params) {
- var user = this._addUser(params.site, params.username, false);
- if(user) {
- this._notify('join', [user], this.count);
- }
- },
-
- /**
- * Called when a remote application leaves the session.
- * @private
- */
- _onRemoteLeave: function(params) {
- var user = this._removeUser(params.site);
- if(user) {
- this._notify('leave', [user], this.count);
- }
- },
-
- /**
- * Add a new user to track.
- * @private
- */
- _addUser: function(site, username, local) {
- var user = this.users[site];
- // don't increment count or construct a user
- if(user) { return; }
- ++this.count;
- // build a user object
- user = {site : Number(site), username : username, local : local};
- // store it
- this.users[site] = user;
- return user;
- },
-
- /**
- * Stop tracking a user.
- * @private
- */
- _removeUser: function(site) {
- // get the user
- var user = this.users[site];
- // decrement count if user exists
- if(user) { --this.count; }
- // remove the stored user
- delete this.users[site];
- return user;
- }
- };
-
- // connect to collab events
- var collab = coweb.initCollab({id : 'coweb-ext-attendance'});
- collab.subscribeReady(attendance, '_onLocalJoin');
- collab.subscribeSiteJoin(attendance, '_onRemoteJoin');
- collab.subscribeSiteLeave(attendance, '_onRemoteLeave');
- return attendance;
-});
View
76 js/lib/coweb/jsoe/ContextDifference.js
@@ -1,76 +0,0 @@
-//
-// Difference between two contexts in terms of operations.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/factory'
-], function(factory) {
- /**
- * Stores the difference in operations between two contexts in terms of
- * site IDs and sequence numbers.
- *
- * @constructor
- */
- var ContextDifference = function() {
- this.sites = [];
- this.seqs = [];
- };
-
-
- /**
- * Adds a range of operations to the difference.
- *
- * @param {Number} site Integer site ID
- * @param {Number} start First integer operation sequence number, inclusive
- * @param {Number} end Last integer operation sequence number, exclusive
- */
- ContextDifference.prototype.addRange = function(site, start, end) {
- for(var i=start; i < end; i++) {
- this.addSiteSeq(site, i);
- }
- };
-
- /**
- * Adds a single operation to the difference.
- *
- * @param {Number} site Integer site ID
- * @param {Number} seq Integer sequence number
- */
- ContextDifference.prototype.addSiteSeq = function(site, seq) {
- this.sites.push(site);
- this.seqs.push(seq);
- };
-
- /**
- * Gets the histor buffer keys for all the operations represented in this
- * context difference.
- *
- * @return {String[]} Array of keys for HistoryBuffer lookups
- */
- ContextDifference.prototype.getHistoryBufferKeys = function() {
- var arr = [];
- for(var i=0, l=this.seqs.length; i < l; i++) {
- var key = factory.createHistoryKey(this.sites[i],
- this.seqs[i]);
- arr.push(key);
- }
- return arr;
- };
-
- /**
- * Converts the contents of this context difference to a string.
- *
- * @return {String} All keys in the difference (for debug)
- */
- ContextDifference.prototype.toString = function() {
- return this.getHistoryBufferKeys().toString();
- };
-
- return ContextDifference;
-});
View
226 js/lib/coweb/jsoe/ContextVector.js
@@ -1,226 +0,0 @@
-//
-// Context vector representation of application state. Currently, just a state
-// vector without undo support.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/ContextDifference'
-], function(ContextDifference) {
- /**
- * Represents the context in which an operation occurred at a site in
- * terms of the operation sequence numbers already applied at that site or
- * the state of the document at the time.
- *
- * Initializes the sequence context vector based on the desired size of
- * the vector, an existing context vector, an array of integers from an
- * existing context vector, or the serialized state of an existing context
- * vector. At least one of these must be passed on the args parameter else
- * the constructor throws an exception. The argument properties are checked
- * in the order documented below. The first one encountered is used.
- *
- * @constructor
- * @param {Number} args.count Integer number of vector entries to
- * initialize to zero
- * @param {ContextVector} args.contextVector Context vector to copy
- * @param {Number[]} args.sites Array from a context vector object to copy
- * @param {Number[]} args.state Array from a serialized context vector
- * object to reference without copy
- */
- var ContextVector = function(args) {
- if(typeof args.count !== 'undefined') {
- this.sites = [];
- this.growTo(args.count);
- } else if(args.contextVector) {
- this.sites = args.contextVector.copySites();
- } else if(args.sites) {
- this.sites = args.sites.slice();
- } else if(args.state) {
- this.sites = args.state;
- } else {
- throw new Error('uninitialized context vector');
- }
- };
-
- /**
- * Converts the contents of this context vector sites array to a string.
- *
- * @returns {String} All integers in the vector (for debug)
- */
- ContextVector.prototype.toString = function() {
- return '[' + this.sites.toString() + ']';
- };
-
- /**
- * Serializes this context vector.
- *
- * @returns {Number[]} Array of integer sequence numbers
- */
- ContextVector.prototype.getState = function() {
- return this.sites;
- };
-
- /**
- * Makes an independent copy of this context vector.
- *
- * @returns {ContextVector} Copy of this context vector
- */
- ContextVector.prototype.copy = function() {
- return new ContextVector({contextVector : this});
- };
-
- /**
- * Makes an independent copy of the array in this context vector.
- *
- * @return {Number[]} Copy of this context vector's sites array
- */
- ContextVector.prototype.copySites = function() {
- return this.sites.slice();
- };
-
- /**
- * Computes the difference in sequence numbers at each site between this
- * context vector and the one provided.
- *
- * @param {ContextVector} cv Other context vector object
- * @returns {ContextDifference} Represents the difference between this
- * vector and the one passed
- */
- ContextVector.prototype.subtract = function(cv) {
- var cd = new ContextDifference();
- for(var i=0; i < this.sites.length; i++) {
- var a = this.getSeqForSite(i);
- var b = cv.getSeqForSite(i);
- if(a-b > 0) {
- cd.addRange(i, b+1, a+1);
- }
- }
- return cd;
- };
-
- /**
- * Finds the oldest sequence number in the difference in sequence numbers
- * for each site between this context and the one provided.
- *
- * @param {ContextVector} cv Other context vector object
- * @returns {ContextDifference} Represents the oldest difference for each
- * site between this vector and the one passed
- */
- ContextVector.prototype.oldestDifference = function(cv) {
- var cd = new ContextDifference();
- for(var i=0; i < this.sites.length; i++) {
- var a = this.getSeqForSite(i);
- var b = cv.getSeqForSite(i);
- if(a-b > 0) {
- cd.addSiteSeq(i, b+1);
- }
- }
- return cd;
- };
-
- /**
- * Increases the size of the context vector to the given size. Initializes
- * new entries with zeros.
- *
- * @param {Number} count Desired integer size of the vector
- */
- ContextVector.prototype.growTo = function(count) {
- for(var i=this.sites.length; i < count; i++) {
- this.sites.push(0);
- }
- };
-
- /**
- * Gets the sequence number for the given site in this context vector.
- * Grows the vector if it does not include the site yet.
- *
- * @param {Number} site Integer site ID
- * @returns {Number} Integer sequence number for the site
- */
- ContextVector.prototype.getSeqForSite = function(site) {
- if(this.sites.length <= site) {
- this.growTo(site+1);
- }
- return this.sites[site];
- };
-
- /**
- * Sets the sequence number for the given site in this context vector.
- * Grows the vector if it does not include the site yet.
- *
- * @param {Number} site Integer site ID
- * @param {Number} seq Integer sequence number
- */
- ContextVector.prototype.setSeqForSite = function(site, seq) {
- if(this.sites.length <= site) {
- this.growTo(site+1);
- }
- this.sites[site] = seq;
- };
-
- /**
- * Gets the size of this context vector.
- *
- * @returns {Number} Integer size
- */
- ContextVector.prototype.getSize = function() {
- return this.sites.length;
- };
-
- /**
- * Determines if this context vector equals the other in terms of the
- * sequence IDs at each site. If the vectors are of different sizes, treats
- * missing entries as suffixed zeros.
- *
- * @param {ContextVector} cv Other context vector
- * @returns {Boolean} True if equal, false if not
- */
- ContextVector.prototype.equals = function(cv) {
- var a = this.sites;
- var b = cv.sites;
- // account for different size vectors
- var max = Math.max(a.length, b.length);
- for(var i=0; i < max; i++) {
- var va = (i < a.length) ? a[i] : 0;
- var vb = (i < b.length) ? b[i] : 0;
- if(va !== vb) {
- return false;
- }
- }
- return true;
- };
-
- /**
- * Computes an ordered comparison of two context vectors according to the
- * sequence IDs at each site. If the vectors are of different sizes,
- * treats missing entries as suffixed zeros.
- *
- * @param {ContextVector} cv Other context vector
- * @returns {Number} -1 if this context vector is ordered before the other,
- * 0 if they are equal, or 1 if this context vector is ordered after the
- * other
- */
- ContextVector.prototype.compare = function(cv) {
- var a = this.sites;
- var b = cv.sites;
- // acount for different size vectors
- var max = Math.max(a.length, b.length);
- for(var i=0; i < max; i++) {
- var va = (i < a.length) ? a[i] : 0;
- var vb = (i < b.length) ? b[i] : 0;
- if(va < vb) {
- return -1;
- } else if(va > vb) {
- return 1;
- }
- }
- return 0;
- };
-
- return ContextVector;
-});
View
198 js/lib/coweb/jsoe/ContextVectorTable.js
@@ -1,198 +0,0 @@
-//
-// Table of context vectors of known sites.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/ContextVector'
-], function(ContextVector) {
- /**
- * Stores the context of each site known at this site.
- *
- * Initializes the table to include the given context vector at the given
- * site index. Ensures the table has enough empty context vectors up to
- * the given site ID.
- *
- * Supports the freezing and unfreezing of slots in the table as the
- * corresponding sites start and stop participating in operational
- * transformation.
- *
- * @constructor
- * @param {ContextVector} Context vector to store in the table at the
- * given index
- * @param {Number} index Integer site ID representing the index at which to
- * store the initial context vector
- */
- var ContextVectorTable = function(cv, site) {
- this.cvt = [];
- this.growTo(site+1);
- this.cvt[site] = cv;
- };
-
- /**
- * Converts the contents of this context vector table to a string.
- *
- * @return {String} All context vectors in the table (for debug)
- */
- ContextVectorTable.prototype.toString = function() {
- var arr = [];
- for(var i = 0, l = this.cvt.length; i++; i < l) {
- var cv = this.cvt[i];
- arr[i] = cv.toString();
- }
- return arr.toString();
- };
-
- /**
- * Gets the index of each entry in the table frozen to (i.e., sharing a
- * reference with, the given context vector, skipping the one noted in the
- * skip param.
- *
- * @param {ContextVector} cv Context vector instance
- * @param {Number} skip Integer index to skip
- * @returns {Number[]} Integer indices of table slots referencing the
- * context vector
- */
- ContextVectorTable.prototype.getEquivalents = function(cv, skip) {
- var equiv = [];
- for(var i=0, l=this.cvt.length; i < l; i++) {
- if(i !== skip && this.cvt[i] === cv) {
- equiv.push(i);
- }
- }
- return equiv;
- };
-
- /**
- * Serializes the state of this context vector table for transmission.
- *
- * @returns {Array[]} Array of context vectors serialized as arrays
- */
- ContextVectorTable.prototype.getState = function() {
- var arr = [];
- for(var i=0, l=this.cvt.length; i < l; i++) {
- arr[i] = this.cvt[i].getState();
- }
- return arr;
- };
-
- /**
- * Unserializes context vector table contents to initialize this intance.
- *
- * @param {Array[]} arr Array in the format returned by getState
- */
- ContextVectorTable.prototype.setState = function(arr) {
- // clear out any existing state
- this.cvt = [];
- for(var i=0, l=arr.length; i < l; i++) {
- this.cvt[i] = new ContextVector({state : arr[i]});
- }
- };
-
- /**
- * Increases the size of the context vector table to the given size.
- * Inceases the size of all context vectors in the table to the given size.
- * Initializes new entries with zeroed context vectors.
- *
- * @param {Number} count Desired integer size
- */
- ContextVectorTable.prototype.growTo = function(count) {
- // grow all context vectors
- for(var i=0, l=this.cvt.length; i < l; i++) {
- this.cvt[i].growTo(count);
- }
- // add new vectors of proper size
- for(i=this.cvt.length; i < count; i++) {
- var cv = new ContextVector({count : count});
- this.cvt.push(cv);
- }
- };
-
- /**
- * Gets the context vector for the given site. Grows the table if it does
- * not include the site yet and returns a zeroed context vector if so.
- *
- * @param {Number} site Integer site ID
- * @returns {ContextVector} Context vector for the given site
- */
- ContextVectorTable.prototype.getContextVector = function(site) {
- if(this.cvt.length <= site) {
- // grow to encompass the given site at least
- // this is not necessarily the final desired size...
- this.growTo(site+1);
- }
- return this.cvt[site];
- };
-
- /**
- * Sets the context vector for the given site. Grows the table if it does
- * not include the site yet.
- *
- * @param {Number} site Integer site ID
- * @param {ContextVector} cv Context vector instance
- */
- ContextVectorTable.prototype.updateWithContextVector = function(site, cv) {
- if(this.cvt.length <= site) {
- // grow to encompass the given site at least
- this.growTo(site+1);
- }
- if(cv.getSize() <= site) {
- // make sure the given cv is of the right size too
- cv.growTo(site+1);
- }
- this.cvt[site] = cv;
- };
-
- /**
- * Sets the context vector for the site on the given operation. Grows the
- * table if it does not include the site yet.
- *
- * @param {Operation} op Operation with the site ID and context vector
- */
- ContextVectorTable.prototype.updateWithOperation = function(op) {
- // copy the context vector from the operation
- var cv = op.contextVector.copy();
- // upgrade the cv so it includes the op
- cv.setSeqForSite(op.siteId, op.seqId);
- // store the cv
- this.updateWithContextVector(op.siteId, cv);
- };
-
- /**
- * Gets the context vector with the minimum sequence number for each site
- * among all context vectors in the table. Gets null if the minimum
- * vector cannot be constructed because the table is empty.
- *
- * @returns {ContextVector|null} Minium context vector
- */
- ContextVectorTable.prototype.getMinimumContextVector = function() {
- // if table is empty, abort
- if(!this.cvt.length) {
- return null;
- }
-
- // start with first context vector as a guess of which is minimum
- var mcv = this.cvt[0].copy();
-
- for(var i=1, l=this.cvt.length; i < l; i++) {
- var cv = this.cvt[i];
- // cvt has to equal the max vector size contained within
- for(var site = 0; site < l; site++) {
- var seq = cv.getSeqForSite(site);
- var min = mcv.getSeqForSite(site);
- if(seq < min) {
- // take smaller of the two sequences numbers for each site
- mcv.setSeqForSite(site, seq);
- }
- }
- }
- return mcv;
- };
-
- return ContextVectorTable;
-});
View
83 js/lib/coweb/jsoe/DeleteOperation.js
@@ -1,83 +0,0 @@
-//
-// Represents a delete operation that removes a value from a linear
-// collection.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/Operation',
- 'coweb/jsoe/factory'
-], function(Operation, factory) {
- /**
- * @constructor
- */
- var DeleteOperation = function(args) {
- this.type = 'delete';
- Operation.call(this, args);
- };
- DeleteOperation.prototype = new Operation();
- DeleteOperation.prototype.constructor = DeleteOperation;
- factory.registerOperationForType('delete', DeleteOperation);
-
- /**
- * Gets the method name to use to transform another operation against this
- * delete operation.
- *
- * @returns {String} Method name
- */
- DeleteOperation.prototype.transformMethod = function() {
- return 'transformWithDelete';
- };
-
- /**
- * No-op. Update has no effect on a delete.
- *
- * @param {UpdateOperation} op Update to include in this op
- * @returns {DeleteOperation} This instance
- */
- DeleteOperation.prototype.transformWithUpdate = function(op) {
- return this;
- };
-
- /**
- * Transforms this delete to include the effect of an insert.
- *
- * @param {InsertOperation} op Insert to include in this op
- * @returns {DeleteOperation} This instance
- */
- DeleteOperation.prototype.transformWithInsert = function(op) {
- if(this.key !== op.key) {
- return this;
- }
- if(this.position >= op.position) {
- ++this.position;
- }
- return this;
- };
-
- /**
- * Transforms this delete to include the effect of a delete.
- *
- * @param {DeleteOperation} op Delete to include in this op
- * @returns {DeleteOperation|null} This instance or null if this op has no
- * further effect on other operations
- */
- DeleteOperation.prototype.transformWithDelete = function(op) {
- if(this.key !== op.key) {
- return this;
- }
- if(this.position > op.position) {
- --this.position;
- } else if(this.position === op.position) {
- return null;
- }
- return this;
- };
-
- return DeleteOperation;
-});
View
179 js/lib/coweb/jsoe/HistoryBuffer.js
@@ -1,179 +0,0 @@
-//
-// History buffer storing original operations.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/factory',
- 'coweb/jsoe/Operation'
-], function(factory, Operation) {
- /**
- * Stores information about local and remote operations for future
- * transformations.
- *
- * @constructor
- */
- var HistoryBuffer = function() {
- this.ops = {};
- this.size = 0;
- };
-
- /**
- * Serializes the history buffer contents to seed a remote instance.
- *
- * @return {Object[]} Serialized operations in the history
- */
- HistoryBuffer.prototype.getState = function() {
- // pack keys and values into linear array to minimize wire size
- var arr = [];
- var i = 0;
- for(var key in this.ops) {
- if(this.ops.hasOwnProperty(key)) {
- // only deal with values, keys can be rebuilt from them
- arr[i] = this.ops[key].getState();
- ++i;
- }
- }
- return arr;
- };
-
- /**
- * Unserializes history buffer contents to initialize this instance.
- *
- * @param {Object[]} arr Array in the format returned by getState
- */
- HistoryBuffer.prototype.setState = function(arr) {
- // reset internals
- this.size = 0;
- this.ops = {};
- for(var i=0; i < arr.length; i++) {
- // restore operations
- var op = factory.createOperationFromState(arr[i]);
- this.addLocal(op);
- }
- };
-
- /**
- * Retrieves all of the operations represented by the given context
- * differences from the history buffer. Sorts them by total order, placing
- * any ops with an unknown place in the order (i.e., local ops) at the end
- * sorted by their sequence IDs. Throws an exception when a requested
- * operation is missing from the history.
- *
- * @param {ContextDifference} cd Context difference object
- * @returns {Operation[]} Sorted operations
- */
- HistoryBuffer.prototype.getOpsForDifference = function(cd) {
- // get the ops
- var keys = cd.getHistoryBufferKeys();
- var ops = [];
- for(var i=0, l=keys.length; i < l; i++) {
- var key = keys[i];
- var op = this.ops[key];
- if(op === undefined) {
- throw new Error('missing op for context diff: i=' + i +
- ' key=' + key + ' keys=' + keys.toString());
- }
- ops.push(op);
- }
- // sort by total order
- ops.sort(function(a,b) { return a.compareByOrder(b); });
- return ops;
- };
-
- /**
- * Adds a local operation to the history.
- *
- * @param {Operation} Local operation to add
- */
- HistoryBuffer.prototype.addLocal = function(op) {
- var key = factory.createHistoryKey(op.siteId, op.seqId);
- this.ops[key] = op;
- // make sure ops in the history never change
- op.immutable = true;
- ++this.size;
- };
-
- /**
- * Adds a received operation to the history. If the operation already
- * exists in the history, simply updates its order attribute. If not,
- * adds it. Throws an exception if the op does not include its place in
- * the total order or if the op with the same key already has an assigned
- * place in the total order.
- *
- * @param {Operation} Received operation to add
- */
- HistoryBuffer.prototype.addRemote = function(op) {
- var key = factory.createHistoryKey(op.siteId, op.seqId);
- var eop = this.ops[key];
- if(op.order === null || op.order === undefined ||
- op.order === Infinity) {
- // remote op must have order set by server
- throw new Error('remote op missing total order');
- } else if(eop) {
- if(eop.order !== Infinity) {
- // order should never repeat
- throw new Error('duplicate op in total order: old='+eop.order +
- ' new='+op.order);
- }
- // server has responded with known total order for an op this site
- // previously sent; update the local op with the info
- eop.order = op.order;
- } else {
- // add new remote op to history
- this.ops[key] = op;
- op.immutable = true;
- ++this.size;
- }
- };
-
- /**
- * Removes and returns an operation in the history.
- *
- * @param {Operation} op Operation to locate for removal
- * @returns {Operation} Removed operation
- */
- HistoryBuffer.prototype.remove = function(op) {
- var key = factory.createHistoryKey(op.siteId, op.seqId);
- op = this.ops[key];
- delete this.ops[key];
- // no longer in the history, so allow mutation
- op.immutable = false;
- --this.size;
- return op;
- };
-
- /**
- * Gets the number of operations in the history.
- *
- * @returns {Number} Integer count
- */
- HistoryBuffer.prototype.getCount = function() {
- return this.size;
- };
-
- /**
- * Gets all operations in the history buffer sorted by context.
- *
- * @returns {Operation[]} Sorted operations
- */
- HistoryBuffer.prototype.getContextSortedOperations = function() {
- var ops = [];
- // put all ops into an array
- for(var key in this.ops) {
- if(this.ops.hasOwnProperty(key)) {
- ops.push(this.ops[key]);
- }
- }
- // sort them by context, sequence, and site
- ops.sort(function(a,b) { return a.compareByContext(b); });
- return ops;
- };
-
- return HistoryBuffer;
-});
View
82 js/lib/coweb/jsoe/InsertOperation.js
@@ -1,82 +0,0 @@
-//
-// Represents an insert operation that adds a value to a linear collection.
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/Operation',
- 'coweb/jsoe/factory'
-], function(Operation, factory) {
- /**
- * @constructor
- */
- var InsertOperation = function(args) {
- this.type = 'insert';
- Operation.call(this, args);
- };
- InsertOperation.prototype = new Operation();
- InsertOperation.prototype.constructor = InsertOperation;
- factory.registerOperationForType('insert', InsertOperation);
-
- /**
- * Gets the method name to use to transform another operation against this
- * insert operation.
- *
- * @returns {String} Method name
- */
- InsertOperation.prototype.transformMethod = function() {
- return 'transformWithInsert';
- };
-
- /**
- * No-op. Update has no effect on an insert.
- *
- * @param {UpdateOperation} op Update to include in this op
- * @returns {InsertOperation} This instance
- */
- InsertOperation.prototype.transformWithUpdate = function(op) {
- return this;
- };
-
- /**
- * Transforms this insert to include the effect of an insert. Assumes
- * the control algorithm breaks the CP2 pre-req to ensure convergence.
- *
- * @param {InsertOperation} op Insert to include in this op
- * @returns {InsertOperation} This instance
- */
- InsertOperation.prototype.transformWithInsert = function(op) {
- if(this.key !== op.key) {
- return this;
- }
-
- if(this.position > op.position ||
- (this.position === op.position && this.siteId <= op.siteId)) {
- ++this.position;
- }
- return this;
- };
-
- /**
- * Transforms this insert to include the effect of a delete.
- *
- * @param {DeleteOperation} op Delete to include in this op
- * @return {InsertOperation} This instance
- */
- InsertOperation.prototype.transformWithDelete = function(op) {
- if(this.key !== op.key) {
- return this;
- }
- if(this.position > op.position) {
- --this.position;
- }
- return this;
- };
-
- return InsertOperation;
-});
View
296 js/lib/coweb/jsoe/Operation.js
@@ -1,296 +0,0 @@
-//
-// Defines the base class for operations.
-//
-// @todo: probably shouldn't be a class for performance; a bunch of functions
-// that act on raw js objects representing ops would cut out the serialize
-// steps and make copy simpler most likely
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/ContextVector'
-], function(ContextVector) {
- /**
- * Contains information about a local or remote event for transformation.
- *
- * Initializes the operation from serialized state or individual props if
- * state is not defined in the args parameter.
- *
- * @param {Object[]} args.state Array in format returned by getState
- * bundling the following individual parameter values
- * @param {Number} args.siteId Integer site ID where the op originated
- * @param {ContextVector} args.contextVector Context in which the op
- * occurred
- * @param {String} args.key Name of the property the op affected
- * @param {String} args.value Value of the op
- * @param {Number} args.position Integer position of the op in a linear
- * collection
- * @param {Number} args.order Integer sequence number of the op in the
- * total op order across all sites
- * @param {Number} args.seqId Integer sequence number of the op at its
- * originating site. If undefined, computed from the context vector and
- * site ID.
- * @param {Boolean} args.immutable True if the op cannot be changed, most
- * likely because it is in a history buffer somewhere
- * to this instance
- */
- var Operation = function(args) {
- if(args === undefined) {
- // abstract
- this.type = null;
- return;
- } else if(args.state) {
- // restore from state alone
- this.setState(args.state);
- // never local when building from serialized state
- this.local = false;
- } else {
- // use individual properties
- this.siteId = args.siteId;
- this.contextVector = args.contextVector;
- this.key = args.key;
- this.value = args.value;
- this.position = args.position;
- this.order = (args.order === undefined || args.order === null) ?
- Infinity : args.order;
- if(args.seqId !== undefined) {
- this.seqId = args.seqId;
- } else if(this.contextVector) {
- this.seqId = this.contextVector.getSeqForSite(this.siteId) + 1;
- } else {
- throw new Error('missing sequence id for new operation');
- }
- this.xCache = args.xCache;
- this.local = args.local || false;
- }
- // always mutable to start
- this.immutable = false;
- // define the xcache if not set elsewhere
- if(!this.xCache) {
- this.xCache = [];
- }
- // always mutable to start
- this.immutable = false;
- };
-
- /**
- * Serializes the operation as an array of values for transmission.
- *
- * @return {Object[]} Array with the name of the operation type and all
- * of its instance variables as primitive JS types
- */
- Operation.prototype.getState = function() {
- // use an array to minimize the wire format
- var arr = [this.type, this.key, this.value, this.position,
- this.contextVector.sites, this.seqId, this.siteId,
- this.order];
- return arr;
- };
-
- /**
- * Unserializes operation data and sets it as the instance data. Throws an
- * exception if the state is not from an operation of the same type.
- *
- * @param {Object[]} arr Array in the format returned by getState
- */
- Operation.prototype.setState = function(arr) {
- if(arr[0] !== this.type) {
- throw new Error('setState invoked with state from wrong op type');
- } else if(this.immutable) {
- throw new Error('op is immutable');
- }
- // name args as required by constructor
- this.key = arr[1];
- this.value = arr[2];
- this.position = arr[3];
- this.contextVector = new ContextVector({state : arr[4]});
- this.seqId = arr[5];
- this.siteId = arr[6];
- this.order = arr[7] || Infinity;
- };
-
- /**
- * Makes a copy of this operation object. Takes a shortcut and returns
- * a ref to this instance if the op is marked as mutable.
- *
- * @returns {Operation} Operation object
- */
- Operation.prototype.copy = function() {
- var args = {
- siteId : this.siteId,
- seqId : this.seqId,
- contextVector : this.contextVector.copy(),
- key : this.key,
- value : this.value,
- position : this.position,
- order : this.order,
- local : this.local,
- // reference existing xCache
- xCache : this.xCache
- };
- // respect subclasses
- var op = new this.constructor(args);
- return op;
- };
-
- /**
- * Gets a version of the given operation previously transformed into the
- * given context if available.
- *
- * @param {ContextVector} cv Context of the transformed op to seek
- * @returns {Operation|null} Copy of the transformed operation from the
- * cache or null if not found in the cache
- */
- Operation.prototype.getFromCache = function(cv) {
- // check if the cv is a key in the xCache
- var cache = this.xCache,
- xop, i, l;
- for(i=0, l=cache.length; i<l; i++) {
- xop = cache[i];
- if(xop.contextVector.equals(cv)) {
- return xop.copy();
- }
- }
- return null;
- };
-
- /**
- * Caches a transformed copy of this original operation for faster future
- * transformations.
- *
- * @param {Number} Integer count of active sites, including the local one
- */
- Operation.prototype.addToCache = function(siteCount) {
- // pull some refs local
- var cache = this.xCache,
- cop = this.copy();
-
- // mark copy as immutable
- cop.immutable = true;
-
- // add a copy of this transformed op to the history
- cache.push(cop);
-
- // check the count of cached ops against number of sites - 1
- var diff = cache.length - (siteCount-1);
- if(diff > 0) {
- // if overflow, remove oldest op(s)
- cache = cache.slice(diff);
- }
- };
-
- /**
- * Computes an ordered comparison of this op and another based on their
- * context vectors. Used for sorting operations by their contexts.
- *
- * @param {Operation} op Other operation
- * @returns {Number} -1 if this op is ordered before the other, 0 if they
- * are in the same context, and 1 if this op is ordered after the other
- */
- Operation.prototype.compareByContext = function(op) {
- var rv = this.contextVector.compare(op.contextVector);
- if(rv === 0) {
- if(this.siteId < op.siteId) {
- return -1;
- } else if(this.siteId > op.siteId) {
- return 1;
- } else {
- return 0;
- }
- }
- return rv;
- };
-
- /**
- * Computes an ordered comparison of this op and another based on their
- * position in the total op order.
- *
- * @param {Operation} op Other operation
- * @returns {Number} -1 if this op is ordered before the other, 0 if they
- * are in the same context, and 1 if this op is ordered after the other
- */
- Operation.prototype.compareByOrder = function(op) {
- if(this.order === op.order) {
- // both unknown total order so next check if both ops are from
- // the same site or if one is from the local site and the other
- // remote
- if(this.local === op.local) {
- // compare sequence ids for local-local or remote-remote order
- return (this.seqId < op.seqId) ? -1 : 1;
- } else if(this.local && !op.local) {
- // this local op must appear after the remote one in the total
- // order as the remote one was included in the late joining
- // state sent by the remote site to this one meaning it was
- // sent before this site finished joining
- return 1;
- } else if(!this.local && op.local) {
- // same as above, but this op is the remote one now
- return -1;
- }
- } else if(this.order < op.order) {
- return -1;
- } else if(this.order > op.order) {
- return 1;
- }
- };
-
- /**
- * Transforms this operation to include the effects of the operation
- * provided as a parameter IT(this, op). Upgrade the context of this
- * op to reflect the inclusion of the other.
- *
- * @returns {Operation|null} This operation, transformed in-place, or null
- * if its effects are nullified by the transform
- * @throws {Error} If this op to be transformed is immutable or if the
- * this operation subclass does not implement the transform method needed
- * to handle the passed op
- */
- Operation.prototype.transformWith = function(op) {
- if(this.immutable) {
- throw new Error('attempt to transform immutable op');
- }
- var func = this[op.transformMethod()], rv;
- if(!func) {
- throw new Error('operation cannot handle transform with type: '+ op.type);
- }
- // do the transform
- rv = func.apply(this, arguments);
- // check if op effects nullified
- if(rv) {
- // upgrade the context of this op to include the other
- this.upgradeContextTo(op);
- }
- return rv;
- };
-
- /**
- * Upgrades the context of this operation to reflect the inclusion of a
- * single other operation from some site.
- *
- * @param {Operation} The operation to include in the context of this op
- * @throws {Error} If this op to be upgraded is immutable
- */
- Operation.prototype.upgradeContextTo = function(op) {
- if(this.immutable) {
- throw new Error('attempt to upgrade context of immutable op');
- }
- this.contextVector.setSeqForSite(op.siteId, op.seqId);
- };
-
- /**
- * Gets the name of the method to use to transform this operation with
- * another based on the type of this operation defined by a subclass.
- *
- * Abstract implementation always throws an exception if not overriden.
- */
- Operation.prototype.getTransformMethod = function() {
- throw new Error('transformMethod not implemented');
- };
-
- return Operation;
-});
View
438 js/lib/coweb/jsoe/OperationEngine.js
@@ -1,438 +0,0 @@
-//
-// Operation engine public API.
-//
-// @todo: refactor ops to IT funcs on std objects for performance
-//
-// Copyright (c) The Dojo Foundation 2011. All Rights Reserved.
-// Copyright (c) IBM Corporation 2008, 2011. All Rights Reserved.
-//
-/*jslint white:false, bitwise:true, eqeqeq:true, immed:true, nomen:false,
- onevar:false, plusplus:false, undef:true, browser:true, devel:true,
- forin:false, sub:false*/
-/*global define*/
-define([
- 'coweb/jsoe/ContextVectorTable',
- 'coweb/jsoe/ContextVector',
- 'coweb/jsoe/HistoryBuffer',
- 'coweb/jsoe/factory',
- // load subclasses to get them registered with the factory
- 'coweb/jsoe/UpdateOperation',
- 'coweb/jsoe/InsertOperation',
- 'coweb/jsoe/DeleteOperation'
-], function(ContextVectorTable, ContextVector, HistoryBuffer, factory) {
- /**
- * Controls the operational transformation algorithm. Provides a public
- * API for operation processing, garbage collection, and engine
- * synchronization.
- *
- * @constructor
- * @param {Number} siteId Unique integer site ID for this engine instance
- */
- var OperationEngine = function(siteId) {
- this.siteId = siteId;
- this.cv = new ContextVector({count : siteId+1});
- this.cvt = new ContextVectorTable(this.cv, siteId);
- this.hb = new HistoryBuffer();
- this.siteCount = 1;
- };
-
- /**
- * Gets the state of this engine instance to seed a new instance.
- *
- * @return {Object[]} Array or serialized state
- */
- OperationEngine.prototype.getState = function() {
- // op engine state can be cloned from cvt, hb, site ID, and frozen slots
- // get indices of frozen cvt slots
- var frozen = this.cvt.getEquivalents(this.cv, this.siteId);
- return [this.cvt.getState(), this.hb.getState(), this.siteId, frozen];
- };
-
- /**
- * Sets the state of this engine instance to state received from another
- * instance.
- *
- * @param {Object[]} arr Array in the format returned by getState
- */
- OperationEngine.prototype.setState = function(arr) {
- // configure the history buffer and context vector table
- this.cvt.setState(arr[0]);
- this.hb.setState(arr[1]);
- // pull out the context vector for the sending site
- this.cv = this.cvt.getContextVector(arr[2]);
- // copy it
- this.cv = this.cv.copy();
- // freeze our own site slot
- this.cvt.updateWithContextVector(this.siteId, this.cv);
- // set the initial count of active sites; freeze below will adjust
- this.siteCount = this.cv.getSize();
- // freeze all sites that must be frozen
- var frozen = arr[3];
- for(var i=0, l=frozen.length; i < l; i++) {
- this.freezeSite(frozen[i]);
- }
- };
-
- /**
- * Makes a copy of the engine context vector representing the local
- * document state.
- *
- * @returns {ContextVector} Copy of the context vector for the local site
- */
- OperationEngine.prototype.copyContextVector = function() {
- return this.cv.copy();
- };
-
- /**
- * Factory method that creates an operation object initialized with the
- * given values.
- *
- * @param {Boolean} local True if the operation was originated locally,
- * false if not
- * @param {String} key Operation key
- * @param {String} value Operation value
- * @param {String} type Type of operation: update, insert, delete
- * @param {Number} position Operation integer position
- * @param {Number} site Integer site ID where a remote op originated.
- * Ignored for local operations which adopt the local site ID.
- * @param {ContextVector} cv Operation context. Ignored for local
- * operations which adopt the local site context.
- * @param {Number} order Place of the operation in the total order. Ignored
- * for local operations which are not yet assigned a place in the order.
- * @returns {Operation} Subclass instance matching the given type
- */
- OperationEngine.prototype.createOp = function(local, key, value, type,
- position, site, cv, order) {
- var args;
- if(local) {
- args = {
- key : key,
- position : position,
- value : value,
- siteId : this.siteId,
- contextVector : this.copyContextVector(),
- local : true
- };
- } else {
- // build cv from raw sites array
- cv = new ContextVector({sites : cv});
- args = {
- key : key,
- position : position,
- value : value,
- siteId : site,
- contextVector : cv,
- order : order,
- local : false
- };
- }
- return factory.createOperationFromType(type, args);
- };
-
- /**
- * Creates an operation object and pushes it into the operation engine
- * algorithm. The parameters and return value are the same as those
- * documented for createOp.
- */
- OperationEngine.prototype.push = function(local) {
- var op = this.createOp.apply(this, arguments);
- if(local) {
- return this.pushLocalOp(op);
- } else {
- return this.pushRemoteOp(op);
- }
- };
-
- /**
- * Procceses a local operation and adds it to the history buffer.
- *
- * @param {Operation} Local operation
- * @returns {Operation} Reference to the pass parameter
- */
- OperationEngine.prototype.pushLocalOp = function(op) {
- // update local context vector
- this.cv.setSeqForSite(op.siteId, op.seqId);
- // add to history buffer
- this.hb.addLocal(op);
- return op;
- };
-
- /**
- * Procceses a remote operation, transforming it if required, and adds
- * the original to the history buffer.
- *
- * @param {Operation} Remote operation
- * @returns {Operation|null} New, transformed operation object or null if
- * the effect of the passed operation is nothing and should not be applied
- * to the shared state
- */
- OperationEngine.prototype.pushRemoteOp = function(op) {
- var top = null;
-
- if(this.hasProcessedOp(op)) {
- // let the history buffer track the total order for the op
- this.hb.addRemote(op);
- // engine has already processed this op so ignore it
- return null;
- } else if(this.cv.equals(op.contextVector)) {
- // no transform needed
- // make a copy so return value is independent of input
- top = op.copy();
- } else {
- // transform needed to upgrade context
- var cd = this.cv.subtract(op.contextVector);
- // make the original op immutable
- op.immutable = true;
- // top is a transformed copy of the original
- top = this._transform(op, cd);
- }
-