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;
+})();