/* { : 123 } : 125 # : 35 ! : 33 ^ : 94 / : 47 > : 62 */ var itsMustache = function (config) { 'use strict' var __stateArraySize = 128, stateIndex = 0, STATE = [], START = stateIndex++, //0 OPENBRACKET0 = stateIndex++, //1 OPENBRACKET1 = stateIndex++, //2 VARIABLESWAP = stateIndex++, //3 CLOSEVARIABLESWAP0 = stateIndex++, //4 CLOSEVARIABLESWAP1 = stateIndex++, //5 TEMPLATECOMMENT = stateIndex++, //6 CLOSETEMPLATECOMMENT0 = stateIndex++, //7 CLOSETEMPLATECOMMENT1 = stateIndex++, //8 SECTION = stateIndex++, //9 CLOSESECTION0 = stateIndex++, //10 CLOSESECTION1 = stateIndex++, //11 INVERTEDSECTION = stateIndex++, //12 CLOSEINVERTEDSECTION0 = stateIndex++, //13 CLOSEINVERTEDSECTION1 = stateIndex++, //14 RENDERTEMPLATE = stateIndex++, //15 CLOSERENDERTEMPLATE0 = stateIndex++, //16 CLOSERENDERTEMPLATE1 = stateIndex++, //17 EOF = stateIndex, state0Targets = [[123, OPENBRACKET0]], state1Targets = [[123, OPENBRACKET1]], state2Targets = [[33, TEMPLATECOMMENT], [35, SECTION], [62, RENDERTEMPLATE], [94, INVERTEDSECTION]], state3Targets = [[125, CLOSEVARIABLESWAP0]], state4Targets = [[125, CLOSEVARIABLESWAP1]], templateCommentTargets = [[125, CLOSETEMPLATECOMMENT0]], closeTemplateCommentTarget = [[125, CLOSETEMPLATECOMMENT1]], sectionTarget = [[125, CLOSESECTION0]], closeSectionTarget = [[125, CLOSESECTION1]], invertedSectionTarget = [[125, CLOSEINVERTEDSECTION0]], closeInvertedSectionTarget = [[125, CLOSEINVERTEDSECTION1]], renderTemplateTarget = [[125, CLOSERENDERTEMPLATE0]], closeRenderTemplate0Target = [[125, CLOSERENDERTEMPLATE1]], fullArray, sectionEnd = null, templateReference = false; if (config && config.templates) templateReference = config.templates; function init() { sectionEnd = new getSectionEnd(); if (!config) config = {}; refreshTemplates() if (fullArray) STATE = fullArray; else { initSTATE(); configureStates(); } } function initSTATE() { for (var i = 0, l = EOF; i < l; i++) { STATE[i] = createStateArray(0); } } this.refreshTemplates = refreshTemplates; this.setTemplates = setTemplates; this.setTemplate = setTemplate; function refreshTemplates(t) { if (t) config.templates = t; setTemplateReference(); } function setTemplates(o) { for (var k in o) { setTemplate(k, o[k]); } } function setTemplate(k, v) { if (!config.templates) config.templates = {}; config.templates[k] = v; setTemplateReference(); } function setTemplateReference() { if (config.templates) templateReference = config.templates; } function createStateArray(fillValue) { var array = []; for (var i = 0, l = __stateArraySize; i < l; i++) { array[i] = fillValue; } return array; } function configureStates() { for (var i = 0, l = STATE.length; i < l; i++) { var arrayItem = STATE[i]; switch (i) { case START: arrayItem = configureState(arrayItem, state0Targets, 0); break; case OPENBRACKET0: arrayItem = configureState(arrayItem, state1Targets, 0); break; case OPENBRACKET1: arrayItem = configureState(arrayItem, state2Targets, 3); break; case VARIABLESWAP: arrayItem = configureState(arrayItem, state3Targets, VARIABLESWAP); break; case CLOSEVARIABLESWAP0: arrayItem = configureState(arrayItem, state4Targets, 3); break; case CLOSEVARIABLESWAP1: arrayItem = configureState(arrayItem, [], 5); break; case TEMPLATECOMMENT: arrayItem = configureState(arrayItem, templateCommentTargets, TEMPLATECOMMENT); break; case CLOSETEMPLATECOMMENT0: arrayItem = configureState(arrayItem, closeTemplateCommentTarget, 11); break; case CLOSETEMPLATECOMMENT1: arrayItem = configureState(arrayItem, [], CLOSETEMPLATECOMMENT1); break; case SECTION: arrayItem = configureState(arrayItem, sectionTarget, SECTION); break; case CLOSESECTION0: arrayItem = configureState(arrayItem, closeSectionTarget, SECTION); break; case CLOSESECTION1: arrayItem = configureState(arrayItem, closeSectionTarget, CLOSESECTION1); break; case INVERTEDSECTION: arrayItem = configureState(arrayItem, invertedSectionTarget, INVERTEDSECTION); break; case CLOSEINVERTEDSECTION0: arrayItem = configureState(arrayItem, closeInvertedSectionTarget, INVERTEDSECTION); break; case CLOSEINVERTEDSECTION1: arrayItem = configureState(arrayItem, closeInvertedSectionTarget, CLOSEINVERTEDSECTION1); break; case RENDERTEMPLATE: arrayItem = configureState(arrayItem, renderTemplateTarget, RENDERTEMPLATE); break; case CLOSERENDERTEMPLATE0: arrayItem = configureState(arrayItem, closeRenderTemplate0Target, RENDERTEMPLATE); break; case CLOSERENDERTEMPLATE1: arrayItem = configureState(arrayItem, [], CLOSERENDERTEMPLATE1); break; } STATE[i] = arrayItem; } } function configureState(arrayItem, targets, defaultValue) { for (var i = 0, l = arrayItem.length; i < l; i++) { var check = checkTargets(targets, i), arrayItemValue; if (check >= 0) { arrayItemValue = check; } else arrayItemValue = defaultValue; arrayItem[i] = arrayItemValue; } return arrayItem; } function checkTargets(targets, index) { var matchValue; for (var i = 0, l = targets.length; i < l; i++) { var target = targets[i], targetItem = target[0], targetDestination = target[1]; if (index == targetItem) matchValue = targetDestination; } return matchValue; } function diveIntoObject(key, object) { if (key !== undefined) { var kSplit = key.split(/[\.]{1}/), target; for (var i = 0, l = kSplit.length; i < l; i++) { if (i == 0) target = object[kSplit[i]]; else if (target) target = target[kSplit[i]]; } return target; } else return undefined; } this.render = function (template, data) { var text, copy = JSON.parse(JSON.stringify(data)); if (template != undefined) { if (templateReference) { text = diveIntoObject(template, templateReference); if (text == undefined) text = template; } else text = template; return render(text, copy); } else return ''; }; function sanitizeData(d) { if (isObject(d)) return sanitizeObject(d); else if (isArray(d)) return sanitizeArray(d); else if (isNumber(d)) return sanitize(d); else return d; } function sanitizeObject(o) { for (var k in o) { o[k] = sanitize(o[k]); } return o; } function sanitizeArray(a) { for (var i, l = a.length; i < l; i++) { a[i] = sanitize(a[i]); } return a; } function sanitize(t) { var type = typeof t; switch (type) { case 'number': t = t.toString(); break; case 'function': t = t.toString(); break; case 'object': t = sanitizeData(t); break; } return t; } function render(template, data) { var data = data ? sanitizeData(data) : null, templateLength = template.length, state = 0, stateEntryIndex, stateExitIndex, stateIndentValue = 0; return data !== null ? cycleTemplate() : template; function cycleTemplate() { for (var i = 0, l = templateLength; i < l; i++) { var charCode = template.charCodeAt(i); if (charCode > 128) charCode = 97; // why? because life is too damn short state = STATE[state][charCode]; switch (state) { case START: break; case OPENBRACKET0: stateEntryIndex = i; break; case CLOSEVARIABLESWAP1: stateExitIndex = i; var updatedData = getVariableSwap(); i -= updatedData[1]; l = updatedData[0]; resetState() break; case CLOSETEMPLATECOMMENT1: stateExitIndex = i; var updatedData = removeComment(); i -= updatedData[1]; l = updatedData[0]; resetState(); break; case CLOSESECTION1: stateExitIndex = i; stateIndentValue = 1; var updatedData = getSection(); i -= updatedData[1]; l = updatedData[0]; resetState(); break; case CLOSEINVERTEDSECTION1: stateExitIndex = i; stateIndentValue = 1; var updatedData = getInvertedSection(); i -= updatedData[1]; l = updatedData[0]; resetState(); break; case CLOSERENDERTEMPLATE1: stateExitIndex = i; stateIndentValue = 1; var updatedData = getInPartialTemplate(); i -= updatedData[1]; l = updatedData[0]; resetState(); break; } } return template; function resetState() { state = 0; stateIndentValue = 0; } } function getInPartialTemplate() { var preString = template.substring(0, stateEntryIndex), postString = template.substring(stateExitIndex + 1, template.length), focusBracket = template.substring(stateEntryIndex, stateExitIndex + 1), focusValue = focusBracket.substring(2 + stateIndentValue, focusBracket.length - 2), focusBracketLength = focusBracket.length, string, stringLength, updatedTemplate, updatedTemplateLength, spacesRemoved = focusValue.replace(/\s/g, ''), focusTemplate = diveIntoObject(spacesRemoved, templateReference); if (focusTemplate != undefined) string = render(focusTemplate, data); else string = ''; stringLength = string.length; updatedTemplate = preString + string + postString; updatedTemplateLength = updatedTemplate.length; updateTemplateData(updatedTemplate, updatedTemplateLength); return [updatedTemplateLength, (focusBracketLength - stringLength)]; } function updateTemplateData(string, length) { template = string; templateLength = length; } function getSection() { var preString = template.substring(0, stateEntryIndex), currentToEnd = template.substring(stateEntryIndex, template.length), focusBracket = template.substring(stateEntryIndex, stateExitIndex + 1), focusBracketLength = focusBracket.length, focusValue = focusBracket.substring(2 + stateIndentValue, focusBracketLength - 2), spacesRemoved = focusValue.replace(/\s/g, ''), focusData = diveIntoObject(spacesRemoved, data), match = sectionEnd.cycle(currentToEnd, spacesRemoved), closeBracketStartPosition, closeBracketEndPosition, closeBracket, inBetweenBrackets, string, stringLength, section, updatedTemplate, updatedTemplateLength, postString; if (match > -1) { closeBracketStartPosition = currentToEnd.lastIndexOf('{{', match); closeBracketEndPosition = match; closeBracket = currentToEnd.substring(closeBracketStartPosition, closeBracketEndPosition); inBetweenBrackets = currentToEnd.substring(focusBracketLength, closeBracketStartPosition); postString = currentToEnd.substring(match + 1, currentToEnd.length); if (spacesRemoved == '.' && data instanceof Array) focusData = data; if (focusData != undefined && focusData != null && focusData != false) { if (focusData instanceof Array) string = handleSectionRepeats(inBetweenBrackets, focusData); else string = sectionFunctions(inBetweenBrackets, focusData); } else string = ''; } if (string != undefined) { stringLength = string.length; updatedTemplate = preString + string + postString; } else { stringLength = 0; updatedTemplate = preString + postString; console.error("No match", focusBracket, currentToEnd); } updatedTemplateLength = updatedTemplate.length; updateTemplateData(updatedTemplate, updatedTemplateLength); return [updatedTemplateLength, (focusBracketLength - stringLength)]; } function sectionFunctions(text, focusData) { var string = '', focusDataTypeOf = typeof focusData; if (focusDataTypeOf == 'string' || focusDataTypeOf == 'number') { var obj = createObjectCopy(data); obj['.'] = focusData; string = render(text, obj); } else if (focusDataTypeOf == 'boolean' && focusData == true) string = render(text, data); else if (focusData instanceof Object && focusDataTypeOf != 'function') string = render(text, focusData) else if (focusDataTypeOf == 'function') string = processLambda(text, focusData) return string; } function createObjectCopy(obj) { var copy = {}; for (var key in obj) { copy[key] = obj[key]; } return copy; } function processLambda(text, focusData) { var fn = focusData(), string = fn(text, data); return string; } function getInvertedSection() { var preString = template.substring(0, stateEntryIndex), currentToEnd = template.substring(stateEntryIndex, template.length), focusBracket = template.substring(stateEntryIndex, stateExitIndex + 1), focusBracketLength = focusBracket.length, focusValue = focusBracket.substring(2 + stateIndentValue, focusBracketLength - 2), spacesRemoved = focusValue.replace(/\s/g, ''), focusData = data[spacesRemoved], match = sectionEnd.cycle(currentToEnd, spacesRemoved), closeBracketStartPosition, closeBracketEndPosition, closeBracket, string, stringLength, section, updatedTemplate, updatedTemplateLength, postString; if (match > -1) { closeBracketStartPosition = currentToEnd.lastIndexOf('{{', match); closeBracketEndPosition = match; closeBracket = currentToEnd.substring(closeBracketStartPosition, closeBracketEndPosition); postString = currentToEnd.substring(match + 1, currentToEnd.length); if (focusData == undefined || focusData == null || focusData == false || (focusData instanceof Array && focusData.length == 0)) { var focusTemplate = string = currentToEnd.substring(focusBracketLength, closeBracketStartPosition) string = render(focusTemplate, data); } else string = ''; } else console.error("Error, no ending section"); if (string != undefined) { stringLength = string.length; updatedTemplate = preString + string + postString; updatedTemplateLength = updatedTemplate.length; updateTemplateData(updatedTemplate, updatedTemplateLength); return [updatedTemplateLength, (focusBracketLength - stringLength)]; } } function handleSectionRepeats(focusTemplate, focusData) { var string = ''; for (var i = 0, l = focusData.length; i < l; i++) { var item = focusData[i], itemObj; if (item instanceof Array) string += render(focusTemplate, item); else string += sectionFunctions(focusTemplate, item); } return string; } function getFocusData(focusValue) { return isObject(data) || isArray(data) ? getDefaultVariable(focusValue) : data; } function getDefaultVariable(v) { var spacesRemoved = v.replace(/\s/g, ''); return !getDeepReferenceState(spacesRemoved) ? data[spacesRemoved] : diveIntoObject(spacesRemoved, data); } function getDeepReferenceState(v) { return v.split('.').length > 1 && v != '.'; } function getVariableString(v) { return isDefined(v) && !isNumber(v) ? v : ''; } function getVariableSwap() { var preString = template.substring(0, stateEntryIndex), postString = template.substring(stateExitIndex + 1, template.length), focusBracket = template.substring(stateEntryIndex, stateExitIndex + 1), focusValue = focusBracket.substring(2 + stateIndentValue, focusBracket.length - 2), focusBracketLength = focusBracket.length, focusValueLength = focusValue.length, focusData = getFocusData(focusValue), string = getVariableString(focusData), stringLength = string.length, updatedTemplate = preString + string + postString, updatedTemplateLength = updatedTemplate.length; updateTemplateData(updatedTemplate, updatedTemplateLength); return [updatedTemplateLength, (focusBracketLength - stringLength)]; } function removeComment() { var preString = template.substring(0, stateEntryIndex), postString = template.substring(stateExitIndex + 1, template.length), focusBracket = template.substring(stateEntryIndex, stateExitIndex + 1), focusBracketLength = focusBracket.length, stringLength = 0, updatedTemplate, updatedTemplateLength; updatedTemplate = preString + postString; updatedTemplateLength = updatedTemplate.length; updateTemplateData(updatedTemplate, updatedTemplateLength); return [updatedTemplateLength, (focusBracketLength - stringLength)]; } } var getSectionEnd = function (string, value) { 'use strict' var __stateArraySize = 128, stateIndex = 0, STATE = [], START = stateIndex++, //0 OPENBRACKET0 = stateIndex++, //1 OPENBRACKET1 = stateIndex++, //2 STARTSECTION = stateIndex++, //3 SECTION = stateIndex++, //4 OPENENDSECTION0 = stateIndex++, //5 OPENENDSECTION1 = stateIndex++, //6 ENDSECTION = stateIndex++, //7 WAITINGFORCLOSE = stateIndex++, //8 CLOSESECTION0 = stateIndex++, //9 CLOSESECTION1 = stateIndex++, //10 EOF = stateIndex, STARTTargets = [[123, OPENBRACKET0]], OPENBRACKET1Targets = [[123, OPENBRACKET1]], OPENBRACKET2Targets = [[35, STARTSECTION], [94, STARTSECTION]], SECTIONTargets = [[123, OPENENDSECTION0], [94, STARTSECTION]], WAITINGFORCLOSETargets = [[125, CLOSESECTION0]], CLOSESECTION1Targets = [[125, CLOSESECTION1]], OPENENDSECTION0Targets = [[123, OPENENDSECTION1]], OPENENDSECTION1Targets = [[47, ENDSECTION], [35, STARTSECTION], [94, STARTSECTION]]; init(); this.cycle = render; function init() { initSTATE(); configureStates(); } function initSTATE() { for (var i = 0, l = EOF; i < l; i++) { STATE[i] = createStateArray(0); } } function createStateArray(fillValue) { var array = []; for (var i = 0, l = __stateArraySize; i < l; i++) { array[i] = fillValue; } return array; } function configureStates() { for (var i = 0, l = STATE.length; i < l; i++) { var arrayItem = STATE[i]; switch (i) { case START: arrayItem = configureState(arrayItem, STARTTargets, START); break; case OPENBRACKET0: arrayItem = configureState(arrayItem, OPENBRACKET1Targets, START); break; case OPENBRACKET1: arrayItem = configureState(arrayItem, OPENBRACKET2Targets, START); break; case STARTSECTION: arrayItem = configureState(arrayItem, [], SECTION); break; case SECTION: arrayItem = configureState(arrayItem, SECTIONTargets, SECTION); break; case OPENENDSECTION0: arrayItem = configureState(arrayItem, OPENENDSECTION0Targets, SECTION); break; case OPENENDSECTION1: arrayItem = configureState(arrayItem, OPENENDSECTION1Targets, SECTION); break; case ENDSECTION: arrayItem = configureState(arrayItem, [], SECTION); break; case WAITINGFORCLOSE: arrayItem = configureState(arrayItem, WAITINGFORCLOSETargets, WAITINGFORCLOSE); break; case CLOSESECTION0: arrayItem = configureState(arrayItem, CLOSESECTION1Targets, WAITINGFORCLOSE); break; case CLOSESECTION1: arrayItem = configureState(arrayItem, [], 0); break; } STATE[i] = arrayItem; } } function configureState(arrayItem, targets, defaultValue) { for (var i = 0, l = arrayItem.length; i < l; i++) { var check = checkTargets(targets, i), arrayItemValue; if (check >= 0) { arrayItemValue = check; } else arrayItemValue = defaultValue; arrayItem[i] = arrayItemValue; } return arrayItem; } function checkTargets(targets, index) { var matchValue; for (var i = 0, l = targets.length; i < l; i++) { var target = targets[i], targetItem = target[0], targetDestination = target[1]; if (index == targetItem) matchValue = targetDestination; } return matchValue; } function render(template, data) { var templateLength = template.length, state = 0, stateIndentValue = 0, sectionCount = 0; return cycleTemplate(); function cycleTemplate() { for (var i = 0, l = templateLength; i < l; i++) { var charCode = template.charCodeAt(i); state = STATE[state][charCode]; switch (state) { case STARTSECTION: sectionCount++; break; case ENDSECTION: sectionCount--; if (sectionCount == 0) state = WAITINGFORCLOSE; break; case CLOSESECTION1: if (sectionCount == 0) { return i; } break; } } return -1; function resetState() { state = 0; stateIndentValue = 0; } } } }; function isObject(t) { return t instanceof Object && typeof t != 'function' && !(t instanceof Array); } function isArray(t) { return Array.isArray(t); } function isNumber(t) { return typeof t === 'number'; } function isDefined(t) { return t !== null && t !== undefined; } init(); }