Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: adamsanderson/ivy
base: 1519d22a61
...
head fork: adamsanderson/ivy
compare: 54c662c15d
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 6 files changed
  • 0 commit comments
  • 1 contributor
View
9 api.html
@@ -71,7 +71,8 @@ <h4 class='fn-signature'>
<a name='IvyAttr.prototype.set' href='#IvyAttr.prototype.set'>IvyAttr.prototype.set(value)</a>
</h4>
- <p>Sets the internal value of the attribute.</p>
+ <p>Sets the internal value of the attribute, and emits a <code>change</code>
+event with the new value and the old value as the arguments.</p>
</div>
<div class='fn'>
<h4 class='fn-signature'>
@@ -89,8 +90,10 @@ <h4 class='fn-signature'>
The most common event is <code>change</code>.
</p>
<pre><code>var a = Ivy.attr(4);
-a.on(&#39;change&#39;, function(value){ console.log(&#39;New value is&#39;, value); });
-a.set(5); //=&gt; &#39;New value is 5&#39;</code></pre>
+a.on(&#39;change&#39;, function(value,oldValue){
+ console.log(&#39;New value is&#39;, value, &#39;old value was&#39;, oldValue);
+});
+a.set(5); //=&gt; &#39;New value is 5 old value was 4&#39;</code></pre>
</div>
<div class='fn'>
<h4 class='fn-signature'>
View
32 index.html
@@ -15,7 +15,8 @@
<div class='col col-9'>
<h1>Ivy.js</h1>
<p>Ivy ties JavaScript objects to the DOM, allowing you to focus on the
-relationships of your data.
+relationships of your data. It does this with no external dependencies,
+and with a small footprint (9k minified).
</p>
<h2>Using Ivy</h2>
@@ -30,9 +31,9 @@
&lt;script&gt;
Ivy.bindDom({name: Ivy.attr(&#39;World&#39;)});
&lt;/script&gt;</code></pre>
-<p>Ivy will fill in &quot;World&quot; both in the span, and in the input. If you change the input, the span
-will also change. You don&#39;t need to write event listeners, or update the HTML, Ivy does that for
-you.
+<p>Ivy will fill in &quot;World&quot; both in the span, and in the input. If you change the
+input, the span will also change. You don&#39;t need to write event listeners, or
+update the HTML, Ivy does that for you.
</p>
<p>You can also bind complex objects and functions:
@@ -56,16 +57,17 @@
Ivy.bindDom( new Calculator(20.00) );
&lt;/script&gt;</code></pre>
<p>Ivy plays nicely with others, and stays out of the way when it&#39;s not needed.
-In many cases you can treat the value encapsulated by an <code>Ivy.attr</code> like a normal object:
+In many cases you can treat the value encapsulated by an <code>Ivy.attr</code> like a
+normal object:
</p>
<pre><code>Ivy.attr(3) + Ivy.attr(4); //=&gt; 7
var point = {x: Ivy.attr(2.0), y: Ivy.attr(4.6)};
JSON.stringify(point); //=&gt; &quot;{x: 2.0, y: 4.6}&quot;</code></pre>
-<p>You can use Ivy as much, or as little as you like.
-If a value like a user&#39;s id will never change, then don&#39;t wrap it. If you only want to bind
-a model to a specific part of your HTML, pass Ivy an element or its id:
+<p>Use Ivy as much, or as little as you like. If a value like a user&#39;s id will
+never change, then don&#39;t wrap it. If you only want to bind a model to a
+specific part of your HTML, pass Ivy an element or its id:
</p>
<pre><code>&lt;h1 id=&#39;user-greeting&#39;&gt;
@@ -77,10 +79,11 @@
Ivy.bindDom( {name: Ivy.attr(&#39;Mr. Monkey&#39;), id: 32}, &#39;user-greeting&#39;);
&lt;/script&gt;</code></pre>
<h2>Why Ivy</h2>
-<p>Ivy&#39;s design cleanly separates your HTML and JavaScript. Ivy&#39;s bindings ensure that
-the information about where and how your data is displayed stays in the HTML. Your
-JavaScript does not need to keep references to the DOM since that&#39;s Ivy&#39;s job. This
-separation lets each layer of your application focus on its immediate concern.
+<p>Ivy&#39;s design cleanly separates your HTML and JavaScript. Ivy&#39;s bindings ensure
+that the information about where and how your data is displayed stays in the
+HTML. Your JavaScript does not need to keep references to the DOM since that&#39;s
+Ivy&#39;s job. This separation lets each layer of your application focus on its
+immediate concern.
</p>
<p>Writing event handling and DOM manipulation code is tedious, Ivy does most of
@@ -112,8 +115,9 @@
<li>Because Ivy binds to DOM Elements, your HTML can get verbose</li>
</ul>
<h2>Browser Support</h2>
-<p>Ivy works great in modern browsers like Firefox, and Chrome, and IE9. IE8 is supported,
-but I am still looking into better ways to minimize its impact on the general code base.
+<p>Ivy works great in modern browsers like Firefox, and Chrome, and IE9. IE8 is
+supported, but I am still looking into better ways to minimize its impact on the
+general code base.
</p>
<h2>Attributions</h2>
View
20 ivy.js
@@ -29,15 +29,18 @@ function IvyAttr(value, parseFn){
};
/**
- * Sets the internal value of the attribute.
+ * Sets the internal value of the attribute, and emits a `change`
+ * event with the new value and the old value as the arguments.
*/
IvyAttr.prototype.set = function(value){
+ var oldValue = this.value;
+
value = this.parseFn ? this.parseFn(value) : value;
- if (this.value === value) return this;
+ if (oldValue === value) return this;
this.value = value;
- this.emit('change', this.get());
+ this.emit('change', this.get(), oldValue);
return this;
};
@@ -54,8 +57,11 @@ IvyAttr.prototype.get = function(){
* The most common event is `change`.
*
* var a = Ivy.attr(4);
- * a.on('change', function(value){ console.log('New value is', value); });
- * a.set(5); //=> 'New value is 5'
+ * a.on('change', function(value,oldValue){
+ * console.log('New value is', value, 'old value was', oldValue);
+ * });
+ *
+ * a.set(5); //=> 'New value is 5 old value was 4'
*
*/
IvyAttr.prototype.on = function(event, fn){
@@ -871,10 +877,6 @@ Ivy.dom._template = document.createElement('div');
// ----------------------------------------------------------------------------
Ivy.util = {};
-Ivy.util.copy = function(src){
- return JSON.parse(JSON.stringify(src));
-};
-
// From prototype.js
Ivy.util.argumentNames = function(fn){
var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
View
2  ivy.min.js
@@ -1 +1 @@
-function IvyAttr(e,t){if(!(this instanceof IvyAttr))return new IvyAttr(e,t);this.value=e,this.parseFn=t,this.callbacks={},this._id=Ivy._id++}function IvyArray(e){if(!(this instanceof Ivy.array))return new Ivy.array(e);this.value=e||[],this.callbacks={},this._id=Ivy._id++}function IvyWrap(e,t){if(!(this instanceof IvyWrap))return new IvyWrap(e,t);typeof t=="function"&&(t={get:t}),t.get=t.get||function(e){return e},t.set=t.set||function(e){return e},this.attr=e,this.wrapper=t,this.callbacks={},this._id=Ivy._id++;var n=this;this.attr.on("change",function(e){e=n.wrapper.get(e),n.emit.call(n,"change",e)})}Ivy={attr:IvyAttr,array:IvyArray,wrap:IvyWrap},IvyAttr.prototype.set=function(e){return e=this.parseFn?this.parseFn(e):e,this.value===e?this:(this.value=e,this.emit("change",this.get()),this)},IvyAttr.prototype.get=function(){return this.value},IvyAttr.prototype.on=function(e,t){return(this.callbacks[e]=this.callbacks[e]||[]).push(t),this},IvyAttr.prototype.off=function(e,t){var n=this.callbacks[e];if(!n)return this;if(arguments.length===1)delete this.callbacks[e];else{var r=Ivy.util.indexOf(n,t);n.splice(r,1)}return this},IvyAttr.prototype.emit=function(e){var t=[].slice.call(arguments,1),n=this.callbacks[e];if(n){var r=Ivy._callers[e]=Ivy._callers[e]||{};if(r[this._id])return console.warn("Cycle detected on",this._id);r[this._id]=1;for(var i=0,s=n.length;i<s;++i)n[i].apply(this,t);delete r[this._id]}return this},IvyAttr.prototype.valueOf=function(){return this.get()},IvyAttr.prototype.toJSON=function(){return this.get()},IvyArray.prototype=new IvyAttr,IvyArray.prototype.set=function(e,t){if(arguments.length===1)return this.replace(e);var n=this.value[e];return this.value[e]=t,this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[n]).add(e,[t])),this},IvyArray.prototype.get=function(e){return arguments.length===0?this.value:this.value[e]},IvyArray.prototype.push=function(e){var t=this.length;return this.value.push(e),this.emit("change",this.get(),(new Ivy.ChangeSet).add(t,[e])),this},IvyArray.prototype.unshift=function(e){return this.value.unshift(e),this.emit("change",this.get(),(new Ivy.ChangeSet).add(0,[e])),this},IvyArray.prototype.pop=function(){var e=this.length,t=this.value.pop();return this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[t])),t},IvyArray.prototype.shift=function(){var e=this.value.shift();return this.emit("change",this.get(),(new Ivy.ChangeSet).remove(0,[e])),e},IvyArray.prototype.replace=function(e){var t=this.value;return this.value=e,this.emit("change",this.get(),(new Ivy.ChangeSet).remove(0,t).add(0,e)),this},IvyArray.prototype.remove=function(e){var t=Ivy.util.indexOf(this.value,e);return this.removeIndex(t)},IvyArray.prototype.removeEach=function(e){var t=this.value.length,n=[],r;while(t--)r=this.value[t],e(r)&&n.push(this.removeIndex(t));return n},IvyArray.prototype.removeIndex=function(e){if(e===-1)return;var t=this.value.splice(e,1)[0];return t&&this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[t])),t},IvyArray.prototype.onEach=function(e,t,n){function i(r,i){for(var s=0;s<i.length;s++){var o=n?n(i[s]):i[s];if(!o)continue;o[r==="add"?"on":"off"](e,t)}}var r;return this.on("change",function(e,t){for(var n=0;n<t.length;n++)r=t[n],i(r.operation,r.items)}),i("add",this.value),this},IvyArray.prototype.length=function(){return this.value.length},Ivy.ChangeSet=function(){},Ivy.ChangeSet.prototype=[],Ivy.ChangeSet.prototype.add=function(e,t){return this.push({operation:"add",index:e,items:t}),this},Ivy.ChangeSet.prototype.remove=function(e,t){return this.push({operation:"remove",index:e,items:t}),this},IvyWrap.prototype=new IvyAttr,IvyWrap.prototype.get=function(){var e=this.attr.get();return this.wrapper.get(e)},IvyWrap.prototype.set=function(e){return e=this.wrapper.set(e),this.attr.set(e),this},Ivy.fn=function(){function r(){n.set(t.apply(n,e))}var e=Array.prototype.slice.call(arguments),t=e.pop(),n=IvyAttr();for(var i=0;i<e.length;i++)e[i].on("change",r);return r(),n},Ivy.fnWith=function(e,t){var n=Ivy.util.argumentNames(t),r=[];for(var i=0,s=n.length;i<s;i++)r.push(e[n[i]]);return r.push(t),Ivy.fn.apply(this,r)},Ivy.bindAttrToValue=function(e,t,n){function s(t){if(document.activeElement===e){if(i)return;i=function(t){s(r.valueOf()),Ivy.dom.off(e,"blur",i),i=null},Ivy.dom.on(e,"blur",i)}else e.value=t}function o(){r.set(e.value)}var r=this.atPath(t),i;n=n||"change",Ivy.watchAttr(r,"change",s),r.set&&Ivy.dom.on(e,n,o)},Ivy.bindAttrToChecked=function(e,t,n){function s(t){e.checked=i?t==e.value:!!t}function o(){i?e.checked&&r.set(e.value):r.set(!!e.checked)}var r=this.atPath(t),i=e.type==="radio";n=n||"click",Ivy.watchAttr(r,"change",s),r.set&&Ivy.dom.on(e,n,o)},Ivy.bindAttrToText=function(e,t){function r(t){Ivy.dom.clear(e),e.appendChild(document.createTextNode(t))}var n=this.atPath(t);Ivy.watchAttr(n,"change",r)},Ivy.bindAttrToClassName=function(e,t,n,r){function s(t){var i=e.className.split(/\s+/),s=[],o=t.valueOf()?n:r,u=t.valueOf()?r:n,a;for(var f=0,l=i.length,c;f<l;f++)c=i[f],a|=c===o,c!=u&&c!=""&&s.push(c);a||s.push(o),e.className=s.join(" ")}var i=this.atPath(t);Ivy.watchAttr(i,"change",s)},Ivy.bindAttrToDomAttr=function(e,t,n){function s(t){i?t?e.setAttribute(n,n):e.removeAttribute(n):e.setAttribute(n,t)}var r=this.atPath(t),i=Ivy.bindAttrToDomAttr.booleanProperties[n];Ivy.watchAttr(r,"change",s)},Ivy.bindAttrToDomAttr.booleanProperties={disabled:!0},Ivy.bindAttrToFocus=function(e,t){function r(t){setTimeout(function(){t?e.focus():e.blur()})}function i(){n.set(document.activeElement===e)}var n=this.atPath(t);Ivy.watchAttr(n,"change",r),n.set&&(Ivy.dom.on(e,"blur",i),Ivy.dom.on(e,"focus",i))},Ivy.bindAttrToEach=function(e,t,n){function o(t){Ivy.dom.clear(e);for(var n=0,r=t.length;n<r;n++){var o=i.cloneNode(!0);Ivy.bindDom(o,t[n],s),e.appendChild(o)}}var r=this.atPath(t),i=Ivy.dom.getTemplate(e,n),s=this.context;e.__managed=!0,Ivy.watchAttr(r,"change",o)},Ivy.bindAttrToWith=function(e,t,n){function o(t){var n=s.cloneNode(!0);Ivy.dom.clear(e),Ivy.bindDom(n,t,i),e.appendChild(n)}var r=this.atPath(t),i=this.context,s=Ivy.dom.getTemplate(e,n);e.__managed=!0,Ivy.watchAttr(r,"change",o)},Ivy.bindAttrToWith.templateParent=document.createElement("div"),Ivy.bindAttrToShow=function(e,t){function i(t){e.style.display=t?r:"none"}var n=this.atPath(t),r=e.style.display;Ivy.watchAttr(n,"change",i)},Ivy.bindFnToEvent=function(e,t,n){function o(e,t){return function(n){r.call(e,t),n.preventDefault?n.preventDefault():n.returnValue=!1}}var r=this.atPath(n),i=this.atPath(n.split("/").slice(0,-1).join("/")),s=this.context;s["ivy:proto"]&&(s=s["ivy:proto"]),Ivy.dom.on(e,t,o(i,s))},Ivy.bindings={value:Ivy.bindAttrToValue,checked:Ivy.bindAttrToChecked,text:Ivy.bindAttrToText,attr:Ivy.bindAttrToDomAttr,"class":Ivy.bindAttrToClassName,show:Ivy.bindAttrToShow,focus:Ivy.bindAttrToFocus,each:Ivy.bindAttrToEach,"with":Ivy.bindAttrToWith,on:Ivy.bindFnToEvent},Ivy.watchAttr=function(e,t,n){e.on&&e.on(t,n),n(e.valueOf())},Ivy.bindDom=function(e,t,n){var e=e||document.body,t=t||window,r;typeof e=="string"&&(e=document.getElementById(e)),n&&(t=Ivy.util.beget(t),t[".."]=n);if(e.nodeType===Ivy.dom.ELEMENT_NODE){r=Ivy.getBindings(e,t);if(r)for(var i=0,s=r.length;i<s;i++)Ivy.bindElement(e,r[i])}if(e.__managed)return;for(var i=0,o=e.children||e.childNodes,s=o.length;i<s;i++)Ivy.bindDom(o[i],t)},Ivy.getBindings=function(e,t){var n=e.getAttribute("data-bind"),r=[],i;if(!n)return null;n=n.replace(/^\s*/m,""),i=n.split(/\s*;\s*/);for(var s=0,o=i.length;s<o;s++){if(i[s].match(/^\s*$/))continue;r.push(new Ivy.BindingRule(i[s],t))}return r},Ivy.bindElement=function(e,t){var n=t.name,r=[e].concat(t.options),i;i=Ivy.bindings[n],i?i.apply(t,r):console.warn("Unkown binding: ",n,t)},Ivy.BindingRule=function(e,t){var n=e.replace(/^\s*/m,"").split(/\s+/),r=n.shift();if(r[r.length-1]!=":")throw new Error("Invalid syntax for binding name.\n "+e);this.name=r.slice(0,-1),this.options=n,this.context=t},Ivy.BindingRule.prototype.atPath=function(e,t){return t=t||this.context,e==="."||e===""?t:e.indexOf("../")===0?this.atPath(e.slice(3),t[".."]):t[e]},Ivy.dom={},Ivy.dom.ELEMENT_NODE=document.ELEMENT_NODE||1,document.addEventListener?(Ivy.dom.on=function(e,t,n){e.addEventListener(t,n)},Ivy.dom.off=function(e,t,n){e.removeEventListener(t,n)}):(Ivy.dom.on=function(e,t,n){e.attachEvent("on"+t,n)},Ivy.dom.off=function(e,t,n){e.detachEvent("on"+t,n)}),Ivy.dom.clear=function(e){while(e.hasChildNodes())e.removeChild(e.firstChild)},Ivy.dom.detachChildren=function(e){var t=document.createDocumentFragment(),n=e.children,r=n.length;while(r--)t.appendChild(n[0]);return t},Ivy.dom.getTemplate=function(e,t){var n;if(t){var r=document.getElementById(t);Ivy.dom._template.innerHTML=r.innerText||r.textContent||r.innerHTML,n=Ivy.dom.detachChildren(Ivy.dom._template)}else n=Ivy.dom.detachChildren(e);return n},Ivy.dom._template=document.createElement("div"),Ivy.util={},Ivy.util.copy=function(e){return JSON.parse(JSON.stringify(e))},Ivy.util.argumentNames=function(e){var t=e.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g,"").split(",");return t.length==1&&!t[0]?[]:t},Ivy.util.indexOf=function(e,t,n){if(e.indexOf)return e.indexOf(t,n);for(var r=n||0,i=e.length;r<i;r++)if(e[r]===t)return r;return-1},Ivy.util.beget=function(e){var t=function(){};t.prototype=e;var n=new t;return n["ivy:proto"]=e,n},Ivy.util.bind=function(e,t){return function(){e.call(t,arguments)}},Ivy._id=1,Ivy._callers={}
+function IvyAttr(e,t){if(!(this instanceof IvyAttr))return new IvyAttr(e,t);this.value=e,this.parseFn=t,this.callbacks={},this._id=Ivy._id++}function IvyArray(e){if(!(this instanceof Ivy.array))return new Ivy.array(e);this.value=e||[],this.callbacks={},this._id=Ivy._id++}function IvyWrap(e,t){if(!(this instanceof IvyWrap))return new IvyWrap(e,t);typeof t=="function"&&(t={get:t}),t.get=t.get||function(e){return e},t.set=t.set||function(e){return e},this.attr=e,this.wrapper=t,this.callbacks={},this._id=Ivy._id++;var n=this;this.attr.on("change",function(e){e=n.wrapper.get(e),n.emit.call(n,"change",e)})}Ivy={attr:IvyAttr,array:IvyArray,wrap:IvyWrap},IvyAttr.prototype.set=function(e){var t=this.value;return e=this.parseFn?this.parseFn(e):e,t===e?this:(this.value=e,this.emit("change",this.get(),t),this)},IvyAttr.prototype.get=function(){return this.value},IvyAttr.prototype.on=function(e,t){return(this.callbacks[e]=this.callbacks[e]||[]).push(t),this},IvyAttr.prototype.off=function(e,t){var n=this.callbacks[e];if(!n)return this;if(arguments.length===1)delete this.callbacks[e];else{var r=Ivy.util.indexOf(n,t);n.splice(r,1)}return this},IvyAttr.prototype.emit=function(e){var t=[].slice.call(arguments,1),n=this.callbacks[e];if(n){var r=Ivy._callers[e]=Ivy._callers[e]||{};if(r[this._id])return console.warn("Cycle detected on",this._id);r[this._id]=1;for(var i=0,s=n.length;i<s;++i)n[i].apply(this,t);delete r[this._id]}return this},IvyAttr.prototype.valueOf=function(){return this.get()},IvyAttr.prototype.toJSON=function(){return this.get()},IvyArray.prototype=new IvyAttr,IvyArray.prototype.set=function(e,t){if(arguments.length===1)return this.replace(e);var n=this.value[e];return this.value[e]=t,this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[n]).add(e,[t])),this},IvyArray.prototype.get=function(e){return arguments.length===0?this.value:this.value[e]},IvyArray.prototype.push=function(e){var t=this.length;return this.value.push(e),this.emit("change",this.get(),(new Ivy.ChangeSet).add(t,[e])),this},IvyArray.prototype.unshift=function(e){return this.value.unshift(e),this.emit("change",this.get(),(new Ivy.ChangeSet).add(0,[e])),this},IvyArray.prototype.pop=function(){var e=this.length,t=this.value.pop();return this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[t])),t},IvyArray.prototype.shift=function(){var e=this.value.shift();return this.emit("change",this.get(),(new Ivy.ChangeSet).remove(0,[e])),e},IvyArray.prototype.replace=function(e){var t=this.value;return this.value=e,this.emit("change",this.get(),(new Ivy.ChangeSet).remove(0,t).add(0,e)),this},IvyArray.prototype.remove=function(e){var t=Ivy.util.indexOf(this.value,e);return this.removeIndex(t)},IvyArray.prototype.removeEach=function(e){var t=this.value.length,n=[],r;while(t--)r=this.value[t],e(r)&&n.push(this.removeIndex(t));return n},IvyArray.prototype.removeIndex=function(e){if(e===-1)return;var t=this.value.splice(e,1)[0];return t&&this.emit("change",this.get(),(new Ivy.ChangeSet).remove(e,[t])),t},IvyArray.prototype.onEach=function(e,t,n){function i(r,i){for(var s=0;s<i.length;s++){var o=n?n(i[s]):i[s];if(!o)continue;o[r==="add"?"on":"off"](e,t)}}var r;return this.on("change",function(e,t){for(var n=0;n<t.length;n++)r=t[n],i(r.operation,r.items)}),i("add",this.value),this},IvyArray.prototype.length=function(){return this.value.length},Ivy.ChangeSet=function(){},Ivy.ChangeSet.prototype=[],Ivy.ChangeSet.prototype.add=function(e,t){return this.push({operation:"add",index:e,items:t}),this},Ivy.ChangeSet.prototype.remove=function(e,t){return this.push({operation:"remove",index:e,items:t}),this},IvyWrap.prototype=new IvyAttr,IvyWrap.prototype.get=function(){var e=this.attr.get();return this.wrapper.get(e)},IvyWrap.prototype.set=function(e){return e=this.wrapper.set(e),this.attr.set(e),this},Ivy.fn=function(){function r(){n.set(t.apply(n,e))}var e=Array.prototype.slice.call(arguments),t=e.pop(),n=IvyAttr();for(var i=0;i<e.length;i++)e[i].on("change",r);return r(),n},Ivy.fnWith=function(e,t){var n=Ivy.util.argumentNames(t),r=[];for(var i=0,s=n.length;i<s;i++)r.push(e[n[i]]);return r.push(t),Ivy.fn.apply(this,r)},Ivy.bindAttrToValue=function(e,t,n){function s(t){if(document.activeElement===e){if(i)return;i=function(t){s(r.valueOf()),Ivy.dom.off(e,"blur",i),i=null},Ivy.dom.on(e,"blur",i)}else e.value=t}function o(){r.set(e.value)}var r=this.atPath(t),i;n=n||"change",Ivy.watchAttr(r,"change",s),r.set&&Ivy.dom.on(e,n,o)},Ivy.bindAttrToChecked=function(e,t,n){function s(t){e.checked=i?t==e.value:!!t}function o(){i?e.checked&&r.set(e.value):r.set(!!e.checked)}var r=this.atPath(t),i=e.type==="radio";n=n||"click",Ivy.watchAttr(r,"change",s),r.set&&Ivy.dom.on(e,n,o)},Ivy.bindAttrToText=function(e,t){function r(t){Ivy.dom.clear(e),e.appendChild(document.createTextNode(t))}var n=this.atPath(t);Ivy.watchAttr(n,"change",r)},Ivy.bindAttrToClassName=function(e,t,n,r){function s(t){var i=e.className.split(/\s+/),s=[],o=t.valueOf()?n:r,u=t.valueOf()?r:n,a;for(var f=0,l=i.length,c;f<l;f++)c=i[f],a|=c===o,c!=u&&c!=""&&s.push(c);a||s.push(o),e.className=s.join(" ")}var i=this.atPath(t);Ivy.watchAttr(i,"change",s)},Ivy.bindAttrToDomAttr=function(e,t,n){function s(t){i?t?e.setAttribute(n,n):e.removeAttribute(n):e.setAttribute(n,t)}var r=this.atPath(t),i=Ivy.bindAttrToDomAttr.booleanProperties[n];Ivy.watchAttr(r,"change",s)},Ivy.bindAttrToDomAttr.booleanProperties={disabled:!0},Ivy.bindAttrToFocus=function(e,t){function r(t){setTimeout(function(){t?e.focus():e.blur()})}function i(){n.set(document.activeElement===e)}var n=this.atPath(t);Ivy.watchAttr(n,"change",r),n.set&&(Ivy.dom.on(e,"blur",i),Ivy.dom.on(e,"focus",i))},Ivy.bindAttrToEach=function(e,t,n){function o(t){Ivy.dom.clear(e);for(var n=0,r=t.length;n<r;n++){var o=i.cloneNode(!0);Ivy.bindDom(o,t[n],s),e.appendChild(o)}}var r=this.atPath(t),i=Ivy.dom.getTemplate(e,n),s=this.context;e.__managed=!0,Ivy.watchAttr(r,"change",o)},Ivy.bindAttrToWith=function(e,t,n){function o(t){var n=s.cloneNode(!0);Ivy.dom.clear(e),Ivy.bindDom(n,t,i),e.appendChild(n)}var r=this.atPath(t),i=this.context,s=Ivy.dom.getTemplate(e,n);e.__managed=!0,Ivy.watchAttr(r,"change",o)},Ivy.bindAttrToWith.templateParent=document.createElement("div"),Ivy.bindAttrToShow=function(e,t){function i(t){e.style.display=t?r:"none"}var n=this.atPath(t),r=e.style.display;Ivy.watchAttr(n,"change",i)},Ivy.bindFnToEvent=function(e,t,n){function o(e,t){return function(n){r.call(e,t),n.preventDefault?n.preventDefault():n.returnValue=!1}}var r=this.atPath(n),i=this.atPath(n.split("/").slice(0,-1).join("/")),s=this.context;s["ivy:proto"]&&(s=s["ivy:proto"]),Ivy.dom.on(e,t,o(i,s))},Ivy.bindings={value:Ivy.bindAttrToValue,checked:Ivy.bindAttrToChecked,text:Ivy.bindAttrToText,attr:Ivy.bindAttrToDomAttr,"class":Ivy.bindAttrToClassName,show:Ivy.bindAttrToShow,focus:Ivy.bindAttrToFocus,each:Ivy.bindAttrToEach,"with":Ivy.bindAttrToWith,on:Ivy.bindFnToEvent},Ivy.watchAttr=function(e,t,n){e.on&&e.on(t,n),n(e.valueOf())},Ivy.bindDom=function(e,t,n){var e=e||document.body,t=t||window,r;typeof e=="string"&&(e=document.getElementById(e)),n&&(t=Ivy.util.beget(t),t[".."]=n);if(e.nodeType===Ivy.dom.ELEMENT_NODE){r=Ivy.getBindings(e,t);if(r)for(var i=0,s=r.length;i<s;i++)Ivy.bindElement(e,r[i])}if(e.__managed)return;for(var i=0,o=e.children||e.childNodes,s=o.length;i<s;i++)Ivy.bindDom(o[i],t)},Ivy.getBindings=function(e,t){var n=e.getAttribute("data-bind"),r=[],i;if(!n)return null;n=n.replace(/^\s*/m,""),i=n.split(/\s*;\s*/);for(var s=0,o=i.length;s<o;s++){if(i[s].match(/^\s*$/))continue;r.push(new Ivy.BindingRule(i[s],t))}return r},Ivy.bindElement=function(e,t){var n=t.name,r=[e].concat(t.options),i;i=Ivy.bindings[n],i?i.apply(t,r):console.warn("Unkown binding: ",n,t)},Ivy.BindingRule=function(e,t){var n=e.replace(/^\s*/m,"").split(/\s+/),r=n.shift();if(r[r.length-1]!=":")throw new Error("Invalid syntax for binding name.\n "+e);this.name=r.slice(0,-1),this.options=n,this.context=t},Ivy.BindingRule.prototype.atPath=function(e,t){return t=t||this.context,e==="."||e===""?t:e.indexOf("../")===0?this.atPath(e.slice(3),t[".."]):t[e]},Ivy.dom={},Ivy.dom.ELEMENT_NODE=document.ELEMENT_NODE||1,document.addEventListener?(Ivy.dom.on=function(e,t,n){e.addEventListener(t,n)},Ivy.dom.off=function(e,t,n){e.removeEventListener(t,n)}):(Ivy.dom.on=function(e,t,n){e.attachEvent("on"+t,n)},Ivy.dom.off=function(e,t,n){e.detachEvent("on"+t,n)}),Ivy.dom.clear=function(e){while(e.hasChildNodes())e.removeChild(e.firstChild)},Ivy.dom.detachChildren=function(e){var t=document.createDocumentFragment(),n=e.children,r=n.length;while(r--)t.appendChild(n[0]);return t},Ivy.dom.getTemplate=function(e,t){var n;if(t){var r=document.getElementById(t);Ivy.dom._template.innerHTML=r.innerText||r.textContent||r.innerHTML,n=Ivy.dom.detachChildren(Ivy.dom._template)}else n=Ivy.dom.detachChildren(e);return n},Ivy.dom._template=document.createElement("div"),Ivy.util={},Ivy.util.argumentNames=function(e){var t=e.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g,"").split(",");return t.length==1&&!t[0]?[]:t},Ivy.util.indexOf=function(e,t,n){if(e.indexOf)return e.indexOf(t,n);for(var r=n||0,i=e.length;r<i;r++)if(e[r]===t)return r;return-1},Ivy.util.beget=function(e){var t=function(){};t.prototype=e;var n=new t;return n["ivy:proto"]=e,n},Ivy.util.bind=function(e,t){return function(){e.call(t,arguments)}},Ivy._id=1,Ivy._callers={}
View
32 readme.markdown
@@ -1,7 +1,8 @@
Ivy.js
======
Ivy ties JavaScript objects to the DOM, allowing you to focus on the
-relationships of your data.
+relationships of your data. It does this with no external dependencies,
+and with a small footprint (9k minified).
Using Ivy
---------
@@ -16,9 +17,9 @@ Define attributes with `Ivy.attr`, and then bind them in HTML using a `data-bind
Ivy.bindDom({name: Ivy.attr('World')});
</script>
-Ivy will fill in "World" both in the span, and in the input. If you change the input, the span
-will also change. You don't need to write event listeners, or update the HTML, Ivy does that for
-you.
+Ivy will fill in "World" both in the span, and in the input. If you change the
+input, the span will also change. You don't need to write event listeners, or
+update the HTML, Ivy does that for you.
You can also bind complex objects and functions:
@@ -41,16 +42,17 @@ You can also bind complex objects and functions:
</script>
Ivy plays nicely with others, and stays out of the way when it's not needed.
-In many cases you can treat the value encapsulated by an `Ivy.attr` like a normal object:
+In many cases you can treat the value encapsulated by an `Ivy.attr` like a
+normal object:
Ivy.attr(3) + Ivy.attr(4); //=> 7
var point = {x: Ivy.attr(2.0), y: Ivy.attr(4.6)};
JSON.stringify(point); //=> "{x: 2.0, y: 4.6}"
-You can use Ivy as much, or as little as you like.
-If a value like a user's id will never change, then don't wrap it. If you only want to bind
-a model to a specific part of your HTML, pass Ivy an element or its id:
+Use Ivy as much, or as little as you like. If a value like a user's id will
+never change, then don't wrap it. If you only want to bind a model to a
+specific part of your HTML, pass Ivy an element or its id:
<h1 id='user-greeting'>
Greetings <span data-bind='text: name'> </span>
@@ -63,10 +65,11 @@ a model to a specific part of your HTML, pass Ivy an element or its id:
Why Ivy
-------
-Ivy's design cleanly separates your HTML and JavaScript. Ivy's bindings ensure that
-the information about where and how your data is displayed stays in the HTML. Your
-JavaScript does not need to keep references to the DOM since that's Ivy's job. This
-separation lets each layer of your application focus on its immediate concern.
+Ivy's design cleanly separates your HTML and JavaScript. Ivy's bindings ensure
+that the information about where and how your data is displayed stays in the
+HTML. Your JavaScript does not need to keep references to the DOM since that's
+Ivy's job. This separation lets each layer of your application focus on its
+immediate concern.
Writing event handling and DOM manipulation code is tedious, Ivy does most of
this for you. Now you can spend your time fiddling around with something more
@@ -94,8 +97,9 @@ Issues at the moment:
Browser Support
---------------
-Ivy works great in modern browsers like Firefox, and Chrome, and IE9. IE8 is supported,
-but I am still looking into better ways to minimize its impact on the general code base.
+Ivy works great in modern browsers like Firefox, and Chrome, and IE9. IE8 is
+supported, but I am still looking into better ways to minimize its impact on the
+general code base.
Attributions
------------
View
7 test/tests.js
@@ -65,11 +65,12 @@ describe('IvyAttr', function(){
assert.equal(attr.get(), 5);
});
- it('emits a change event with the new value', function(done){
- var attr = Ivy.attr();
+ it('emits a change event with the new value and the old value', function(done){
+ var attr = Ivy.attr(5);
- attr.on('change', function(value){
+ attr.on('change', function(value, oldValue){
assert.equal(value, 7);
+ assert.equal(oldValue, 5);
done();
});

No commit comments for this range

Something went wrong with that request. Please try again.