Skip to content

Instantly share code, notes, and snippets.

@jiggliemon
Created February 25, 2012 23:17
Show Gist options
  • Save jiggliemon/1911558 to your computer and use it in GitHub Desktop.
Save jiggliemon/1911558 to your computer and use it in GitHub Desktop.
blocks.js a module wrapper
define(['block/mixin','template/mixin', 'mediator/mixin'],
function ( BlockMixin, TemplateMixin, EventsMixin, undef ) {
var slice = Array.prototype.slice
,toString = Object.prototype.toString;
function typeOf (obj) {
return toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function extend (){
var target = arguments[0]
for (var key, i = 1, l = arguments.length; i < l; i++)
for (key in arguments[i])
target[key] = arguments[i][key]
return target
}
function implement (key, value, retain){
var k;
if (key === undef) return;
if (typeOf(key) === 'object') {
for ( k in key ) {
if (key.hasOwnProperty(k)) {
implement.call(this,k, key[k]);
}
}
return;
}
this[key] = (typeof value === 'function') ? (retain) ? value : wrap(this, key, value) : value;
return this;
}
function wrap (self, key, method){
function wrapper () {
var caller = this.caller
,current = this.$caller
,result;
this.caller = current;
this.$caller = wrapper;
result = method.apply(this, arguments);
this.$caller = current;
this.caller = caller;
return result;
}
wrapper.$parent = self[key];
wrapper.$name = key;
return wrapper;
}
function BlockProto (methods) {
implement.call(this,methods);
}
BlockProto.prototype = extend({
parent: function (){
var name = this.$caller.$name
,parent = this.$caller.$parent;
if (!parent) {
throw new Error('The method "' + name + '" has no parent.');
}
return parent.apply(this, arguments);
}
}, BlockMixin, TemplateMixin, EventsMixin );
/** @constructor */
function Block (methods) {
function Block () {
return (this.initialize) ? this.initialize.apply(this, arguments) : this;
}
Block.prototype = new BlockProto(methods);
return Block;
}
return Block;
});
define( function () {
var blockCount = 0;
var typeOf
,mixin
,forEach = Array.prototype.forEach
,slice = Array.prototype.slice
,toString = Object.prototype.toString;
function typeOf (obj) {
return toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
mixin = {
_children: {}
,_bound: {}
,_uniqueId: null
,initialize: function (options) {
options = options || this.options;
var self = this
,container = options.container;
this.setChildren(options.children);
this.setTemplate(options.template);
this.setContainer(options.container);
this.setContext(options.context);
this.setContext('$this',this);
this.bindTemplate();
this.ready = true;
}
,setOptions: function (options) {
return (function (){
var target = arguments[0]
,key;
for (key, i = 1, l = arguments.length; i < l; i++) {
for (key in arguments[i]) {
target[key] = arguments[i][key]
}
}
return target;
}(this.options,options));
}
/**
* #setChild
*
* @param {string} key The childs name
* @param {block} value
*/
,setChild: function (key, value) {
if(!key) return;
this._children[key] = value;
}
/**
*
*
*
*/
,getChild: function (key) {
return this._children[key];
}
/**
*
*
*
*/
,removeChild: function (key) {
return this.removeChildren(key);
}
/**
*
*
*
*/
,setChildren: function (children) {
if(!children) return;
var key;
for(key in children) {
if(children.hasOwnProperty(key))
this.setChild(key, children[key]);
}
}
/**
* Block#getChildren
*
* getChildren(key [,...]) // { key: `Block` child }
*/
,getChildren: function (args) {
var args = (Array.isArray(args))? args : slice.call(arguments,0)
,children;
if(arguments.length > 0) {
children = {};
args.forEach(function (arg) {
children[arg] = this.getChild(arg);
});
}
return children || this._children;
}
/**
* Block#getChildHtml
*
* getChildHtml(key)
*/
,getChildHtml: function (key) {
return String(this.getChild(key));
}
/**
* Block#getChildrenHtml
*
* getChildrenHtml(key)
*/
,getChildrenHtml: function (args) {
var children = this.getChildren.apply(this,arguments)
,str = '',key;
for(key in children) {
if(children.hasOwnProperty(key)) {
str += String(children[key]);
}
}
return str;
}
/**
* Block#removeChildren
* removeChildren("key" [,...]) // { "key": `Block` child }
*
* returns Hash of removed children who's keys match the ones passed
*
* @param {array || arguments} args The keys to remove from the children
*
*/
,removeChildren: function (args) {
var args = (Array.isArray(args))? args : slice.call(arguments,0)
,children = this.getChildren()
,subSet = {}
,rejected = {}
,key;
if(args.length > 0) {
for(key in children) {
if(children.hasOwnProperty(key)){
if(args.indexOf(key) === -1) {
subSet[key] = children[key]
} else {
rejected[key] = children[key];
}
}
}
this._children = subSet;
}
return rejected;
}
,bindTemplate: function () {
var blank = document.createElement('div')
,container = this.getContainer();
blank.innerHTML = this.compile(this._context);
//this.parseBoundElements(blank);
while( blank.children.length ) {
container.appendChild( blank.children[0] );
}
}
,bindElements: function (el) {
var self = this
,bound;
if(!(el instanceof Element)) {
throw new Error(Block.errors.parseElements[0]);
}
this.clearBoundElements();
bound = el.querySelectorAll('[bind]');
forEach.call(bound, function (el) {
self.setBoundElement(el.getAttribute('bind'),el);
});
}
/**
*
*
*/
,bindChildren : function () {
var children = this.getChildren();
var placeholder, module,parent;
var placeholders = [];
for(key in children) {
placeholder = null;
if(children.hasOwnProperty(key)){
module = children[key];
placeholder = this.getBoundElement(module.getUniqueId());
if(!!(placeholder)) {
parent = placeholder.parentNode;
// Have to reference parent, or make sure it exists
// because it was throwing some weird
// `cannot call replaceChild on undefined` error
parent && parent.replaceChild(module.toElement(), placeholder);
}
}
}
}
,clearBoundElements: function (args) {
var args = (Array.isArray(args))? args : slice.call(arguments,0)
,els = this.getBoundElements(args)
this._bound = {};
}
,setBoundElement: function (key, element) {
var bound = this._bound[key] = this._bound[key] || [];
bound.push(element);
}
,getBoundElements: function (args) {
var args = (Array.isArray(args))? args : slice.call(arguments,0)
,elements = {}
,self = this;
if (args.length) {
args.forEach(function (el) {
elements[el] = self.getBoundElement(el);
});
} else {
elements = this._bound;
}
return elements;
}
,getBoundElement: function (key) {
var element;
if(!(element = this._bound[key])){
return undefined;
}
return (element.length === 1) ? element[0]: element;
}
/**
*
*
*/
,getContainer: function () {
return this.container || this.setContainer();
}
/**
*
*
*/
,setContainer: function (container) {
return this.container = (container) ?
((typeOf(container)==='string')?document.createElement(container):container)
:document.createElement('div');
}
/**
*
*
*
*/
,getUniqueId: function () {
return this._uniqueId = this._uniqueId || Date.now().toString(36) + (blockCount++);
}
/**
*
*
*
*/
,toString: function () {
return '<span bind="'+ this.getUniqueId() +'" data-type="module"></span>';
}
,fillContainer: function (frag) {
var container = this.getContainer()
,clone = container.cloneNode(true)
,frag = frag || document.createDocumentFragment();
this.bindElements(clone);
this.attachEvents && this.attachEvents();
while(clone.children.length) {
frag.appendChild(clone.children[0]);
}
this.bindChildren();
if(this.placeholder) {
this.placeholder.parentNode.replaceChild(frag,this.placeholder);
}
}
/**
*
*
*
*/
,toElement: function () {
var frag = document.createDocumentFragment();
if(this.ready) {
this.fillContainer(frag);
} else {
this.placeholder = document.createElement('span');
frag.appendChild(this.placeholder)
}
return frag;
}
};
mixin.getBound = mixin.getBoundElement;
mixin.getChildHTML = mixin.getChildHtml;
mixin.getChildrenHTML = mixin.getChildrenHtml;
return mixin;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment