common-dustjs-helpers
Version:
A small library of "standard" helper functions for the Dust.js web templating framework.
456 lines (409 loc) • 16 kB
text/coffeescript
class CommonDustjsHelpers
= null
export_to: (dust)=>
return this
export_helpers_to: (dust)=>
dust.helpers =
CommonDustjsHelpers.dust = dust
return this
export_filters_to: (dust)=>
dust.filters =
CommonDustjsHelpers.dust = dust
return this
# Render the given `context` using the dust script in the given `template_body`,
# invoking `callback(err,output)` when done.
render_template:(template_body, context, callback)->
CommonDustjsHelpers.dust.renderSource template_body, context, callback
get_helpers: (helpers)=>
helpers ?= {}
helpers['count'] =
helpers['deorphan'] =
helpers['downcase'] =
helpers['elements'] =
helpers['even'] =
helpers['filter'] =
helpers['first'] =
helpers['idx'] = unless helpers['idx']? # restore default {} if not found
helpers['if'] =
helpers['index'] =
helpers['last'] =
helpers['odd'] =
helpers['random'] =
helpers['regexp'] =
helpers['repeat'] =
helpers['sep'] = unless helpers['sep']? # restore default {} if not found
helpers['substring'] =
helpers['switch'] =
helpers['titlecase'] = helpers['Titlecase'] =
helpers['trim'] =
helpers['unless'] =
helpers['upcase'] = helpers['UPCASE']=
return helpers
get_filters: (filters)=>
filters ?= {}
filters['json'] =
return filters
# FILTER IMPLEMENTATIONS
#############################################################################
json_filter: (value)->
if typeof value in ['number','boolean']
return "#{value}"
else if typeof value is 'string'
json = JSON.stringify(value)
json = json.substring(1,json.length-1)
return json
else if value?
return JSON.stringify(value)
else
return value
# HELPER IMPLEMENTATIONS
#############################################################################
count_helper: (chunk,context,bodies,params)=>
value =
if Array.isArray(value)
chunk.write(value.length)
else if typeof value is "object"
chunk.write(Object.keys(value).length)
return chunk
deorphan_helper:(chunk,context,bodies,params)=>
return chunk.capture bodies.block, context, (data,chunk)=>
data =
match = data.match /^((.|\s)+[^\s]{1})\s+([^\s]+\s*)$/
if match? and match[1]? and match[2]?
data = "#{match[1]} #{match[3]}"
chunk.write(data)
chunk.end()
downcase_helper: (chunk,context,bodies,params)=>
return chunk.capture bodies.block, context, (data,chunk) ->
chunk.write(data.toLowerCase())
chunk.end()
elements_helper: (chunk,context,bodies,params)=>
obj = params.of ? params.in
if typeof obj isnt 'object'
obj = null
index_name = "$idx"
if params?['idx']? or params?['index']?
index_name =
key_name = "$key"
if params?['key']?
key_name =
value_name = "$value"
if params?['value']?
value_name =
sort = null
if params?['sort']?
sort =
if /^(t(rue)?$)/i.test sort
sort = true
else if /^(f(alse)?$)/i.test sort
sort = null
if sort?
reverse_sort = false
if params?['dir']?
dir =
if /^D((SC)|(ES(C(END(ING)?)?)?))?$/i.test dir
reverse_sort = true
pairs = []
if obj?
index = 0
for k,v of obj
pair = {key:k, value:v}
if typeof sort is 'string'
if sort is ""
pair.sortkey = v
else
pair.sortkey = v?[sort]
pairs.push pair
if sort?
if sort is true
comparator =
else if typeof sort is 'string'
comparator =
pairs = pairs.sort(comparator)
if pairs.length > 0
for p in pairs
ctx = {}
ctx[index_name] = index
ctx[key_name] = p.key
ctx[value_name] = p.value
context = context.push(ctx)
context.stack.index = index
context.stack.of = pairs.length
chunk = bodies.block(chunk, context)
index++
else
if bodies.else?
chunk = chunk.render(bodies.else,context)
return chunk
even_helper: (chunk,context,bodies,params)=>
if context?.stack?.index?
c = (context.stack.index % 2 is 0)
return
else
return chunk
filter_helper: (chunk,context,bodies,params)=>
filter_type = if params?.type?
return chunk.capture bodies.block, context, (data,chunk)->
if filter_type?
data = CommonDustjsHelpers.dust.filters[filter_type](data)
chunk.write(data)
chunk.end()
first_helper: (chunk,context,bodies,params)=>
if context?.stack?.index?
c = (context.stack.index is 0)
return
return chunk
classic_idx: (chunk, context, bodies)->
return bodies.block(chunk, context.push(context.stack.index))
if_helper: (chunk,context,bodies,params)=>
execute_body =
return
index_helper: (chunk, context, bodies, params)->
if context?.stack?.index?
index = 1 + context.stack.index
else
index = null
if bodies?.block?
return bodies.block(chunk, context.push(index))
else
chunk.write index
return chunk
last_helper: (chunk,context,bodies,params)=>
if context?.stack?.index?
c = (context.stack.index is (context.stack.of - 1))
return
return chunk
odd_helper: (chunk,context,bodies,params)=>
if context?.stack?.index?
c = (context.stack.index % 2 is 1)
return
return chunk
random_helper: (chunk,context,bodies,params)=>
if params?
if params.m? or params.min? or params.from?
min =
if params.M? or params.max? or params.to?
max =
if params.v? or params.var? or params.val? or params.set? or params.s?
ctxvar =
min ?= 0
max ?= 1
if min > max
[min,max] = [max,min]
if max - min is 0
v = 0
else
v = Math.round(Math.random()*(max-min))+min
if ctxvar?
context.stack.head?[ctxvar] = v
if bodies?.block?
return bodies.block(chunk, context.push(v))
else
return chunk
regexp_helper:(chunk,context,bodies,params)=>
if params?.string?
string =
if params?.pattern?
pattern =
if params?.flags?
flags =
if params?.var?
match =
unless match?
match = ""
match = "$#{match}"
unless string? and pattern?
return false, chunk, context, bodies, params
else
pattern = new RegExp(pattern,flags)
ctx = {}
ctx[match] = string.match pattern
return ctx[match]?, chunk, context.push(ctx), bodies, params
repeat_helper: (chunk,context,bodies,params)=>
times = parseInt()
if times? and not isNaN(times)
context.stack.head?['$len'] = times
for i in [0...times]
context.stack.head?['$idx'] = i
chunk = bodies.block(chunk, context.push(i, i, times))
context.stack.head?['$idx'] = undefined
context.stack.head?['$len'] = undefined
return chunk
classic_sep:(chunk, context, bodies)->
if (context.stack.index is (context.stack.of - 1))
return chunk
return bodies.block(chunk, context)
substring_helper: (chunk,context,bodies,params)=>
if params?
if params["of"]? or params.string? or params.str? or params.value? or params.val?
str = (params["of"] ? params.string ? params.str ? params.value ? params.val), chunk, context
if params.from? and ?
from_index =
if params.to? and ?
to_index =
if str?
chunk.write()
return chunk
else
return chunk.capture bodies.block, context, (data,chunk)=>
chunk.write()
chunk.end()
titlecase_helper: (chunk,context,bodies,params)=>
return chunk.capture bodies.block, context, (data,chunk) ->
chunk.write( data.replace(/([^\W_]+[^\s-]*) */g, ((txt)->txt.charAt(0).toUpperCase()+txt.substr(1))) )
chunk.end()
trim_helper: (chunk,context,bodies,params)=>
return chunk.capture bodies.block, context, (data,chunk)->
chunk.write(data.trim())
chunk.end()
switch_helper: (chunk,context,bodies,params)=>
val = params.on ? params.when ? params.for ? params.value ? params.val ? params.v
if val?
val =
if val in [null,undefined,""] # render the main block for the null or empty string case
chunk = chunk.render(bodies.block,context) if bodies.block?
else
found_match = false
for name, body of bodies
if "#{name}" is "#{val}"
chunk = chunk.render(body,context)
found_match = true
break
unless found_match
chunk = chunk.render(bodies.else,context) if bodies.else?
return chunk
unless_helper: (chunk,context,bodies,params)=>
execute_body =
execute_body = not execute_body
return
upcase_helper: (chunk,context,bodies,params)=>
return chunk.capture bodies.block, context, (data,chunk) ->
chunk.write(data.toUpperCase())
chunk.end()
# INTERNAL UTILITY METHODS
#############################################################################
_eval_dust_string: ( str, chunk, context )->
if typeof str is "function"
if str.length is 0
str = str()
else
buf = ''
(chunk.tap (data) ->
buf += data; return '').render( str, context ).untap()
str = buf
return str
# if `val` is a simple integer, return it as an integer, otherwise return null
_to_int: (val)=>
if /^-?[0-9]+/.test val
return parseInt(val)
else
return null
# renders bodies.block iff b is true, bodies.else otherwise
_render_if_else:(b, chunk, context, bodies, params)->
if b is true
chunk = chunk.render(bodies.block,context) if bodies.block?
else
chunk = chunk.render(bodies.else,context) if bodies.else?
return chunk
#coffeelint:disable=cyclomatic_complexity
_inner_if_helper: (chunk,context,bodies,params)=>
execute_body = false
if params?
if params.test?
value =
for c in [ 'count', 'count_of', 'count-of', 'countof' ]
if params[c]?
countof =
if countof?.length?
value = countof.length
value ?=
if value?
if "#{value}" is "#{parseFloat(value)}"
value = parseFloat(value)
if params.matches?
matches =
re = new RegExp(matches)
execute_body = re.test(value)
else if params['is']?
isval =
if typeof value is 'number' and (not isNaN(parseFloat(isval)))
isval = parseFloat(isval)
execute_body = value is isval
else if params['isnt']?
isntval =
if typeof value is 'number' and (not isNaN(parseFloat(isntval)))
isntval = parseFloat(isntval)
execute_body = value isnt isntval
else if params.above?
above =
if typeof value is 'number' and (not isNaN(parseFloat(above)))
above = parseFloat(above)
execute_body = value > above
else if params.below?
below =
if typeof value is 'number' and (not isNaN(parseFloat(below)))
below = parseFloat(below)
execute_body = value < below
else
if typeof value is 'boolean'
execute_body = value
else if typeof value is 'number'
execute_body = value > 0
else if typeof value is 'string'
if /^(T|Y|(ON))/i.test value
execute_body = true
else if /^-?[0-9]+(\.[0-9]+)?$/.test(value) and parseInt(value) > 0
execute_body = true
else
execute_body = false
else if Array.isArray(value) and value.length > 0
execute_body = true
else if value? and typeof value is "object" and (Object.keys(value).length > 0)
execute_body = true
else
execute_body = false
return execute_body
#coffeelint:enable=cyclomatic_complexity
_get_substring:(str, from_index, to_index)=>
substring = null
if from_index? and from_index < 0
from_index = str.length + from_index
if to_index? and to_index < 0
to_index = str.length + to_index
if from_index? and to_index? and from_index > to_index
[from_index,to_index] = [to_index,from_index]
if from_index? and not to_index?
substring = str.substring(from_index)
else if to_index? and not from_index?
substring = str.substring(0,to_index)
else if from_index? and to_index?
substring = str.substring(from_index,to_index)
else
substring = str
return substring
# generates a comparator that compares two objects based on one of their attributes
_attribute_comparator:(attr,reverse=false)=>
(A,B)=>
if reverse
[A,B] = [B,A]
a = A?[attr]
b = B?[attr]
return
_compare:(a,b)=>
if a? and b?
if a.localeCompare?
return a.localeCompare(b)
else
return (if a > b then 1 else (if a < b then -1 else 0))
else if a? and not b?
return 1
else if b? and not a?
return -1
else
return 0
# EXPORTS
###############################################################################
exports = exports ? this
exports.CommonDustjsHelpers = CommonDustjsHelpers