You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@clerezza.apache.org by re...@apache.org on 2013/10/08 23:20:14 UTC

svn commit: r1530427 [1/3] - in /clerezza/trunk/platform.editor: ./ src/main/resources/META-INF/ src/main/resources/META-INF/resources/ src/main/resources/META-INF/resources/tools/ src/main/resources/META-INF/resources/tools/editor/ src/main/resources/...

Author: reto
Date: Tue Oct  8 21:20:13 2013
New Revision: 1530427

URL: http://svn.apache.org/r1530427
Log:
CLEREZZA-829: Added first attempt of Etch based editor

Added:
    clerezza/trunk/platform.editor/nbactions.xml
    clerezza/trunk/platform.editor/src/main/resources/META-INF/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone-min.js
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone.stickit.js
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/etch.js
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/jquery.min.js
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/underscore-min.js
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/styles/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/styles/etch.css
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/styles/images/
    clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/styles/images/editor_icons.png   (with props)
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/HtmlInfoDicobitRDFaNaked.scala
      - copied, changed from r1530046, clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/HtmlInfoDicobitCreateNaked.scala
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/OrderedContentRDFaNaked.scala
      - copied, changed from r1530046, clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/OrderedContentCreateNaked.scala
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentEtch.scala
      - copied, changed from r1530046, clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentCreate.scala
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentRDFaNaked.scala
      - copied, changed from r1530046, clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentCreateNaked.scala
Removed:
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/HtmlInfoDicobitCreateNaked.scala
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/OrderedContentCreateNaked.scala
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentCreateNaked.scala
Modified:
    clerezza/trunk/platform.editor/pom.xml
    clerezza/trunk/platform.editor/src/main/scala/org/apache/clerezza/platform/editor/renderlets/TitledContentCreate.scala

Added: clerezza/trunk/platform.editor/nbactions.xml
URL: http://svn.apache.org/viewvc/clerezza/trunk/platform.editor/nbactions.xml?rev=1530427&view=auto
==============================================================================
--- clerezza/trunk/platform.editor/nbactions.xml (added)
+++ clerezza/trunk/platform.editor/nbactions.xml Tue Oct  8 21:20:13 2013
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<actions>
+  <action>
+    <actionName>CUSTOM-deploy to localhost:8080</actionName>
+    <displayName>deploy to localhost:8080</displayName>
+    <goals>
+      <goal>install</goal>
+      <goal>org.apache.sling:maven-sling-plugin:install</goal>
+    </goals>
+  </action>
+</actions>

Modified: clerezza/trunk/platform.editor/pom.xml
URL: http://svn.apache.org/viewvc/clerezza/trunk/platform.editor/pom.xml?rev=1530427&r1=1530426&r2=1530427&view=diff
==============================================================================
--- clerezza/trunk/platform.editor/pom.xml (original)
+++ clerezza/trunk/platform.editor/pom.xml Tue Oct  8 21:20:13 2013
@@ -181,7 +181,7 @@
                         <configuration>
                             <includeTypes>js</includeTypes>
                             <outputDirectory>
-                                ${project.build.directory}/generated-resources/CLEREZZA-INF/web-resources/tools/create
+                                ${project.build.directory}/generated-resources/META-INF/resources/tools/create
                             </outputDirectory>
                         </configuration>
                     </execution>
@@ -194,7 +194,7 @@
                         <configuration>
                             <includeArtifactIds>createjs-theme</includeArtifactIds>
                             <outputDirectory>
-                                ${project.build.directory}/generated-resources/CLEREZZA-INF/web-resources/tools/create
+                                ${project.build.directory}/generated-resources/META-INF/resources/tools/create
                             </outputDirectory>
                         </configuration>
                     </execution>

Added: clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone-min.js
URL: http://svn.apache.org/viewvc/clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone-min.js?rev=1530427&view=auto
==============================================================================
--- clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone-min.js (added)
+++ clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone-min.js Tue Oct  8 21:20:13 2013
@@ -0,0 +1,4 @@
+(function(){var t=this;var e=t.Backbone;var i=[];var r=i.push;var s=i.slice;var n=i.splice;var a;if(typeof exports!=="undefined"){a=exports}else{a=t.Backbone={}}a.VERSION="1.0.0";var h=t._;if(!h&&typeof require!=="undefined")h=require("underscore");a.$=t.jQuery||t.Zepto||t.ender||t.$;a.noConflict=function(){t.Backbone=e;return this};a.emulateHTTP=false;a.emulateJSON=false;var o=a.Events={on:function(t,e,i){if(!l(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,i){if(!l(this,"once",t,[e,i])||!e)return this;var r=this;var s=h.once(function(){r.off(t,s);e.apply(this,arguments)});s._callback=e;return this.on(t,s,i)},off:function(t,e,i){var r,s,n,a,o,u,c,f;if(!this._events||!l(this,"off",t,[e,i]))return this;if(!t&&!e&&!i){this._events={};return this}a=t?[t]:h.keys(this._events);for(o=0,u=a.length;o<u;o++){t=a[o];if(n=this._events[t]){this._events[t]=r=[
 ];if(e||i){for(c=0,f=n.length;c<f;c++){s=n[c];if(e&&e!==s.callback&&e!==s.callback._callback||i&&i!==s.context){r.push(s)}}}if(!r.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=s.call(arguments,1);if(!l(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)c(i,e);if(r)c(r,arguments);return this},stopListening:function(t,e,i){var r=this._listeners;if(!r)return this;var s=!e&&!i;if(typeof e==="object")i=this;if(t)(r={})[t._listenerId]=t;for(var n in r){r[n].off(e,i,this);if(s)delete this._listeners[n]}return this}};var u=/\s+/;var l=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(u.test(i)){var n=i.split(u);for(var a=0,h=n.length;a<h;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var c=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],h=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;ca
 se 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,h);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e)}};var f={listenTo:"on",listenToOnce:"once"};h.each(f,function(t,e){o[e]=function(e,i,r){var s=this._listeners||(this._listeners={});var n=e._listenerId||(e._listenerId=h.uniqueId("l"));s[n]=e;if(typeof i==="object")r=this;e[t](i,r,this);return this}});o.bind=o.on;o.unbind=o.off;h.extend(a,o);var d=a.Model=function(t,e){var i;var r=t||{};e||(e={});this.cid=h.uniqueId("c");this.attributes={};h.extend(this,h.pick(e,p));if(e.parse)r=this.parse(r,e)||{};if(i=h.result(this,"defaults")){r=h.defaults({},r,i)}this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};var p=["url","urlRoot","collection"];h.extend(d.prototype,o,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return h.clone(this.attributes)},syn
 c:function(){return a.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return h.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,i){var r,s,n,a,o,u,l,c;if(t==null)return this;if(typeof t==="object"){s=t;i=e}else{(s={})[t]=e}i||(i={});if(!this._validate(s,i))return false;n=i.unset;o=i.silent;a=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=h.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in s)this.id=s[this.idAttribute];for(r in s){e=s[r];if(!h.isEqual(c[r],e))a.push(r);if(!h.isEqual(l[r],e)){this.changed[r]=e}else{delete this.changed[r]}n?delete c[r]:c[r]=e}if(!o){if(a.length)this._pending=true;for(var f=0,d=a.length;f<d;f++){this.trigger("change:"+a[f],this,c[a[f]],i)}}if(u)return this;if(!o){while(this._pending){this._pending=false;this.trigger("change",this,i)}}this._pending=false;this._changing=false;return this},unset:function
 (t,e){return this.set(t,void 0,h.extend({},e,{unset:true}))},clear:function(t){var e={};for(var i in this.attributes)e[i]=void 0;return this.set(e,h.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!h.isEmpty(this.changed);return h.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?h.clone(this.changed):false;var e,i=false;var r=this._changing?this._previousAttributes:this.attributes;for(var s in t){if(h.isEqual(r[s],e=t[s]))continue;(i||(i={}))[s]=e}return i},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return h.clone(this._previousAttributes)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var i=t.success;t.success=function(r){if(!e.set(e.parse(r,t),t))return false;if(i)i(e,r,t);e.trigger("sync",e,r,t)};R(this,t);return this.sync("read",this,t)},save:function(t,e,i){var r,s,n,a=this.attributes;if(t==null||typ
 eof t==="object"){r=t;i=e}else{(r={})[t]=e}if(r&&(!i||!i.wait)&&!this.set(r,i))return false;i=h.extend({validate:true},i);if(!this._validate(r,i))return false;if(r&&i.wait){this.attributes=h.extend({},a,r)}if(i.parse===void 0)i.parse=true;var o=this;var u=i.success;i.success=function(t){o.attributes=a;var e=o.parse(t,i);if(i.wait)e=h.extend(r||{},e);if(h.isObject(e)&&!o.set(e,i)){return false}if(u)u(o,t,i);o.trigger("sync",o,t,i)};R(this,i);s=this.isNew()?"create":i.patch?"patch":"update";if(s==="patch")i.attrs=r;n=this.sync(s,this,i);if(r&&i.wait)this.attributes=a;return n},destroy:function(t){t=t?h.clone(t):{};var e=this;var i=t.success;var r=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(s){if(t.wait||e.isNew())r();if(i)i(e,s,t);if(!e.isNew())e.trigger("sync",e,s,t)};if(this.isNew()){t.success();return false}R(this,t);var s=this.sync("delete",this,t);if(!t.wait)r();return s},url:function(){var t=h.result(this,"urlRoot")||h.result(this.collection,"url")||U();
 if(this.isNew())return t;return t+(t.charAt(t.length-1)==="/"?"":"/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return this.id==null},isValid:function(t){return this._validate({},h.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=h.extend({},this.attributes,t);var i=this.validationError=this.validate(t,e)||null;if(!i)return true;this.trigger("invalid",this,i,h.extend(e||{},{validationError:i}));return false}});var v=["keys","values","pairs","invert","pick","omit"];h.each(v,function(t){d.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.attributes);return h[t].apply(h,e)}});var g=a.Collection=function(t,e){e||(e={});if(e.url)this.url=e.url;if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,h.extend({silent:true},e))};var m=
 {add:true,remove:true,merge:true};var y={add:true,merge:false,remove:false};h.extend(g.prototype,o,{model:d,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return a.sync.apply(this,arguments)},add:function(t,e){return this.set(t,h.defaults(e||{},y))},remove:function(t,e){t=h.isArray(t)?t.slice():[t];e||(e={});var i,r,s,n;for(i=0,r=t.length;i<r;i++){n=this.get(t[i]);if(!n)continue;delete this._byId[n.id];delete this._byId[n.cid];s=this.indexOf(n);this.models.splice(s,1);this.length--;if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}this._removeReference(n)}return this},set:function(t,e){e=h.defaults(e||{},m);if(e.parse)t=this.parse(t,e);if(!h.isArray(t))t=t?[t]:[];var i,s,a,o,u,l;var c=e.at;var f=this.comparator&&c==null&&e.sort!==false;var d=h.isString(this.comparator)?this.comparator:null;var p=[],v=[],g={};for(i=0,s=t.length;i<s;i++){if(!(a=this._prepareModel(t[i],e)))continue;if(u=this.get(a)){if(e.remove)g[u.cid]=t
 rue;if(e.merge){u.set(a.attributes,e);if(f&&!l&&u.hasChanged(d))l=true}}else if(e.add){p.push(a);a.on("all",this._onModelEvent,this);this._byId[a.cid]=a;if(a.id!=null)this._byId[a.id]=a}}if(e.remove){for(i=0,s=this.length;i<s;++i){if(!g[(a=this.models[i]).cid])v.push(a)}if(v.length)this.remove(v,e)}if(p.length){if(f)l=true;this.length+=p.length;if(c!=null){n.apply(this.models,[c,0].concat(p))}else{r.apply(this.models,p)}}if(l)this.sort({silent:true});if(e.silent)return this;for(i=0,s=p.length;i<s;i++){(a=p[i]).trigger("add",a,this,e)}if(l)this.trigger("sort",this,e);return this},reset:function(t,e){e||(e={});for(var i=0,r=this.models.length;i<r;i++){this._removeReference(this.models[i])}e.previousModels=this.models;this._reset();this.add(t,h.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return this},push:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:this.length},e));return t},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return 
 e},unshift:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:0},e));return t},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(t,e){return this.models.slice(t,e)},get:function(t){if(t==null)return void 0;return this._byId[t.id!=null?t.id:t.cid||t]},at:function(t){return this.models[t]},where:function(t,e){if(h.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(h.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(h.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},sortedIndex:function(t,e,i){e||(e=this.comparator);var r=h.isFunction(e)?e:function(t){return t.get(e)};return h.sortedIndex(this.models,t,r,
 i)},pluck:function(t){return h.invoke(this.models,"get",t)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var i=this;t.success=function(r){var s=t.reset?"reset":"set";i[s](r,t);if(e)e(i,r,t);i.trigger("sync",i,r,t)};R(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?h.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var i=this;var r=e.success;e.success=function(s){if(e.wait)i.add(t,e);if(r)r(t,s,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof d){if(!t.collection)t.collection=this;return t}e||(e={});e.collection=this;var i=new this.model(t,e);if(!i._validate(t,e)){this.trigger("invalid",this,t,e);return false}return i},_removeReference:function(t){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,thi
 s)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","indexOf","shuffle","lastIndexOf","isEmpty","chain"];h.each(_,function(t){g.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.models);return h[t].apply(h,e)}});var w=["groupBy","countBy","sortBy"];h.each(w,function(t){g.prototype[t]=function(e,i){var r=h.isFunction(e)?e:function(t){return t.get(e)};return h[t](this.models,r,i)}});var b=a.View=function(t){this.cid=h.uniqueId("view");this._configure(t||{});this._ensureElement();
 this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];h.extend(b.prototype,o,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,e){if(this.$el)this.undelegateEvents();this.$el=t instanceof a.$?t:a.$(t);this.el=this.$el[0];if(e!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=h.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var i=t[e];if(!h.isFunction(i))i=this[t[e]];if(!i)continue;var r=e.match(x);var s=r[1],n=r[2];i=h.bind(i,this);s+=".delegateEvents"+this.cid;if(n===""){this.$el.on(s,i)}else{this.$el.on(s,n,i)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_configure:function(t){if(this.options)t=h.extend({},h.resu
 lt(this,"options"),t);h.extend(this,h.pick(t,E));this.options=t},_ensureElement:function(){if(!this.el){var t=h.extend({},h.result(this,"attributes"));if(this.id)t.id=h.result(this,"id");if(this.className)t["class"]=h.result(this,"className");var e=a.$("<"+h.result(this,"tagName")+">").attr(t);this.setElement(e,false)}else{this.setElement(h.result(this,"el"),false)}}});a.sync=function(t,e,i){var r=k[t];h.defaults(i||(i={}),{emulateHTTP:a.emulateHTTP,emulateJSON:a.emulateJSON});var s={type:r,dataType:"json"};if(!i.url){s.url=h.result(e,"url")||U()}if(i.data==null&&e&&(t==="create"||t==="update"||t==="patch")){s.contentType="application/json";s.data=JSON.stringify(i.attrs||e.toJSON(i))}if(i.emulateJSON){s.contentType="application/x-www-form-urlencoded";s.data=s.data?{model:s.data}:{}}if(i.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){s.type="POST";if(i.emulateJSON)s.data._method=r;var n=i.beforeSend;i.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",r);if(n)re
 turn n.apply(this,arguments)}}if(s.type!=="GET"&&!i.emulateJSON){s.processData=false}if(s.type==="PATCH"&&window.ActiveXObject&&!(window.external&&window.external.msActiveXFilteringEnabled)){s.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var o=i.xhr=a.ajax(h.extend(s,i));e.trigger("request",e,o,i);return o};var k={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};a.ajax=function(){return a.$.ajax.apply(a.$,arguments)};var S=a.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var $=/\((.*?)\)/g;var T=/(\(\?)?:\w+/g;var H=/\*\w+/g;var A=/[\-{}\[\]+?.,\\\^$|#\s]/g;h.extend(S.prototype,o,{initialize:function(){},route:function(t,e,i){if(!h.isRegExp(t))t=this._routeToRegExp(t);if(h.isFunction(e)){i=e;e=""}if(!i)i=this[e];var r=this;a.history.route(t,function(s){var n=r._extractParameters(t,s);i&&i.apply(r,n);r.trigger.apply(r,["route:"+e].concat(n));r.trigger("route",e,n);a.history
 .trigger("route",r,e,n)});return this},navigate:function(t,e){a.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=h.result(this,"routes");var t,e=h.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(A,"\\$&").replace($,"(?:$1)?").replace(T,function(t,e){return e?t:"([^/]+)"}).replace(H,"(.*?)");return new RegExp("^"+t+"$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return h.map(i,function(t){return t?decodeURIComponent(t):null})}});var I=a.History=function(){this.handlers=[];h.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var P=/^\/+|\/+$/g;var O=/msie [\w.]+/;var C=/\/$/;I.started=false;h.extend(I.prototype,o,{interval:50,getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._w
 antsHashChange||e){t=this.location.pathname;var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.substr(i.length)}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(I.started)throw new Error("Backbone.history has already been started");I.started=true;this.options=h.extend({},{root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var e=this.getFragment();var i=document.documentMode;var r=O.exec(navigator.userAgent.toLowerCase())&&(!i||i<=7);this.root=("/"+this.root+"/").replace(P,"/");if(r&&this._wantsHashChange){this.iframe=a.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow;this.navigate(e)}if(this._hasPushState){a.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!r){a.$(window).on("hashchange",this.ch
 eckUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=e;var s=this.location;var n=s.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!n){this.fragment=this.getFragment(null,true);this.location.replace(this.root+this.location.search+"#"+this.fragment);return true}else if(this._wantsPushState&&this._hasPushState&&n&&s.hash){this.fragment=this.getHash().replace(N,"");this.history.replaceState({},document.title,this.root+this.fragment+s.search)}if(!this.options.silent)return this.loadUrl()},stop:function(){a.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);I.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(thi
 s.iframe)this.navigate(e);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(t){var e=this.fragment=this.getFragment(t);var i=h.any(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}});return i},navigate:function(t,e){if(!I.started)return false;if(!e||e===true)e={trigger:e};t=this.getFragment(t||"");if(this.fragment===t)return;this.fragment=t;var i=this.root+t;if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});a.history=new I;var j=function(t,e){var i=this;var r;if(t&&h.has(t,"construc
 tor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}h.extend(r,i,e);var s=function(){this.constructor=r};s.prototype=i.prototype;r.prototype=new s;if(t)h.extend(r.prototype,t);r.__super__=i.prototype;return r};d.extend=g.extend=S.extend=b.extend=I.extend=j;var U=function(){throw new Error('A "url" property or function must be specified')};var R=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}}}).call(this);
+/*
+//@ sourceMappingURL=backbone-min.map
+*/
\ No newline at end of file

Added: clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone.stickit.js
URL: http://svn.apache.org/viewvc/clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone.stickit.js?rev=1530427&view=auto
==============================================================================
--- clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone.stickit.js (added)
+++ clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/backbone.stickit.js Tue Oct  8 21:20:13 2013
@@ -0,0 +1,468 @@
+//
+// backbone.stickit - v0.6.3
+// The MIT License
+// Copyright (c) 2012 The New York Times, CMS Group, Matthew DeLambo <de...@gmail.com> 
+//
+(function($) {
+
+  // Backbone.Stickit Namespace
+  // --------------------------
+
+  Backbone.Stickit = {
+
+    _handlers: [],
+
+    addHandler: function(handlers) {
+      // Fill-in default values.
+      handlers = _.map(_.flatten([handlers]), function(handler) {
+        return _.extend({
+          updateModel: true,
+          updateView: true,
+          updateMethod: 'text'
+        }, handler);
+      });
+      this._handlers = this._handlers.concat(handlers);
+    }
+  };
+
+  // Backbone.View Mixins
+  // --------------------
+
+  _.extend(Backbone.View.prototype, {
+
+    // Collection of model event bindings.
+    //   [{model,event,fn}, ...]
+    _modelBindings: null,
+
+    // Unbind the model and event bindings from `this._modelBindings` and
+    // `this.$el`. If the optional `model` parameter is defined, then only
+    // delete bindings for the given `model` and its corresponding view events.
+    unstickit: function(model) {
+      _.each(this._modelBindings, _.bind(function(binding, i) {
+        if (model && binding.model !== model) return false;
+        binding.model.off(binding.event, binding.fn);
+        delete this._modelBindings[i];
+      }, this));
+      this._modelBindings = _.compact(this._modelBindings);
+
+      this.$el.off('.stickit' + (model ? '.' + model.cid : ''));
+    },
+
+    // Using `this.bindings` configuration or the `optionalBindingsConfig`, binds `this.model`
+    // or the `optionalModel` to elements in the view.
+    stickit: function(optionalModel, optionalBindingsConfig) {
+      var self = this,
+        model = optionalModel || this.model,
+        namespace = '.stickit.' + model.cid,
+        bindings = optionalBindingsConfig || this.bindings || {};
+
+      this._modelBindings || (this._modelBindings = []);
+      this.unstickit(model);
+
+      // Iterate through the selectors in the bindings configuration and configure
+      // the various options for each field.
+      _.each(_.keys(bindings), function(selector) {
+        var $el, options, modelAttr, config,
+          binding = bindings[selector] || {},
+          bindKey = _.uniqueId();
+
+        // Support ':el' selector - special case selector for the view managed delegate.
+        if (selector != ':el') $el = self.$(selector);
+        else {
+          $el = self.$el;
+          selector = '';
+        }
+
+        // Fail fast if the selector didn't match an element.
+        if (!$el.length) return;
+
+        // Allow shorthand setting of model attributes - `'selector':'observe'`.
+        if (_.isString(binding)) binding = {observe:binding};
+
+        config = getConfiguration($el, binding);
+
+        modelAttr = config.observe;
+
+        // Create the model set options with a unique `bindKey` so that we
+        // can avoid double-binding in the `change:attribute` event handler.
+        options = _.extend({bindKey:bindKey}, config.setOptions || {});
+
+        initializeAttributes(self, $el, config, model, modelAttr);
+
+        initializeVisible(self, $el, config, model, modelAttr);
+
+        if (modelAttr) {
+          // Setup one-way, form element to model, bindings.
+          _.each(config.events || [], function(type) {
+            var event = type + namespace;
+            var method = function(event) {
+              var val = config.getVal.call(self, $el, event, config);
+              // Don't update the model if false is returned from the `updateModel` configuration.
+              if (evaluateBoolean(self, config.updateModel, val, config))
+                setAttr(model, modelAttr, val, options, self, config);
+            };
+            if (selector === '') self.$el.on(event, method);
+            else self.$el.on(event, selector, method);
+          });
+
+          // Setup a `change:modelAttr` observer to keep the view element in sync.
+          // `modelAttr` may be an array of attributes or a single string value.
+          _.each(_.flatten([modelAttr]), function(attr) {
+            observeModelEvent(model, self, 'change:'+attr, function(model, val, options) {
+              if (options == null || options.bindKey != bindKey)
+                updateViewBindEl(self, $el, config, getAttr(model, modelAttr, config, self), model);
+            });
+          });
+
+          updateViewBindEl(self, $el, config, getAttr(model, modelAttr, config, self), model, true);
+        }
+
+        // After each binding is setup, call the `initialize` callback.
+        applyViewFn(self, config.initialize, $el, model, config);
+      });
+
+      // Wrap `view.remove` to unbind stickit model and dom events.
+      this.remove = _.wrap(this.remove, function(oldRemove) {
+        self.unstickit();
+        if (oldRemove) oldRemove.call(self);
+      });
+    }
+  });
+
+  // Helpers
+  // -------
+
+  // Evaluates the given `path` (in object/dot-notation) relative to the given
+  // `obj`. If the path is null/undefined, then the given `obj` is returned.
+  var evaluatePath = function(obj, path) {
+    var parts = (path || '').split('.');
+    var result = _.reduce(parts, function(memo, i) { return memo[i]; }, obj);
+    return result == null ? obj : result;
+  };
+
+  // If the given `fn` is a string, then view[fn] is called, otherwise it is
+  // a function that should be executed.
+  var applyViewFn = function(view, fn) {
+    if (fn) return (_.isString(fn) ? view[fn] : fn).apply(view, _.toArray(arguments).slice(2));
+  };
+
+  var getSelectedOption = function($select) { return $select.find('option').not(function(){ return !this.selected; }); };
+
+  // Given a function, string (view function reference), or a boolean
+  // value, returns the truthy result. Any other types evaluate as false.
+  var evaluateBoolean = function(view, reference) {
+    if (_.isBoolean(reference)) return reference;
+    else if (_.isFunction(reference) || _.isString(reference))
+      return applyViewFn.apply(this, _.toArray(arguments));
+    return false;
+  };
+
+  // Setup a model event binding with the given function, and track the event
+  // in the view's _modelBindings.
+  var observeModelEvent = function(model, view, event, fn) {
+    model.on(event, fn, view);
+    view._modelBindings.push({model:model, event:event, fn:fn});
+  };
+
+  // Prepares the given `val`ue and sets it into the `model`.
+  var setAttr = function(model, attr, val, options, context, config) {
+    if (config.onSet) val = applyViewFn(context, config.onSet, val, config);
+    model.set(attr, val, options);
+  };
+
+  // Returns the given `attr`'s value from the `model`, escaping and
+  // formatting if necessary. If `attr` is an array, then an array of
+  // respective values will be returned.
+  var getAttr = function(model, attr, config, context) {
+    var val, retrieveVal = function(field) {
+      var retrieved = config.escape ? model.escape(field) : model.get(field);
+      return _.isUndefined(retrieved) ? '' : retrieved;
+    };
+    val = _.isArray(attr) ? _.map(attr, retrieveVal) : retrieveVal(attr);
+    return config.onGet ? applyViewFn(context, config.onGet, val, config) : val;
+  };
+
+  // Find handlers in `Backbone.Stickit._handlers` with selectors that match
+  // `$el` and generate a configuration by mixing them in the order that they
+  // were found with the with the givne `binding`.
+  var getConfiguration = function($el, binding) {
+    var handlers = [{
+      updateModel: false,
+      updateView: true,
+      updateMethod: 'text',
+      update: function($el, val, m, opts) { $el[opts.updateMethod](val); },
+      getVal: function($el, e, opts) { return $el[opts.updateMethod](); }
+    }];
+    _.each(Backbone.Stickit._handlers, function(handler) {
+      if ($el.is(handler.selector)) handlers.push(handler);
+    });
+    handlers.push(binding);
+    var config = _.extend.apply(_, handlers);
+    delete config.selector;
+    return config;
+  };
+
+  // Setup the attributes configuration - a list that maps an attribute or
+  // property `name`, to an `observe`d model attribute, using an optional
+  // `onGet` formatter.
+  //
+  //     attributes: [{
+  //       name: 'attributeOrPropertyName',
+  //       observe: 'modelAttrName'
+  //       onGet: function(modelAttrVal, modelAttrName) { ... }
+  //     }, ...]
+  //
+  var initializeAttributes = function(view, $el, config, model, modelAttr) {
+    var props = ['autofocus', 'autoplay', 'async', 'checked', 'controls', 'defer', 'disabled', 'hidden', 'loop', 'multiple', 'open', 'readonly', 'required', 'scoped', 'selected'];
+
+    _.each(config.attributes || [], function(attrConfig) {
+      var lastClass = '',
+        observed = attrConfig.observe || (attrConfig.observe = modelAttr),
+        updateAttr = function() {
+          var updateType = _.indexOf(props, attrConfig.name, true) > -1 ? 'prop' : 'attr',
+            val = getAttr(model, observed, attrConfig, view);
+          // If it is a class then we need to remove the last value and add the new.
+          if (attrConfig.name == 'class') {
+            $el.removeClass(lastClass).addClass(val);
+            lastClass = val;
+          }
+          else $el[updateType](attrConfig.name, val);
+        };
+      _.each(_.flatten([observed]), function(attr) {
+        observeModelEvent(model, view, 'change:' + attr, updateAttr);
+      });
+      updateAttr();
+    });
+  };
+
+  // If `visible` is configured, then the view element will be shown/hidden
+  // based on the truthiness of the modelattr's value or the result of the
+  // given callback. If a `visibleFn` is also supplied, then that callback
+  // will be executed to manually handle showing/hiding the view element.
+  //
+  //     observe: 'isRight',
+  //     visible: true, // or function(val, options) {}
+  //     visibleFn: function($el, isVisible, options) {} // optional handler
+  //
+  var initializeVisible = function(view, $el, config, model, modelAttr) {
+    if (config.visible == null) return;
+    var visibleCb = function() {
+      var visible = config.visible,
+          visibleFn = config.visibleFn,
+          val = getAttr(model, modelAttr, config, view),
+          isVisible = !!val;
+      // If `visible` is a function then it should return a boolean result to show/hide.
+      if (_.isFunction(visible) || _.isString(visible)) isVisible = applyViewFn(view, visible, val, config);
+      // Either use the custom `visibleFn`, if provided, or execute the standard show/hide.
+      if (visibleFn) applyViewFn(view, visibleFn, $el, isVisible, config);
+      else {
+        if (isVisible) $el.show();
+        else $el.hide();
+      }
+    };
+    _.each(_.flatten([modelAttr]), function(attr) {
+      observeModelEvent(model, view, 'change:' + attr, visibleCb);
+    });
+    visibleCb();
+  };
+
+  // Update the value of `$el` using the given configuration and trigger the
+  // `afterUpdate` callback. This action may be blocked by `config.updateView`.
+  //
+  //     update: function($el, val, model, options) {},  // handler for updating
+  //     updateView: true, // defaults to true
+  //     afterUpdate: function($el, val, options) {} // optional callback
+  //
+  var updateViewBindEl = function(view, $el, config, val, model, isInitializing) {
+    if (!evaluateBoolean(view, config.updateView, val, config)) return;
+    config.update.call(view, $el, val, model, config);
+    if (!isInitializing) applyViewFn(view, config.afterUpdate, $el, val, config);
+  };
+
+  // Default Handlers
+  // ----------------
+
+  Backbone.Stickit.addHandler([{
+    selector: '[contenteditable="true"]',
+    updateMethod: 'html',
+    events: ['keyup', 'change', 'paste', 'cut']
+  }, {
+    selector: 'input',
+    events: ['keyup', 'change', 'paste', 'cut'],
+    update: function($el, val) { $el.val(val); },
+    getVal: function($el) {
+      var val = $el.val();
+      if ($el.is('[type="number"]')) return val == null ? val : Number(val);
+      else return val;
+    }
+  }, {
+    selector: 'textarea',
+    events: ['keyup', 'change', 'paste', 'cut'],
+    update: function($el, val) { $el.val(val); },
+    getVal: function($el) { return $el.val(); }
+  }, {
+    selector: 'input[type="radio"]',
+    events: ['change'],
+    update: function($el, val) {
+      $el.filter('[value="'+val+'"]').prop('checked', true);
+    },
+    getVal: function($el) {
+      return $el.filter(':checked').val();
+    }
+  }, {
+    selector: 'input[type="checkbox"]',
+    events: ['change'],
+    update: function($el, val, model, options) {
+      if ($el.length > 1) {
+        // There are multiple checkboxes so we need to go through them and check
+        // any that have value attributes that match what's in the array of `val`s.
+        val || (val = []);
+        _.each($el, function(el) {
+          if (_.indexOf(val, $(el).val()) > -1) $(el).prop('checked', true);
+          else $(el).prop('checked', false);
+        });
+      } else {
+        if (_.isBoolean(val)) $el.prop('checked', val);
+        else $el.prop('checked', val == $el.val());
+      }
+    },
+    getVal: function($el) {
+      var val;
+      if ($el.length > 1) {
+        val = _.reduce($el, function(memo, el) {
+          if ($(el).prop('checked')) memo.push($(el).val());
+          return memo;
+        }, []);
+      } else {
+        val = $el.prop('checked');
+        // If the checkbox has a value attribute defined, then
+        // use that value. Most browsers use "on" as a default.
+        var boxval = $el.val();
+        if (boxval != 'on' && boxval != null) {
+          if (val) val = $el.val();
+          else val = null;
+        }
+      }
+      return val;
+    }
+  }, {
+    selector: 'select',
+    events: ['change'],
+    update: function($el, val, model, options) {
+      var optList,
+        selectConfig = options.selectOptions,
+        list = selectConfig && selectConfig.collection || undefined,
+        isMultiple = $el.prop('multiple');
+
+      // If there are no `selectOptions` then we assume that the `<select>`
+      // is pre-rendered and that we need to generate the collection.
+      if (!selectConfig) {
+        selectConfig = {};
+        var getList = function($el) {
+          return $el.find('option').map(function() {
+            return {value:this.value, label:this.text};
+          }).get();
+        };
+        if ($el.find('optgroup').length) {
+          list = {opt_labels:[]};
+          _.each($el.find('optgroup'), function(el) {
+            var label = $(el).attr('label');
+            list.opt_labels.push(label);
+            list[label] = getList($(el));
+          });
+        } else {
+          list = getList($el);
+        }
+      }
+
+      // Fill in default label and path values.
+      selectConfig.valuePath = selectConfig.valuePath || 'value';
+      selectConfig.labelPath = selectConfig.labelPath || 'label';
+
+      var addSelectOptions = function(optList, $el, fieldVal) {
+        // Add a flag for default option at the beginning of the list.
+        if (selectConfig.defaultOption) {
+          optList = _.clone(optList);
+          optList.unshift('__default__');
+        }
+        _.each(optList, function(obj) {
+          var option = $('<option/>'), optionVal = obj;
+
+          var fillOption = function(text, val) {
+            option.text(text);
+            optionVal = val;
+            // Save the option value as data so that we can reference it later.
+            option.data('stickit_bind_val', optionVal);
+            if (!_.isArray(optionVal) && !_.isObject(optionVal)) option.val(optionVal);
+          };
+
+          if (obj === '__default__')
+            fillOption(selectConfig.defaultOption.label, selectConfig.defaultOption.value);
+          else
+            fillOption(evaluatePath(obj, selectConfig.labelPath), evaluatePath(obj, selectConfig.valuePath));
+
+          // Determine if this option is selected.
+          if (!isMultiple && optionVal != null && fieldVal != null && optionVal == fieldVal || (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)))
+            option.prop('selected', true);
+          else if (isMultiple && _.isArray(fieldVal)) {
+            _.each(fieldVal, function(val) {
+              if (_.isObject(val)) val = evaluatePath(val, selectConfig.valuePath);
+              if (val == optionVal || (_.isObject(val) && _.isEqual(optionVal, val)))
+                option.prop('selected', true);
+            });
+          }
+
+          $el.append(option);
+        });
+      };
+
+      $el.html('');
+
+      // The `list` configuration is a function that returns the options list or a string
+      // which represents the path to the list relative to `window` or the view/`this`.
+      var evaluate = function(view, list) {
+        var context = window;
+        if (list.indexOf('this.') === 0) context = view;
+        list = list.replace(/^[a-z]*\.(.+)$/, '$1');
+        return evaluatePath(context, list);
+      };
+      if (_.isString(list)) optList = evaluate(this, list);
+      else if (_.isFunction(list)) optList = applyViewFn(this, list, $el, options);
+      else optList = list;
+
+      // Support Backbone.Collection and deserialize.
+      if (optList instanceof Backbone.Collection) optList = optList.toJSON();
+
+      if (_.isArray(optList)) {
+        addSelectOptions(optList, $el, val);
+      } else {
+        // If the optList is an object, then it should be used to define an optgroup. An
+        // optgroup object configuration looks like the following:
+        //
+        //     {
+        //       'opt_labels': ['Looney Tunes', 'Three Stooges'],
+        //       'Looney Tunes': [{id: 1, name: 'Bugs Bunny'}, {id: 2, name: 'Donald Duck'}],
+        //       'Three Stooges': [{id: 3, name : 'moe'}, {id: 4, name : 'larry'}, {id: 5, name : 'curly'}]
+        //     }
+        //
+        _.each(optList.opt_labels, function(label) {
+          var $group = $('<optgroup/>').attr('label', label);
+          addSelectOptions(optList[label], $group, val);
+          $el.append($group);
+        });
+      }
+    },
+    getVal: function($el) {
+      var val;
+      if ($el.prop('multiple')) {
+        val = $(getSelectedOption($el).map(function() {
+          return $(this).data('stickit_bind_val');
+        })).get();
+      } else {
+        val = getSelectedOption($el).data('stickit_bind_val');
+      }
+      return val;
+    }
+  }]);
+
+})(window.jQuery || window.Zepto);

Added: clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/etch.js
URL: http://svn.apache.org/viewvc/clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/etch.js?rev=1530427&view=auto
==============================================================================
--- clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/etch.js (added)
+++ clerezza/trunk/platform.editor/src/main/resources/META-INF/resources/tools/editor/scripts/etch.js Tue Oct  8 21:20:13 2013
@@ -0,0 +1,366 @@
+(function() {
+  'use strict';
+    
+  var models = {},
+    views = {},
+    collections = {},
+    etch = {};
+	
+  // versioning as per semver.org
+  etch.VERSION = '0.6.2';
+
+  etch.config = {
+    // selector to specify editable elements   
+    selector: '.editable',
+      
+    // Named sets of buttons to be specified on the editable element
+    // in the markup as "data-button-class"   
+    buttonClasses: {
+      'default': ['save'],
+      'all': ['bold', 'italic', 'underline', 'unordered-list', 'ordered-list', 'link', 'clear-formatting', 'save'],
+      'title': ['bold', 'italic', 'underline', 'save']
+    }
+  };
+
+  models.Editor = Backbone.Model;
+
+  views.Editor = Backbone.View.extend({
+    initialize: function() {
+      this.$el = $(this.el);
+            
+      // Model attribute event listeners:
+      _.bindAll(this, 'changeButtons', 'changePosition', 'changeEditable', 'insertImage');
+      this.model.bind('change:buttons', this.changeButtons);
+      this.model.bind('change:position', this.changePosition);
+      this.model.bind('change:editable', this.changeEditable);
+
+      // Init Routines:
+      this.changeEditable();
+    },
+
+    events: {
+      'click .etch-bold': 'toggleBold',
+      'click .etch-italic': 'toggleItalic',
+      'click .etch-underline': 'toggleUnderline',
+      'click .etch-heading': 'toggleHeading',
+      'click .etch-unordered-list': 'toggleUnorderedList',
+      'click .etch-justify-left': 'justifyLeft',
+      'click .etch-justify-center': 'justifyCenter',
+      'click .etch-justify-right': 'justifyRight',
+      'click .etch-ordered-list': 'toggleOrderedList',
+      'click .etch-link': 'toggleLink',
+      'click .etch-image': 'getImage',
+      'click .etch-save': 'save',
+      'click .etch-clear-formatting': 'clearFormatting'
+    },
+        
+    changeEditable: function() {
+      this.setButtonClass();
+      // Im assuming that Ill add more functionality here
+    },
+
+    setButtonClass: function() {
+      // check the button class of the element being edited and set the associated buttons on the model
+      var editorModel = this.model;
+      var buttonClass = editorModel.get('editable').attr('data-button-class') || 'default';
+      editorModel.set({ buttons: etch.config.buttonClasses[buttonClass] });
+    },
+
+    changeButtons: function() {
+      // render the buttons into the editor-panel
+      this.$el.empty();
+      var view = this;
+      var buttons = this.model.get('buttons');
+            
+      // hide editor panel if there are no buttons in it and exit early
+      if (!buttons.length) { $(this.el).hide(); return; }
+            
+      _.each(this.model.get('buttons'), function(button){
+        var $buttonEl = $('<a href="#" class="etch-editor-button etch-'+ button +'" title="'+ button.replace('-', ' ') +'"><span></span></a>');
+        view.$el.append($buttonEl);
+      });
+            
+      $(this.el).show('fast');
+    },
+
+    changePosition: function() {
+      // animate editor-panel to new position
+      var pos = this.model.get('position');
+      this.$el.animate({'top': pos.y, 'left': pos.x}, { queue: false });
+    },
+        
+    wrapSelection: function(selectionOrRange, elString, cb) {
+      // wrap current selection with elString tag
+      var range = selectionOrRange === Range ? selectionOrRange : selectionOrRange.getRangeAt(0);
+      var el = document.createElement(elString);
+      range.surroundContents(el);
+    },
+        
+    clearFormatting: function(e) {
+      e.preventDefault();
+      document.execCommand('removeFormat', false, null);
+    },
+        
+    toggleBold: function(e) {
+      e.preventDefault();
+      document.execCommand('bold', false, null);
+    },
+
+    toggleItalic: function(e) {
+      e.preventDefault();
+      document.execCommand('italic', false, null);
+    },
+
+    toggleUnderline: function(e) {
+      e.preventDefault();
+      document.execCommand('underline', false, null);
+    },
+        
+    toggleHeading: function(e) {
+      e.preventDefault();
+      var range = window.getSelection().getRangeAt(0);
+      var wrapper = range.commonAncestorContainer.parentElement
+      if ($(wrapper).is('h3')) {
+        $(wrapper).replaceWith(wrapper.textContent)
+        return;
+      }
+      var h3 = document.createElement('h3');
+      range.surroundContents(h3);
+    },
+
+    urlPrompt: function(callback) {
+      // This uses the default browser UI prompt to get a url.
+      // Override this function if you want to implement a custom UI.
+        
+      var url = prompt('Enter a url', 'http://');
+        
+      // Ensure a new link URL starts with http:// or https:// 
+      // before it's added to the DOM.
+      //
+      // NOTE: This implementation will disallow relative URLs from being added
+      // but will make it easier for users typing external URLs.
+      if (/^((http|https)...)/.test(url)) {
+        callback(url);
+      } else {
+        callback("http://" + url);
+      }
+    },
+    
+    toggleLink: function(e) {
+      e.preventDefault();
+      var range = window.getSelection().getRangeAt(0);
+
+      // are we in an anchor element?
+      if (range.startContainer.parentNode.tagName === 'A' || range.endContainer.parentNode.tagName === 'A') {
+        // unlink anchor
+        document.execCommand('unlink', false, null);
+      } else {
+        // promt for url and create link
+        this.urlPrompt(function(url) {
+          document.execCommand('createLink', false, url);
+        });
+      }
+    },
+
+    toggleUnorderedList: function(e) {
+      e.preventDefault();
+      document.execCommand('insertUnorderedList', false, null);
+    },
+
+    toggleOrderedList: function(e){
+      e.preventDefault();
+      document.execCommand('insertOrderedList', false, null);
+    },
+        
+    justifyLeft: function(e) {
+      e.preventDefault();
+      document.execCommand('justifyLeft', false, null);
+    },
+
+    justifyCenter: function(e) {
+      e.preventDefault();
+      document.execCommand('justifyCenter', false, null);
+    },
+
+    justifyRight: function(e) {
+      e.preventDefault();
+      document.execCommand('justifyRight', false, null);
+    },
+
+    getImage: function(e) {
+      e.preventDefault();
+
+      // call startUploader with callback to handle inserting it once it is uploded/cropped
+      this.startUploader(this.insertImage);
+    },
+        
+    startUploader: function(cb) {
+      // initialize Image Uploader
+      var model = new models.ImageUploader();
+      var view = new views.ImageUploader({model: model});
+            
+      // stash a reference to the callback to be called after image is uploaded
+      model._imageCallback = function(image) {
+        view.startCropper(image, cb);
+      };
+
+
+      // stash reference to saved range for inserting the image once its 
+      this._savedRange = window.getSelection().getRangeAt(0);
+
+      // insert uploader html into DOM
+      $('body').append(view.render().el);
+    },
+        
+    insertImage: function(image) {
+      // insert image - passed as a callback to startUploader
+      var sel = window.getSelection();
+      sel.removeAllRanges();
+      sel.addRange(this._savedRange);
+            
+      var attrs = {
+        'editable': this.model.get('editable'),
+        'editableModel': this.model.get('editableModel')
+      };
+            
+      _.extend(attrs, image);
+
+      var model = new models.EditableImage(attrs);
+      var view = new views.EditableImage({model: model});
+      this._savedRange.insertNode($(view.render().el).addClass('etch-float-left')[0]);
+    },
+        
+    save: function(e) {
+      e.preventDefault();
+      var editableModel = this.model.get('editableModel');
+      editableModel.trigger('save');
+    }
+  });
+
+  // tack on models, views, etc... as well as init function
+  _.extend(etch, {
+    models: models,
+    views: views,
+    collections: collections,
+
+    // This function is to be used as callback to whatever event
+    // you use to initialize editing 
+    editableInit: function(e) {
+      e.stopPropagation();
+      var target = e.target || e.srcElement;
+      var $editable = $(target).etchFindEditable();
+      $editable.attr('contenteditable', true);
+
+      // if the editor isn't already built, build it
+      var $editor = $('.etch-editor-panel');
+      var editorModel = $editor.data('model');
+      if (!$editor.size()) {
+        $editor = $('<div class="etch-editor-panel">');
+        var editorAttrs = { editable: $editable, editableModel: this.model };
+        document.body.appendChild($editor[0]);
+        $editor.etchInstantiate({classType: 'Editor', attrs: editorAttrs});
+        editorModel = $editor.data('model');
+
+      // check if we are on a new editable
+      } else if ($editable[0] !== editorModel.get('editable')[0]) {
+        // set new editable
+        editorModel.set({
+          editable: $editable,
+          editableModel: this.model
+        });
+      }
+      
+      // Firefox seems to be only browser that defaults to `StyleWithCSS == true`
+      // so we turn it off here. Plus a try..catch to avoid an error being thrown in IE8.
+      try {
+        document.execCommand('StyleWithCSS', false, false);
+      }
+      catch (err) {
+        // expecting to just eat IE8 error, but if different error, rethrow
+        if (err.message !== "Invalid argument.") {
+          throw err;
+        }
+      }
+
+      if (models.EditableImage) {
+        // instantiate any images that may be in the editable
+        var $imgs = $editable.find('img');
+        if ($imgs.size()) {
+          var attrs = { editable: $editable, editableModel: this.model };
+          $imgs.each(function() {
+            var $this = $(this);
+            if (!$this.data('editableImageModel')) {
+              var editableImageModel =  new models.EditableImage(attrs);
+              var editableImageView = new views.EditableImage({model: editableImageModel, el: this, tagName: this.tagName});
+              $this.data('editableImageModel', editableImageModel);
+            }
+          });
+        }
+      }
+
+      // listen for mousedowns that are not coming from the editor
+      // and close the editor
+      $('body').bind('mousedown.editor', function(e) {
+        // check to see if the click was in an etch tool
+        var target = e.target || e.srcElement;
+        if ($(target).not('.etch-editor-panel, .etch-editor-panel *, .etch-image-tools, .etch-image-tools *').size()) {
+          // remove editor
+          $editor.remove();
+                    
+                    
+          if (models.EditableImage) {
+            // unblind the image-tools if the editor isn't active
+            $editable.find('img').unbind('mouseenter');
+
+            // remove any latent image tool model references
+            $(etch.config.selector+' img').data('editableImageModel', false)
+          }
+                    
+          // once the editor is removed, remove the body binding for it
+          $(this).unbind('mousedown.editor');
+        }
+      });
+
+      editorModel.set({position: {x: e.pageX - 15, y: e.pageY - 80}});
+    }
+  });
+
+  // jquery helper functions
+  $.fn.etchInstantiate = function(options, cb) {
+    return this.each(function() {
+      var $el = $(this);
+      options || (options = {});
+
+      var settings = {
+        el: this,
+        attrs: {}
+      }
+
+      _.extend(settings, options);
+
+      var model = new models[settings.classType](settings.attrs, settings);
+
+      // initialize a view is there is one
+      if (_.isFunction(views[settings.classType])) {
+        var view = new views[settings.classType]({model: model, el: this, tagName: this.tagName});
+      }
+           
+      // stash the model and view on the elements data object
+      $el.data({model: model});
+      $el.data({view: view});
+
+      if (_.isFunction(cb)) {
+        cb({model: model, view: view});
+      }
+    });
+  }
+
+  $.fn.etchFindEditable = function() {
+    // function that looks for the editable selector on itself or its parents
+    // and returns that el when it is found
+    var $el = $(this);
+    return $el.is(etch.config.selector) ? $el : $el.closest(etch.config.selector);
+  }
+    
+  window.etch = etch;
+})();