Modified: 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/backbone.js
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/backbone.js?rev=1294730&r1=1294729&r2=1294730&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/backbone.js
 (original)
+++ 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/backbone.js
 Tue Feb 28 16:56:02 2012
@@ -1,4 +1,4 @@
-//     Backbone.js 0.3.3
+//     Backbone.js 0.5.3
 //     (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
 //     Backbone may be freely distributed under the MIT license.
 //     For all details and documentation:
@@ -9,26 +9,39 @@
   // Initial Setup
   // -------------
 
+  // Save a reference to the global object.
+  var root = this;
+
+  // Save the previous value of the `Backbone` variable.
+  var previousBackbone = root.Backbone;
+
   // The top-level namespace. All public Backbone classes and modules will
   // be attached to this. Exported for both CommonJS and the browser.
   var Backbone;
   if (typeof exports !== 'undefined') {
     Backbone = exports;
   } else {
-    Backbone = this.Backbone = {};
+    Backbone = root.Backbone = {};
   }
 
   // Current version of the library. Keep in sync with `package.json`.
-  Backbone.VERSION = '0.3.3';
+  Backbone.VERSION = '0.5.3';
 
   // Require Underscore, if we're on the server, and it's not already present.
-  var _ = this._;
-  if (!_ && (typeof require !== 'undefined')) _ = require("underscore")._;
+  var _ = root._;
+  if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
 
-  // For Backbone's purposes, either jQuery or Zepto owns the `$` variable.
-  var $ = this.jQuery || this.Zepto;
+  // For Backbone's purposes, jQuery or Zepto owns the `$` variable.
+  var $ = root.jQuery || root.Zepto;
 
-  // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this 
option will
+  // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+  // to its previous owner. Returns a reference to this Backbone object.
+  Backbone.noConflict = function() {
+    root.Backbone = previousBackbone;
+    return this;
+  };
+
+  // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option 
will
   // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
   // `X-Http-Method-Override` header.
   Backbone.emulateHTTP = false;
@@ -55,10 +68,10 @@
 
     // Bind an event, specified by a string name, `ev`, to a `callback` 
function.
     // Passing `"all"` will bind the callback to all events fired.
-    bind : function(ev, callback) {
+    bind : function(ev, callback, context) {
       var calls = this._callbacks || (this._callbacks = {});
-      var list  = this._callbacks[ev] || (this._callbacks[ev] = []);
-      list.push(callback);
+      var list  = calls[ev] || (calls[ev] = []);
+      list.push([callback, context]);
       return this;
     },
 
@@ -76,8 +89,8 @@
           var list = calls[ev];
           if (!list) return this;
           for (var i = 0, l = list.length; i < l; i++) {
-            if (callback === list[i]) {
-              list.splice(i, 1);
+            if (list[i] && callback === list[i][0]) {
+              list[i] = null;
               break;
             }
           }
@@ -89,17 +102,21 @@
     // Trigger an event, firing all bound callbacks. Callbacks are passed the
     // same arguments as `trigger` is, apart from the event name.
     // Listening for `"all"` passes the true event name as the first argument.
-    trigger : function(ev) {
-      var list, calls, i, l;
+    trigger : function(eventName) {
+      var list, calls, ev, callback, args;
+      var both = 2;
       if (!(calls = this._callbacks)) return this;
-      if (list = calls[ev]) {
-        for (i = 0, l = list.length; i < l; i++) {
-          list[i].apply(this, Array.prototype.slice.call(arguments, 1));
-        }
-      }
-      if (list = calls['all']) {
-        for (i = 0, l = list.length; i < l; i++) {
-          list[i].apply(this, arguments);
+      while (both--) {
+        ev = both ? eventName : 'all';
+        if (list = calls[ev]) {
+          for (var i = 0, l = list.length; i < l; i++) {
+            if (!(callback = list[i])) {
+              list.splice(i, 1); i--; l--;
+            } else {
+              args = both ? Array.prototype.slice.call(arguments, 1) : 
arguments;
+              callback[0].apply(callback[1] || this, args);
+            }
+          }
         }
       }
       return this;
@@ -113,12 +130,17 @@
   // Create a new model, with defined attributes. A client id (`cid`)
   // is automatically generated and assigned for you.
   Backbone.Model = function(attributes, options) {
+    var defaults;
     attributes || (attributes = {});
-    if (this.defaults) attributes = _.extend({}, this.defaults, attributes);
+    if (defaults = this.defaults) {
+      if (_.isFunction(defaults)) defaults = defaults.call(this);
+      attributes = _.extend({}, defaults, attributes);
+    }
     this.attributes = {};
     this._escapedAttributes = {};
     this.cid = _.uniqueId('c');
     this.set(attributes, {silent : true});
+    this._changed = false;
     this._previousAttributes = _.clone(this.attributes);
     if (options && options.collection) this.collection = options.collection;
     this.initialize(attributes, options);
@@ -134,6 +156,10 @@
     // Has the item been changed since the last `"change"` event?
     _changed : false,
 
+    // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+    // CouchDB users may want to set this to `"_id"`.
+    idAttribute : 'id',
+
     // Initialize is an empty function by default. Override it with your own
     // initialization logic.
     initialize : function(){},
@@ -153,7 +179,13 @@
       var html;
       if (html = this._escapedAttributes[attr]) return html;
       var val = this.attributes[attr];
-      return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : 
val);
+      return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' 
+ val);
+    },
+
+    // Returns `true` if the attribute contains a value that is not null
+    // or undefined.
+    has : function(attr) {
+      return this.attributes[attr] != null;
     },
 
     // Set a hash of model attributes on the object, firing `"change"` unless 
you
@@ -170,7 +202,11 @@
       if (!options.silent && this.validate && !this._performValidation(attrs, 
options)) return false;
 
       // Check for changes of `id`.
-      if ('id' in attrs) this.id = attrs.id;
+      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+      // We're about to start triggering change events.
+      var alreadyChanging = this._changing;
+      this._changing = true;
 
       // Update attributes.
       for (var attr in attrs) {
@@ -178,21 +214,21 @@
         if (!_.isEqual(now[attr], val)) {
           now[attr] = val;
           delete escaped[attr];
-          if (!options.silent) {
-            this._changed = true;
-            this.trigger('change:' + attr, this, val, options);
-          }
+          this._changed = true;
+          if (!options.silent) this.trigger('change:' + attr, this, val, 
options);
         }
       }
 
       // Fire the `"change"` event, if the model has been changed.
-      if (!options.silent && this._changed) this.change(options);
+      if (!alreadyChanging && !options.silent && this._changed) 
this.change(options);
+      this._changing = false;
       return this;
     },
 
     // Remove an attribute from the model, firing `"change"` unless you choose
-    // to silence it.
+    // to silence it. `unset` is a noop if the attribute doesn't exist.
     unset : function(attr, options) {
+      if (!(attr in this.attributes)) return this;
       options || (options = {});
       var value = this.attributes[attr];
 
@@ -204,8 +240,9 @@
       // Remove the attribute.
       delete this.attributes[attr];
       delete this._escapedAttributes[attr];
+      if (attr == this.idAttribute) delete this.id;
+      this._changed = true;
       if (!options.silent) {
-        this._changed = true;
         this.trigger('change:' + attr, this, void 0, options);
         this.change(options);
       }
@@ -216,6 +253,7 @@
     // to silence it.
     clear : function(options) {
       options || (options = {});
+      var attr;
       var old = this.attributes;
 
       // Run validation.
@@ -225,8 +263,8 @@
 
       this.attributes = {};
       this._escapedAttributes = {};
+      this._changed = true;
       if (!options.silent) {
-        this._changed = true;
         for (attr in old) {
           this.trigger('change:' + attr, this, void 0, options);
         }
@@ -241,13 +279,13 @@
     fetch : function(options) {
       options || (options = {});
       var model = this;
-      var success = function(resp) {
-        if (!model.set(model.parse(resp), options)) return false;
-        if (options.success) options.success(model, resp);
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        if (!model.set(model.parse(resp, xhr), options)) return false;
+        if (success) success(model, resp);
       };
-      var error = wrapError(options.error, model, options);
-      (this.sync || Backbone.sync)('read', this, success, error);
-      return this;
+      options.error = wrapError(options.error, model, options);
+      return (this.sync || Backbone.sync).call(this, 'read', this, options);
     },
 
     // Set a hash of model attributes, and sync the model to the server.
@@ -257,42 +295,43 @@
       options || (options = {});
       if (attrs && !this.set(attrs, options)) return false;
       var model = this;
-      var success = function(resp) {
-        if (!model.set(model.parse(resp), options)) return false;
-        if (options.success) options.success(model, resp);
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        if (!model.set(model.parse(resp, xhr), options)) return false;
+        if (success) success(model, resp, xhr);
       };
-      var error = wrapError(options.error, model, options);
+      options.error = wrapError(options.error, model, options);
       var method = this.isNew() ? 'create' : 'update';
-      (this.sync || Backbone.sync)(method, this, success, error);
-      return this;
+      return (this.sync || Backbone.sync).call(this, method, this, options);
     },
 
-    // Destroy this model on the server. Upon success, the model is removed
+    // Destroy this model on the server if it was already persisted. Upon 
success, the model is removed
     // from its collection, if it has one.
     destroy : function(options) {
       options || (options = {});
+      if (this.isNew()) return this.trigger('destroy', this, this.collection, 
options);
       var model = this;
-      var success = function(resp) {
-        if (model.collection) model.collection.remove(model);
-        if (options.success) options.success(model, resp);
+      var success = options.success;
+      options.success = function(resp) {
+        model.trigger('destroy', model, model.collection, options);
+        if (success) success(model, resp);
       };
-      var error = wrapError(options.error, model, options);
-      (this.sync || Backbone.sync)('delete', this, success, error);
-      return this;
+      options.error = wrapError(options.error, model, options);
+      return (this.sync || Backbone.sync).call(this, 'delete', this, options);
     },
 
     // Default URL for the model's representation on the server -- if you're
     // using Backbone's restful methods, override this to change the endpoint
     // that will be called.
     url : function() {
-      var base = getUrl(this.collection);
+      var base = getUrl(this.collection) || this.urlRoot || urlError();
       if (this.isNew()) return base;
-      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
+      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + 
encodeURIComponent(this.id);
     },
 
     // **parse** converts a response into the hash of attributes to be `set` on
     // the model. The default implementation is just to pass the response 
along.
-    parse : function(resp) {
+    parse : function(resp, xhr) {
       return resp;
     },
 
@@ -301,10 +340,9 @@
       return new this.constructor(this);
     },
 
-    // A model is new if it has never been saved to the server, and has a 
negative
-    // ID.
+    // A model is new if it has never been saved to the server, and lacks an 
id.
     isNew : function() {
-      return !this.id;
+      return this.id == null;
     },
 
     // Call this method to manually fire a `change` event for this model.
@@ -359,7 +397,7 @@
       var error = this.validate(attrs);
       if (error) {
         if (options.error) {
-          options.error(this, error);
+          options.error(this, error, options);
         } else {
           this.trigger('error', this, error, options);
         }
@@ -378,14 +416,11 @@
   // its models in sort order, as they're added and removed.
   Backbone.Collection = function(models, options) {
     options || (options = {});
-    if (options.comparator) {
-      this.comparator = options.comparator;
-      delete options.comparator;
-    }
-    this._boundOnModelEvent = _.bind(this._onModelEvent, this);
+    if (options.comparator) this.comparator = options.comparator;
+    _.bindAll(this, '_onModelEvent', '_removeReference');
     this._reset();
-    if (models) this.refresh(models, {silent: true});
-    this.initialize(models, options);
+    if (models) this.reset(models, {silent: true});
+    this.initialize.apply(this, arguments);
   };
 
   // Define the Collection's inheritable methods.
@@ -453,7 +488,7 @@
       options || (options = {});
       if (!this.comparator) throw new Error('Cannot sort a set without a 
comparator');
       this.models = this.sortBy(this.comparator);
-      if (!options.silent) this.trigger('refresh', this, options);
+      if (!options.silent) this.trigger('reset', this, options);
       return this;
     },
 
@@ -463,51 +498,53 @@
     },
 
     // When you have more items than you want to add or remove individually,
-    // you can refresh the entire set with a new list of models, without firing
-    // any `added` or `removed` events. Fires `refresh` when finished.
-    refresh : function(models, options) {
+    // you can reset the entire set with a new list of models, without firing
+    // any `added` or `removed` events. Fires `reset` when finished.
+    reset : function(models, options) {
       models  || (models = []);
       options || (options = {});
+      this.each(this._removeReference);
       this._reset();
       this.add(models, {silent: true});
-      if (!options.silent) this.trigger('refresh', this, options);
+      if (!options.silent) this.trigger('reset', this, options);
       return this;
     },
 
-    // Fetch the default set of models for this collection, refreshing the
-    // collection when they arrive.
+    // Fetch the default set of models for this collection, resetting the
+    // collection when they arrive. If `add: true` is passed, appends the
+    // models to the collection instead of resetting.
     fetch : function(options) {
       options || (options = {});
       var collection = this;
-      var success = function(resp) {
-        collection.refresh(collection.parse(resp));
-        if (options.success) options.success(collection, resp);
+      var success = options.success;
+      options.success = function(resp, status, xhr) {
+        collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), 
options);
+        if (success) success(collection, resp);
       };
-      var error = wrapError(options.error, collection, options);
-      (this.sync || Backbone.sync)('read', this, success, error);
-      return this;
+      options.error = wrapError(options.error, collection, options);
+      return (this.sync || Backbone.sync).call(this, 'read', this, options);
     },
 
     // Create a new instance of a model in this collection. After the model
     // has been created on the server, it will be added to the collection.
+    // Returns the model, or 'false' if validation on a new model fails.
     create : function(model, options) {
       var coll = this;
       options || (options = {});
-      if (!(model instanceof Backbone.Model)) {
-        model = new this.model(model, {collection: coll});
-      } else {
-        model.collection = coll;
-      }
-      var success = function(nextModel, resp) {
-        coll.add(nextModel);
-        if (options.success) options.success(nextModel, resp);
+      model = this._prepareModel(model, options);
+      if (!model) return false;
+      var success = options.success;
+      options.success = function(nextModel, resp, xhr) {
+        coll.add(nextModel, options);
+        if (success) success(nextModel, resp, xhr);
       };
-      return model.save(null, {success : success, error : options.error});
+      model.save(null, options);
+      return model;
     },
 
     // **parse** converts a response into a list of models to be added to the
     // collection. The default implementation is just to pass it through.
-    parse : function(resp) {
+    parse : function(resp, xhr) {
       return resp;
     },
 
@@ -518,7 +555,7 @@
       return _(this.models).chain();
     },
 
-    // Reset all internal state. Called when the collection is refreshed.
+    // Reset all internal state. Called when the collection is reset.
     _reset : function(options) {
       this.length = 0;
       this.models = [];
@@ -526,21 +563,34 @@
       this._byCid = {};
     },
 
+    // Prepare a model to be added to this collection
+    _prepareModel: function(model, options) {
+      if (!(model instanceof Backbone.Model)) {
+        var attrs = model;
+        model = new this.model(attrs, {collection: this});
+        if (model.validate && !model._performValidation(attrs, options)) model 
= false;
+      } else if (!model.collection) {
+        model.collection = this;
+      }
+      return model;
+    },
+
     // Internal implementation of adding a single model to the set, updating
     // hash indexes for `id` and `cid` lookups.
+    // Returns the model, or 'false' if validation on a new model fails.
     _add : function(model, options) {
       options || (options = {});
-      if (!(model instanceof Backbone.Model)) {
-        model = new this.model(model, {collection: this});
-      }
+      model = this._prepareModel(model, options);
+      if (!model) return false;
       var already = this.getByCid(model);
       if (already) throw new Error(["Can't add the same model to a set twice", 
already.id]);
       this._byId[model.id] = model;
       this._byCid[model.cid] = model;
-      model.collection = this;
-      var index = this.comparator ? this.sortedIndex(model, this.comparator) : 
this.length;
+      var index = options.at != null ? options.at :
+                  this.comparator ? this.sortedIndex(model, this.comparator) :
+                  this.length;
       this.models.splice(index, 0, model);
-      model.bind('all', this._boundOnModelEvent);
+      model.bind('all', this._onModelEvent);
       this.length++;
       if (!options.silent) model.trigger('add', model, this, options);
       return model;
@@ -554,20 +604,32 @@
       if (!model) return null;
       delete this._byId[model.id];
       delete this._byCid[model.cid];
-      delete model.collection;
       this.models.splice(this.indexOf(model), 1);
       this.length--;
       if (!options.silent) model.trigger('remove', model, this, options);
-      model.unbind('all', this._boundOnModelEvent);
+      this._removeReference(model);
       return model;
     },
 
+    // Internal method to remove a model's ties to a collection.
+    _removeReference : function(model) {
+      if (this == model.collection) {
+        delete model.collection;
+      }
+      model.unbind('all', this._onModelEvent);
+    },
+
     // Internal method called every time a model in the set fires an event.
     // Sets need to update their indexes when models change ids. All other
-    // events simply proxy through.
-    _onModelEvent : function(ev, model) {
-      if (ev === 'change:id') {
-        delete this._byId[model.previous('id')];
+    // events simply proxy through. "add" and "remove" events that originate
+    // in other collections are ignored.
+    _onModelEvent : function(ev, model, collection, options) {
+      if ((ev == 'add' || ev == 'remove') && collection != this) return;
+      if (ev == 'destroy') {
+        this._remove(model, options);
+      }
+      if (model && ev === 'change:' + model.idAttribute) {
+        delete this._byId[model.previous(model.idAttribute)];
         this._byId[model.id] = model;
       }
       this.trigger.apply(this, arguments);
@@ -578,8 +640,8 @@
   // Underscore methods that we want to implement on the Collection.
   var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 
'detect',
     'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
-    'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
-    'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
+    'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 
'size',
+    'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 
'groupBy'];
 
   // Mix in each Underscore method as a proxy to `Collection#models`.
   _.each(methods, function(method) {
@@ -588,25 +650,26 @@
     };
   });
 
-  // Backbone.Controller
+  // Backbone.Router
   // -------------------
 
-  // Controllers map faux-URLs to actions, and fire events when routes are
+  // Routers map faux-URLs to actions, and fire events when routes are
   // matched. Creating a new one sets its `routes` hash, if not set statically.
-  Backbone.Controller = function(options) {
+  Backbone.Router = function(options) {
     options || (options = {});
     if (options.routes) this.routes = options.routes;
     this._bindRoutes();
-    this.initialize(options);
+    this.initialize.apply(this, arguments);
   };
 
   // Cached regular expressions for matching named param parts and splatted
   // parts of route strings.
-  var namedParam = /:([\w\d]+)/g;
-  var splatParam = /\*([\w\d]+)/g;
+  var namedParam    = /:([\w\d]+)/g;
+  var splatParam    = /\*([\w\d]+)/g;
+  var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
 
-  // Set up all inheritable **Backbone.Controller** properties and methods.
-  _.extend(Backbone.Controller.prototype, Backbone.Events, {
+  // Set up all inheritable **Backbone.Router** properties and methods.
+  _.extend(Backbone.Router.prototype, Backbone.Events, {
 
     // Initialize is an empty function by default. Override it with your own
     // initialization logic.
@@ -628,25 +691,31 @@
       }, this));
     },
 
-    // Simple proxy to `Backbone.history` to save a fragment into the history,
-    // without triggering routes.
-    saveLocation : function(fragment) {
-      Backbone.history.saveLocation(fragment);
+    // Simple proxy to `Backbone.history` to save a fragment into the history.
+    navigate : function(fragment, triggerRoute) {
+      Backbone.history.navigate(fragment, triggerRoute);
     },
 
-    // Bind all defined routes to `Backbone.history`.
+    // Bind all defined routes to `Backbone.history`. We have to reverse the
+    // order of the routes here to support behavior where the most general
+    // routes can be defined at the bottom of the route map.
     _bindRoutes : function() {
       if (!this.routes) return;
+      var routes = [];
       for (var route in this.routes) {
-        var name = this.routes[route];
-        this.route(route, name, this[name]);
+        routes.unshift([route, this.routes[route]]);
+      }
+      for (var i = 0, l = routes.length; i < l; i++) {
+        this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
       }
     },
 
     // Convert a route string into a regular expression, suitable for matching
-    // against the current location fragment.
+    // against the current location hash.
     _routeToRegExp : function(route) {
-      route = route.replace(namedParam, "([^\/]*)").replace(splatParam, 
"(.*?)");
+      route = route.replace(escapeRegExp, "\\$&")
+                   .replace(namedParam, "([^\/]*)")
+                   .replace(splatParam, "(.*?)");
       return new RegExp('^' + route + '$');
     },
 
@@ -661,17 +730,22 @@
   // Backbone.History
   // ----------------
 
-  // Handles cross-browser history management, based on URL hashes. If the
+  // Handles cross-browser history management, based on URL fragments. If the
   // browser does not support `onhashchange`, falls back to polling.
   Backbone.History = function() {
     this.handlers = [];
-    this.fragment = this.getFragment();
     _.bindAll(this, 'checkUrl');
   };
 
   // Cached regex for cleaning hashes.
   var hashStrip = /^#*/;
 
+  // Cached regex for detecting MSIE.
+  var isExplorer = /msie [\w.]+/;
+
+  // Has the history handling already been started?
+  var historyStarted = false;
+
   // Set up all inheritable **Backbone.History** properties and methods.
   _.extend(Backbone.History.prototype, {
 
@@ -679,53 +753,92 @@
     // twenty times a second.
     interval: 50,
 
-    // Get the cross-browser normalized URL fragment.
-    getFragment : function(loc) {
-      return (loc || window.location).hash.replace(hashStrip, '');
+    // Get the cross-browser normalized URL fragment, either from the URL,
+    // the hash, or the override.
+    getFragment : function(fragment, forcePushState) {
+      if (fragment == null) {
+        if (this._hasPushState || forcePushState) {
+          fragment = window.location.pathname;
+          var search = window.location.search;
+          if (search) fragment += search;
+          if (fragment.indexOf(this.options.root) == 0) fragment = 
fragment.substr(this.options.root.length);
+        } else {
+          fragment = window.location.hash;
+        }
+      }
+      return decodeURIComponent(fragment.replace(hashStrip, ''));
     },
 
     // Start the hash change handling, returning `true` if the current URL 
matches
     // an existing route, and `false` otherwise.
-    start : function() {
-      var docMode = document.documentMode;
-      var oldIE = ($.browser.msie && (!docMode || docMode <= 7));
+    start : function(options) {
+
+      // Figure out the initial configuration. Do we need an iframe?
+      // Is pushState desired ... is it available?
+      if (historyStarted) throw new Error("Backbone.history has already been 
started");
+      this.options          = _.extend({}, {root: '/'}, this.options, options);
+      this._wantsPushState  = !!this.options.pushState;
+      this._hasPushState    = !!(this.options.pushState && window.history && 
window.history.pushState);
+      var fragment          = this.getFragment();
+      var docMode           = document.documentMode;
+      var oldIE             = 
(isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 
7));
       if (oldIE) {
         this.iframe = $('<iframe src="javascript:0" tabindex="-1" 
/>').hide().appendTo('body')[0].contentWindow;
+        this.navigate(fragment);
       }
-      if ('onhashchange' in window && !oldIE) {
+
+      // Depending on whether we're using pushState or hashes, and whether
+      // 'onhashchange' is supported, determine how we check the URL state.
+      if (this._hasPushState) {
+        $(window).bind('popstate', this.checkUrl);
+      } else if ('onhashchange' in window && !oldIE) {
         $(window).bind('hashchange', this.checkUrl);
       } else {
         setInterval(this.checkUrl, this.interval);
       }
-      return this.loadUrl();
+
+      // Determine if we need to change the base url, for a pushState link
+      // opened by a non-pushState browser.
+      this.fragment = fragment;
+      historyStarted = true;
+      var loc = window.location;
+      var atRoot  = loc.pathname == this.options.root;
+      if (this._wantsPushState && !this._hasPushState && !atRoot) {
+        this.fragment = this.getFragment(null, true);
+        window.location.replace(this.options.root + '#' + this.fragment);
+        // Return immediately as browser will do redirect to new url
+        return true;
+      } else if (this._wantsPushState && this._hasPushState && atRoot && 
loc.hash) {
+        this.fragment = loc.hash.replace(hashStrip, '');
+        window.history.replaceState({}, document.title, loc.protocol + '//' + 
loc.host + this.options.root + this.fragment);
+      }
+
+      if (!this.options.silent) {
+        return this.loadUrl();
+      }
     },
 
-    // Add a route to be tested when the hash changes. Routes are matched in 
the
-    // order they are added.
+    // Add a route to be tested when the fragment changes. Routes added later 
may
+    // override previous routes.
     route : function(route, callback) {
-      this.handlers.push({route : route, callback : callback});
+      this.handlers.unshift({route : route, callback : callback});
     },
 
     // Checks the current URL to see if it has changed, and if it has,
     // calls `loadUrl`, normalizing across the hidden iframe.
-    checkUrl : function() {
+    checkUrl : function(e) {
       var current = this.getFragment();
-      if (current == this.fragment && this.iframe) {
-        current = this.getFragment(this.iframe.location);
-      }
-      if (current == this.fragment ||
-          current == decodeURIComponent(this.fragment)) return false;
-      if (this.iframe) {
-        window.location.hash = this.iframe.location.hash = current;
-      }
-      this.loadUrl();
+      if (current == this.fragment && this.iframe) current = 
this.getFragment(this.iframe.location.hash);
+      if (current == this.fragment || current == 
decodeURIComponent(this.fragment)) return false;
+      if (this.iframe) this.navigate(current);
+      this.loadUrl() || this.loadUrl(window.location.hash);
     },
 
     // Attempt to load the current URL fragment. If a route succeeds with a
     // match, returns `true`. If no defined routes matches the fragment,
     // returns `false`.
-    loadUrl : function() {
-      var fragment = this.fragment = this.getFragment();
+    loadUrl : function(fragmentOverride) {
+      var fragment = this.fragment = this.getFragment(fragmentOverride);
       var matched = _.any(this.handlers, function(handler) {
         if (handler.route.test(fragment)) {
           handler.callback(fragment);
@@ -738,14 +851,22 @@
     // Save a fragment into the hash history. You are responsible for properly
     // URL-encoding the fragment in advance. This does not trigger
     // a `hashchange` event.
-    saveLocation : function(fragment) {
-      fragment = (fragment || '').replace(hashStrip, '');
-      if (this.fragment == fragment) return;
-      window.location.hash = this.fragment = fragment;
-      if (this.iframe && (fragment != this.getFragment(this.iframe.location))) 
{
-        this.iframe.document.open().close();
-        this.iframe.location.hash = fragment;
+    navigate : function(fragment, triggerRoute) {
+      var frag = (fragment || '').replace(hashStrip, '');
+      if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) 
return;
+      if (this._hasPushState) {
+        var loc = window.location;
+        if (frag.indexOf(this.options.root) != 0) frag = this.options.root + 
frag;
+        this.fragment = frag;
+        window.history.pushState({}, document.title, loc.protocol + '//' + 
loc.host + frag);
+      } else {
+        window.location.hash = this.fragment = frag;
+        if (this.iframe && (frag != 
this.getFragment(this.iframe.location.hash))) {
+          this.iframe.document.open().close();
+          this.iframe.location.hash = frag;
+        }
       }
+      if (triggerRoute) this.loadUrl(fragment);
     }
 
   });
@@ -756,10 +877,11 @@
   // Creating a Backbone.View creates its initial element outside of the DOM,
   // if an existing element is not provided...
   Backbone.View = function(options) {
+    this.cid = _.uniqueId('view');
     this._configure(options || {});
     this._ensureElement();
     this.delegateEvents();
-    this.initialize(options);
+    this.initialize.apply(this, arguments);
   };
 
   // Element lookup, scoped to DOM elements within the current view.
@@ -770,7 +892,10 @@
   };
 
   // Cached regex to split keys for `delegate`.
-  var eventSplitter = /^(\w+)\s*(.*)$/;
+  var eventSplitter = /^(\S+)\s*(.*)$/;
+
+  // List of view options to be merged as properties.
+  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 
'className', 'tagName'];
 
   // Set up all inheritable **Backbone.View** properties and methods.
   _.extend(Backbone.View.prototype, Backbone.Events, {
@@ -827,12 +952,15 @@
     // not `change`, `submit`, and `reset` in Internet Explorer.
     delegateEvents : function(events) {
       if (!(events || (events = this.events))) return;
-      $(this.el).unbind();
+      if (_.isFunction(events)) events = events.call(this);
+      $(this.el).unbind('.delegateEvents' + this.cid);
       for (var key in events) {
-        var methodName = events[key];
+        var method = this[events[key]];
+        if (!method) throw new Error('Event "' + events[key] + '" does not 
exist');
         var match = key.match(eventSplitter);
         var eventName = match[1], selector = match[2];
-        var method = _.bind(this[methodName], this);
+        method = _.bind(method, this);
+        eventName += '.delegateEvents' + this.cid;
         if (selector === '') {
           $(this.el).bind(eventName, method);
         } else {
@@ -846,22 +974,26 @@
     // attached directly to the view.
     _configure : function(options) {
       if (this.options) options = _.extend({}, this.options, options);
-      if (options.model)      this.model      = options.model;
-      if (options.collection) this.collection = options.collection;
-      if (options.el)         this.el         = options.el;
-      if (options.id)         this.id         = options.id;
-      if (options.className)  this.className  = options.className;
-      if (options.tagName)    this.tagName    = options.tagName;
+      for (var i = 0, l = viewOptions.length; i < l; i++) {
+        var attr = viewOptions[i];
+        if (options[attr]) this[attr] = options[attr];
+      }
       this.options = options;
     },
 
     // Ensure that the View has a DOM element to render into.
+    // If `this.el` is a string, pass it through `$()`, take the first
+    // matching element, and re-assign it to `el`. Otherwise, create
+    // an element from the `id`, `className` and `tagName` proeprties.
     _ensureElement : function() {
-      if (this.el) return;
-      var attrs = {};
-      if (this.id) attrs.id = this.id;
-      if (this.className) attrs["class"] = this.className;
-      this.el = this.make(this.tagName, attrs);
+      if (!this.el) {
+        var attrs = this.attributes || {};
+        if (this.id) attrs.id = this.id;
+        if (this.className) attrs['class'] = this.className;
+        this.el = this.make(this.tagName, attrs);
+      } else if (_.isString(this.el)) {
+        this.el = $(this.el).get(0);
+      }
     }
 
   });
@@ -869,13 +1001,13 @@
   // The self-propagating extend function that Backbone classes use.
   var extend = function (protoProps, classProps) {
     var child = inherits(this, protoProps, classProps);
-    child.extend = extend;
+    child.extend = this.extend;
     return child;
   };
 
   // Set up inheritance for the model, collection, and view.
   Backbone.Model.extend = Backbone.Collection.extend =
-    Backbone.Controller.extend = Backbone.View.extend = extend;
+    Backbone.Router.extend = Backbone.View.extend = extend;
 
   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
   var methodMap = {
@@ -903,28 +1035,30 @@
   // `application/json` with the model in a param named `model`.
   // Useful when interfacing with server-side languages like **PHP** that make
   // it difficult to read the body of `PUT` requests.
-  Backbone.sync = function(method, model, success, error) {
+  Backbone.sync = function(method, model, options) {
     var type = methodMap[method];
-    var modelJSON = (method === 'create' || method === 'update') ?
-                    JSON.stringify(model.toJSON()) : null;
 
     // Default JSON-request options.
-    var params = {
-      url:          getUrl(model),
+    var params = _.extend({
       type:         type,
-      contentType:  'application/json',
-      data:         modelJSON,
-      dataType:     'json',
-      processData:  false,
-      success:      success,
-      error:        error
-    };
+      dataType:     'json'
+    }, options);
+
+    // Ensure that we have a URL.
+    if (!params.url) {
+      params.url = getUrl(model) || urlError();
+    }
+
+    // Ensure that we have the appropriate request data.
+    if (!params.data && model && (method == 'create' || method == 'update')) {
+      params.contentType = 'application/json';
+      params.data = JSON.stringify(model.toJSON());
+    }
 
     // For older servers, emulate JSON by encoding the request into an 
HTML-form.
     if (Backbone.emulateJSON) {
       params.contentType = 'application/x-www-form-urlencoded';
-      params.processData = true;
-      params.data        = modelJSON ? {model : modelJSON} : {};
+      params.data        = params.data ? {model : params.data} : {};
     }
 
     // For older servers, emulate HTTP by mimicking the HTTP method with 
`_method`
@@ -934,13 +1068,18 @@
         if (Backbone.emulateJSON) params.data._method = type;
         params.type = 'POST';
         params.beforeSend = function(xhr) {
-          xhr.setRequestHeader("X-HTTP-Method-Override", type);
+          xhr.setRequestHeader('X-HTTP-Method-Override', type);
         };
       }
     }
 
+    // Don't process data on a non-GET request.
+    if (params.type !== 'GET' && !Backbone.emulateJSON) {
+      params.processData = false;
+    }
+
     // Make the request.
-    $.ajax(params);
+    return $.ajax(params);
   };
 
   // Helpers
@@ -964,6 +1103,9 @@
       child = function(){ return parent.apply(this, arguments); };
     }
 
+    // Inherit class (static) properties from parent.
+    _.extend(child, parent);
+
     // Set the prototype chain to inherit from `parent`, without calling
     // `parent`'s constructor function.
     ctor.prototype = parent.prototype;
@@ -976,7 +1118,7 @@
     // Add static properties to the constructor function, if supplied.
     if (staticProps) _.extend(child, staticProps);
 
-    // Correctly set child's `prototype.constructor`, for `instanceof`.
+    // Correctly set child's `prototype.constructor`.
     child.prototype.constructor = child;
 
     // Set a convenience property in case the parent's prototype is needed 
later.
@@ -988,15 +1130,20 @@
   // Helper function to get a URL from a Model or Collection as a property
   // or as a function.
   var getUrl = function(object) {
-    if (!(object && object.url)) throw new Error("A 'url' property or function 
must be specified");
+    if (!(object && object.url)) return null;
     return _.isFunction(object.url) ? object.url() : object.url;
   };
 
+  // Throw an error when a URL is needed, and none is supplied.
+  var urlError = function() {
+    throw new Error('A "url" property or function must be specified');
+  };
+
   // Wrap an optional error callback with a fallback error event.
   var wrapError = function(onError, model, options) {
     return function(resp) {
       if (onError) {
-        onError(model, resp);
+        onError(model, resp, options);
       } else {
         model.trigger('error', model, resp, options);
       }
@@ -1005,7 +1152,7 @@
 
   // Helper function to escape a string for HTML rendering.
   var escapeHTML = function(string) {
-    return string.replace(/&(?!\w+;)/g, '&amp;').replace(/</g, 
'&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
+    return string.replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, 
'&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, 
'&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
   };
 
-})();
+}).call(this);

Modified: 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/format.js
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/format.js?rev=1294730&r1=1294729&r2=1294730&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/format.js
 (original)
+++ 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/format.js
 Tue Feb 28 16:56:02 2012
@@ -1,42 +1,64 @@
+
+/*     Hallo - a rich text editing jQuery UI widget
+#     (c) 2011 Henri Bergius, IKS Consortium
+#     Hallo may be freely distributed under the MIT license
+*/
+
 (function() {
-  var __bind = function(fn, me){ return function(){ return fn.apply(me, 
arguments); }; };
+
   (function(jQuery) {
     return jQuery.widget("IKS.halloformat", {
       options: {
         editable: null,
         toolbar: null,
         uuid: "",
-        formattings: ["bold", "italic"]
+        formattings: {
+          bold: true,
+          italic: true,
+          strikeThrough: true,
+          underline: true
+        }
       },
       _create: function() {
-        var buttonize, buttonset, format, widget, _i, _len, _ref;
+        var buttonize, buttonset, enabled, format, widget, _ref,
+          _this = this;
         widget = this;
-        buttonset = jQuery("<span></span>");
-        buttonize = __bind(function(format) {
-          var button, id, label;
+        buttonset = jQuery("<span class=\"" + widget.widgetName + 
"\"></span>");
+        buttonize = function(format) {
+          var button, element, id, label, queryState;
           label = format.substr(0, 1).toUpperCase();
-          id = "" + this.options.uuid + "-" + format;
-          buttonset.append(jQuery("<input id=\"" + id + "\" type=\"checkbox\" 
/><label for=\"" + id + "\">" + label + "</label>").button());
+          id = "" + _this.options.uuid + "-" + format;
+          buttonset.append(jQuery("<input id=\"" + id + "\" type=\"checkbox\" 
/><label for=\"" + id + "\" class=\"" + format + "_button\">" + label + 
"</label>").button());
           button = jQuery("#" + id, buttonset);
           button.attr("hallo-command", format);
+          button.addClass(format);
           button.bind("change", function(event) {
             format = jQuery(this).attr("hallo-command");
             return widget.options.editable.execute(format);
           });
-          return this.element.bind("keyup paste change mouseup", 
function(event) {
+          queryState = function(event) {
             if (document.queryCommandState(format)) {
               button.attr("checked", true);
+              button.next("label").addClass("ui-state-clicked");
               return button.button("refresh");
             } else {
               button.attr("checked", false);
+              button.next("label").removeClass("ui-state-clicked");
               return button.button("refresh");
             }
+          };
+          element = _this.element;
+          _this.element.bind("halloenabled", function() {
+            return element.bind("keyup paste change mouseup", queryState);
+          });
+          return _this.element.bind("hallodisabled", function() {
+            return element.unbind("keyup paste change mouseup", queryState);
           });
-        }, this);
+        };
         _ref = this.options.formattings;
-        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-          format = _ref[_i];
-          buttonize(format);
+        for (format in _ref) {
+          enabled = _ref[format];
+          if (enabled) buttonize(format);
         }
         buttonset.buttonset();
         return this.options.toolbar.append(buttonset);
@@ -44,4 +66,5 @@
       _init: function() {}
     });
   })(jQuery);
+
 }).call(this);

Modified: 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/hallo.js
URL: 
http://svn.apache.org/viewvc/incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/hallo.js?rev=1294730&r1=1294729&r2=1294730&view=diff
==============================================================================
--- 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/hallo.js
 (original)
+++ 
incubator/stanbol/trunk/demos/webvie/src/main/resources/org/apache/stanbol/commons/web/vie/static/lib/hallo/hallo.js
 Tue Feb 28 16:56:02 2012
@@ -1,15 +1,34 @@
+
+/*     Hallo - a rich text editing jQuery UI widget
+#     (c) 2011 Henri Bergius, IKS Consortium
+#     Hallo may be freely distributed under the MIT license
+*/
+
 (function() {
+
   (function(jQuery) {
     return jQuery.widget("IKS.hallo", {
       toolbar: null,
       bound: false,
       originalContent: "",
       uuid: "",
+      selection: null,
       options: {
         editable: true,
         plugins: {},
+        floating: true,
+        offset: {
+          x: 0,
+          y: 0
+        },
+        showAlways: false,
         activated: function() {},
-        deactivated: function() {}
+        deactivated: function() {},
+        selected: function() {},
+        unselected: function() {},
+        enabled: function() {},
+        disabled: function() {},
+        placeholder: ''
       },
       _create: function() {
         var options, plugin, _ref, _results;
@@ -20,9 +39,7 @@
         _results = [];
         for (plugin in _ref) {
           options = _ref[plugin];
-          if (!jQuery.isPlainObject(options)) {
-            options = {};
-          }
+          if (!jQuery.isPlainObject(options)) options = {};
           options["editable"] = this;
           options["toolbar"] = this.toolbar;
           options["uuid"] = this.id;
@@ -41,34 +58,100 @@
         this.element.attr("contentEditable", false);
         this.element.unbind("focus", this._activated);
         this.element.unbind("blur", this._deactivated);
-        this.element.unbind("keyup paste change", this, this._checkModified);
-        return this.bound = false;
+        this.element.unbind("keyup paste change", this._checkModified);
+        this.element.unbind("keyup", this._keys);
+        this.element.unbind("keyup mouseup", this._checkSelection);
+        this.bound = false;
+        return this._trigger("disabled", null);
       },
       enable: function() {
         var widget;
         this.element.attr("contentEditable", true);
+        if (!this.element.html()) this.element.html(this.options.placeholder);
         if (!this.bound) {
           this.element.bind("focus", this, this._activated);
-          this.element.bind("blur", this, this._deactivated);
+          if (!this.options.showAlways) {
+            this.element.bind("blur", this, this._deactivated);
+          }
           this.element.bind("keyup paste change", this, this._checkModified);
+          this.element.bind("keyup", this, this._keys);
+          this.element.bind("keyup mouseup", this, this._checkSelection);
           widget = this;
-          return this.bound = true;
+          this.bound = true;
         }
+        return this._trigger("enabled", null);
       },
       activate: function() {
         return this.element.focus();
       },
+      getSelection: function() {
+        var range, userSelection;
+        if (jQuery.browser.msie) {
+          range = document.selection.createRange();
+        } else {
+          if (window.getSelection) {
+            userSelection = window.getSelection();
+          } else if (document.selection) {
+            userSelection = document.selection.createRange();
+          } else {
+            throw "Your browser does not support selection handling";
+          }
+          if (userSelection.rangeCount > 0) {
+            range = userSelection.getRangeAt(0);
+          } else {
+            range = userSelection;
+          }
+        }
+        return range;
+      },
+      restoreSelection: function(range) {
+        if (jQuery.browser.msie) {
+          return range.select();
+        } else {
+          window.getSelection().removeAllRanges();
+          return window.getSelection().addRange(range);
+        }
+      },
+      replaceSelection: function(cb) {
+        var newTextNode, r, range, sel, t;
+        if (jQuery.browser.msie) {
+          t = document.selection.createRange().text;
+          r = document.selection.createRange();
+          return r.pasteHTML(cb(t));
+        } else {
+          sel = window.getSelection();
+          range = sel.getRangeAt(0);
+          newTextNode = document.createTextNode(cb(range.extractContents()));
+          range.insertNode(newTextNode);
+          range.setStartAfter(newTextNode);
+          sel.removeAllRanges();
+          return sel.addRange(range);
+        }
+      },
+      removeAllSelections: function() {
+        if (jQuery.browser.msie) {
+          return range.empty();
+        } else {
+          return window.getSelection().removeAllRanges();
+        }
+      },
       getContents: function() {
         return this.element.html();
       },
+      setContents: function(contents) {
+        return this.element.html(contents);
+      },
       isModified: function() {
         return this.originalContent !== this.getContents();
       },
       setUnmodified: function() {
         return this.originalContent = this.getContents();
       },
-      execute: function(command) {
-        if (document.execCommand(command, false, null)) {
+      restoreOriginalContent: function() {
+        return this.element.html(this.originalContent);
+      },
+      execute: function(command, value) {
+        if (document.execCommand(command, false, value)) {
           return this.element.trigger("change");
         }
       },
@@ -79,15 +162,80 @@
         };
         return "" + (S4()) + (S4()) + "-" + (S4()) + "-" + (S4()) + "-" + 
(S4()) + "-" + (S4()) + (S4()) + (S4());
       },
+      _getToolbarPosition: function(event, selection) {
+        var offset;
+        if (!event) return;
+        if (this.options.floating) {
+          if (event.originalEvent instanceof KeyboardEvent) {
+            return this._getCaretPosition(selection);
+          } else if (event.originalEvent instanceof MouseEvent) {
+            return {
+              top: event.pageY,
+              left: event.pageX
+            };
+          }
+        } else {
+          offset = parseFloat(this.element.css('outline-width')) + 
parseFloat(this.element.css('outline-offset'));
+          return {
+            top: this.element.offset().top - this.toolbar.outerHeight() - 
offset,
+            left: this.element.offset().left - offset
+          };
+        }
+      },
+      _getCaretPosition: function(range) {
+        var newRange, position, tmpSpan;
+        tmpSpan = jQuery("<span/>");
+        newRange = document.createRange();
+        newRange.setStart(range.endContainer, range.endOffset);
+        newRange.insertNode(tmpSpan.get(0));
+        position = {
+          top: tmpSpan.offset().top,
+          left: tmpSpan.offset().left
+        };
+        tmpSpan.remove();
+        return position;
+      },
       _prepareToolbar: function() {
-        this.toolbar = jQuery('<div></div>').hide();
+        var that;
+        that = this;
+        this.toolbar = jQuery('<div class="hallotoolbar"></div>').hide();
         this.toolbar.css("position", "absolute");
         this.toolbar.css("top", this.element.offset().top - 20);
         this.toolbar.css("left", this.element.offset().left);
         jQuery('body').append(this.toolbar);
-        return this.toolbar.bind("mousedown", function(event) {
+        this.toolbar.bind("mousedown", function(event) {
           return event.preventDefault();
         });
+        if (this.options.showAlways) {
+          this.options.floating = false;
+          this.element.bind("halloactivated", function(event, data) {
+            that._updateToolbarPosition(that._getToolbarPosition(event));
+            return that.toolbar.show();
+          });
+          this.element.bind("hallodeactivated", function(event, data) {
+            return that.toolbar.hide();
+          });
+        } else {
+          this.element.bind("halloselected", function(event, data) {
+            var position, widget;
+            widget = data.editable;
+            position = widget._getToolbarPosition(data.originalEvent, 
data.selection);
+            if (position) {
+              that._updateToolbarPosition(position);
+              return that.toolbar.show();
+            }
+          });
+          this.element.bind("hallounselected", function(event, data) {
+            return data.editable.toolbar.hide();
+          });
+        }
+        return jQuery(window).resize(function(event) {
+          return that._updateToolbarPosition(that._getToolbarPosition(event));
+        });
+      },
+      _updateToolbarPosition: function(position) {
+        this.toolbar.css("top", position.top);
+        return this.toolbar.css("left", position.left);
       },
       _checkModified: function(event) {
         var widget;
@@ -99,21 +247,86 @@
           });
         }
       },
-      _activated: function(event) {
+      _keys: function(event) {
         var widget;
         widget = event.data;
-        if (widget.toolbar.html() !== "") {
-          widget.toolbar.css("top", widget.element.offset().top - 
widget.toolbar.height());
-          widget.toolbar.show();
+        if (event.keyCode === 27) {
+          widget.restoreOriginalContent();
+          return widget.turnOff();
         }
-        return widget._trigger("activated", event);
       },
-      _deactivated: function(event) {
+      _rangesEqual: function(r1, r2) {
+        return r1.startContainer === r2.startContainer && r1.startOffset === 
r2.startOffset && r1.endContainer === r2.endContainer && r1.endOffset === 
r2.endOffset;
+      },
+      _checkSelection: function(event) {
         var widget;
+        if (event.keyCode === 27) return;
         widget = event.data;
-        widget.toolbar.hide();
-        return widget._trigger("deactivated", event);
+        return setTimeout(function() {
+          var sel;
+          sel = widget.getSelection();
+          if (widget._isEmptySelection(sel) || widget._isEmptyRange(sel)) {
+            if (widget.selection) {
+              widget.selection = null;
+              widget._trigger("unselected", null, {
+                editable: widget,
+                originalEvent: event
+              });
+            }
+            return;
+          }
+          if (!widget.selection || !widget._rangesEqual(sel, 
widget.selection)) {
+            widget.selection = sel.cloneRange();
+            return widget._trigger("selected", null, {
+              editable: widget,
+              selection: widget.selection,
+              ranges: [widget.selection],
+              originalEvent: event
+            });
+          }
+        }, 0);
+      },
+      _isEmptySelection: function(selection) {
+        if (selection.type === "Caret") return true;
+        return false;
+      },
+      _isEmptyRange: function(range) {
+        if (range.collapsed) return true;
+        if (range.isCollapsed) return range.isCollapsed();
+        return false;
+      },
+      turnOn: function() {
+        var el, widthToAdd;
+        if (this.getContents() === this.options.placeholder) 
this.setContents('');
+        jQuery(this.element).addClass('inEditMode');
+        if (!this.options.floating) {
+          el = jQuery(this.element);
+          widthToAdd = parseFloat(el.css('padding-left'));
+          widthToAdd += parseFloat(el.css('padding-right'));
+          widthToAdd += parseFloat(el.css('border-left-width'));
+          widthToAdd += parseFloat(el.css('border-right-width'));
+          widthToAdd += (parseFloat(el.css('outline-width'))) * 2;
+          widthToAdd += (parseFloat(el.css('outline-offset'))) * 2;
+          jQuery(this.toolbar).css("width", el.width() + widthToAdd);
+        } else {
+          this.toolbar.css("width", "auto");
+        }
+        return this._trigger("activated", this);
+      },
+      turnOff: function() {
+        this.toolbar.hide();
+        jQuery(this.element).removeClass('inEditMode');
+        if (this.options.showAlways) this.element.blur();
+        this._trigger("deactivated", this);
+        if (!this.getContents()) return 
this.setContents(this.options.placeholder);
+      },
+      _activated: function(event) {
+        return event.data.turnOn();
+      },
+      _deactivated: function(event) {
+        return event.data.turnOff();
       }
     });
   })(jQuery);
+
 }).call(this);


Reply via email to