// dna.js Template Cloner ~~ v0.2.9 // MIT/GPLv3 ~~ dnajs.org/license.html // Copyright (c) 2013-2014 Center Key Software and other contributors var dna = { // API: // dna.clone() // dna.cloneSubTemplate() // dna.load() // dna.getModel() // dna.empty() // dna.mutate() // dna.mutateAll() // dna.destroy() // dna.getClone() // dna.getClones() // dna.bye() // dna.registerInitializer() // dna.clearInitializers() // dna.info() // See: http://dnajs.org/manual.html#api clone: function(name, data, options) { var settings = { fade: false, top: false, container: null, empty: false, html: false, callback: null }; $.extend(settings, options); var template = dna.store.getTemplate(name); if (template.nested && !settings.container) dna.core.berserk('Container missing for nested template: ' + name); if (settings.empty) dna.empty(name); var list = data instanceof Array ? data : [data]; var clones = $(); for (var i = 0; i < list.length; i++){ console.log('-----------------------------') console.log(template) console.log(list[i]) console.log(i+1) console.log(settings) clones = clones.add(dna.core.replicate(template, list[i], i + 1, settings)); } console.log('//////') console.log(clones) console.log('//////') return clones; }, cloneSubTemplate: function(holderClone, arrayField, data, options) { var name = dna.compile.subTemplateName(holderClone, arrayField); var selector = '.dna-contains-' + name; var settings = { container: holderClone.find(selector).addBack(selector) }; $.extend(settings, options); dna.clone(name, data, settings); var array = dna.getModel(holderClone)[arrayField]; function append() { array.push(this); } $.each(data instanceof Array ? data : [data], append); return holderClone; }, load: function(name, url, options) { function processJson(data) { dna.core.unload(name, data, options); } return $.getJSON(url, processJson); }, getModel: function(nameOrClone) { function getModelArray() { var model = []; dna.getClones(nameOrClone).each( function() { model.push($(this).data('dna-model')); }); return model; } return nameOrClone instanceof jQuery ? dna.getClone(nameOrClone).data('dna-model') : getModelArray(); }, empty: function(name, options) { var settings = { fade: false }; $.extend(settings, options); var clones = dna.store.getTemplate(name).container.find('.dna-clone'); return settings.fade ? dna.ui.slideFadeDelete(clones) : clones.remove(); }, mutate: function(clone, data, options) { var settings = { html: false }; $.extend(settings, options); clone = dna.getClone(clone); if (!data) data = dna.getModel(clone); dna.core.inject(clone, data, null, settings); return clone; }, mutateAll: function(name) { function mutate() { dna.mutate($(this)); } return dna.getClones(name).each(mutate); }, destroy: function(clone, options) { var settings = { fade: false }; $.extend(settings, options); clone = dna.getClone(clone); function removeArrayItem(holder, field) { var arrayClones = holder.children('.' + dna.compile.subTemplateName(holder, field)); dna.getModel(holder)[field].splice(arrayClones.index(clone), 1); } if (clone.hasClass('dna-array')) removeArrayItem(clone.parent(), clone.data().dnaRules.array); return settings.fade ? dna.ui.slideFadeDelete(clone) : clone.remove(); }, getClone: function(elem) { return elem instanceof jQuery ? elem.closest('.dna-clone') : $(); }, getClones: function(name) { return dna.store.getTemplate(name).container.children().filter('.dna-clone'); }, bye: function(elemOrEventOrIndex) { return dna.destroy(dna.ui.toElem(elemOrEventOrIndex, this), { fade: true }); }, registerInitializer: function(func, options) { var settings = { onDocumentLoad: true }; $.extend(settings, options); if (settings.onDocumentLoad) dna.util.apply(func, [settings.selector ? $(settings.selector).not('.dna-template ' + settings.selector).addClass('dna-initialized') : $(document)].concat(settings.params)); return dna.events.initializers.push( { func: func, selector: settings.selector, params: settings.params }); }, clearInitializers: function() { dna.events.initializers = []; }, info: function() { var names = Object.keys(dna.store.templates); console.log('~~ dns.js v0.2.9 ~~'); console.log('templates:', names.length); console.log('names:', names); console.log('store:', dna.store.templates); console.log('initializers:', dna.events.initializers.length); return navigator.appVersion; } }; dna.util = { toCamel: function(codeStr) { //example: 'ready-set-go' ==> 'readySetGo' function hump(match, char) { return char.toUpperCase(); } return ('' + codeStr).replace(/\-(.)/g, hump); }, toCode: function(camelCaseStr) { //example: 'readySetGo' ==> 'ready-set-go' function dash(word) { return '-' + word.toLowerCase(); } return ('' + camelCaseStr).replace(/([A-Z])/g, dash).replace(/\s|^-/g, ''); }, value: function(data, field) { //example: dna.util.value({ a: { b: 7 }}, 'a.b'); ==> 7 if (typeof field === 'string') field = field.split('.'); return (data === null || data === undefined || field === undefined) ? null : (field.length === 1 ? data[field[0]] : this.value(data[field[0]], field.slice(1))); }, realTruth: function(value) { //returns a boolean // Example true values: true, 7, '7', [5], 't', 'T', 'TRue', {}, 'Colbert' // Example false values: false, 0, '0', [], 'f', 'F', 'faLSE', null, undefined, NaN function falseyStr() { return /^(f|false|0)$/i.test(value); } function emptyArray() { return value instanceof Array && value.length === 0; } return value ? !emptyArray() && !falseyStr() : false; }, apply: function(func, params) { //calls func (string name or actual function) passing in params // Example: dna.util.apply('app.cart.buy', 7); ==> app.cart.buy(7); var args = [].concat(params); var elem = args[0], result; function contextApply(obj, names) { if (!obj || (names.length == 1 && typeof obj[names[0]] !== 'function')) dna.core.berserk('Callback function not found: ' + func); else if (names.length == 1) result = obj[names[0]].apply(elem, args); //'app.cart.buy' ==> window['app']['cart']['buy'] else contextApply(obj[names[0]], names.slice(1)); } if (elem instanceof jQuery && elem.length === 0) result = elem; else if (typeof func === 'function') result = func.apply(elem, args); else if (elem && elem[func]) result = elem[func](args[1], args[2], args[3]); else if (func === '' || $.inArray(typeof func, ['number', 'boolean']) !== -1) dna.core.berserk('Invalid callback function: ' + func); else if (typeof func === 'string' && func.length > 0) contextApply(window, func.split('.')); return result; } }; dna.ui = { toElem: function(elemOrEventOrIndex, that) { return elemOrEventOrIndex instanceof jQuery ? elemOrEventOrIndex : elemOrEventOrIndex ? $(elemOrEventOrIndex.target) : $(that); }, deleteElem: function(elemOrEventOrIndex) { //example: $('.box').fadeOut(dna.ui.deleteElem); return dna.ui.toElem(elemOrEventOrIndex, this).remove(); }, slideFade: function(elem, callback, show) { var obscure = { opacity: 0.0, transition: 'opacity 0s ease 0s' }; var easyIn = { opacity: 1.0, transition: 'opacity 0.4s ease-in' }; var easyOut = { opacity: 0.0, transition: 'opacity 0.4s ease-out' }; var reset = { transition: 'opacity 0s ease 0s' }; function clearOpacityTransition() { elem.css(reset); } window.setTimeout(clearOpacityTransition, 1000); //keep clean for other animations if (show) elem.css(obscure).hide().slideDown({ complete: callback }).css(easyIn); else elem.css(easyOut).slideUp({ complete: callback }); return elem; }, slideFadeIn: function(elem, callback) { return dna.ui.slideFade(elem, callback, true); }, slideFadeOut: function(elem, callback) { return dna.ui.slideFade(elem, callback, false); }, slideFadeToggle: function(elem, callback) { return dna.ui.slideFade(elem, callback, elem.is(':hidden')); }, slideFadeDelete: function(elem) { return dna.ui.slideFadeOut(elem, dna.ui.deleteElem); }, slidingFlasher: function(elem, callback) { return elem.is(':hidden') ? dna.ui.slideFadeIn(elem, callback) : elem.hide().fadeIn(); } }; dna.compile = { // Pre-compile Example Post-compile class + data().dnaRules // ----------- -------------------------------- ------------------------------------ // templates
class=dna-clone // arrays
class=dna-nucleotide + array='tags' // fields
~~tag~~
class=dna-nucleotide + text='tag' // attributesclass=dna-nucleotide + attrs=['id', ['', 'num', '']] // rules
class=dna-nucleotide + truthy='on' // attr rules
class=dna-nucleotide + attrs=['src', ['', 'url', '']] // prop rules class=dna-nucleotide + props=['checked', 'on'] // // Rules data().dnaRules // --------------------------------------------- --------------- // data-dna-class=~~field,name-true,name-false~~ class=['field','name-true','name-false'] // data-dna-attr-{NAME}=pre~~field~~post attrs=['{NAME}', ['pre', 'field', 'post']] // data-dna-prop-{NAME}=pre~~field~~post props=['{NAME}', 'field'] // data-dna-require=~~field~~ require='field' // data-dna-missing=~~field~~ missing='field' // data-dna-truthy=~~field~~ truthy='field' // data-dna-falsey=~~field~~ falsey='field' // regexDnaField: /^[\s]*(~~|\{\{).*(~~|\}\})[\s]*$/, //example: ~~title~~ regexDnaBasePair: /~~|{{|}}/, //matches the '~~' string regexDnaBasePairs: /~~|\{\{|\}\}/g, //matches the two '~~' strings so they can be removed setupNucleotide: function(elem) { if (elem.data().dnaRules === undefined) elem.data().dnaRules = {}; return elem.addClass('dna-nucleotide'); }, isDnaField: function() { var firstNode = $(this)[0].childNodes[0]; return firstNode && firstNode.nodeValue && firstNode.nodeValue.match(dna.compile.regexDnaField); }, field: function() { // Example: //
~~name~~
==> var elem = dna.compile.setupNucleotide($(this)); console.log(elem) elem.data().dnaRules.text = $.trim(elem.text()).replace(dna.compile.regexDnaBasePairs, ''); return elem.empty(); }, propsAndAttrs: function() { // Examples: //