--- a/src/js/libs/tracemanager.js Mon May 21 13:17:47 2012 +0200
+++ b/src/js/libs/tracemanager.js Tue May 22 16:49:48 2012 +0200
@@ -1,540 +1,540 @@
-/*
- * Modelled Trace API
- *
- * This file is part of ktbs4js.
- *
- * ktbs4js is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * ktbs4js is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with ktbs4js. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-/* FIXME: properly use require.js feature. This will do for debugging in the meantime */
-window.tracemanager = (function($) {
- // If there are more than MAX_FAILURE_COUNT synchronisation
- // failures, then disable synchronisation
- MAX_FAILURE_COUNT = 20;
-
- // If there are more than MAX_BUFFER_SIZE obsels in the buffer,
- // then "compress" them as a single "ktbsFullBuffer"
- MAX_BUFFER_SIZE = 500;
-
- var BufferedService_prototype = {
- /*
- * Buffered service for traces
- */
- // url: "",
- // buffer: [],
- // isReady: false,
- // timer: null,
- // failureCount: 0,
-
- /* Flush buffer */
- flush: function() {
- // FIXME: add mutex on this.buffer
- if (! this.isReady)
- {
- if (window.console) window.console.log("Sync service not ready");
- } else if (this.failureCount > MAX_FAILURE_COUNT)
- {
- // Disable synchronisation
- this.set_sync_mode('none');
- } else if (this.buffer.length) {
- var temp = this.buffer;
- this.buffer = [];
-
- if (this.mode == 'GET')
- {
- // GET mode: do some data mangline. We mark the
- // "compressed" nature of the generated JSON by
- // prefixing it with c
- var data = 'c' + JSON.stringify(temp.map(function (o) { return o.toCompactJSON(); }));
- // Swap " (very frequent, which will be
- // serialized into %22) and ; (rather rare), this
- // saves some bytes
- data = data.replace(/[;"#]/g, function(s){ return s == ';' ? '"' : ( s == '"' ? ';' : '%23'); });
- // FIXME: check data length (< 2K is safe)
- var request=$('<img />').error( function() { this.failureCount += 1; })
- .load( function() { this.failureCount = 0; })
- .attr('src', this.url + 'trace/?data=' + data);
- }
- else
- {
- $.ajax({ url: this.url + 'trace/',
- type: 'POST',
- contentType: 'application/json',
- data: JSON.stringify(temp.map(function (o) { return o.toJSON(); })),
- processData: false,
- // Type of the returned data.
- dataType: "html",
- error: function(jqXHR, textStatus, errorThrown) {
- if (window.console) window.console.log("Error when sending buffer:", textStatus);
- this.failureCount += 1;
- },
- success: function(data, textStatus, jqXHR) {
- // Reset failureCount to 0 as soon as there is 1 valid answer
- this.failureCount = 0;
- }
- });
- }
- }
- },
-
- /* Sync mode: delayed, sync (immediate sync), none (no
- * synchronisation with server, the trace has to be explicitly saved
- * if needed */
- set_sync_mode: function(mode, default_subject) {
- this.sync_mode = mode;
- if (! this.isReady && mode !== "none")
- this.init(default_subject);
- if (mode == 'delayed') {
- this.start_timer();
- } else {
- this.stop_timer();
- }
- },
-
- /* Enqueue an obsel */
- enqueue: function(obsel) {
- if (this.buffer.length > MAX_BUFFER_SIZE)
- {
- obsel = new Obsel('ktbsFullBuffer', this.buffer[0].begin,
- this.buffer[this.buffer.length - 1].end, this.buffer[0].subject);
- obsel.trace = this.buffer[0].trace;
- this.buffer = [];
- }
- this.buffer.push(obsel);
- if (this.sync_mode === 'sync') {
- // Immediate sync of the obsel.
- this.flush();
- }
- },
-
- start_timer: function() {
- var self = this;
- if (this.timer === null) {
- this.timer = window.setInterval(function() {
- self.flush();
- }, this.timeOut);
- }
- },
-
- stop_timer: function() {
- if (this.timer !== null) {
- window.clearInterval(this.timer);
- this.timer = null;
- }
- },
-
- /*
- * Initialize the sync service
- */
- init: function(default_subject) {
- var self = this;
- if (this.isReady)
- /* Already initialized */
- return;
- if (typeof default_subject === 'undefined')
- default_subject = 'anonymous';
- if (this.mode == 'GET')
- {
- var request=$('<img/>').attr('src', this.url + 'login?userinfo={"default_subject": "' + default_subject + '"}');
- // Do not wait for the return, assume it is
- // initialized. This assumption will not work anymore
- // if login returns some necessary information
- this.isReady = true;
- }
- else
- {
- $.ajax({ url: this.url + 'login',
- type: 'POST',
- data: 'userinfo={"default_subject":"' + default_subject + '"}',
- success: function(data, textStatus, jqXHR) {
- self.isReady = true;
- if (self.buffer.length) {
- self.flush();
- }
- }
- });
- }
- }
- };
- var BufferedService = function(url, mode) {
- this.url = url;
- this.buffer = [];
- this.isReady = false;
- this.timer = null;
- this.failureCount = 0;
- // sync_mode is either "none", "sync" or "buffered"
- this.sync_mode = "none";
- /* mode can be either POST or GET */
- if (mode == 'POST' || mode == 'GET')
- this.mode = mode;
- else
- this.mode = 'POST';
- /* Flush buffer every timeOut ms if the sync_mode is delayed */
- this.timeOut = 2000;
- };
- BufferedService.prototype = BufferedService_prototype;
-
- var Trace_prototype = {
- /* FIXME: We could/should use a sorted list such as
- http://closure-library.googlecode.com/svn/docs/class_goog_structs_AvlTree.html
- to speed up queries based on time */
- obsels: [],
- /* Trace URI */
- uri: "",
- default_subject: "",
- /* baseuri is used as the base URI to resolve relative
- * attribute-type names in obsels. Strictly speaking, this
- * should rather be expressed as a reference to model, or
- * more generically, as a qname/URI dict */
- baseuri: "",
- /* Mapping of obsel type or property name to a compact
- * representation (shorthands).
- */
- shorthands: null,
- syncservice: null,
-
- /* Define the trace URI */
- set_uri: function(uri) {
- this.uri = uri;
- },
-
- /* Sync mode: delayed, sync (immediate sync), none (no
- * synchronisation with server, the trace has to be explicitly saved
- * if needed */
- set_sync_mode: function(mode) {
- if (this.syncservice !== null) {
- this.syncservice.set_sync_mode(mode, this.default_subject);
- }
- },
-
- /*
- * Return a list of the obsels of this trace matching the parameters
- */
- list_obsels: function(_begin, _end, _reverse) {
- var res;
- if (typeof _begin !== 'undefined' || typeof _end !== 'undefined') {
- /*
- * Not optimized yet.
- */
- res = [];
- var l = this.obsels.length;
- for (var i = 0; i < l; i++) {
- var o = this.obsels[i];
- if ((typeof _begin !== 'undefined' && o.begin > _begin) && (typeof _end !== 'undefined' && o.end < _end)) {
- res.push(o);
- }
- }
- }
-
- if (typeof _reverse !== 'undefined') {
- if (res !== undefined) {
- /* Should reverse the whole list. Make a copy. */
- res = this.obsels.slice(0);
- }
- res.sort(function(a, b) { return b.begin - a.begin; });
- return res;
- }
-
- if (res === undefined) {
- res = this.obsels;
- }
- return res;
-
- },
-
- /*
- * Return the obsel of this trace identified by the URI, or undefined
- */
- get_obsel: function(id) {
- for (var i = 0; i < this.obsels.length; i++) {
- /* FIXME: should check against variations of id/uri, take this.baseuri into account */
- if (this.obsels[i].uri === id) {
- return this.obsels[i];
- }
- }
- return undefined;
- },
-
- set_default_subject: function(subject) {
- // FIXME: if we call this method after the sync_service
- // init method, then the default_subject will not be
- // consistent anymore. Maybe we should then call init() again?
- this.default_subject = subject;
- },
-
- get_default_subject: function() {
- return this.default_subject;
- },
-
- /* (type:ObselType, begin:int, end:int?, subject:str?, attributes:[AttributeType=>any]?) */
- /* Create a new obsel and add it to the trace */
- create_obsel: function(type, begin, end, subject, _attributes) {
- var o = new Obsel(type, begin, end, subject);
- if (typeof _attributes !== 'undefined') {
- o.attributes = _attributes;
- }
- o.trace = this;
- this.obsels.push(o);
- if (this.syncservice !== null)
- this.syncservice.enqueue(o);
- },
-
- /* Helper methods */
-
- /* Create a new obsel with the given attributes */
- trace: function(type, _attributes, _begin, _end, _subject) {
- var t = (new Date()).getTime();
- if (typeof begin === 'undefined') {
- _begin = t;
- }
- if (typeof end === 'undefined') {
- _end = _begin;
- }
- if (typeof subject === 'undefined') {
- _subject = this.default_subject;
- }
- if (typeof _attributes === 'undefined') {
- _attributes = {};
- }
- return this.create_obsel(type, _begin, _end, _subject, _attributes);
- }
- };
-
- var Trace = function(uri, requestmode) {
- /* FIXME: We could/should use a sorted list such as
- http://closure-library.googlecode.com/svn/docs/class_goog_structs_AvlTree.html
- to speed up queries based on time */
- this.obsels = [];
- /* Trace URI */
- if (uri === undefined)
- uri = "";
- this.uri = uri;
- this.sync_mode = "none";
- this.default_subject = "";
- this.shorthands = {};
- /* baseuri is used a the base URI to resolve relative attribute names in obsels */
- this.baseuri = "";
-
- this.syncservice = new BufferedService(uri, requestmode);
- $(window).unload( function () {
- if (this.syncservice && this.sync_mode !== 'none') {
- this.syncservice.flush();
- this.syncservice.stop_timer();
- }
- });
- };
- Trace.prototype = Trace_prototype;
-
- var Obsel_prototype = {
- /* The following attributes are here for documentation
- * purposes. They MUST be defined in the constructor
- * function. */
- trace: undefined,
- type: undefined,
- begin: undefined,
- end: undefined,
- subject: undefined,
- /* Dictionary indexed by ObselType URIs */
- attributes: {},
-
- /* Method definitions */
- get_trace: function() {
- return this.trace;
- },
-
- get_obsel_type: function() {
- return this.type;
- },
- get_begin: function() {
- return this.begin;
- },
- get_end: function() {
- return this.end;
- },
- get_subject: function() {
- return this.subject;
- },
-
- list_attribute_types: function() {
- var result = [];
- for (var prop in this.attributes) {
- if (this.attributes.hasOwnProperty(prop))
- result.push(prop);
- }
- /* FIXME: we return URIs here instead of AttributeType elements */
- return result;
- },
-
- list_relation_types: function() {
- /* FIXME: not implemented yet */
- },
-
- list_related_obsels: function (rt) {
- /* FIXME: not implemented yet */
- },
- list_inverse_relation_types: function () {
- /* FIXME: not implemented yet */
- },
- list_relating_obsels: function (rt) {
- /* FIXME: not implemented yet */
- },
- /*
- * Return the value of the given attribute type for this obsel
- */
- get_attribute_value: function(at) {
- if (typeof at === "string")
- /* It is a URI */
- return this.attributes[at];
- else
- /* FIXME: check that at is instance of AttributeType */
- return this.attributes[at.uri];
- },
-
-
- /* obsel modification (trace amendment) */
-
- set_attribute_value: function(at, value) {
- if (typeof at === "string")
- /* It is a URI */
- this.attributes[at] = value;
- /* FIXME: check that at is instance of AttributeType */
- else
- this.attributes[at.uri] = value;
- },
-
- del_attribute_value: function(at) {
- if (typeof at === "string")
- /* It is a URI */
- delete this.attributes[at];
- /* FIXME: check that at is instance of AttributeType */
- else
- delete this.attributes[at.uri];
- },
-
- add_related_obsel: function(rt, value) {
- /* FIXME: not implemented yet */
- },
-
- del_related_obsel: function(rt, value) {
- /* FIXME: not implemented yet */
- },
-
- /*
- * Return a JSON representation of the obsel
- */
- toJSON: function() {
- var r = {
- "@id": this.id,
- "@type": this.type,
- "begin": this.begin,
- "end": this.end,
- "subject": this.subject
- };
- for (var prop in this.attributes) {
- if (this.attributes.hasOwnProperty(prop))
- r[prop] = this.attributes[prop];
- }
- return r;
- },
-
- /*
- * Return a compact JSON representation of the obsel.
- * Use predefined + custom shorthands for types/properties
- */
- toCompactJSON: function() {
- var r = {
- "@t": (this.trace.shorthands.hasOwnProperty(this.type) ? this.trace.shorthands[this.type] : this.type),
- "@b": this.begin,
- };
- // Transmit subject only if different from default_subject
- if (this.subject !== this.trace.default_subject)
- r["@s"] = this.subject;
-
- // Store duration (to save some bytes) and only if it is non-null
- if (this.begin !== this.end)
- r["@d"] = this.end - this.begin;
-
- // Store id only if != ""
- if (this.id !== "")
- r["@i"] = this.id;
-
- for (var prop in this.attributes) {
- if (this.attributes.hasOwnProperty(prop))
- {
- var v = this.attributes[prop];
- r[prop] = this.trace.shorthands.hasOwnProperty(v) ? this.trace.shorthands[v] : v;
- }
- }
- return r;
- },
-
- toJSONstring: function() {
- return JSON.stringify(this.toJSON());
- }
- };
-
- var Obsel = function(type, begin, end, subject, attributes) {
- this.trace = undefined;
- this.uri = "";
- this.id = "";
- this.type = type;
- this.begin = begin;
- this.end = end;
- this.subject = subject;
- /* Is the obsel synched with the server ? */
- this.sync_status = false;
- /* Dictionary indexed by ObselType URIs */
- this.attributes = {};
- };
- Obsel.prototype = Obsel_prototype;
-
- var TraceManager_prototype = {
- traces: [],
- /*
- * Return the trace with id name
- * If it was not registered, return undefined.
- */
- get_trace: function(name) {
- return this.traces[name];
- },
-
- /*
- * Explicitly create and initialize a new trace with the given name.
- * The optional uri parameter allows to initialize the trace URI.
- *
- * If another existed with the same name before, then it is replaced by a new one.
- */
- init_trace: function(name, params)
- {
- if (window.console) window.console.log("init_trace", params);
- url = params.url ? params.url : "";
- requestmode = params.requestmode ? params.requestmode : "POST";
- syncmode = params.syncmode ? params.syncmode : "none";
- default_subject = params.default_subject ? params.default_subject : "default";
- var t = new Trace(url, requestmode);
- t.set_default_subject(default_subject);
- t.set_sync_mode(syncmode);
- this.traces[name] = t;
- return t;
- }
- };
-
- var TraceManager = function() {
- this.traces = {};
- };
- TraceManager.prototype = TraceManager_prototype;
-
- var tracemanager = new TraceManager();
- return tracemanager;
- })(jQuery);
+/*
+ * Modelled Trace API
+ *
+ * This file is part of ktbs4js.
+ *
+ * ktbs4js is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * ktbs4js is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with ktbs4js. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+/* FIXME: properly use require.js feature. This will do for debugging in the meantime */
+window.tracemanager = (function($) {
+ // If there are more than MAX_FAILURE_COUNT synchronisation
+ // failures, then disable synchronisation
+ MAX_FAILURE_COUNT = 20;
+
+ // If there are more than MAX_BUFFER_SIZE obsels in the buffer,
+ // then "compress" them as a single "ktbsFullBuffer"
+ MAX_BUFFER_SIZE = 500;
+
+ var BufferedService_prototype = {
+ /*
+ * Buffered service for traces
+ */
+ // url: "",
+ // buffer: [],
+ // isReady: false,
+ // timer: null,
+ // failureCount: 0,
+
+ /* Flush buffer */
+ flush: function() {
+ // FIXME: add mutex on this.buffer
+ if (! this.isReady)
+ {
+ if (window.console) window.console.log("Sync service not ready");
+ } else if (this.failureCount > MAX_FAILURE_COUNT)
+ {
+ // Disable synchronisation
+ this.set_sync_mode('none');
+ } else if (this.buffer.length) {
+ var temp = this.buffer;
+ this.buffer = [];
+
+ if (this.mode == 'GET')
+ {
+ // GET mode: do some data mangline. We mark the
+ // "compressed" nature of the generated JSON by
+ // prefixing it with c
+ var data = 'c' + JSON.stringify(temp.map(function (o) { return o.toCompactJSON(); }));
+ // Swap " (very frequent, which will be
+ // serialized into %22) and ; (rather rare), this
+ // saves some bytes
+ data = data.replace(/[;"#]/g, function(s){ return s == ';' ? '"' : ( s == '"' ? ';' : '%23'); });
+ // FIXME: check data length (< 2K is safe)
+ var request=$('<img />').error( function() { this.failureCount += 1; })
+ .load( function() { this.failureCount = 0; })
+ .attr('src', this.url + 'trace/?data=' + data);
+ }
+ else
+ {
+ $.ajax({ url: this.url + 'trace/',
+ type: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(temp.map(function (o) { return o.toJSON(); })),
+ processData: false,
+ // Type of the returned data.
+ dataType: "html",
+ error: function(jqXHR, textStatus, errorThrown) {
+ if (window.console) window.console.log("Error when sending buffer:", textStatus);
+ this.failureCount += 1;
+ },
+ success: function(data, textStatus, jqXHR) {
+ // Reset failureCount to 0 as soon as there is 1 valid answer
+ this.failureCount = 0;
+ }
+ });
+ }
+ }
+ },
+
+ /* Sync mode: delayed, sync (immediate sync), none (no
+ * synchronisation with server, the trace has to be explicitly saved
+ * if needed */
+ set_sync_mode: function(mode, default_subject) {
+ this.sync_mode = mode;
+ if (! this.isReady && mode !== "none")
+ this.init(default_subject);
+ if (mode == 'delayed') {
+ this.start_timer();
+ } else {
+ this.stop_timer();
+ }
+ },
+
+ /* Enqueue an obsel */
+ enqueue: function(obsel) {
+ if (this.buffer.length > MAX_BUFFER_SIZE)
+ {
+ obsel = new Obsel('ktbsFullBuffer', this.buffer[0].begin,
+ this.buffer[this.buffer.length - 1].end, this.buffer[0].subject);
+ obsel.trace = this.buffer[0].trace;
+ this.buffer = [];
+ }
+ this.buffer.push(obsel);
+ if (this.sync_mode === 'sync') {
+ // Immediate sync of the obsel.
+ this.flush();
+ }
+ },
+
+ start_timer: function() {
+ var self = this;
+ if (this.timer === null) {
+ this.timer = window.setInterval(function() {
+ self.flush();
+ }, this.timeOut);
+ }
+ },
+
+ stop_timer: function() {
+ if (this.timer !== null) {
+ window.clearInterval(this.timer);
+ this.timer = null;
+ }
+ },
+
+ /*
+ * Initialize the sync service
+ */
+ init: function(default_subject) {
+ var self = this;
+ if (this.isReady)
+ /* Already initialized */
+ return;
+ if (typeof default_subject === 'undefined')
+ default_subject = 'anonymous';
+ if (this.mode == 'GET')
+ {
+ var request=$('<img/>').attr('src', this.url + 'login?userinfo={"default_subject": "' + default_subject + '"}');
+ // Do not wait for the return, assume it is
+ // initialized. This assumption will not work anymore
+ // if login returns some necessary information
+ this.isReady = true;
+ }
+ else
+ {
+ $.ajax({ url: this.url + 'login',
+ type: 'POST',
+ data: 'userinfo={"default_subject":"' + default_subject + '"}',
+ success: function(data, textStatus, jqXHR) {
+ self.isReady = true;
+ if (self.buffer.length) {
+ self.flush();
+ }
+ }
+ });
+ }
+ }
+ };
+ var BufferedService = function(url, mode) {
+ this.url = url;
+ this.buffer = [];
+ this.isReady = false;
+ this.timer = null;
+ this.failureCount = 0;
+ // sync_mode is either "none", "sync" or "buffered"
+ this.sync_mode = "none";
+ /* mode can be either POST or GET */
+ if (mode == 'POST' || mode == 'GET')
+ this.mode = mode;
+ else
+ this.mode = 'POST';
+ /* Flush buffer every timeOut ms if the sync_mode is delayed */
+ this.timeOut = 2000;
+ };
+ BufferedService.prototype = BufferedService_prototype;
+
+ var Trace_prototype = {
+ /* FIXME: We could/should use a sorted list such as
+ http://closure-library.googlecode.com/svn/docs/class_goog_structs_AvlTree.html
+ to speed up queries based on time */
+ obsels: [],
+ /* Trace URI */
+ uri: "",
+ default_subject: "",
+ /* baseuri is used as the base URI to resolve relative
+ * attribute-type names in obsels. Strictly speaking, this
+ * should rather be expressed as a reference to model, or
+ * more generically, as a qname/URI dict */
+ baseuri: "",
+ /* Mapping of obsel type or property name to a compact
+ * representation (shorthands).
+ */
+ shorthands: null,
+ syncservice: null,
+
+ /* Define the trace URI */
+ set_uri: function(uri) {
+ this.uri = uri;
+ },
+
+ /* Sync mode: delayed, sync (immediate sync), none (no
+ * synchronisation with server, the trace has to be explicitly saved
+ * if needed */
+ set_sync_mode: function(mode) {
+ if (this.syncservice !== null) {
+ this.syncservice.set_sync_mode(mode, this.default_subject);
+ }
+ },
+
+ /*
+ * Return a list of the obsels of this trace matching the parameters
+ */
+ list_obsels: function(_begin, _end, _reverse) {
+ var res;
+ if (typeof _begin !== 'undefined' || typeof _end !== 'undefined') {
+ /*
+ * Not optimized yet.
+ */
+ res = [];
+ var l = this.obsels.length;
+ for (var i = 0; i < l; i++) {
+ var o = this.obsels[i];
+ if ((typeof _begin !== 'undefined' && o.begin > _begin) && (typeof _end !== 'undefined' && o.end < _end)) {
+ res.push(o);
+ }
+ }
+ }
+
+ if (typeof _reverse !== 'undefined') {
+ if (res !== undefined) {
+ /* Should reverse the whole list. Make a copy. */
+ res = this.obsels.slice(0);
+ }
+ res.sort(function(a, b) { return b.begin - a.begin; });
+ return res;
+ }
+
+ if (res === undefined) {
+ res = this.obsels;
+ }
+ return res;
+
+ },
+
+ /*
+ * Return the obsel of this trace identified by the URI, or undefined
+ */
+ get_obsel: function(id) {
+ for (var i = 0; i < this.obsels.length; i++) {
+ /* FIXME: should check against variations of id/uri, take this.baseuri into account */
+ if (this.obsels[i].uri === id) {
+ return this.obsels[i];
+ }
+ }
+ return undefined;
+ },
+
+ set_default_subject: function(subject) {
+ // FIXME: if we call this method after the sync_service
+ // init method, then the default_subject will not be
+ // consistent anymore. Maybe we should then call init() again?
+ this.default_subject = subject;
+ },
+
+ get_default_subject: function() {
+ return this.default_subject;
+ },
+
+ /* (type:ObselType, begin:int, end:int?, subject:str?, attributes:[AttributeType=>any]?) */
+ /* Create a new obsel and add it to the trace */
+ create_obsel: function(type, begin, end, subject, _attributes) {
+ var o = new Obsel(type, begin, end, subject);
+ if (typeof _attributes !== 'undefined') {
+ o.attributes = _attributes;
+ }
+ o.trace = this;
+ this.obsels.push(o);
+ if (this.syncservice !== null)
+ this.syncservice.enqueue(o);
+ },
+
+ /* Helper methods */
+
+ /* Create a new obsel with the given attributes */
+ trace: function(type, _attributes, _begin, _end, _subject) {
+ var t = (new Date()).getTime();
+ if (typeof begin === 'undefined') {
+ _begin = t;
+ }
+ if (typeof end === 'undefined') {
+ _end = _begin;
+ }
+ if (typeof subject === 'undefined') {
+ _subject = this.default_subject;
+ }
+ if (typeof _attributes === 'undefined') {
+ _attributes = {};
+ }
+ return this.create_obsel(type, _begin, _end, _subject, _attributes);
+ }
+ };
+
+ var Trace = function(uri, requestmode) {
+ /* FIXME: We could/should use a sorted list such as
+ http://closure-library.googlecode.com/svn/docs/class_goog_structs_AvlTree.html
+ to speed up queries based on time */
+ this.obsels = [];
+ /* Trace URI */
+ if (uri === undefined)
+ uri = "";
+ this.uri = uri;
+ this.sync_mode = "none";
+ this.default_subject = "";
+ this.shorthands = {};
+ /* baseuri is used a the base URI to resolve relative attribute names in obsels */
+ this.baseuri = "";
+
+ this.syncservice = new BufferedService(uri, requestmode);
+ $(window).unload( function () {
+ if (this.syncservice && this.sync_mode !== 'none') {
+ this.syncservice.flush();
+ this.syncservice.stop_timer();
+ }
+ });
+ };
+ Trace.prototype = Trace_prototype;
+
+ var Obsel_prototype = {
+ /* The following attributes are here for documentation
+ * purposes. They MUST be defined in the constructor
+ * function. */
+ trace: undefined,
+ type: undefined,
+ begin: undefined,
+ end: undefined,
+ subject: undefined,
+ /* Dictionary indexed by ObselType URIs */
+ attributes: {},
+
+ /* Method definitions */
+ get_trace: function() {
+ return this.trace;
+ },
+
+ get_obsel_type: function() {
+ return this.type;
+ },
+ get_begin: function() {
+ return this.begin;
+ },
+ get_end: function() {
+ return this.end;
+ },
+ get_subject: function() {
+ return this.subject;
+ },
+
+ list_attribute_types: function() {
+ var result = [];
+ for (var prop in this.attributes) {
+ if (this.attributes.hasOwnProperty(prop))
+ result.push(prop);
+ }
+ /* FIXME: we return URIs here instead of AttributeType elements */
+ return result;
+ },
+
+ list_relation_types: function() {
+ /* FIXME: not implemented yet */
+ },
+
+ list_related_obsels: function (rt) {
+ /* FIXME: not implemented yet */
+ },
+ list_inverse_relation_types: function () {
+ /* FIXME: not implemented yet */
+ },
+ list_relating_obsels: function (rt) {
+ /* FIXME: not implemented yet */
+ },
+ /*
+ * Return the value of the given attribute type for this obsel
+ */
+ get_attribute_value: function(at) {
+ if (typeof at === "string")
+ /* It is a URI */
+ return this.attributes[at];
+ else
+ /* FIXME: check that at is instance of AttributeType */
+ return this.attributes[at.uri];
+ },
+
+
+ /* obsel modification (trace amendment) */
+
+ set_attribute_value: function(at, value) {
+ if (typeof at === "string")
+ /* It is a URI */
+ this.attributes[at] = value;
+ /* FIXME: check that at is instance of AttributeType */
+ else
+ this.attributes[at.uri] = value;
+ },
+
+ del_attribute_value: function(at) {
+ if (typeof at === "string")
+ /* It is a URI */
+ delete this.attributes[at];
+ /* FIXME: check that at is instance of AttributeType */
+ else
+ delete this.attributes[at.uri];
+ },
+
+ add_related_obsel: function(rt, value) {
+ /* FIXME: not implemented yet */
+ },
+
+ del_related_obsel: function(rt, value) {
+ /* FIXME: not implemented yet */
+ },
+
+ /*
+ * Return a JSON representation of the obsel
+ */
+ toJSON: function() {
+ var r = {
+ "@id": this.id,
+ "@type": this.type,
+ "begin": this.begin,
+ "end": this.end,
+ "subject": this.subject
+ };
+ for (var prop in this.attributes) {
+ if (this.attributes.hasOwnProperty(prop))
+ r[prop] = this.attributes[prop];
+ }
+ return r;
+ },
+
+ /*
+ * Return a compact JSON representation of the obsel.
+ * Use predefined + custom shorthands for types/properties
+ */
+ toCompactJSON: function() {
+ var r = {
+ "@t": (this.trace.shorthands.hasOwnProperty(this.type) ? this.trace.shorthands[this.type] : this.type),
+ "@b": this.begin,
+ };
+ // Transmit subject only if different from default_subject
+ if (this.subject !== this.trace.default_subject)
+ r["@s"] = this.subject;
+
+ // Store duration (to save some bytes) and only if it is non-null
+ if (this.begin !== this.end)
+ r["@d"] = this.end - this.begin;
+
+ // Store id only if != ""
+ if (this.id !== "")
+ r["@i"] = this.id;
+
+ for (var prop in this.attributes) {
+ if (this.attributes.hasOwnProperty(prop))
+ {
+ var v = this.attributes[prop];
+ r[prop] = this.trace.shorthands.hasOwnProperty(v) ? this.trace.shorthands[v] : v;
+ }
+ }
+ return r;
+ },
+
+ toJSONstring: function() {
+ return JSON.stringify(this.toJSON());
+ }
+ };
+
+ var Obsel = function(type, begin, end, subject, attributes) {
+ this.trace = undefined;
+ this.uri = "";
+ this.id = "";
+ this.type = type;
+ this.begin = begin;
+ this.end = end;
+ this.subject = subject;
+ /* Is the obsel synched with the server ? */
+ this.sync_status = false;
+ /* Dictionary indexed by ObselType URIs */
+ this.attributes = {};
+ };
+ Obsel.prototype = Obsel_prototype;
+
+ var TraceManager_prototype = {
+ traces: [],
+ /*
+ * Return the trace with id name
+ * If it was not registered, return undefined.
+ */
+ get_trace: function(name) {
+ return this.traces[name];
+ },
+
+ /*
+ * Explicitly create and initialize a new trace with the given name.
+ * The optional uri parameter allows to initialize the trace URI.
+ *
+ * If another existed with the same name before, then it is replaced by a new one.
+ */
+ init_trace: function(name, params)
+ {
+ if (window.console) window.console.log("init_trace", params);
+ url = params.url ? params.url : "";
+ requestmode = params.requestmode ? params.requestmode : "POST";
+ syncmode = params.syncmode ? params.syncmode : "none";
+ default_subject = params.default_subject ? params.default_subject : "default";
+ var t = new Trace(url, requestmode);
+ t.set_default_subject(default_subject);
+ t.set_sync_mode(syncmode);
+ this.traces[name] = t;
+ return t;
+ }
+ };
+
+ var TraceManager = function() {
+ this.traces = {};
+ };
+ TraceManager.prototype = TraceManager_prototype;
+
+ var tracemanager = new TraceManager();
+ return tracemanager;
+ })(jQuery);