Created
August 21, 2015 16:23
-
-
Save mrlannigan/08ec753740509e9907c8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var BPromise = require('bluebird'), | |
| Hoek = require('hoek'); | |
| (function (exports) { | |
| exports.shortCodeRegex = /\{\{\s*(.+?)\}\}/g; | |
| exports.hasWhitespaceRegex = /\s+/; | |
| /** | |
| * @param {string} input - Parsed string | |
| * @param {object} [context] - context object defining shortcodes | |
| * @param {object} [callContext] - call context for any functions called within any shortcode replacement | |
| * | |
| * Supported Inputs: | |
| * {{Count_ForSale_Total}} | |
| * {{Count_ForSale_Total test="argument"}} | |
| * {{Test_Func test="argument" nice="dude" }} | |
| * | |
| * Not Supported Inputs: | |
| * {{Count_ForSale_Total test=arguments}} | |
| */ | |
| exports.replace = function doShortCodeReplace(input, context, callContext) { | |
| var typeofInput = typeof input, | |
| queue = []; | |
| if (!input) { | |
| return input; | |
| } | |
| if (typeofInput !== 'string') { | |
| input = input.toString(); | |
| } | |
| context = context || {}; | |
| callContext = callContext || this; | |
| input.replace(exports.shortCodeRegex, function (wholeMatch, shortCode, offset) { | |
| queue.push({ | |
| wholeMatch: wholeMatch, | |
| shortCode: shortCode, | |
| offset: offset | |
| }); | |
| return wholeMatch; | |
| }); | |
| return BPromise.reduce(queue, function (reduceResult, item) { | |
| var result, | |
| typeofResult, | |
| codeArguments, | |
| codeKey, | |
| tmpCodeArguments, | |
| matchedCodeArguments, | |
| argumentsRegex, | |
| hasWhitespace, | |
| wholeMatch = item.wholeMatch, | |
| shortCode = item.shortCode, | |
| offset = item.offset; | |
| hasWhitespace = exports.hasWhitespaceRegex.test(shortCode); | |
| // test for no whitespace before checking context hash | |
| if (!hasWhitespace) { | |
| // grab value from keyed hash | |
| result = Hoek.reach(context, shortCode); | |
| typeofResult = typeof result; | |
| // check if valid | |
| if (typeofResult === 'undefined') { | |
| reduceResult.push('{{! Invalid Shortcode : "' + shortCode + '" }}'); | |
| return reduceResult; | |
| } | |
| // short-circuit processing, no need to continue if not a function | |
| if (typeofResult !== 'function') { | |
| reduceResult.push(result); | |
| return reduceResult; | |
| } | |
| } else { | |
| // if whitespace exists, start processing for arguments | |
| result = shortCode; | |
| } | |
| // check if has arguments | |
| if (hasWhitespace) { | |
| argumentsRegex = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[$"']))+.)["']?/g; | |
| result = result.split(' '); | |
| codeKey = result.shift(); | |
| tmpCodeArguments = result.join(' '); | |
| // grab value from keyed hash | |
| result = context[codeKey]; | |
| typeofResult = typeof result; | |
| if (typeofResult !== 'function') { | |
| reduceResult.push(result); | |
| return reduceResult; | |
| } | |
| codeArguments = {}; | |
| while ((matchedCodeArguments = argumentsRegex.exec(tmpCodeArguments)) != null) { | |
| codeArguments[matchedCodeArguments[1]] = matchedCodeArguments[2]; | |
| } | |
| if (Object.keys(codeArguments).length < 1) { | |
| reduceResult.push('{{! Invalid Shortcode : "' + shortCode + '" : Invalid arguments format given (no matches) }}'); | |
| return reduceResult; | |
| } | |
| return safeFunctionCall(result, callContext, codeArguments, context).then(function (res) { | |
| reduceResult.push(res); | |
| return reduceResult; | |
| }); | |
| } | |
| reduceResult.push(result); | |
| return reduceResult; | |
| }, []).then(function (result) { | |
| for (var i = queue.length - 1; i >= 0 ; i--) { | |
| input = strSplice(queue[i].offset, queue[i].wholeMatch.length, input, result[i]); | |
| } | |
| return input; | |
| }); | |
| }; | |
| /** | |
| Pass through function to safely call function and not disqualifying from compilation | |
| **/ | |
| function safeFunctionCall(func, callContext, options, context) { | |
| try { | |
| return func.call(callContext, options, context); | |
| } catch (err) { | |
| var _toString = err.toString; | |
| err.toString = function () { | |
| return '{{! ' + _toString.call(err) + ' }}'; | |
| }; | |
| return err; | |
| } | |
| } | |
| function strSplice(start, length, string, replacement) { | |
| return string.substr(0,start) + replacement + string.substr(start+length); | |
| } | |
| })(exports); | |
| //TEST | |
| var context = { | |
| "count.for_sale.total": 123456, | |
| Count_ForRent_Total: 9876, | |
| Test_Func: function (options) { | |
| return 'Test Function: ' + JSON.stringify(options); | |
| } | |
| }; | |
| // console.log(exports.replace('{{Count_ForSale_Total}} For Sale Homes and {{Test_Func test="dude" neat="oh" sweet="man" }} For Rent Homes', context)); | |
| // console.log(exports.replace('', context)); | |
| // console.log(exports.replace(false, context)); | |
| // console.log(exports.replace('{{count.for_sale.total}}', context)); | |
| str = '{{blog name="wpMain" feed="https://wordpress.org/feed/" cache=5}}{{blog name="secondaryFun" feed="https://wordpress.org/feed/" cache=5}}This is a multi-lined string on the {{day_of_month}} of August.\n'; | |
| str += 'That works to provide a cool {{wpMain.table_flip}}, another table_flip {{secondaryFun.table_flip}}'; | |
| context = { | |
| blog: function (options, context) { | |
| return BPromise.resolve('').delay(1000).then(function (returnStr) { | |
| context.day_of_month = new Date().getDate(); | |
| context[options.name] = {}; | |
| context[options.name].table_flip = ' (╯°□°)╯︵ ┻━┻'; | |
| return returnStr; | |
| }); | |
| } | |
| }; | |
| exports.replace(str, context).then(function (output) { | |
| console.log(output); | |
| console.log(''); | |
| console.log('done'); | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment