From 252965ca8db99b6537b0fbbf143ab4496ec397b2 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Thu, 19 Dec 2013 22:13:57 +0000 Subject: [PATCH 1/7] Added some missing tests for toJSON --- test/tests/module.toJSON.js | 58 ++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/test/tests/module.toJSON.js b/test/tests/module.toJSON.js index 35cf748..06f31b1 100644 --- a/test/tests/module.toJSON.js +++ b/test/tests/module.toJSON.js @@ -1,13 +1,69 @@ buster.testCase('View#toJSON()', { - "Should return an fmid": function() { + setUp: helpers.createView, + + "Should return an object": function() { + var apple = new Apple(); + var json = apple.toJSON(); + + assert(json instanceof Object); + }, + + "Should call toJSON of child": function() { + var apple = new Apple(); + var orange = new Orange(); + var spy = this.spy(orange, 'toJSON'); + + apple.add(orange); + + apple.toJSON(); + assert(spy.called); + }, + + "Should add the id": function() { + var apple = new Apple(); + var json = apple.toJSON(); + + assert(json.id); + }, + + "Should add the fmid": function() { var apple = new Apple(); var json = apple.toJSON(); assert(json.fmid); }, + "Should add the module name": function() { + var apple = new Apple(); + var json = apple.toJSON(); + + assert.equals(json.module, 'apple'); + }, + + "Should add the slot": function() { + var json = this.view.toJSON(); + + refute.defined(json.slot); + assert.same(1, json.children[0].slot); + assert.same(2, json.children[1].slot); + assert.same(3, json.children[2].slot); + }, + + "Should call toJSON of model": function() { + var apple = new Apple(); + var model = { + toJSON: function() {} + }; + var spy = this.spy(model, 'toJSON'); + + apple.model = model; + + apple.toJSON(); + assert(spy.called); + }, + "Should fire `tojson` event": function() { var apple = new Apple(); var spy = this.spy(); From d49a24792d6e9ebcb877cf26a79140fe77fefb19 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Thu, 19 Dec 2013 22:24:32 +0000 Subject: [PATCH 2/7] Optionally omit fmids when inflatable JSON is not required. Resolves #54. --- lib/module/index.js | 16 ++++++++++++++-- test/tests/module.toJSON.js | 9 ++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/module/index.js b/lib/module/index.js index 10e10e3..76c52c9 100644 --- a/lib/module/index.js +++ b/lib/module/index.js @@ -832,11 +832,22 @@ module.exports = function(fm) { * to inflate serverside rendered * views. * + * Options: + * + * - `inflatable` Whether the returned JSON should retain references to DOM ids for use with client-side inflation of views + * + * @param {Object} options * @return {Object} * @api public */ - proto.toJSON = function() { + proto.toJSON = function(options) { var json = {}; + + // Shallow clone the options + options = mixin({ + inflatable: true + }, options); + json.children = []; // Recurse @@ -845,11 +856,12 @@ module.exports = function(fm) { }); json.id = this.id(); - json.fmid = this._fmid; json.module = this.module(); json.model = this.model.toJSON(); json.slot = this.slot; + if (options.inflatable) json.fmid = this._fmid; + // Fire a hook to allow third // parties to alter the json output this.fireStatic('tojson', json); diff --git a/test/tests/module.toJSON.js b/test/tests/module.toJSON.js index 06f31b1..2b524c3 100644 --- a/test/tests/module.toJSON.js +++ b/test/tests/module.toJSON.js @@ -28,13 +28,20 @@ buster.testCase('View#toJSON()', { assert(json.id); }, - "Should add the fmid": function() { + "Should add the fmid by default": function() { var apple = new Apple(); var json = apple.toJSON(); assert(json.fmid); }, + "Should omit the fmid if inflatable is false": function() { + var apple = new Apple(); + var json = apple.toJSON({inflatable: false}); + + refute.defined(json.fmid); + }, + "Should add the module name": function() { var apple = new Apple(); var json = apple.toJSON(); From f669bc6a998cfd5b278704a4d2a4712b35e4dcbc Mon Sep 17 00:00:00 2001 From: George Crawford Date: Thu, 19 Dec 2013 22:39:03 +0000 Subject: [PATCH 3/7] Rename module.toJSON to module.serialize, as it doesn't actually return a JSON string. Some work still to do to remove reference to the old name. --- lib/module/index.js | 38 +++++++++++-------- .../{module.toJSON.js => module.serialize.js} | 36 +++++++++--------- 2 files changed, 41 insertions(+), 33 deletions(-) rename test/tests/{module.toJSON.js => module.serialize.js} (73%) diff --git a/lib/module/index.js b/lib/module/index.js index 76c52c9..41b961a 100644 --- a/lib/module/index.js +++ b/lib/module/index.js @@ -825,48 +825,56 @@ module.exports = function(fm) { }; /** - * Returns a JSON represention of + * @deprecated + */ + proto.toJSON = function(options) { + return this.serialize(options); + }; + + /** + * Returns a serialized represention of * a FruitMachine Module. This can * be generated serverside and - * passed into new FruitMachine(json) + * passed into new FruitMachine(serialized) * to inflate serverside rendered * views. * * Options: * - * - `inflatable` Whether the returned JSON should retain references to DOM ids for use with client-side inflation of views + * - `inflatable` Whether the returned object should retain references to DOM ids for use with client-side inflation of views * * @param {Object} options * @return {Object} * @api public */ - proto.toJSON = function(options) { - var json = {}; + proto.serialize = function(options) { + var serialized = {}; // Shallow clone the options options = mixin({ inflatable: true }, options); - json.children = []; + serialized.children = []; // Recurse this.each(function(child) { - json.children.push(child.toJSON()); + serialized.children.push(child.serialize()); }); - json.id = this.id(); - json.module = this.module(); - json.model = this.model.toJSON(); - json.slot = this.slot; + serialized.id = this.id(); + serialized.module = this.module(); + serialized.model = this.model.toJSON(); + serialized.slot = this.slot; - if (options.inflatable) json.fmid = this._fmid; + if (options.inflatable) serialized.fmid = this._fmid; // Fire a hook to allow third - // parties to alter the json output - this.fireStatic('tojson', json); + // parties to alter the output + this.fireStatic('tojson', serialized); // @deprecated + this.fireStatic('serialize', serialized); - return json; + return serialized; }; // Events diff --git a/test/tests/module.toJSON.js b/test/tests/module.serialize.js similarity index 73% rename from test/tests/module.toJSON.js rename to test/tests/module.serialize.js index 2b524c3..ef6346d 100644 --- a/test/tests/module.toJSON.js +++ b/test/tests/module.serialize.js @@ -1,56 +1,56 @@ -buster.testCase('View#toJSON()', { +buster.testCase('View#serialize()', { setUp: helpers.createView, "Should return an object": function() { var apple = new Apple(); - var json = apple.toJSON(); + var json = apple.serialize(); assert(json instanceof Object); }, - "Should call toJSON of child": function() { + "Should call serialize of child": function() { var apple = new Apple(); var orange = new Orange(); - var spy = this.spy(orange, 'toJSON'); + var spy = this.spy(orange, 'serialize'); apple.add(orange); - apple.toJSON(); + apple.serialize(); assert(spy.called); }, "Should add the id": function() { var apple = new Apple(); - var json = apple.toJSON(); + var json = apple.serialize(); assert(json.id); }, "Should add the fmid by default": function() { var apple = new Apple(); - var json = apple.toJSON(); + var json = apple.serialize(); assert(json.fmid); }, "Should omit the fmid if inflatable is false": function() { var apple = new Apple(); - var json = apple.toJSON({inflatable: false}); + var json = apple.serialize({inflatable: false}); refute.defined(json.fmid); }, "Should add the module name": function() { var apple = new Apple(); - var json = apple.toJSON(); + var json = apple.serialize(); assert.equals(json.module, 'apple'); }, "Should add the slot": function() { - var json = this.view.toJSON(); + var json = this.view.serialize(); refute.defined(json.slot); assert.same(1, json.children[0].slot); @@ -67,28 +67,28 @@ buster.testCase('View#toJSON()', { apple.model = model; - apple.toJSON(); + apple.serialize(); assert(spy.called); }, - "Should fire `tojson` event": function() { + "Should fire `serialize` event": function() { var apple = new Apple(); var spy = this.spy(); - apple.on('tojson', spy); - apple.toJSON(); + apple.on('serialize', spy); + apple.serialize(); assert(spy.called); }, - "Should be able to manipulate json output via `tojson` event": function() { + "Should be able to manipulate json output via `serialize` event": function() { var apple = new Apple(); - apple.on('tojson', function(json) { + apple.on('serialize', function(json) { json.test = 'data'; }); - var json = apple.toJSON(); + var json = apple.serialize(); assert.equals(json.test, 'data'); }, @@ -108,7 +108,7 @@ buster.testCase('View#toJSON()', { var layoutEl = layout.el; var appleEl = layout.module('apple').el; - var json = layout.toJSON(); + var json = layout.serialize(); var inflated = fruitmachine(json); inflated.setup(); From 09ff378a915837dba0d09d28b83134b58725ef35 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Fri, 20 Dec 2013 01:11:02 +0000 Subject: [PATCH 4/7] Seems to match the current compiled README --- docs/templates/readme.hogan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/templates/readme.hogan b/docs/templates/readme.hogan index 95f82a5..b5460eb 100644 --- a/docs/templates/readme.hogan +++ b/docs/templates/readme.hogan @@ -1,4 +1,4 @@ -# {{pkg.title}} [![Build Status](https://travis-ci.org/ftlabs/fruitmachine.png?branch=master)](https://travis-ci.org/ftlabs/fruitmachine) +# {{pkg.title}} [![Build Status](https://travis-ci.org/ftlabs/fruitmachine.png?branch=master)](https://travis-ci.org/ftlabs/fruitmachine) [![Coverage Status](https://coveralls.io/repos/ftlabs/fruitmachine/badge.png?branch=master)](https://coveralls.io/r/ftlabs/fruitmachine?branch=master) [![Dependency Status](https://gemnasium.com/ftlabs/fruitmachine.png)](https://gemnasium.com/ftlabs/fruitmachine) {{pkg.description}} From 2b2b81ee2bd31e7d74a448f7d77f1f3fbca28b59 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Fri, 20 Dec 2013 01:12:52 +0000 Subject: [PATCH 5/7] Rename module.toJSON to module.serialize --- build/fruitmachine.js | 52 +- build/fruitmachine.min.js | 2 +- docs/api.md | 9 +- docs/server-side-rendering.md | 5 +- examples/express/build/build.css | 9 +- examples/express/build/build.js | 1498 +++++++++++---------- examples/express/lib/page-about/server.js | 2 +- examples/express/lib/page-home/server.js | 2 +- examples/express/lib/page-links/server.js | 2 +- 9 files changed, 813 insertions(+), 768 deletions(-) diff --git a/build/fruitmachine.js b/build/fruitmachine.js index d84e893..c66b272 100644 --- a/build/fruitmachine.js +++ b/build/fruitmachine.js @@ -547,8 +547,8 @@ module.exports = function(fm) { this._id = options.id || util.uniqueId(); this._fmid = options.fmid || util.uniqueId('fmid'); this.tag = options.tag || this.tag || 'div'; - this.classes = this.classes || options.classes || []; - this.helpers = this.helpers || options.helpers || []; + this.classes = options.classes || this.classes || []; + this.helpers = options.helpers || this.helpers || []; this.template = this._setTemplate(options.template || this.template); this.slot = options.slot; @@ -1303,36 +1303,56 @@ module.exports = function(fm) { }; /** - * Returns a JSON represention of + * @deprecated + */ + proto.toJSON = function(options) { + return this.serialize(options); + }; + + /** + * Returns a serialized represention of * a FruitMachine Module. This can * be generated serverside and - * passed into new FruitMachine(json) + * passed into new FruitMachine(serialized) * to inflate serverside rendered * views. * + * Options: + * + * - `inflatable` Whether the returned object should retain references to DOM ids for use with client-side inflation of views + * + * @param {Object} options * @return {Object} * @api public */ - proto.toJSON = function() { - var json = {}; - json.children = []; + proto.serialize = function(options) { + var serialized = {}; + + // Shallow clone the options + options = mixin({ + inflatable: true + }, options); + + serialized.children = []; // Recurse this.each(function(child) { - json.children.push(child.toJSON()); + serialized.children.push(child.serialize()); }); - json.id = this.id(); - json.fmid = this._fmid; - json.module = this.module(); - json.model = this.model.toJSON(); - json.slot = this.slot; + serialized.id = this.id(); + serialized.module = this.module(); + serialized.model = this.model.toJSON(); + serialized.slot = this.slot; + + if (options.inflatable) serialized.fmid = this._fmid; // Fire a hook to allow third - // parties to alter the json output - this.fireStatic('tojson', json); + // parties to alter the output + this.fireStatic('tojson', serialized); // @deprecated + this.fireStatic('serialize', serialized); - return json; + return serialized; }; // Events diff --git a/build/fruitmachine.min.js b/build/fruitmachine.min.js index f9c5703..bf6d110 100644 --- a/build/fruitmachine.min.js +++ b/build/fruitmachine.min.js @@ -1 +1 @@ -(function(t){if("function"==typeof bootstrap)bootstrap("fruitmachine",t);else if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeFruitmachine=t}else"undefined"!=typeof window?window.fruitmachine=t():global.fruitmachine=t()})(function(){return function(t,e,i){function n(i,s){if(!e[i]){if(!t[i]){var o="function"==typeof require&&require;if(!s&&o)return o(i,!0);if(r)return r(i,!0);throw Error("Cannot find module '"+i+"'")}var h=e[i]={exports:{}};t[i][0].call(h.exports,function(e){var r=t[i][1][e];return n(r?r:e)},h,h.exports)}return e[i].exports}for(var r="function"==typeof require&&require,s=0;i.length>s;s++)n(i[s]);return n}({1:[function(t,e){"use strict";var i=t("./fruitmachine"),n=t("model");e.exports=i({Model:n})},{"./fruitmachine":2,model:3}],4:[function(t,e){"use strict";e.exports=function(t){return function(e){var i="object"==typeof e?t.Module.extend(e):e,n=i.prototype,r=n.name||n._module;return r&&(t.modules[r]=i),i}}},{}],2:[function(t,e){"use strict";var i=t("./module"),n=t("./define"),r=t("utils"),s=t("event");e.exports=function(t){function o(t){var e=o.modules[t.module];return e?new e(t):void 0}return o.create=e.exports,o.Model=t.Model,o.Events=s,o.Module=i(o),o.define=n(o),o.util=r,o.modules={},o.config={templateIterator:"children",templateInstance:"child"},s(o)}},{"./define":4,"./module":5,utils:6,event:7}],6:[function(t,e,i){"use strict";i.bind=function(t,e){return function(){return t.apply(e,arguments)}},i.isArray=function(t){return t instanceof Array},i.mixin=function(t,e){for(var i in e)t[i]=e[i];return t},i.byId=function(t,e){return e?e.querySelector("#"+t):void 0},i.insert=function(t,e,i){i!==void 0?e.splice(i,0,t):e.push(t)},i.toNode=function(t){var e=document.createElement("div");return e.innerHTML=t,e.removeChild(e.firstElementChild)},i.hasDom=function(){return"undefined"!=typeof document};var n=0;i.uniqueId=function(t){return(t||"id")+ ++n*Math.round(1e5*Math.random())},i.keys=function(t){var e=[];for(var i in t)e.push(i);return e},i.isPlainObject=function(t){if(!t)return!1;var e=""+(t.constructor||"");return!!~e.indexOf("Object")}},{}],7:[function(t,e){function i(t){return this instanceof i?t?n(t,r):void 0:new i(t)}function n(t,e){for(var i in e)t[i]=e[i];return t}var r=i.prototype;e.exports=i,r.on=function(t,e){return this._cbs=this._cbs||{},(this._cbs[t]||(this._cbs[t]=[])).unshift(e),this},r.off=function(t,e){if(this._cbs=this._cbs||{},!t)return this._cbs={};if(!e)return delete this._cbs[t];for(var i,n=this._cbs[t]||[];n&&~(i=n.indexOf(e));)n.splice(i,1);return this},r.fire=function(t){this._cbs=this._cbs||{};var e=t.name||t,i=t.ctx||this,n=this._cbs[e];if(n)for(var r=[].slice.call(arguments,1),s=n.length;s--;)n[s].apply(i,r);return this}},{}],8:[function(t,e){"use strict";var i={}.hasOwnProperty;e.exports=function(t){for(var e,n,r=arguments,s=r.length,o=0;s>++o;){e=r[o];for(n in e)i.call(e,n)&&(t[n]=e[n])}return t}},{}],3:[function(t,e){"use strict";function i(t){this._data=r({},t)}var n=t("event"),r=t("mixin");e.exports=i;var s=i.prototype;s.get=function(t){return t?this._data[t]:this._data},s.set=function(t,e){if("string"==typeof t&&e!==void 0&&(this._data[t]=e,this.fire("change:"+t,e)),"object"==typeof t){r(this._data,t);for(var i in t)this.fire("change:"+i,t[i])}return this.fire("change"),this},s.clear=function(){return this._data={},this.fire("change"),this},s.destroy=function(){for(var t in this._data)this._data[t]=null;delete this._data,this.fire("destroy")},s.toJSON=function(){return r({},this._data)},n(s)},{mixin:8,event:7}],5:[function(t,e){"use strict";var i=t("utils"),n=t("./events"),r=t("extend"),s=i.mixin;e.exports=function(t){function e(t){t=s({},t),this._configure(t),this._add(t.children),this.initialize&&this.initialize(t),this.fireStatic("initialize",t)}var o=e.prototype;return o._configure=function(e){this._id=e.id||i.uniqueId(),this._fmid=e.fmid||i.uniqueId("fmid"),this.tag=e.tag||this.tag||"div",this.classes=this.classes||e.classes||[],this.helpers=this.helpers||e.helpers||[],this.template=this._setTemplate(e.template||this.template),this.slot=e.slot,this.children=[],this._ids={},this._modules={},this.slots={};var n=e.model||e.data||{};this.model=i.isPlainObject(n)?new this.Model(n):n,this.helpers.forEach(this.attachHelper,this),e.fmid&&(t.fire("inflation",this,e),this.fireStatic("inflation",e))},o._add=function(t){if(t){var e,n=i.isArray(t);for(var r in t)e=t[r],n||(e.slot=r),this.add(e)}},o.attachHelper=function(t){t&&t(this)},o._setTemplate=function(t){return t&&t.render?i.bind(t.render,t):t},o.add=function(n,r){if(!n)return this;var s=r&&r.at,o=r&&r.inject,h="object"==typeof r?r.slot:r;n.parent&&n.remove({fromDOM:!1}),h=n.slot=h||n.slot;var u=this.slots[h];return u&&u.remove({fromDOM:!1}),n instanceof e||(n=t(n)),i.insert(n,this.children,s),this._addLookup(n),o&&this._injectEl(n.el,r),this},o.remove=function(t,i){if(1===arguments.length&&!t)return this;if(t instanceof e)return t.remove(i||{}),this;var n,r=t||{},s=r.fromDOM!==!1,o=this.parent,h=this.el,u=h&&h.parentNode;return s&&u&&u.removeChild(h),o&&(n=o.children.indexOf(this),o.children.splice(n,1),o._removeLookup(this)),this},o._addLookup=function(t){var e=t.module();(this._modules[e]=this._modules[e]||[]).push(t),this._ids[t.id()]=t,t.slot&&(this.slots[t.slot]=t),t.parent=this},o._removeLookup=function(t){var e=t.module(),i=this._modules[e].indexOf(t);this._modules[e].splice(i,1),delete this._ids[t._id],delete this.slots[t.slot],delete t.parent},o._injectEl=function(t,e){var i=e&&e.at,n=this.el;t&&n&&(i!==void 0?n.insertBefore(t,n.children[i]):n.appendChild(t))},o.id=function(t){if(!arguments.length)return this._id;var e=this._ids[t];return e?e:this.each(function(e){return e.id(t)})},o.module=function(t){if(!arguments.length)return this._module||this.name;var e=this._modules[t];return e?e[0]:this.each(function(e){return e.module(t)})},o.modules=function(t){var e=this._modules[t]||[];return this.each(function(i){e=e.concat(i.modules(t))}),e},o.each=function(t){for(var e,i=this.children.length,n=0;i>n;n++)if(e=t(this.children[n]))return e},o.toHTML=function(){var e,i,n={};return n[t.config.templateIterator]=[],this.each(function(r){i={},e=r.toHTML(),n[r.slot||r.id()]=e,i[t.config.templateInstance]=e,n.children.push(s(i,r.model.toJSON()))}),e=this.template?this.template(s(n,this.model.toJSON())):"",this._wrapHTML(e)},o._wrapHTML=function(t){return"<"+this.tag+' class="'+this.module()+" "+this.classes.join(" ")+'" id="'+this._fmid+'">'+t+""},o.render=function(){var t=this.toHTML(),e=i.toNode(t);return this._setEl(e),this._fetchEls(this.el),this.fireStatic("render"),this},o.setup=function(t){var e=t&&t.shallow;return this._getEl()?(this.isSetup&&this.teardown({shallow:!0}),this.fireStatic("before setup"),this._setup&&this._setup(),this.fireStatic("setup"),this.isSetup=!0,e||this.each(function(t){t.setup()}),this):this},o.teardown=function(t){var e=t&&t.shallow;return e||this.each(function(t){t.teardown()}),this.isSetup&&(this.fireStatic("before teardown"),this._teardown&&this._teardown(),this.fireStatic("teardown"),this.isSetup=!1),this},o.destroy=function(t){t=t||{};for(var e=t.remove!==!1,i=this.children.length;i--;)this.children[i].destroy({remove:!1});return this.destroyed?this:(e&&this.remove(t),this.teardown({shallow:!0}),this.fireStatic("before destroy"),this._destroy&&this._destroy(),this.fireStatic("destroy"),this.off(),this.destroyed=!0,this.el=this.model=this.parent=this._modules=this._ids=this._id=null,void 0)},o.empty=function(){for(var t=this.children.length;t--;)this.children[t].destroy();return this},o._fetchEls=function(t){t&&this.each(function(e){e.el=i.byId(e._fmid,t),e._fetchEls(e.el||t)})},o._getEl=function(){return i.hasDom()?this.el=this.el||document.getElementById(this._fmid):void 0},o._setEl=function(t){var e=this.el,i=e&&e.parentNode;return i&&i.replaceChild(t,e),this.el=t,this},o.inject=function(t){return t&&(t.innerHTML="",this.appendTo(t),this.fireStatic("inject")),this},o.appendTo=function(t){return this.el&&t&&t.appendChild&&(t.appendChild(this.el),this.fireStatic("appendto")),this},o.toJSON=function(){var t={};return t.children=[],this.each(function(e){t.children.push(e.toJSON())}),t.id=this.id(),t.fmid=this._fmid,t.module=this.module(),t.model=this.model.toJSON(),t.slot=this.slot,this.fireStatic("tojson",t),t},o.on=n.on,o.off=n.off,o.fire=n.fire,o.fireStatic=n.fireStatic,e.extend=r(i.keys(o)),o.Model=t.Model,e}},{"./events":9,utils:6,extend:10}],9:[function(t,e,i){function n(t,e,i){t&&i.propagate&&(t.event=i,r.prototype.fire.apply(t,e),n(t.parent,e,i))}var r=t("event");i.on=function(t,e,i){return 2===arguments.length&&(i=e,e=null),e?r.prototype.on.call(this,t,function(){this.event.target.module()===e&&i.apply(this,arguments)}):r.prototype.on.call(this,t,i),this},i.fire=function(){var t=this.event,e={target:this,propagate:!0,stopPropagation:function(){this.propagate=!1}};return n(this,arguments,e),t?this.event=t:delete this.event,this},i.fireStatic=r.prototype.fire,i.off=r.prototype.off},{event:7}],10:[function(t,e){"use strict";function i(t,e){for(var i in e)e.hasOwnProperty(i)&&~t.indexOf(i)&&(e["_"+i]=e[i],delete e[i])}var n=t("utils").mixin;e.exports=function(t){return function(e){function r(){this.constructor=o}var s=this,o=function(){return s.apply(this,arguments)};return n(o,s),t&&i(t,e),r.prototype=s.prototype,o.prototype=new r,n(o.prototype,e),o.__super__=s.prototype,o}}},{utils:6}]},{},[1])(1)}); \ No newline at end of file +(function(t){if("function"==typeof bootstrap)bootstrap("fruitmachine",t);else if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeFruitmachine=t}else"undefined"!=typeof window?window.fruitmachine=t():global.fruitmachine=t()})(function(){return function(t,e,i){function n(i,s){if(!e[i]){if(!t[i]){var o="function"==typeof require&&require;if(!s&&o)return o(i,!0);if(r)return r(i,!0);throw Error("Cannot find module '"+i+"'")}var h=e[i]={exports:{}};t[i][0].call(h.exports,function(e){var r=t[i][1][e];return n(r?r:e)},h,h.exports)}return e[i].exports}for(var r="function"==typeof require&&require,s=0;i.length>s;s++)n(i[s]);return n}({1:[function(t,e){"use strict";var i=t("./fruitmachine"),n=t("model");e.exports=i({Model:n})},{"./fruitmachine":2,model:3}],4:[function(t,e){"use strict";e.exports=function(t){return function(e){var i="object"==typeof e?t.Module.extend(e):e,n=i.prototype,r=n.name||n._module;return r&&(t.modules[r]=i),i}}},{}],2:[function(t,e){"use strict";var i=t("./module"),n=t("./define"),r=t("utils"),s=t("event");e.exports=function(t){function o(t){var e=o.modules[t.module];return e?new e(t):void 0}return o.create=e.exports,o.Model=t.Model,o.Events=s,o.Module=i(o),o.define=n(o),o.util=r,o.modules={},o.config={templateIterator:"children",templateInstance:"child"},s(o)}},{"./define":4,"./module":5,utils:6,event:7}],6:[function(t,e,i){"use strict";i.bind=function(t,e){return function(){return t.apply(e,arguments)}},i.isArray=function(t){return t instanceof Array},i.mixin=function(t,e){for(var i in e)t[i]=e[i];return t},i.byId=function(t,e){return e?e.querySelector("#"+t):void 0},i.insert=function(t,e,i){i!==void 0?e.splice(i,0,t):e.push(t)},i.toNode=function(t){var e=document.createElement("div");return e.innerHTML=t,e.removeChild(e.firstElementChild)},i.hasDom=function(){return"undefined"!=typeof document};var n=0;i.uniqueId=function(t){return(t||"id")+ ++n*Math.round(1e5*Math.random())},i.keys=function(t){var e=[];for(var i in t)e.push(i);return e},i.isPlainObject=function(t){if(!t)return!1;var e=""+(t.constructor||"");return!!~e.indexOf("Object")}},{}],7:[function(t,e){function i(t){return this instanceof i?t?n(t,r):void 0:new i(t)}function n(t,e){for(var i in e)t[i]=e[i];return t}var r=i.prototype;e.exports=i,r.on=function(t,e){return this._cbs=this._cbs||{},(this._cbs[t]||(this._cbs[t]=[])).unshift(e),this},r.off=function(t,e){if(this._cbs=this._cbs||{},!t)return this._cbs={};if(!e)return delete this._cbs[t];for(var i,n=this._cbs[t]||[];n&&~(i=n.indexOf(e));)n.splice(i,1);return this},r.fire=function(t){this._cbs=this._cbs||{};var e=t.name||t,i=t.ctx||this,n=this._cbs[e];if(n)for(var r=[].slice.call(arguments,1),s=n.length;s--;)n[s].apply(i,r);return this}},{}],8:[function(t,e){"use strict";var i={}.hasOwnProperty;e.exports=function(t){for(var e,n,r=arguments,s=r.length,o=0;s>++o;){e=r[o];for(n in e)i.call(e,n)&&(t[n]=e[n])}return t}},{}],3:[function(t,e){"use strict";function i(t){this._data=r({},t)}var n=t("event"),r=t("mixin");e.exports=i;var s=i.prototype;s.get=function(t){return t?this._data[t]:this._data},s.set=function(t,e){if("string"==typeof t&&e!==void 0&&(this._data[t]=e,this.fire("change:"+t,e)),"object"==typeof t){r(this._data,t);for(var i in t)this.fire("change:"+i,t[i])}return this.fire("change"),this},s.clear=function(){return this._data={},this.fire("change"),this},s.destroy=function(){for(var t in this._data)this._data[t]=null;delete this._data,this.fire("destroy")},s.toJSON=function(){return r({},this._data)},n(s)},{mixin:8,event:7}],5:[function(t,e){"use strict";var i=t("utils"),n=t("./events"),r=t("extend"),s=i.mixin;e.exports=function(t){function e(t){t=s({},t),this._configure(t),this._add(t.children),this.initialize&&this.initialize(t),this.fireStatic("initialize",t)}var o=e.prototype;return o._configure=function(e){this._id=e.id||i.uniqueId(),this._fmid=e.fmid||i.uniqueId("fmid"),this.tag=e.tag||this.tag||"div",this.classes=e.classes||this.classes||[],this.helpers=e.helpers||this.helpers||[],this.template=this._setTemplate(e.template||this.template),this.slot=e.slot,this.children=[],this._ids={},this._modules={},this.slots={};var n=e.model||e.data||{};this.model=i.isPlainObject(n)?new this.Model(n):n,this.helpers.forEach(this.attachHelper,this),e.fmid&&(t.fire("inflation",this,e),this.fireStatic("inflation",e))},o._add=function(t){if(t){var e,n=i.isArray(t);for(var r in t)e=t[r],n||(e.slot=r),this.add(e)}},o.attachHelper=function(t){t&&t(this)},o._setTemplate=function(t){return t&&t.render?i.bind(t.render,t):t},o.add=function(n,r){if(!n)return this;var s=r&&r.at,o=r&&r.inject,h="object"==typeof r?r.slot:r;n.parent&&n.remove({fromDOM:!1}),h=n.slot=h||n.slot;var u=this.slots[h];return u&&u.remove({fromDOM:!1}),n instanceof e||(n=t(n)),i.insert(n,this.children,s),this._addLookup(n),o&&this._injectEl(n.el,r),this},o.remove=function(t,i){if(1===arguments.length&&!t)return this;if(t instanceof e)return t.remove(i||{}),this;var n,r=t||{},s=r.fromDOM!==!1,o=this.parent,h=this.el,u=h&&h.parentNode;return s&&u&&u.removeChild(h),o&&(n=o.children.indexOf(this),o.children.splice(n,1),o._removeLookup(this)),this},o._addLookup=function(t){var e=t.module();(this._modules[e]=this._modules[e]||[]).push(t),this._ids[t.id()]=t,t.slot&&(this.slots[t.slot]=t),t.parent=this},o._removeLookup=function(t){var e=t.module(),i=this._modules[e].indexOf(t);this._modules[e].splice(i,1),delete this._ids[t._id],delete this.slots[t.slot],delete t.parent},o._injectEl=function(t,e){var i=e&&e.at,n=this.el;t&&n&&(i!==void 0?n.insertBefore(t,n.children[i]):n.appendChild(t))},o.id=function(t){if(!arguments.length)return this._id;var e=this._ids[t];return e?e:this.each(function(e){return e.id(t)})},o.module=function(t){if(!arguments.length)return this._module||this.name;var e=this._modules[t];return e?e[0]:this.each(function(e){return e.module(t)})},o.modules=function(t){var e=this._modules[t]||[];return this.each(function(i){e=e.concat(i.modules(t))}),e},o.each=function(t){for(var e,i=this.children.length,n=0;i>n;n++)if(e=t(this.children[n]))return e},o.toHTML=function(){var e,i,n={};return n[t.config.templateIterator]=[],this.each(function(r){i={},e=r.toHTML(),n[r.slot||r.id()]=e,i[t.config.templateInstance]=e,n.children.push(s(i,r.model.toJSON()))}),e=this.template?this.template(s(n,this.model.toJSON())):"",this._wrapHTML(e)},o._wrapHTML=function(t){return"<"+this.tag+' class="'+this.module()+" "+this.classes.join(" ")+'" id="'+this._fmid+'">'+t+""},o.render=function(){var t=this.toHTML(),e=i.toNode(t);return this._setEl(e),this._fetchEls(this.el),this.fireStatic("render"),this},o.setup=function(t){var e=t&&t.shallow;return this._getEl()?(this.isSetup&&this.teardown({shallow:!0}),this.fireStatic("before setup"),this._setup&&this._setup(),this.fireStatic("setup"),this.isSetup=!0,e||this.each(function(t){t.setup()}),this):this},o.teardown=function(t){var e=t&&t.shallow;return e||this.each(function(t){t.teardown()}),this.isSetup&&(this.fireStatic("before teardown"),this._teardown&&this._teardown(),this.fireStatic("teardown"),this.isSetup=!1),this},o.destroy=function(t){t=t||{};for(var e=t.remove!==!1,i=this.children.length;i--;)this.children[i].destroy({remove:!1});return this.destroyed?this:(e&&this.remove(t),this.teardown({shallow:!0}),this.fireStatic("before destroy"),this._destroy&&this._destroy(),this.fireStatic("destroy"),this.off(),this.destroyed=!0,this.el=this.model=this.parent=this._modules=this._ids=this._id=null,void 0)},o.empty=function(){for(var t=this.children.length;t--;)this.children[t].destroy();return this},o._fetchEls=function(t){t&&this.each(function(e){e.el=i.byId(e._fmid,t),e._fetchEls(e.el||t)})},o._getEl=function(){return i.hasDom()?this.el=this.el||document.getElementById(this._fmid):void 0},o._setEl=function(t){var e=this.el,i=e&&e.parentNode;return i&&i.replaceChild(t,e),this.el=t,this},o.inject=function(t){return t&&(t.innerHTML="",this.appendTo(t),this.fireStatic("inject")),this},o.appendTo=function(t){return this.el&&t&&t.appendChild&&(t.appendChild(this.el),this.fireStatic("appendto")),this},o.toJSON=function(t){return this.serialize(t)},o.serialize=function(t){var e={};return t=s({inflatable:!0},t),e.children=[],this.each(function(t){e.children.push(t.serialize())}),e.id=this.id(),e.module=this.module(),e.model=this.model.toJSON(),e.slot=this.slot,t.inflatable&&(e.fmid=this._fmid),this.fireStatic("tojson",e),this.fireStatic("serialize",e),e},o.on=n.on,o.off=n.off,o.fire=n.fire,o.fireStatic=n.fireStatic,e.extend=r(i.keys(o)),o.Model=t.Model,e}},{"./events":9,utils:6,extend:10}],9:[function(t,e,i){function n(t,e,i){t&&i.propagate&&(t.event=i,r.prototype.fire.apply(t,e),n(t.parent,e,i))}var r=t("event");i.on=function(t,e,i){return 2===arguments.length&&(i=e,e=null),e?r.prototype.on.call(this,t,function(){this.event.target.module()===e&&i.apply(this,arguments)}):r.prototype.on.call(this,t,i),this},i.fire=function(){var t=this.event,e={target:this,propagate:!0,stopPropagation:function(){this.propagate=!1}};return n(this,arguments,e),t?this.event=t:delete this.event,this},i.fireStatic=r.prototype.fire,i.off=r.prototype.off},{event:7}],10:[function(t,e){"use strict";function i(t,e){for(var i in e)e.hasOwnProperty(i)&&~t.indexOf(i)&&(e["_"+i]=e[i],delete e[i])}var n=t("utils").mixin;e.exports=function(t){return function(e){function r(){this.constructor=o}var s=this,o=function(){return s.apply(this,arguments)};return n(o,s),t&&i(t,e),r.prototype=s.prototype,o.prototype=new r,n(o.prototype,e),o.__super__=s.prototype,o}}},{utils:6}]},{},[1])(1)}); \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index b281f63..d5dfc61 100644 --- a/docs/api.md +++ b/docs/api.md @@ -199,14 +199,17 @@ and appends the view into it. Appends the view element into the destination element. -### Module#toJSON() +### Module#serialize() -Returns a JSON represention of +Returns a serialized represention of a FruitMachine Module. This can be generated serverside and -passed into new FruitMachine(json) +passed into new FruitMachine(serialized) to inflate serverside rendered views. +\nOptions: + + - `inflatable` Whether the returned object should retain references to DOM ids for use with client-side inflation of views ### Module#on() diff --git a/docs/server-side-rendering.md b/docs/server-side-rendering.md index 6a4dd26..3e24e6f 100644 --- a/docs/server-side-rendering.md +++ b/docs/server-side-rendering.md @@ -25,9 +25,8 @@ var Apple = require('./apple'); app.get('/', function(req, res) { var apple = new Apple(); var html = apple.toHTML(); - var json = apple.toJSON(); - - json = JSON.stringify(json); + var serialized = apple.serialize(); + var json = JSON.stringify(serialized); // Imagine this response is also // wrapped in usual document boilerplate diff --git a/examples/express/build/build.css b/examples/express/build/build.css index 46b798f..f0a73f5 100644 --- a/examples/express/build/build.css +++ b/examples/express/build/build.css @@ -1,8 +1 @@ -/*========================================================================= - Module Apple - ========================================================================== */ -.module-apple { - padding: 14px; } - -.module-apple_title { - font: 30px 'Helvetica'; } +ERROR: Cannot load compass. diff --git a/examples/express/build/build.js b/examples/express/build/build.js index ca9a7c2..d5e5e62 100644 --- a/examples/express/build/build.js +++ b/examples/express/build/build.js @@ -1,13 +1,246 @@ -;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0](function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_1",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_2",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_3",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b("\n" + i);_.b("");return _.fl();;}); +},{}],4:[function(require,module,exports){ + +/** + * Module Dependencies + */ + +var fm = require('../../../../lib/'); +var template = require('./template'); + +/** + * Exports + */ + +module.exports = fm.define({ + module: 'apple', + template: template +}); +},{"../../../../lib/":21,"./template":5}],5:[function(require,module,exports){ +module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
");_.b("\n" + i);_.b("
");_.b(_.v(_.f("title",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");return _.fl();;}); +},{}],6:[function(require,module,exports){ + +/** + * Module Dependencies + */ + +var fm = require('../../../../lib/'); +var template = require('./template'); + +/** + * Exports + */ + +module.exports = fm.define({ + module: 'banana', + template: template +}); +},{"../../../../lib/":21,"./template":7}],7:[function(require,module,exports){ +module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
Module Banana
");return _.fl();;}); +},{}],8:[function(require,module,exports){ + +/** + * Module Dependencies + */ + +var fm = require('../../../../lib/'); +var template = require('./template'); + +/** + * Exports + */ + +module.exports = fm.define({ + module: 'orange', + template: template +}); +},{"../../../../lib/":21,"./template":9}],9:[function(require,module,exports){ +module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
Module Orange
");return _.fl();;}); +},{}],10:[function(require,module,exports){ +var content = document.querySelector('.js-app_content'); +var View = require('./view'); + +var database = { + title: 'This is the About page' +}; + +module.exports = function() { + app.view = View(database); + + app.view + .render() + .inject(content); +}; +},{"./view":11}],11:[function(require,module,exports){ +var fruitmachine = require('../../../../lib/'); + +// Require these views so that +// fruitmachine registers them +var LayoutA = require('../layout-a'); +var ModuleApple = require('../module-apple'); +var ModuleOrange = require('../module-orange'); +var ModuleBanana = require('../module-banana'); + +module.exports = function(data) { + var layout = { + module: 'layout-a', + children: [ + { + id: 'slot_1', + module: 'apple', + model: { + title: data.title + } + }, + { + id: 'slot_2', + module: 'banana' + }, + { + id: 'slot_3', + module: 'orange' + } + ] + }; + + return fruitmachine(layout); +}; +},{"../../../../lib/":21,"../layout-a":2,"../module-apple":4,"../module-banana":6,"../module-orange":8}],12:[function(require,module,exports){ +var content = document.querySelector('.js-app_content'); +var View = require('./view'); + +var database = { + title: 'This is the Home page' +}; + +module.exports = function() { + app.view = View(database); + + app.view + .render() + .inject(content); +}; +},{"./view":13}],13:[function(require,module,exports){ +var fruitmachine = require('../../../../lib/'); + +// Require these views so that +// fruitmachine registers them +var LayoutA = require('../layout-a'); +var ModuleApple = require('../module-apple'); +var ModuleOrange = require('../module-orange'); +var ModuleBanana = require('../module-banana'); + +module.exports = function(data) { + var layout = { + module: 'layout-a', + children: [ + { + id: 'slot_1', + module: 'apple', + model: { + title: data.title + } + }, + { + id: 'slot_2', + module: 'orange' + }, + { + id: 'slot_3', + module: 'banana' + } + ] + }; + + return fruitmachine(layout); +}; +},{"../../../../lib/":21,"../layout-a":2,"../module-apple":4,"../module-banana":6,"../module-orange":8}],14:[function(require,module,exports){ +var content = document.querySelector('.js-app_content'); +var View = require('./view'); + +var database = { + title: 'This is the Links page' +}; + +module.exports = function() { + app.view = View(database); + + app.view + .render() + .inject(content); +}; +},{"./view":15}],15:[function(require,module,exports){ +var fruitmachine = require('../../../../lib/'); + +// Require these views so that +// fruitmachine registers them +var LayoutA = require('../layout-a'); +var ModuleApple = require('../module-apple'); +var ModuleOrange = require('../module-orange'); +var ModuleBanana = require('../module-banana'); + +module.exports = function(data) { + var layout = { + module: 'layout-a', + children: [ + { + id: 'slot_1', + module: 'orange' + }, + { + id: 'slot_2', + module: 'apple', + model: { + title: data.title + } + }, + { + id: 'slot_3', + module: 'banana' + } + ] + }; + + return fruitmachine(layout); +}; +},{"../../../../lib/":21,"../layout-a":2,"../module-apple":4,"../module-banana":6,"../module-orange":8}],16:[function(require,module,exports){ +var page = require('page'); +var home = require('../page-home/client'); +var about = require('../page-about/client'); +var links = require('../page-links/client'); + +page('/', home); +page('/about', about); +page('/links', links); + +page({ dispatch: false }); +},{"../page-about/client":10,"../page-home/client":12,"../page-links/client":14,"page":18}],17:[function(require,module,exports){ /* * Copyright 2011 Twitter, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -250,224 +483,27 @@ var Hogan = {}; })(typeof exports !== 'undefined' ? exports : Hogan); -},{}],2:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ -/*jslint browser:true, node:true*/ +;(function(){ -/** - * FruitMachine Singleton - * - * Renders layouts/modules from a basic layout definition. - * If views require custom interactions devs can extend - * the basic functionality. - * - * @version 0.3.3 - * @copyright The Financial Times Limited [All Rights Reserved] - * @author Wilson Page - */ + /** + * Perform initial dispatch. + */ -'use strict'; + var dispatch = true; -/** - * Module Dependencies - */ + /** + * Base path. + */ -var fruitMachine = require('./fruitmachine'); -var Model = require('model'); + var base = ''; -/** - * Exports - */ + /** + * Running flag. + */ -module.exports = fruitMachine({ Model: Model }); -},{"./fruitmachine":5,"model":6}],7:[function(require,module,exports){ - -/*jslint browser:true, node:true, laxbreak:true*/ - -'use strict'; - -module.exports = function(fm) { - - /** - * Defines a module. - * - * Options: - * - * - `name {String}` the name of the module - * - `tag {String}` the tagName to use for the root element - * - `classes {Array}` a list of classes to add to the root element - * - `template {Function}` the template function to use when rendering - * - `helpers {Array}` a lsit of helpers to apply to the module - * - `initialize {Function}` custom logic to run when module instance created - * - `setup {Function}` custom logic to run when `.setup()` is called (directly or indirectly) - * - `teardown {Function}` custom logic to unbind/undo anything setup introduced (called on `.destroy()` and sometimes on `.setup()` to avoid double binding events) - * - `destroy {Function}` logic to permanently destroy all references - * - * @param {Object|View} props - * @return {View} - * @public true - */ - return function(props) { - var Module = ('object' === typeof props) - ? fm.Module.extend(props) - : props; - - // Allow modules to be named - // via 'name:' or 'module:' - var proto = Module.prototype; - var name = proto.name || proto._module; - - // Store the module by module type - // so that module can be referred to - // by just a string in layout definitions - if (name) fm.modules[name] = Module; - - return Module; - }; -}; - -},{}],8:[function(require,module,exports){ -var content = document.querySelector('.js-app_content'); -var View = require('./view'); - -var database = { - title: 'This is the Home page' -}; - -module.exports = function() { - app.view = View(database); - - app.view - .render() - .inject(content); -}; -},{"./view":9}],10:[function(require,module,exports){ -var content = document.querySelector('.js-app_content'); -var View = require('./view'); - -var database = { - title: 'This is the About page' -}; - -module.exports = function() { - app.view = View(database); - - app.view - .render() - .inject(content); -}; -},{"./view":11}],12:[function(require,module,exports){ -var content = document.querySelector('.js-app_content'); -var View = require('./view'); - -var database = { - title: 'This is the Links page' -}; - -module.exports = function() { - app.view = View(database); - - app.view - .render() - .inject(content); -}; -},{"./view":13}],3:[function(require,module,exports){ -var page = require('page'); -var home = require('../page-home/client'); -var about = require('../page-about/client'); -var links = require('../page-links/client'); - -page('/', home); -page('/about', about); -page('/links', links); - -page({ dispatch: false }); -},{"../page-home/client":8,"../page-about/client":10,"../page-links/client":12,"page":14}],5:[function(require,module,exports){ - -/*jslint browser:true, node:true*/ - -/** - * FruitMachine - * - * Renders layouts/modules from a basic layout definition. - * If views require custom interactions devs can extend - * the basic functionality. - * - * @version 0.3.3 - * @copyright The Financial Times Limited [All Rights Reserved] - * @author Wilson Page - */ - -'use strict'; - -/** - * Module Dependencies - */ - -var mod = require('./module'); -var define = require('./define'); -var utils = require('utils'); -var events = require('event'); - -/** - * Creates a fruitmachine - * - * Options: - * - * - `Model` A model constructor to use (must have `.toJSON()`) - * - * @param {Object} options - */ -module.exports = function(options) { - - /** - * Shortcut method for - * creating lazy views. - * - * @param {Object} options - * @return {View} - */ - function fm(options) { - var Module = fm.modules[options.module]; - if (Module) return new Module(options); - } - - fm.create = module.exports; - fm.Model = options.Model; - fm.Events = events; - fm.Module = mod(fm); - fm.define = define(fm); - fm.util = utils; - fm.modules = {}; - fm.config = { - templateIterator: 'children', - templateInstance: 'child' - }; - - // Mixin events and return - return events(fm); -}; -},{"./define":7,"./module":15,"utils":16,"event":17}],14:[function(require,module,exports){ - -;(function(){ - - /** - * Perform initial dispatch. - */ - - var dispatch = true; - - /** - * Base path. - */ - - var base = ''; - - /** - * Running flag. - */ - - var running; + var running; /** * Register `path` with callback `fn()`, @@ -545,7 +581,8 @@ module.exports = function(options) { if (false !== options.popstate) window.addEventListener('popstate', onpopstate, false); if (false !== options.click) window.addEventListener('click', onclick, false); if (!dispatch) return; - page.replace(location.pathname + location.search, null, true, dispatch); + var url = location.pathname + location.search + location.hash; + page.replace(url, null, true, dispatch); }; /** @@ -624,7 +661,8 @@ module.exports = function(options) { */ function unhandled(ctx) { - if (window.location.pathname + window.location.search == ctx.canonicalPath) return; + var current = window.location.pathname + window.location.search; + if (current == ctx.canonicalPath) return; page.stop(); ctx.unhandled = true; window.location = ctx.canonicalPath; @@ -642,14 +680,24 @@ module.exports = function(options) { function Context(path, state) { if ('/' == path[0] && 0 != path.indexOf(base)) path = base + path; var i = path.indexOf('?'); + this.canonicalPath = path; this.path = path.replace(base, '') || '/'; + this.title = document.title; this.state = state || {}; this.state.path = path; this.querystring = ~i ? path.slice(i + 1) : ''; this.pathname = ~i ? path.slice(0, i) : path; this.params = []; + + // fragment + this.hash = ''; + if (!~this.path.indexOf('#')) return; + var parts = this.path.split('#'); + this.path = parts[0]; + this.hash = parts[1] || ''; + this.querystring = this.querystring.split('#')[0]; } /** @@ -722,7 +770,7 @@ module.exports = function(options) { return function(ctx, next){ if (self.match(ctx.path, ctx.params)) return fn(ctx, next); next(); - } + }; }; /** @@ -798,7 +846,7 @@ module.exports = function(options) { .replace(/([\/.])/g, '\\$1') .replace(/\*/g, '(.*)'); return new RegExp('^' + path + '$', sensitive ? '' : 'i'); - }; + } /** * Handle "populate" events. @@ -825,19 +873,22 @@ module.exports = function(options) { while (el && 'A' != el.nodeName) el = el.parentNode; if (!el || 'A' != el.nodeName) return; - // ensure non-hash - var href = el.href; - var path = el.pathname + el.search; - if (el.hash || '#' == el.getAttribute('href')) return; + // ensure non-hash for the same path + var link = el.getAttribute('href'); + if (el.pathname == location.pathname && (el.hash || '#' == link)) return; // check target if (el.target) return; // x-origin - if (!sameOrigin(href)) return; + if (!sameOrigin(el.href)) return; + + // rebuild path + var path = el.pathname + el.search + (el.hash || ''); // same page - var orig = path; + var orig = path + el.hash; + path = path.replace(base, ''); if (base && orig == path) return; @@ -878,446 +929,234 @@ module.exports = function(options) { })(); -},{}],16:[function(require,module,exports){ +},{}],19:[function(require,module,exports){ -/*jshint browser:true, node:true*/ +/*jslint browser:true, node:true, laxbreak:true*/ 'use strict'; -exports.bind = function(method, context) { - return function() { return method.apply(context, arguments); }; -}; +module.exports = function(fm) { -exports.isArray = function(arg) { - return arg instanceof Array; -}, + /** + * Defines a module. + * + * Options: + * + * - `name {String}` the name of the module + * - `tag {String}` the tagName to use for the root element + * - `classes {Array}` a list of classes to add to the root element + * - `template {Function}` the template function to use when rendering + * - `helpers {Array}` a list of helpers to apply to the module + * - `initialize {Function}` custom logic to run when module instance created + * - `setup {Function}` custom logic to run when `.setup()` is called (directly or indirectly) + * - `teardown {Function}` custom logic to unbind/undo anything setup introduced (called on `.destroy()` and sometimes on `.setup()` to avoid double binding events) + * - `destroy {Function}` logic to permanently destroy all references + * + * @param {Object|View} props + * @return {View} + * @public true + */ + return function(props) { + var Module = ('object' === typeof props) + ? fm.Module.extend(props) + : props; -exports.mixin = function(original, source) { - for (var key in source) original[key] = source[key]; - return original; -}, + // Allow modules to be named + // via 'name:' or 'module:' + var proto = Module.prototype; + var name = proto.name || proto._module; -exports.byId = function(id, el) { - if (el) return el.querySelector('#' + id); -}, + // Store the module by module type + // so that module can be referred to + // by just a string in layout definitions + if (name) fm.modules[name] = Module; + + return Module; + }; +}; + +},{}],20:[function(require,module,exports){ +/*jslint browser:true, node:true*/ /** - * Inserts an item into an array. - * Has the option to state an index. + * FruitMachine * - * @param {*} item - * @param {Array} array - * @param {Number} index - * @return void - */ -exports.insert = function(item, array, index) { - if (typeof index !== 'undefined') { - array.splice(index, 0, item); - } else { - array.push(item); - } -}, - -exports.toNode = function(html) { - var el = document.createElement('div'); - el.innerHTML = html; - return el.removeChild(el.firstElementChild); -}, - -// Determine if we have a DOM -// in the current environment. -exports.hasDom = function() { - return typeof document !== 'undefined'; -}; - -var i = 0; -exports.uniqueId = function(prefix, suffix) { - prefix = prefix || 'id'; - suffix = suffix || 'a'; - return [prefix, (++i) * Math.round(Math.random() * 100000), suffix].join('-'); -}; - -exports.keys = function(object) { - var keys = []; - for (var key in object) keys.push(key); - return keys; -}; - -exports.isPlainObject = function(ob) { - if (!ob) return false; - var c = (ob.constructor || '').toString(); - return !!~c.indexOf('Object'); -}; -},{}],17:[function(require,module,exports){ - -/** - * Event - * - * A super lightweight - * event emitter library. - * - * @version 0.1.4 - * @author Wilson Page + * Renders layouts/modules from a basic layout definition. + * If views require custom interactions devs can extend + * the basic functionality. + * + * @version 0.3.3 + * @copyright The Financial Times Limited [All Rights Reserved] + * @author Wilson Page */ -/** - * Locals - */ - -var proto = Event.prototype; +'use strict'; /** - * Expose `Event` + * Module Dependencies */ -module.exports = Event; +var mod = require('./module'); +var define = require('./define'); +var utils = require('utils'); +var events = require('event'); /** - * Creates a new event emitter - * instance, or if passed an - * object, mixes the event logic - * into it. + * Creates a fruitmachine * - * @param {Object} obj - * @return {Object} - */ -function Event(obj) { - if (!(this instanceof Event)) return new Event(obj); - if (obj) return mixin(obj, proto); -} - -/** - * Registers a callback - * with an event name. + * Options: * - * @param {String} name - * @param {Function} cb - * @return {Event} - */ -proto.on = function(name, cb) { - this._cbs = this._cbs || {}; - (this._cbs[name] || (this._cbs[name] = [])).unshift(cb); - return this; -}; - -/** - * Removes a single callback, - * or all callbacks associated - * with the passed event name. + * - `Model` A model constructor to use (must have `.toJSON()`) * - * @param {String} name - * @param {Function} cb - * @return {Event} + * @param {Object} options */ -proto.off = function(name, cb) { - this._cbs = this._cbs || {}; +module.exports = function(options) { - if (!name) return this._cbs = {}; - if (!cb) return delete this._cbs[name]; + /** + * Shortcut method for + * creating lazy views. + * + * @param {Object} options + * @return {Module} + */ + function fm(options) { + var Module = fm.modules[options.module]; + if (Module) return new Module(options); + } - var cbs = this._cbs[name] || []; - var i; + fm.create = module.exports; + fm.Model = options.Model; + fm.Events = events; + fm.Module = mod(fm); + fm.define = define(fm); + fm.util = utils; + fm.modules = {}; + fm.config = { + templateIterator: 'children', + templateInstance: 'child' + }; - while (cbs && ~(i = cbs.indexOf(cb))) cbs.splice(i, 1); - return this; + // Mixin events and return + return events(fm); }; -/** - * Fires an event. Which triggers - * all callbacks registered on this - * event name. - * - * @param {String} name - * @return {Event} - */ -proto.fire = function(options) { - this._cbs = this._cbs || {}; - var name = options.name || options; - var ctx = options.ctx || this; - var cbs = this._cbs[name]; +},{"./define":19,"./module":23,"event":24,"utils":28}],21:[function(require,module,exports){ - if (cbs) { - var args = [].slice.call(arguments, 1); - var l = cbs.length; - while (l--) cbs[l].apply(ctx, args); - } - - return this; -}; - -/** - * Util - */ +/*jslint browser:true, node:true*/ /** - * Mixes in the properties - * of the second object into - * the first. + * FruitMachine Singleton * - * @param {Object} a - * @param {Object} b - * @return {Object} + * Renders layouts/modules from a basic layout definition. + * If views require custom interactions devs can extend + * the basic functionality. + * + * @version 0.3.3 + * @copyright The Financial Times Limited [All Rights Reserved] + * @author Wilson Page */ -function mixin(a, b) { - for (var key in b) a[key] = b[key]; - return a; -} -},{}],9:[function(require,module,exports){ -var fruitmachine = require('../../../../lib/'); - -// Require these views so that -// fruitmachine registers them -var LayoutA = require('../layout-a'); -var ModuleApple = require('../module-apple'); -var ModuleOrange = require('../module-orange'); -var ModuleBanana = require('../module-banana'); - -module.exports = function(data) { - var layout = { - module: 'layout-a', - children: [ - { - id: 'slot_1', - module: 'apple', - model: { - title: data.title - } - }, - { - id: 'slot_2', - module: 'orange' - }, - { - id: 'slot_3', - module: 'banana' - } - ] - }; - - return fruitmachine(layout); -}; -},{"../../../../lib/":2,"../layout-a":18,"../module-apple":19,"../module-orange":20,"../module-banana":21}],11:[function(require,module,exports){ -var fruitmachine = require('../../../../lib/'); - -// Require these views so that -// fruitmachine registers them -var LayoutA = require('../layout-a'); -var ModuleApple = require('../module-apple'); -var ModuleOrange = require('../module-orange'); -var ModuleBanana = require('../module-banana'); - -module.exports = function(data) { - var layout = { - module: 'layout-a', - children: [ - { - id: 'slot_1', - module: 'apple', - model: { - title: data.title - } - }, - { - id: 'slot_2', - module: 'banana' - }, - { - id: 'slot_3', - module: 'orange' - } - ] - }; - - return fruitmachine(layout); -}; -},{"../../../../lib/":2,"../layout-a":18,"../module-apple":19,"../module-orange":20,"../module-banana":21}],13:[function(require,module,exports){ -var fruitmachine = require('../../../../lib/'); - -// Require these views so that -// fruitmachine registers them -var LayoutA = require('../layout-a'); -var ModuleApple = require('../module-apple'); -var ModuleOrange = require('../module-orange'); -var ModuleBanana = require('../module-banana'); - -module.exports = function(data) { - var layout = { - module: 'layout-a', - children: [ - { - id: 'slot_1', - module: 'orange' - }, - { - id: 'slot_2', - module: 'apple', - model: { - title: data.title - } - }, - { - id: 'slot_3', - module: 'banana' - } - ] - }; - - return fruitmachine(layout); -}; -},{"../../../../lib/":2,"../layout-a":18,"../module-apple":19,"../module-orange":20,"../module-banana":21}],22:[function(require,module,exports){ 'use strict'; /** - * Locals + * Module Dependencies */ -var has = {}.hasOwnProperty; +var fruitMachine = require('./fruitmachine'); +var Model = require('model'); /** * Exports */ -module.exports = function(main) { - var args = arguments; - var l = args.length; - var i = 0; - var src; - var key; - - while (++i < l) { - src = args[i]; - for (key in src) { - if (has.call(src, key)) { - main[key] = src[key]; - } - } - } - - return main; -}; - -},{}],6:[function(require,module,exports){ - -'use strict'; +module.exports = fruitMachine({ Model: Model }); +},{"./fruitmachine":20,"model":26}],22:[function(require,module,exports){ /** * Module Dependencies */ var events = require('event'); -var mixin = require('mixin'); /** * Exports */ -module.exports = Model; - -/** - * Locals - */ - -var proto = Model.prototype; - -/** - * Model constructor. - * - * @constructor - * @param {Object} data - * @api public - */ -function Model(data) { - this._data = mixin({}, data); -} - -/** - * Gets a value by key - * - * If no key is given, the - * whole model is returned. - * - * @param {String} key - * @return {*} - * @api public - */ -proto.get = function(key) { - return key - ? this._data[key] - : this._data; -}; - /** - * Sets data on the model. - * - * Accepts either a key and - * value, or an object literal. - * - * @param {String|Object} key - * @param {*|undefined} value + * Registers a event listener. + * + * @param {String} name + * @param {String} module + * @param {Function} cb + * @return {View} */ -proto.set = function(data, value) { +exports.on = function(name, module, cb) { - // If a string key is passed - // with a value. Set the value - // on the key in the data store. - if ('string' === typeof data && typeof value !== 'undefined') { - this._data[data] = value; - this.fire('change:' + data, value); + // cb can be passed as + // the second or third argument + if (arguments.length === 2) { + cb = module; + module = null; } - // Merge the object into the data store - if ('object' === typeof data) { - mixin(this._data, data); - for (var prop in data) this.fire('change:' + prop, data[prop]); + // if a module is provided + // pass in a special callback + // function that checks the + // module + if (module) { + events.prototype.on.call(this, name, function() { + if (this.event.target.module() === module) { + cb.apply(this, arguments); + } + }); + } else { + events.prototype.on.call(this, name, cb); } - // Always fire a - // generic change event - this.fire('change'); - - // Allow chaining return this; }; /** - * CLears the data store. + * Fires an event on a view. * - * @return {Model} + * @param {String} name + * @return {View} */ -proto.clear = function() { - this._data = {}; - this.fire('change'); +exports.fire = function(name) { + var _event = this.event; + var event = { + target: this, + propagate: true, + stopPropagation: function(){ this.propagate = false; } + }; + + propagate(this, arguments, event); + + // COMPLEX: + // If an earlier event object was + // cached, restore the the event + // back onto the view. If there + // wasn't an earlier event, make + // sure the `event` key has been + // deleted off the view. + if (_event) this.event = _event; + else delete this.event; // Allow chaining return this; }; -/** - * Deletes the data store. - * - * @return {undefined} - */ -proto.destroy = function() { - for (var key in this._data) this._data[key] = null; - delete this._data; - this.fire('destroy'); -}; - -/** - * Returns a shallow - * clone of the data store. - * - * @return {Object} - */ -proto.toJSON = function() { - return mixin({}, this._data); -}; +function propagate(view, args, event) { + if (!view || !event.propagate) return; -// Mixin events -events(proto); + view.event = event; + events.prototype.fire.apply(view, args); + propagate(view.parent, args, event); +} -},{"mixin":22,"event":17}],15:[function(require,module,exports){ +exports.fireStatic = events.prototype.fire; +exports.off = events.prototype.off; +},{"event":24}],23:[function(require,module,exports){ /*jshint browser:true, node:true*/ @@ -1386,11 +1225,11 @@ module.exports = function(fm) { proto._configure = function(options) { // Setup static properties - this._id = options.id || util.uniqueId('id-'); + this._id = options.id || util.uniqueId(); this._fmid = options.fmid || util.uniqueId('fmid'); this.tag = options.tag || this.tag || 'div'; - this.classes = this.classes || options.classes || []; - this.helpers = this.helpers || options.helpers || []; + this.classes = options.classes || this.classes || []; + this.helpers = options.helpers || this.helpers || []; this.template = this._setTemplate(options.template || this.template); this.slot = options.slot; @@ -2145,36 +1984,56 @@ module.exports = function(fm) { }; /** - * Returns a JSON represention of + * @deprecated + */ + proto.toJSON = function(options) { + return this.serialize(options); + }; + + /** + * Returns a serialized represention of * a FruitMachine Module. This can * be generated serverside and - * passed into new FruitMachine(json) + * passed into new FruitMachine(serialized) * to inflate serverside rendered * views. * + * Options: + * + * - `inflatable` Whether the returned object should retain references to DOM ids for use with client-side inflation of views + * + * @param {Object} options * @return {Object} * @api public */ - proto.toJSON = function() { - var json = {}; - json.children = []; + proto.serialize = function(options) { + var serialized = {}; + + // Shallow clone the options + options = mixin({ + inflatable: true + }, options); + + serialized.children = []; // Recurse this.each(function(child) { - json.children.push(child.toJSON()); + serialized.children.push(child.serialize()); }); - json.id = this.id(); - json.fmid = this._fmid; - json.module = this.module(); - json.model = this.model.toJSON(); - json.slot = this.slot; + serialized.id = this.id(); + serialized.module = this.module(); + serialized.model = this.model.toJSON(); + serialized.slot = this.slot; + + if (options.inflatable) serialized.fmid = this._fmid; // Fire a hook to allow third - // parties to alter the json output - this.fireStatic('tojson', json); + // parties to alter the output + this.fireStatic('tojson', serialized); // @deprecated + this.fireStatic('serialize', serialized); - return json; + return serialized; }; // Events @@ -2194,170 +2053,121 @@ module.exports = function(fm) { return Module; }; -},{"./events":23,"utils":16,"extend":24}],18:[function(require,module,exports){ - -/** - * Module Dependencies - */ - -var fm = require('../../../../lib/'); -var template = require('./template'); - -/** - * Exports - */ - -module.exports = fm.define({ - module: 'layout-a', - template: template -}); -},{"./template":25,"../../../../lib/":2}],19:[function(require,module,exports){ - -/** - * Module Dependencies - */ - -var fm = require('../../../../lib/'); -var template = require('./template'); - -/** - * Exports - */ - -module.exports = fm.define({ - module: 'apple', - template: template -}); -},{"./template":26,"../../../../lib/":2}],20:[function(require,module,exports){ - -/** - * Module Dependencies - */ - -var fm = require('../../../../lib/'); -var template = require('./template'); +},{"./events":22,"extend":25,"utils":28}],24:[function(require,module,exports){ /** - * Exports + * Event + * + * A super lightweight + * event emitter library. + * + * @version 0.1.4 + * @author Wilson Page */ -module.exports = fm.define({ - module: 'orange', - template: template -}); -},{"./template":27,"../../../../lib/":2}],21:[function(require,module,exports){ - /** - * Module Dependencies + * Locals */ -var fm = require('../../../../lib/'); -var template = require('./template'); +var proto = Event.prototype; /** - * Exports + * Expose `Event` */ -module.exports = fm.define({ - module: 'banana', - template: template -}); -},{"./template":28,"../../../../lib/":2}],25:[function(require,module,exports){ -module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_1",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_2",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b(_.t(_.f("slot_3",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");_.b("\n" + i);_.b("
");return _.fl();;}); -},{}],26:[function(require,module,exports){ -module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
");_.b("\n" + i);_.b("
");_.b(_.v(_.f("title",c,p,0)));_.b("
");_.b("\n" + i);_.b("
");return _.fl();;}); -},{}],27:[function(require,module,exports){ -module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
Module Orange
");return _.fl();;}); -},{}],28:[function(require,module,exports){ -module.exports = new Hogan(function(c,p,i){var _=this;_.b(i=i||"");_.b("
Module Banana
");return _.fl();;}); -},{}],23:[function(require,module,exports){ +module.exports = Event; /** - * Module Dependencies + * Creates a new event emitter + * instance, or if passed an + * object, mixes the event logic + * into it. + * + * @param {Object} obj + * @return {Object} */ - -var events = require('event'); +function Event(obj) { + if (!(this instanceof Event)) return new Event(obj); + if (obj) return mixin(obj, proto); +} /** - * Exports + * Registers a callback + * with an event name. + * + * @param {String} name + * @param {Function} cb + * @return {Event} */ +proto.on = function(name, cb) { + this._cbs = this._cbs || {}; + (this._cbs[name] || (this._cbs[name] = [])).unshift(cb); + return this; +}; /** - * Registers a event listener. + * Removes a single callback, + * or all callbacks associated + * with the passed event name. * * @param {String} name - * @param {String} module * @param {Function} cb - * @return {View} + * @return {Event} */ -exports.on = function(name, module, cb) { +proto.off = function(name, cb) { + this._cbs = this._cbs || {}; - // cb can be passed as - // the second or third argument - if (arguments.length === 2) { - cb = module; - module = null; - } + if (!name) return this._cbs = {}; + if (!cb) return delete this._cbs[name]; - // if a module is provided - // pass in a special callback - // function that checks the - // module - if (module) { - events.prototype.on.call(this, name, function() { - if (this.event.target.module() === module) { - cb.apply(this, arguments); - } - }); - } else { - events.prototype.on.call(this, name, cb); - } + var cbs = this._cbs[name] || []; + var i; + while (cbs && ~(i = cbs.indexOf(cb))) cbs.splice(i, 1); return this; }; /** - * Fires an event on a view. + * Fires an event. Which triggers + * all callbacks registered on this + * event name. * * @param {String} name - * @return {View} + * @return {Event} */ -exports.fire = function(name) { - var parent = this.parent; - var _event = this.event; - var event = { - target: this, - propagate: true, - stopPropagation: function(){ this.propagate = false; } - }; - - propagate(this, arguments, event); +proto.fire = function(options) { + this._cbs = this._cbs || {}; + var name = options.name || options; + var ctx = options.ctx || this; + var cbs = this._cbs[name]; - // COMPLEX: - // If an earlier event object was - // cached, restore the the event - // back onto the view. If there - // wasn't an earlier event, make - // sure the `event` key has been - // deleted off the view. - if (_event) this.event = _event; - else delete this.event; + if (cbs) { + var args = [].slice.call(arguments, 1); + var l = cbs.length; + while (l--) cbs[l].apply(ctx, args); + } - // Allow chaining return this; }; -function propagate(view, args, event) { - if (!view || !event.propagate) return; +/** + * Util + */ - view.event = event; - events.prototype.fire.apply(view, args); - propagate(view.parent, args, event); +/** + * Mixes in the properties + * of the second object into + * the first. + * + * @param {Object} a + * @param {Object} b + * @return {Object} + */ +function mixin(a, b) { + for (var key in b) a[key] = b[key]; + return a; } - -exports.fireStatic = events.prototype.fire; -exports.off = events.prototype.off; -},{"event":17}],24:[function(require,module,exports){ +},{}],25:[function(require,module,exports){ /*jshint browser:true, node:true*/ 'use strict'; @@ -2427,5 +2237,225 @@ function protect(keys, ob) { } } } -},{"utils":16}]},{},[1]) -; \ No newline at end of file +},{"utils":28}],26:[function(require,module,exports){ + +'use strict'; + +/** + * Module Dependencies + */ + +var events = require('event'); +var mixin = require('mixin'); + +/** + * Exports + */ + +module.exports = Model; + +/** + * Locals + */ + +var proto = Model.prototype; + +/** + * Model constructor. + * + * @constructor + * @param {Object} data + * @api public + */ +function Model(data) { + this._data = mixin({}, data); +} + +/** + * Gets a value by key + * + * If no key is given, the + * whole model is returned. + * + * @param {String} key + * @return {*} + * @api public + */ +proto.get = function(key) { + return key + ? this._data[key] + : this._data; +}; + +/** + * Sets data on the model. + * + * Accepts either a key and + * value, or an object literal. + * + * @param {String|Object} key + * @param {*|undefined} value + */ +proto.set = function(data, value) { + + // If a string key is passed + // with a value. Set the value + // on the key in the data store. + if ('string' === typeof data && typeof value !== 'undefined') { + this._data[data] = value; + this.fire('change:' + data, value); + } + + // Merge the object into the data store + if ('object' === typeof data) { + mixin(this._data, data); + for (var prop in data) this.fire('change:' + prop, data[prop]); + } + + // Always fire a + // generic change event + this.fire('change'); + + // Allow chaining + return this; +}; + +/** + * CLears the data store. + * + * @return {Model} + */ +proto.clear = function() { + this._data = {}; + this.fire('change'); + + // Allow chaining + return this; +}; + +/** + * Deletes the data store. + * + * @return {undefined} + */ +proto.destroy = function() { + for (var key in this._data) this._data[key] = null; + delete this._data; + this.fire('destroy'); +}; + +/** + * Returns a shallow + * clone of the data store. + * + * @return {Object} + */ +proto.toJSON = function() { + return mixin({}, this._data); +}; + +// Mixin events +events(proto); + +},{"event":24,"mixin":27}],27:[function(require,module,exports){ + +'use strict'; + +/** + * Locals + */ + +var has = {}.hasOwnProperty; + +/** + * Exports + */ + +module.exports = function(main) { + var args = arguments; + var l = args.length; + var i = 0; + var src; + var key; + + while (++i < l) { + src = args[i]; + for (key in src) { + if (has.call(src, key)) { + main[key] = src[key]; + } + } + } + + return main; +}; + +},{}],28:[function(require,module,exports){ + +/*jshint browser:true, node:true*/ + +'use strict'; + +exports.bind = function(method, context) { + return function() { return method.apply(context, arguments); }; +}; + +exports.isArray = function(arg) { + return arg instanceof Array; +}, + +exports.mixin = function(original, source) { + for (var key in source) original[key] = source[key]; + return original; +}, + +exports.byId = function(id, el) { + if (el) return el.querySelector('#' + id); +}, + +/** + * Inserts an item into an array. + * Has the option to state an index. + * + * @param {*} item + * @param {Array} array + * @param {Number} index + * @return void + */ +exports.insert = function(item, array, index) { + if (typeof index !== 'undefined') { + array.splice(index, 0, item); + } else { + array.push(item); + } +}, + +exports.toNode = function(html) { + var el = document.createElement('div'); + el.innerHTML = html; + return el.removeChild(el.firstElementChild); +}, + +// Determine if we have a DOM +// in the current environment. +exports.hasDom = function() { + return typeof document !== 'undefined'; +}; + +var i = 0; +exports.uniqueId = function(prefix) { + return (prefix || 'id') + ((++i) * Math.round(Math.random() * 100000)); +}; + +exports.keys = function(object) { + var keys = []; + for (var key in object) keys.push(key); + return keys; +}; + +exports.isPlainObject = function(ob) { + if (!ob) return false; + var c = (ob.constructor || '').toString(); + return !!~c.indexOf('Object'); +}; +},{}]},{},[1]) \ No newline at end of file diff --git a/examples/express/lib/page-about/server.js b/examples/express/lib/page-about/server.js index 229ca29..3748282 100644 --- a/examples/express/lib/page-about/server.js +++ b/examples/express/lib/page-about/server.js @@ -10,7 +10,7 @@ var database = { module.exports = function(req, res){ var view = View(database); - res.expose(view.toJSON(), 'layout'); + res.expose(view.serialize(), 'layout'); res.render('wrapper', { title: 'About', body: view.toHTML() diff --git a/examples/express/lib/page-home/server.js b/examples/express/lib/page-home/server.js index 02a3a00..4724de7 100644 --- a/examples/express/lib/page-home/server.js +++ b/examples/express/lib/page-home/server.js @@ -10,7 +10,7 @@ var database = { module.exports = function(req, res){ var view = View(database); - res.expose(view.toJSON(), 'layout'); + res.expose(view.serialize(), 'layout'); res.render('wrapper', { title: 'Home', body: view.toHTML() diff --git a/examples/express/lib/page-links/server.js b/examples/express/lib/page-links/server.js index 0b4053b..ec7522e 100644 --- a/examples/express/lib/page-links/server.js +++ b/examples/express/lib/page-links/server.js @@ -10,7 +10,7 @@ var database = { module.exports = function(req, res){ var view = View(database); - res.expose(view.toJSON(), 'layout'); + res.expose(view.serialize(), 'layout'); res.render('wrapper', { title: 'Links', body: view.toHTML() From b5a55dbacee973a77cabb15a19d25970e42afcf2 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Fri, 20 Dec 2013 11:32:21 +0000 Subject: [PATCH 6/7] Fix missing compass warning, delete Sass cache files --- .../style.scssc | Bin 1292 -> 0 bytes .../main.scssc | Bin 277 -> 0 bytes .../style.scssc | Bin 1292 -> 0 bytes .../main.scssc | Bin 277 -> 0 bytes .../style.scssc | Bin 1292 -> 0 bytes examples/express/build/build.css | 9 ++++++++- 6 files changed, 8 insertions(+), 1 deletion(-) delete mode 100644 examples/express/.sass-cache/02edf621e302928b0497cc899071d282b21c69ae/style.scssc delete mode 100644 examples/express/.sass-cache/34148d82a00c750b7f5bb319d56d454f5d3a0706/main.scssc delete mode 100644 examples/express/.sass-cache/c17ef6a51d16627d3a39ba3670d20574256733ff/style.scssc delete mode 100644 examples/express/.sass-cache/e311ee56d011e27e22137ec9eba8360ee5a6567e/main.scssc delete mode 100644 examples/express/.sass-cache/f489b5958955e5b353f8ff421f5e0e803b363f01/style.scssc diff --git a/examples/express/.sass-cache/02edf621e302928b0497cc899071d282b21c69ae/style.scssc b/examples/express/.sass-cache/02edf621e302928b0497cc899071d282b21c69ae/style.scssc deleted file mode 100644 index 06da364d0a6e62837a5772e2a51bd819adab2841..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1292 zcmdUu&rjPh6vvA+WvN>z?Z)^q7@-{4kOnC&EwxoC6PH~&O)Iw^Dz|wZ-jX}84RuZ(x2nQ-gEtkqOVHEsfewlb%Q73p+QiNx$leU zJ0o6^mG1in6D49z=|C$i+RUaE;Ui!%6`(S0z1V2Q*Q_kcwCcs^FiLJB&9A8)!EYl$ z6NRTkliE?3lKIFD8CvsVt`hJ^rkqGx&n7v5047>0O3gI+0DjFNnt+YN3n3hXN=&ew zMe$r`u?Zwyo@$m#T?Z~F;={( z2*7Zx7Z5{72cIFh0xc%|0QXvDuh{FfzJ{mVctm@og${tdk!;;g6SB>W6x$-~tz_F4 z+sU)DcUjE!hcWa1-85qb19i}_!W@$=Tke@SmDE06TKG^kAat&wzVw&;H*buPN93Ga aQp{fmb<(>|?iM5&93wRIqRx}qkh_1hr)Km3 diff --git a/examples/express/.sass-cache/34148d82a00c750b7f5bb319d56d454f5d3a0706/main.scssc b/examples/express/.sass-cache/34148d82a00c750b7f5bb319d56d454f5d3a0706/main.scssc deleted file mode 100644 index d4c0722b3f96e02ef7e3926d906ee4084137afe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 277 zcmZXOF;BxV5Jo$oKwV&fp+YQCSYV*E2}zpE;+3Hc(5*vt#AnsQ$yu=(Q2#tt1yU=< zPkMUq%O>e0i|-et48{xWKOaP)b>378r?VnY^FpQ1)D4ECua;d z@8~_ivL^y~V-ByZNWi6RHpZ%+x>dw*dkDR>j{7|}f@^8^i_}^hOVc`94d6nu3x+$t z9l-b)=&+?^+_L(z^oY(`dh+4h5(sHcN9J@F4%9k}VJ92PAzaA~`t{$LWj!)ceV*Kj LUvHfJmnHQNM*CPn diff --git a/examples/express/.sass-cache/c17ef6a51d16627d3a39ba3670d20574256733ff/style.scssc b/examples/express/.sass-cache/c17ef6a51d16627d3a39ba3670d20574256733ff/style.scssc deleted file mode 100644 index 06da364d0a6e62837a5772e2a51bd819adab2841..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1292 zcmdUu&rjPh6vvA+WvN>z?Z)^q7@-{4kOnC&EwxoC6PH~&O)Iw^Dz|wZ-jX}84RuZ(x2nQ-gEtkqOVHEsfewlb%Q73p+QiNx$leU zJ0o6^mG1in6D49z=|C$i+RUaE;Ui!%6`(S0z1V2Q*Q_kcwCcs^FiLJB&9A8)!EYl$ z6NRTkliE?3lKIFD8CvsVt`hJ^rkqGx&n7v5047>0O3gI+0DjFNnt+YN3n3hXN=&ew zMe$r`u?Zwyo@$m#T?Z~F;={( z2*7Zx7Z5{72cIFh0xc%|0QXvDuh{FfzJ{mVctm@og${tdk!;;g6SB>W6x$-~tz_F4 z+sU)DcUjE!hcWa1-85qb19i}_!W@$=Tke@SmDE06TKG^kAat&wzVw&;H*buPN93Ga aQp{fmb<(>|?iM5&93wRIqRx}qkh_1hr)Km3 diff --git a/examples/express/.sass-cache/e311ee56d011e27e22137ec9eba8360ee5a6567e/main.scssc b/examples/express/.sass-cache/e311ee56d011e27e22137ec9eba8360ee5a6567e/main.scssc deleted file mode 100644 index d4c0722b3f96e02ef7e3926d906ee4084137afe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 277 zcmZXOF;BxV5Jo$oKwV&fp+YQCSYV*E2}zpE;+3Hc(5*vt#AnsQ$yu=(Q2#tt1yU=< zPkMUq%O>e0i|-et48{xWKOaP)b>378r?VnY^FpQ1)D4ECua;d z@8~_ivL^y~V-ByZNWi6RHpZ%+x>dw*dkDR>j{7|}f@^8^i_}^hOVc`94d6nu3x+$t z9l-b)=&+?^+_L(z^oY(`dh+4h5(sHcN9J@F4%9k}VJ92PAzaA~`t{$LWj!)ceV*Kj LUvHfJmnHQNM*CPn diff --git a/examples/express/.sass-cache/f489b5958955e5b353f8ff421f5e0e803b363f01/style.scssc b/examples/express/.sass-cache/f489b5958955e5b353f8ff421f5e0e803b363f01/style.scssc deleted file mode 100644 index 06da364d0a6e62837a5772e2a51bd819adab2841..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1292 zcmdUu&rjPh6vvA+WvN>z?Z)^q7@-{4kOnC&EwxoC6PH~&O)Iw^Dz|wZ-jX}84RuZ(x2nQ-gEtkqOVHEsfewlb%Q73p+QiNx$leU zJ0o6^mG1in6D49z=|C$i+RUaE;Ui!%6`(S0z1V2Q*Q_kcwCcs^FiLJB&9A8)!EYl$ z6NRTkliE?3lKIFD8CvsVt`hJ^rkqGx&n7v5047>0O3gI+0DjFNnt+YN3n3hXN=&ew zMe$r`u?Zwyo@$m#T?Z~F;={( z2*7Zx7Z5{72cIFh0xc%|0QXvDuh{FfzJ{mVctm@og${tdk!;;g6SB>W6x$-~tz_F4 z+sU)DcUjE!hcWa1-85qb19i}_!W@$=Tke@SmDE06TKG^kAat&wzVw&;H*buPN93Ga aQp{fmb<(>|?iM5&93wRIqRx}qkh_1hr)Km3 diff --git a/examples/express/build/build.css b/examples/express/build/build.css index f0a73f5..46b798f 100644 --- a/examples/express/build/build.css +++ b/examples/express/build/build.css @@ -1 +1,8 @@ -ERROR: Cannot load compass. +/*========================================================================= + Module Apple + ========================================================================== */ +.module-apple { + padding: 14px; } + +.module-apple_title { + font: 30px 'Helvetica'; } From adf5c1d37e40bd689854eec64659a91702a540e0 Mon Sep 17 00:00:00 2001 From: George Crawford Date: Fri, 20 Dec 2013 11:38:47 +0000 Subject: [PATCH 7/7] Remove newline gremlins from docs --- README.md | 2 +- build/fruitmachine.js | 138 +++++++++++++++++++------------------- build/fruitmachine.min.js | 2 +- docs/api.md | 30 ++++----- docs/templates/api.hogan | 2 +- 5 files changed, 87 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 1b121e8..9450704 100644 --- a/README.md +++ b/README.md @@ -98,4 +98,4 @@ Licensed under the MIT license. ## Credits and collaboration -The lead developer of FruitMachine is [Wilson Page](http://github.com/wilsonpage) at FT Labs. All open source code released by FT Labs is licenced under the MIT licence. We welcome comments, feedback and suggestions. Please feel free to raise an issue or pull request. Enjoy... +The lead developer of FruitMachine is [Wilson Page](http://github.com/wilsonpage) at FT Labs. All open source code released by FT Labs is licenced under the MIT licence. We welcome comments, feedback and suggestions. Please feel free to raise an issue or pull request. Enjoy... \ No newline at end of file diff --git a/build/fruitmachine.js b/build/fruitmachine.js index c66b272..7ff3c5f 100644 --- a/build/fruitmachine.js +++ b/build/fruitmachine.js @@ -140,75 +140,7 @@ module.exports = function(options) { return events(fm); }; -},{"./define":4,"./module":5,"utils":6,"event":7}],6:[function(require,module,exports){ - -/*jshint browser:true, node:true*/ - -'use strict'; - -exports.bind = function(method, context) { - return function() { return method.apply(context, arguments); }; -}; - -exports.isArray = function(arg) { - return arg instanceof Array; -}, - -exports.mixin = function(original, source) { - for (var key in source) original[key] = source[key]; - return original; -}, - -exports.byId = function(id, el) { - if (el) return el.querySelector('#' + id); -}, - -/** - * Inserts an item into an array. - * Has the option to state an index. - * - * @param {*} item - * @param {Array} array - * @param {Number} index - * @return void - */ -exports.insert = function(item, array, index) { - if (typeof index !== 'undefined') { - array.splice(index, 0, item); - } else { - array.push(item); - } -}, - -exports.toNode = function(html) { - var el = document.createElement('div'); - el.innerHTML = html; - return el.removeChild(el.firstElementChild); -}, - -// Determine if we have a DOM -// in the current environment. -exports.hasDom = function() { - return typeof document !== 'undefined'; -}; - -var i = 0; -exports.uniqueId = function(prefix) { - return (prefix || 'id') + ((++i) * Math.round(Math.random() * 100000)); -}; - -exports.keys = function(object) { - var keys = []; - for (var key in object) keys.push(key); - return keys; -}; - -exports.isPlainObject = function(ob) { - if (!ob) return false; - var c = (ob.constructor || '').toString(); - return !!~c.indexOf('Object'); -}; -},{}],7:[function(require,module,exports){ +},{"./define":4,"./module":5,"utils":6,"event":7}],7:[function(require,module,exports){ /** * Event @@ -322,6 +254,74 @@ function mixin(a, b) { for (var key in b) a[key] = b[key]; return a; } +},{}],6:[function(require,module,exports){ + +/*jshint browser:true, node:true*/ + +'use strict'; + +exports.bind = function(method, context) { + return function() { return method.apply(context, arguments); }; +}; + +exports.isArray = function(arg) { + return arg instanceof Array; +}, + +exports.mixin = function(original, source) { + for (var key in source) original[key] = source[key]; + return original; +}, + +exports.byId = function(id, el) { + if (el) return el.querySelector('#' + id); +}, + +/** + * Inserts an item into an array. + * Has the option to state an index. + * + * @param {*} item + * @param {Array} array + * @param {Number} index + * @return void + */ +exports.insert = function(item, array, index) { + if (typeof index !== 'undefined') { + array.splice(index, 0, item); + } else { + array.push(item); + } +}, + +exports.toNode = function(html) { + var el = document.createElement('div'); + el.innerHTML = html; + return el.removeChild(el.firstElementChild); +}, + +// Determine if we have a DOM +// in the current environment. +exports.hasDom = function() { + return typeof document !== 'undefined'; +}; + +var i = 0; +exports.uniqueId = function(prefix) { + return (prefix || 'id') + ((++i) * Math.round(Math.random() * 100000)); +}; + +exports.keys = function(object) { + var keys = []; + for (var key in object) keys.push(key); + return keys; +}; + +exports.isPlainObject = function(ob) { + if (!ob) return false; + var c = (ob.constructor || '').toString(); + return !!~c.indexOf('Object'); +}; },{}],8:[function(require,module,exports){ 'use strict'; diff --git a/build/fruitmachine.min.js b/build/fruitmachine.min.js index bf6d110..9b0d695 100644 --- a/build/fruitmachine.min.js +++ b/build/fruitmachine.min.js @@ -1 +1 @@ -(function(t){if("function"==typeof bootstrap)bootstrap("fruitmachine",t);else if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeFruitmachine=t}else"undefined"!=typeof window?window.fruitmachine=t():global.fruitmachine=t()})(function(){return function(t,e,i){function n(i,s){if(!e[i]){if(!t[i]){var o="function"==typeof require&&require;if(!s&&o)return o(i,!0);if(r)return r(i,!0);throw Error("Cannot find module '"+i+"'")}var h=e[i]={exports:{}};t[i][0].call(h.exports,function(e){var r=t[i][1][e];return n(r?r:e)},h,h.exports)}return e[i].exports}for(var r="function"==typeof require&&require,s=0;i.length>s;s++)n(i[s]);return n}({1:[function(t,e){"use strict";var i=t("./fruitmachine"),n=t("model");e.exports=i({Model:n})},{"./fruitmachine":2,model:3}],4:[function(t,e){"use strict";e.exports=function(t){return function(e){var i="object"==typeof e?t.Module.extend(e):e,n=i.prototype,r=n.name||n._module;return r&&(t.modules[r]=i),i}}},{}],2:[function(t,e){"use strict";var i=t("./module"),n=t("./define"),r=t("utils"),s=t("event");e.exports=function(t){function o(t){var e=o.modules[t.module];return e?new e(t):void 0}return o.create=e.exports,o.Model=t.Model,o.Events=s,o.Module=i(o),o.define=n(o),o.util=r,o.modules={},o.config={templateIterator:"children",templateInstance:"child"},s(o)}},{"./define":4,"./module":5,utils:6,event:7}],6:[function(t,e,i){"use strict";i.bind=function(t,e){return function(){return t.apply(e,arguments)}},i.isArray=function(t){return t instanceof Array},i.mixin=function(t,e){for(var i in e)t[i]=e[i];return t},i.byId=function(t,e){return e?e.querySelector("#"+t):void 0},i.insert=function(t,e,i){i!==void 0?e.splice(i,0,t):e.push(t)},i.toNode=function(t){var e=document.createElement("div");return e.innerHTML=t,e.removeChild(e.firstElementChild)},i.hasDom=function(){return"undefined"!=typeof document};var n=0;i.uniqueId=function(t){return(t||"id")+ ++n*Math.round(1e5*Math.random())},i.keys=function(t){var e=[];for(var i in t)e.push(i);return e},i.isPlainObject=function(t){if(!t)return!1;var e=""+(t.constructor||"");return!!~e.indexOf("Object")}},{}],7:[function(t,e){function i(t){return this instanceof i?t?n(t,r):void 0:new i(t)}function n(t,e){for(var i in e)t[i]=e[i];return t}var r=i.prototype;e.exports=i,r.on=function(t,e){return this._cbs=this._cbs||{},(this._cbs[t]||(this._cbs[t]=[])).unshift(e),this},r.off=function(t,e){if(this._cbs=this._cbs||{},!t)return this._cbs={};if(!e)return delete this._cbs[t];for(var i,n=this._cbs[t]||[];n&&~(i=n.indexOf(e));)n.splice(i,1);return this},r.fire=function(t){this._cbs=this._cbs||{};var e=t.name||t,i=t.ctx||this,n=this._cbs[e];if(n)for(var r=[].slice.call(arguments,1),s=n.length;s--;)n[s].apply(i,r);return this}},{}],8:[function(t,e){"use strict";var i={}.hasOwnProperty;e.exports=function(t){for(var e,n,r=arguments,s=r.length,o=0;s>++o;){e=r[o];for(n in e)i.call(e,n)&&(t[n]=e[n])}return t}},{}],3:[function(t,e){"use strict";function i(t){this._data=r({},t)}var n=t("event"),r=t("mixin");e.exports=i;var s=i.prototype;s.get=function(t){return t?this._data[t]:this._data},s.set=function(t,e){if("string"==typeof t&&e!==void 0&&(this._data[t]=e,this.fire("change:"+t,e)),"object"==typeof t){r(this._data,t);for(var i in t)this.fire("change:"+i,t[i])}return this.fire("change"),this},s.clear=function(){return this._data={},this.fire("change"),this},s.destroy=function(){for(var t in this._data)this._data[t]=null;delete this._data,this.fire("destroy")},s.toJSON=function(){return r({},this._data)},n(s)},{mixin:8,event:7}],5:[function(t,e){"use strict";var i=t("utils"),n=t("./events"),r=t("extend"),s=i.mixin;e.exports=function(t){function e(t){t=s({},t),this._configure(t),this._add(t.children),this.initialize&&this.initialize(t),this.fireStatic("initialize",t)}var o=e.prototype;return o._configure=function(e){this._id=e.id||i.uniqueId(),this._fmid=e.fmid||i.uniqueId("fmid"),this.tag=e.tag||this.tag||"div",this.classes=e.classes||this.classes||[],this.helpers=e.helpers||this.helpers||[],this.template=this._setTemplate(e.template||this.template),this.slot=e.slot,this.children=[],this._ids={},this._modules={},this.slots={};var n=e.model||e.data||{};this.model=i.isPlainObject(n)?new this.Model(n):n,this.helpers.forEach(this.attachHelper,this),e.fmid&&(t.fire("inflation",this,e),this.fireStatic("inflation",e))},o._add=function(t){if(t){var e,n=i.isArray(t);for(var r in t)e=t[r],n||(e.slot=r),this.add(e)}},o.attachHelper=function(t){t&&t(this)},o._setTemplate=function(t){return t&&t.render?i.bind(t.render,t):t},o.add=function(n,r){if(!n)return this;var s=r&&r.at,o=r&&r.inject,h="object"==typeof r?r.slot:r;n.parent&&n.remove({fromDOM:!1}),h=n.slot=h||n.slot;var u=this.slots[h];return u&&u.remove({fromDOM:!1}),n instanceof e||(n=t(n)),i.insert(n,this.children,s),this._addLookup(n),o&&this._injectEl(n.el,r),this},o.remove=function(t,i){if(1===arguments.length&&!t)return this;if(t instanceof e)return t.remove(i||{}),this;var n,r=t||{},s=r.fromDOM!==!1,o=this.parent,h=this.el,u=h&&h.parentNode;return s&&u&&u.removeChild(h),o&&(n=o.children.indexOf(this),o.children.splice(n,1),o._removeLookup(this)),this},o._addLookup=function(t){var e=t.module();(this._modules[e]=this._modules[e]||[]).push(t),this._ids[t.id()]=t,t.slot&&(this.slots[t.slot]=t),t.parent=this},o._removeLookup=function(t){var e=t.module(),i=this._modules[e].indexOf(t);this._modules[e].splice(i,1),delete this._ids[t._id],delete this.slots[t.slot],delete t.parent},o._injectEl=function(t,e){var i=e&&e.at,n=this.el;t&&n&&(i!==void 0?n.insertBefore(t,n.children[i]):n.appendChild(t))},o.id=function(t){if(!arguments.length)return this._id;var e=this._ids[t];return e?e:this.each(function(e){return e.id(t)})},o.module=function(t){if(!arguments.length)return this._module||this.name;var e=this._modules[t];return e?e[0]:this.each(function(e){return e.module(t)})},o.modules=function(t){var e=this._modules[t]||[];return this.each(function(i){e=e.concat(i.modules(t))}),e},o.each=function(t){for(var e,i=this.children.length,n=0;i>n;n++)if(e=t(this.children[n]))return e},o.toHTML=function(){var e,i,n={};return n[t.config.templateIterator]=[],this.each(function(r){i={},e=r.toHTML(),n[r.slot||r.id()]=e,i[t.config.templateInstance]=e,n.children.push(s(i,r.model.toJSON()))}),e=this.template?this.template(s(n,this.model.toJSON())):"",this._wrapHTML(e)},o._wrapHTML=function(t){return"<"+this.tag+' class="'+this.module()+" "+this.classes.join(" ")+'" id="'+this._fmid+'">'+t+""},o.render=function(){var t=this.toHTML(),e=i.toNode(t);return this._setEl(e),this._fetchEls(this.el),this.fireStatic("render"),this},o.setup=function(t){var e=t&&t.shallow;return this._getEl()?(this.isSetup&&this.teardown({shallow:!0}),this.fireStatic("before setup"),this._setup&&this._setup(),this.fireStatic("setup"),this.isSetup=!0,e||this.each(function(t){t.setup()}),this):this},o.teardown=function(t){var e=t&&t.shallow;return e||this.each(function(t){t.teardown()}),this.isSetup&&(this.fireStatic("before teardown"),this._teardown&&this._teardown(),this.fireStatic("teardown"),this.isSetup=!1),this},o.destroy=function(t){t=t||{};for(var e=t.remove!==!1,i=this.children.length;i--;)this.children[i].destroy({remove:!1});return this.destroyed?this:(e&&this.remove(t),this.teardown({shallow:!0}),this.fireStatic("before destroy"),this._destroy&&this._destroy(),this.fireStatic("destroy"),this.off(),this.destroyed=!0,this.el=this.model=this.parent=this._modules=this._ids=this._id=null,void 0)},o.empty=function(){for(var t=this.children.length;t--;)this.children[t].destroy();return this},o._fetchEls=function(t){t&&this.each(function(e){e.el=i.byId(e._fmid,t),e._fetchEls(e.el||t)})},o._getEl=function(){return i.hasDom()?this.el=this.el||document.getElementById(this._fmid):void 0},o._setEl=function(t){var e=this.el,i=e&&e.parentNode;return i&&i.replaceChild(t,e),this.el=t,this},o.inject=function(t){return t&&(t.innerHTML="",this.appendTo(t),this.fireStatic("inject")),this},o.appendTo=function(t){return this.el&&t&&t.appendChild&&(t.appendChild(this.el),this.fireStatic("appendto")),this},o.toJSON=function(t){return this.serialize(t)},o.serialize=function(t){var e={};return t=s({inflatable:!0},t),e.children=[],this.each(function(t){e.children.push(t.serialize())}),e.id=this.id(),e.module=this.module(),e.model=this.model.toJSON(),e.slot=this.slot,t.inflatable&&(e.fmid=this._fmid),this.fireStatic("tojson",e),this.fireStatic("serialize",e),e},o.on=n.on,o.off=n.off,o.fire=n.fire,o.fireStatic=n.fireStatic,e.extend=r(i.keys(o)),o.Model=t.Model,e}},{"./events":9,utils:6,extend:10}],9:[function(t,e,i){function n(t,e,i){t&&i.propagate&&(t.event=i,r.prototype.fire.apply(t,e),n(t.parent,e,i))}var r=t("event");i.on=function(t,e,i){return 2===arguments.length&&(i=e,e=null),e?r.prototype.on.call(this,t,function(){this.event.target.module()===e&&i.apply(this,arguments)}):r.prototype.on.call(this,t,i),this},i.fire=function(){var t=this.event,e={target:this,propagate:!0,stopPropagation:function(){this.propagate=!1}};return n(this,arguments,e),t?this.event=t:delete this.event,this},i.fireStatic=r.prototype.fire,i.off=r.prototype.off},{event:7}],10:[function(t,e){"use strict";function i(t,e){for(var i in e)e.hasOwnProperty(i)&&~t.indexOf(i)&&(e["_"+i]=e[i],delete e[i])}var n=t("utils").mixin;e.exports=function(t){return function(e){function r(){this.constructor=o}var s=this,o=function(){return s.apply(this,arguments)};return n(o,s),t&&i(t,e),r.prototype=s.prototype,o.prototype=new r,n(o.prototype,e),o.__super__=s.prototype,o}}},{utils:6}]},{},[1])(1)}); \ No newline at end of file +(function(t){if("function"==typeof bootstrap)bootstrap("fruitmachine",t);else if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeFruitmachine=t}else"undefined"!=typeof window?window.fruitmachine=t():global.fruitmachine=t()})(function(){return function(t,e,i){function n(i,s){if(!e[i]){if(!t[i]){var o="function"==typeof require&&require;if(!s&&o)return o(i,!0);if(r)return r(i,!0);throw Error("Cannot find module '"+i+"'")}var h=e[i]={exports:{}};t[i][0].call(h.exports,function(e){var r=t[i][1][e];return n(r?r:e)},h,h.exports)}return e[i].exports}for(var r="function"==typeof require&&require,s=0;i.length>s;s++)n(i[s]);return n}({1:[function(t,e){"use strict";var i=t("./fruitmachine"),n=t("model");e.exports=i({Model:n})},{"./fruitmachine":2,model:3}],4:[function(t,e){"use strict";e.exports=function(t){return function(e){var i="object"==typeof e?t.Module.extend(e):e,n=i.prototype,r=n.name||n._module;return r&&(t.modules[r]=i),i}}},{}],2:[function(t,e){"use strict";var i=t("./module"),n=t("./define"),r=t("utils"),s=t("event");e.exports=function(t){function o(t){var e=o.modules[t.module];return e?new e(t):void 0}return o.create=e.exports,o.Model=t.Model,o.Events=s,o.Module=i(o),o.define=n(o),o.util=r,o.modules={},o.config={templateIterator:"children",templateInstance:"child"},s(o)}},{"./define":4,"./module":5,utils:6,event:7}],7:[function(t,e){function i(t){return this instanceof i?t?n(t,r):void 0:new i(t)}function n(t,e){for(var i in e)t[i]=e[i];return t}var r=i.prototype;e.exports=i,r.on=function(t,e){return this._cbs=this._cbs||{},(this._cbs[t]||(this._cbs[t]=[])).unshift(e),this},r.off=function(t,e){if(this._cbs=this._cbs||{},!t)return this._cbs={};if(!e)return delete this._cbs[t];for(var i,n=this._cbs[t]||[];n&&~(i=n.indexOf(e));)n.splice(i,1);return this},r.fire=function(t){this._cbs=this._cbs||{};var e=t.name||t,i=t.ctx||this,n=this._cbs[e];if(n)for(var r=[].slice.call(arguments,1),s=n.length;s--;)n[s].apply(i,r);return this}},{}],6:[function(t,e,i){"use strict";i.bind=function(t,e){return function(){return t.apply(e,arguments)}},i.isArray=function(t){return t instanceof Array},i.mixin=function(t,e){for(var i in e)t[i]=e[i];return t},i.byId=function(t,e){return e?e.querySelector("#"+t):void 0},i.insert=function(t,e,i){i!==void 0?e.splice(i,0,t):e.push(t)},i.toNode=function(t){var e=document.createElement("div");return e.innerHTML=t,e.removeChild(e.firstElementChild)},i.hasDom=function(){return"undefined"!=typeof document};var n=0;i.uniqueId=function(t){return(t||"id")+ ++n*Math.round(1e5*Math.random())},i.keys=function(t){var e=[];for(var i in t)e.push(i);return e},i.isPlainObject=function(t){if(!t)return!1;var e=""+(t.constructor||"");return!!~e.indexOf("Object")}},{}],8:[function(t,e){"use strict";var i={}.hasOwnProperty;e.exports=function(t){for(var e,n,r=arguments,s=r.length,o=0;s>++o;){e=r[o];for(n in e)i.call(e,n)&&(t[n]=e[n])}return t}},{}],3:[function(t,e){"use strict";function i(t){this._data=r({},t)}var n=t("event"),r=t("mixin");e.exports=i;var s=i.prototype;s.get=function(t){return t?this._data[t]:this._data},s.set=function(t,e){if("string"==typeof t&&e!==void 0&&(this._data[t]=e,this.fire("change:"+t,e)),"object"==typeof t){r(this._data,t);for(var i in t)this.fire("change:"+i,t[i])}return this.fire("change"),this},s.clear=function(){return this._data={},this.fire("change"),this},s.destroy=function(){for(var t in this._data)this._data[t]=null;delete this._data,this.fire("destroy")},s.toJSON=function(){return r({},this._data)},n(s)},{mixin:8,event:7}],5:[function(t,e){"use strict";var i=t("utils"),n=t("./events"),r=t("extend"),s=i.mixin;e.exports=function(t){function e(t){t=s({},t),this._configure(t),this._add(t.children),this.initialize&&this.initialize(t),this.fireStatic("initialize",t)}var o=e.prototype;return o._configure=function(e){this._id=e.id||i.uniqueId(),this._fmid=e.fmid||i.uniqueId("fmid"),this.tag=e.tag||this.tag||"div",this.classes=e.classes||this.classes||[],this.helpers=e.helpers||this.helpers||[],this.template=this._setTemplate(e.template||this.template),this.slot=e.slot,this.children=[],this._ids={},this._modules={},this.slots={};var n=e.model||e.data||{};this.model=i.isPlainObject(n)?new this.Model(n):n,this.helpers.forEach(this.attachHelper,this),e.fmid&&(t.fire("inflation",this,e),this.fireStatic("inflation",e))},o._add=function(t){if(t){var e,n=i.isArray(t);for(var r in t)e=t[r],n||(e.slot=r),this.add(e)}},o.attachHelper=function(t){t&&t(this)},o._setTemplate=function(t){return t&&t.render?i.bind(t.render,t):t},o.add=function(n,r){if(!n)return this;var s=r&&r.at,o=r&&r.inject,h="object"==typeof r?r.slot:r;n.parent&&n.remove({fromDOM:!1}),h=n.slot=h||n.slot;var u=this.slots[h];return u&&u.remove({fromDOM:!1}),n instanceof e||(n=t(n)),i.insert(n,this.children,s),this._addLookup(n),o&&this._injectEl(n.el,r),this},o.remove=function(t,i){if(1===arguments.length&&!t)return this;if(t instanceof e)return t.remove(i||{}),this;var n,r=t||{},s=r.fromDOM!==!1,o=this.parent,h=this.el,u=h&&h.parentNode;return s&&u&&u.removeChild(h),o&&(n=o.children.indexOf(this),o.children.splice(n,1),o._removeLookup(this)),this},o._addLookup=function(t){var e=t.module();(this._modules[e]=this._modules[e]||[]).push(t),this._ids[t.id()]=t,t.slot&&(this.slots[t.slot]=t),t.parent=this},o._removeLookup=function(t){var e=t.module(),i=this._modules[e].indexOf(t);this._modules[e].splice(i,1),delete this._ids[t._id],delete this.slots[t.slot],delete t.parent},o._injectEl=function(t,e){var i=e&&e.at,n=this.el;t&&n&&(i!==void 0?n.insertBefore(t,n.children[i]):n.appendChild(t))},o.id=function(t){if(!arguments.length)return this._id;var e=this._ids[t];return e?e:this.each(function(e){return e.id(t)})},o.module=function(t){if(!arguments.length)return this._module||this.name;var e=this._modules[t];return e?e[0]:this.each(function(e){return e.module(t)})},o.modules=function(t){var e=this._modules[t]||[];return this.each(function(i){e=e.concat(i.modules(t))}),e},o.each=function(t){for(var e,i=this.children.length,n=0;i>n;n++)if(e=t(this.children[n]))return e},o.toHTML=function(){var e,i,n={};return n[t.config.templateIterator]=[],this.each(function(r){i={},e=r.toHTML(),n[r.slot||r.id()]=e,i[t.config.templateInstance]=e,n.children.push(s(i,r.model.toJSON()))}),e=this.template?this.template(s(n,this.model.toJSON())):"",this._wrapHTML(e)},o._wrapHTML=function(t){return"<"+this.tag+' class="'+this.module()+" "+this.classes.join(" ")+'" id="'+this._fmid+'">'+t+""},o.render=function(){var t=this.toHTML(),e=i.toNode(t);return this._setEl(e),this._fetchEls(this.el),this.fireStatic("render"),this},o.setup=function(t){var e=t&&t.shallow;return this._getEl()?(this.isSetup&&this.teardown({shallow:!0}),this.fireStatic("before setup"),this._setup&&this._setup(),this.fireStatic("setup"),this.isSetup=!0,e||this.each(function(t){t.setup()}),this):this},o.teardown=function(t){var e=t&&t.shallow;return e||this.each(function(t){t.teardown()}),this.isSetup&&(this.fireStatic("before teardown"),this._teardown&&this._teardown(),this.fireStatic("teardown"),this.isSetup=!1),this},o.destroy=function(t){t=t||{};for(var e=t.remove!==!1,i=this.children.length;i--;)this.children[i].destroy({remove:!1});return this.destroyed?this:(e&&this.remove(t),this.teardown({shallow:!0}),this.fireStatic("before destroy"),this._destroy&&this._destroy(),this.fireStatic("destroy"),this.off(),this.destroyed=!0,this.el=this.model=this.parent=this._modules=this._ids=this._id=null,void 0)},o.empty=function(){for(var t=this.children.length;t--;)this.children[t].destroy();return this},o._fetchEls=function(t){t&&this.each(function(e){e.el=i.byId(e._fmid,t),e._fetchEls(e.el||t)})},o._getEl=function(){return i.hasDom()?this.el=this.el||document.getElementById(this._fmid):void 0},o._setEl=function(t){var e=this.el,i=e&&e.parentNode;return i&&i.replaceChild(t,e),this.el=t,this},o.inject=function(t){return t&&(t.innerHTML="",this.appendTo(t),this.fireStatic("inject")),this},o.appendTo=function(t){return this.el&&t&&t.appendChild&&(t.appendChild(this.el),this.fireStatic("appendto")),this},o.toJSON=function(t){return this.serialize(t)},o.serialize=function(t){var e={};return t=s({inflatable:!0},t),e.children=[],this.each(function(t){e.children.push(t.serialize())}),e.id=this.id(),e.module=this.module(),e.model=this.model.toJSON(),e.slot=this.slot,t.inflatable&&(e.fmid=this._fmid),this.fireStatic("tojson",e),this.fireStatic("serialize",e),e},o.on=n.on,o.off=n.off,o.fire=n.fire,o.fireStatic=n.fireStatic,e.extend=r(i.keys(o)),o.Model=t.Model,e}},{"./events":9,utils:6,extend:10}],9:[function(t,e,i){function n(t,e,i){t&&i.propagate&&(t.event=i,r.prototype.fire.apply(t,e),n(t.parent,e,i))}var r=t("event");i.on=function(t,e,i){return 2===arguments.length&&(i=e,e=null),e?r.prototype.on.call(this,t,function(){this.event.target.module()===e&&i.apply(this,arguments)}):r.prototype.on.call(this,t,i),this},i.fire=function(){var t=this.event,e={target:this,propagate:!0,stopPropagation:function(){this.propagate=!1}};return n(this,arguments,e),t?this.event=t:delete this.event,this},i.fireStatic=r.prototype.fire,i.off=r.prototype.off},{event:7}],10:[function(t,e){"use strict";function i(t,e){for(var i in e)e.hasOwnProperty(i)&&~t.indexOf(i)&&(e["_"+i]=e[i],delete e[i])}var n=t("utils").mixin;e.exports=function(t){return function(e){function r(){this.constructor=o}var s=this,o=function(){return s.apply(this,arguments)};return n(o,s),t&&i(t,e),r.prototype=s.prototype,o.prototype=new r,n(o.prototype,e),o.__super__=s.prototype,o}}},{utils:6}]},{},[1])(1)}); \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index d5dfc61..b607da1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3,7 +3,7 @@ ### fruitmachine.define() Defines a module. -\nOptions: +Options: - `name {String}` the name of the module - `tag {String}` the tagName to use for the root element @@ -18,7 +18,7 @@ Defines a module. ### Module#Module Module constructor -\nOptions: +Options: - `id {String}` a unique id to query by - `model {Object|Model}` the data with which to associate this module @@ -31,7 +31,7 @@ Module constructor ### Module#add() Adds a child view(s) to another Module. -\nOptions: +Options: - `at` The child index at which to insert - `inject` Injects the child's view element into the parent's @@ -43,7 +43,7 @@ Removes a child view from its current Module contexts and also from the DOM unless otherwise stated. -\nOptions: +Options: - `fromDOM` Whether the element should be removed from the DOM (default `true`) @@ -65,7 +65,7 @@ otherwise stated. Returns a decendent module by id, or if called with no arguments, returns this view's id. -\n*Example:* +*Example:* myModule.id(); //=> 'my_view_id' @@ -79,7 +79,7 @@ Returns the first descendent Module with the passed module type. If called with no arguments the Module's own module type is returned. -\n*Example:* +*Example:* // Assuming 'myModule' has 3 descendent // views with the module type 'apple' @@ -93,7 +93,7 @@ Returns a list of descendent Modules that match the module type given (Similar to Element.querySelectorAll();). -\n*Example:* +*Example:* // Assuming 'myModule' has 3 descendent // views with the module type 'apple' @@ -106,7 +106,7 @@ Element.querySelectorAll();). Calls the passed function for each of the view's children. -\n*Example:* +*Example:* myModule.each(function(child) { // Do stuff with each child view... @@ -119,7 +119,7 @@ any descendent views returning an html string. All data in the views model is made accessible to the template. -\nChild views are printed into the +Child views are printed into the parent template by `id`. Alternatively children can be iterated over a a list and printed with `{{{child}}}}`. @@ -140,13 +140,13 @@ and printed with `{{{child}}}}`. Renders the view and replaces the `view.el` with a freshly rendered node. -\nFires a `render` event on the view. +Fires a `render` event on the view. ### Module#setup() Sets up a view and all descendent views. -\nSetup will be aborted if no `view.el` +Setup will be aborted if no `view.el` is found. If a view is already setup, teardown is run first to prevent a view being setup twice. @@ -161,7 +161,7 @@ Options: Tearsdown a view and all descendent views that have been setup. -\nYour custom `teardown` method is +Your custom `teardown` method is called and a `teardown` event is fired. Options: @@ -174,7 +174,7 @@ Completely destroys a view. This means a view is torn down, removed from it's current layout context and removed from the DOM. -\nYour custom `destroy` method is +Your custom `destroy` method is called and a `destroy` event is fired. NOTE: `.remove()` is only run on the view @@ -187,7 +187,7 @@ Options: ### Module#empty() Destroys all children. -\nIs this needed? +Is this needed? ### Module#inject() @@ -207,7 +207,7 @@ be generated serverside and passed into new FruitMachine(serialized) to inflate serverside rendered views. -\nOptions: +Options: - `inflatable` Whether the returned object should retain references to DOM ids for use with client-side inflation of views diff --git a/docs/templates/api.hogan b/docs/templates/api.hogan index b05b706..cac7587 100644 --- a/docs/templates/api.hogan +++ b/docs/templates/api.hogan @@ -5,6 +5,6 @@ {{{description.summary}}} {{#description.body}} -\n{{{description.body}}} +{{{description.body}}} {{/description.body}} {{/jsdoc}} \ No newline at end of file