Extract CoffeeScript view helper methods into their own mixin
This commit is contained in:
parent
17fd53f175
commit
48352ece6d
4 changed files with 283 additions and 195 deletions
|
@ -10,200 +10,6 @@
|
|||
#= require_tree ./lib/app_post
|
||||
|
||||
class App extends Spine.Controller
|
||||
helper =
|
||||
|
||||
# define print name helper
|
||||
P: (object, attributeName, attributes, table = false) ->
|
||||
App.viewPrint(object, attributeName, attributes, table)
|
||||
|
||||
# define date format helper
|
||||
date: (time) ->
|
||||
return '' if !time
|
||||
|
||||
timeObject = new Date(time)
|
||||
d = App.Utils.formatTime(timeObject.getDate(), 2)
|
||||
m = App.Utils.formatTime(timeObject.getMonth() + 1, 2)
|
||||
y = timeObject.getFullYear()
|
||||
"#{y}-#{m}-#{d}"
|
||||
|
||||
# define datetime format helper
|
||||
datetime: (time) ->
|
||||
return '' if !time
|
||||
|
||||
timeObject = new Date(time)
|
||||
d = App.Utils.formatTime(timeObject.getDate(), 2)
|
||||
m = App.Utils.formatTime(timeObject.getMonth() + 1, 2)
|
||||
y = timeObject.getFullYear()
|
||||
S = App.Utils.formatTime(timeObject.getSeconds(), 2)
|
||||
M = App.Utils.formatTime(timeObject.getMinutes(), 2)
|
||||
H = App.Utils.formatTime(timeObject.getHours(), 2)
|
||||
"#{y}-#{m}-#{d} #{H}:#{M}:#{S}"
|
||||
|
||||
# define decimal format helper
|
||||
decimal: (data, positions = 2) ->
|
||||
App.Utils.decimal(data, positions)
|
||||
|
||||
# define time_duration / mm:ss / hh:mm:ss format helper
|
||||
time_duration: (time) ->
|
||||
return '' if !time
|
||||
return '' if isNaN(parseInt(time))
|
||||
|
||||
# Hours, minutes and seconds
|
||||
hrs = ~~parseInt((time / 3600))
|
||||
mins = ~~parseInt(((time % 3600) / 60))
|
||||
secs = parseInt(time % 60)
|
||||
|
||||
# Output like "1:01" or "4:03:59" or "123:03:59"
|
||||
mins = "0#{mins}" if mins < 10
|
||||
secs = "0#{secs}" if secs < 10
|
||||
if hrs > 0
|
||||
return "#{hrs}:#{mins}:#{secs}"
|
||||
"#{mins}:#{secs}"
|
||||
|
||||
# define mask helper
|
||||
# mask an value like 'a***********yz'
|
||||
M: (item, start = 1, end = 2) ->
|
||||
return '' if !item
|
||||
string = ''
|
||||
end = item.length - end - 1
|
||||
for n in [0..item.length-1]
|
||||
if start <= n && end >= n
|
||||
string += '*'
|
||||
else
|
||||
string += item[n]
|
||||
string
|
||||
|
||||
# define translation helper
|
||||
T: (item, args...) ->
|
||||
App.i18n.translateContent(item, args...)
|
||||
|
||||
# define translation inline helper
|
||||
Ti: (item, args...) ->
|
||||
App.i18n.translateInline(item, args...)
|
||||
|
||||
# define translation for date helper
|
||||
Tdate: (item, args...) ->
|
||||
App.i18n.translateDate(item, args...)
|
||||
|
||||
# define translation for timestamp helper
|
||||
Ttimestamp: (item, args...) ->
|
||||
App.i18n.translateTimestamp(item, args...)
|
||||
|
||||
# define linkify helper
|
||||
L: (item) ->
|
||||
if item && typeof item is 'string'
|
||||
return App.Utils.linkify(item)
|
||||
item
|
||||
|
||||
# define config helper
|
||||
C: (key) ->
|
||||
App.Config.get(key)
|
||||
|
||||
# define session helper
|
||||
S: (key) ->
|
||||
App.Session.get(key)
|
||||
|
||||
# define view helper for rendering partial views
|
||||
V: (name, params) ->
|
||||
App.view(name)(params)
|
||||
|
||||
# define address line helper
|
||||
AddressLine: (line) ->
|
||||
return '' if !line
|
||||
items = emailAddresses.parseAddressList(line)
|
||||
|
||||
# line was not parsable
|
||||
return App.Utils.htmlEscape(line) if !items
|
||||
|
||||
# set markup
|
||||
result = ''
|
||||
for item in items
|
||||
if result
|
||||
result = result + ', '
|
||||
if item.name
|
||||
item.name = item.name
|
||||
.replace(',', '')
|
||||
.replace(';', '')
|
||||
.replace('"', '')
|
||||
.replace('\'', '')
|
||||
if item.name.match(/\@|,|;|\^|\+|#|§|\$|%|&|\/|\(|\)|=|\?|\*/)
|
||||
item.name = "\"#{item.name}\""
|
||||
result = "#{result}#{App.Utils.htmlEscape(item.name)} "
|
||||
if item.address
|
||||
result = result + " <span class=\"text-muted\"><#{App.Utils.htmlEscape(item.address)}></span>"
|
||||
result
|
||||
|
||||
# define file size helper
|
||||
humanFileSize: (size) ->
|
||||
App.Utils.humanFileSize(size)
|
||||
|
||||
# define pretty/human time helper
|
||||
humanTime: (time, escalation = false, cssClass = '') ->
|
||||
timestamp = App.i18n.translateTimestamp(time)
|
||||
if escalation
|
||||
cssClass += ' escalation'
|
||||
humanTime = App.PrettyDate.humanTime(time, escalation)
|
||||
"<time class=\"humanTimeFromNow #{cssClass}\" data-time=\"#{time}\" title=\"#{timestamp}\">#{humanTime}</time>"
|
||||
|
||||
# define icon helper
|
||||
Icon: (name, className = '') ->
|
||||
App.Utils.icon(name, className)
|
||||
|
||||
# define richtext helper
|
||||
RichText: (string) ->
|
||||
return string if !string
|
||||
if string.match(/@T\('/)
|
||||
string = string.replace(/@T\('(.+?)'\)/g, (match, capture) ->
|
||||
App.i18n.translateContent(capture)
|
||||
)
|
||||
return marked(string)
|
||||
App.i18n.translateContent(string)
|
||||
|
||||
ContentTypeIcon: (contentType) ->
|
||||
contentType = App.Utils.contentTypeCleanup(contentType)
|
||||
icons =
|
||||
# image
|
||||
'image/jpeg': 'file-image'
|
||||
'image/jpg': 'file-image'
|
||||
'image/png': 'file-image'
|
||||
'image/svg': 'file-image'
|
||||
'image/gif': 'file-image'
|
||||
# documents
|
||||
'application/pdf': 'file-pdf'
|
||||
'application/msword': 'file-word' # .doc, .dot
|
||||
'application/vnd.ms-word': 'file-word'
|
||||
'application/vnd.oasis.opendocument.text': 'file-word'
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file-word' # .docx
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': 'file-word' # .dotx
|
||||
'application/vnd.ms-excel': 'file-excel' # .xls
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file-excel' # .xlsx
|
||||
'application/vnd.oasis.opendocument.spreadsheet': 'file-excel'
|
||||
'application/vnd.ms-powerpoint': 'file-powerpoint' # .ppt
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file-powerpoint' # .pptx
|
||||
'application/vnd.oasis.opendocument.presentation': 'file-powerpoint'
|
||||
'text/plain': 'file-text'
|
||||
'text/html': 'file-code'
|
||||
'application/json': 'file-code'
|
||||
'message/rfc822': 'file-email'
|
||||
# code
|
||||
'application/json': 'file-code'
|
||||
# text
|
||||
'text/plain': 'file-text'
|
||||
'text/rtf': 'file-text'
|
||||
# archives
|
||||
'application/gzip': 'file-archive'
|
||||
'application/zip': 'file-archive'
|
||||
return icons[contentType]
|
||||
|
||||
canDownload: (contentType) ->
|
||||
contentType = App.Utils.contentTypeCleanup(contentType)
|
||||
contentType != 'text/html'
|
||||
|
||||
canPreview: (contentType) ->
|
||||
return false if _.isEmpty(contentType)
|
||||
return true if contentType.match(/image\/(png|jpg|jpeg|gif)/i)
|
||||
false
|
||||
|
||||
@viewPrint: (object, attributeName, attributes, table) ->
|
||||
if !attributes
|
||||
attributes = {}
|
||||
|
@ -342,7 +148,7 @@ class App extends Spine.Controller
|
|||
|
||||
@view: (name) ->
|
||||
template = (params = {}) ->
|
||||
JST["app/views/#{name}"](_.extend(params, helper))
|
||||
JST["app/views/#{name}"](_.extend(params, App.ViewHelpers))
|
||||
template
|
||||
|
||||
class App.UiElement
|
||||
|
|
|
@ -952,6 +952,34 @@ class App.Utils
|
|||
path = if window.svgPolyfill then '' else 'assets/images/icons.svg'
|
||||
"<svg class=\"icon icon-#{name} #{className}\"><use xlink:href=\"#{path}#icon-#{name}\" /></svg>"
|
||||
|
||||
@fontIcon: (name, font) ->
|
||||
@loadIconFont(font)
|
||||
"<i class=\"icon\" data-font=\"#{font}\">#{String.fromCharCode('0x'+ name)}</i>"
|
||||
|
||||
@loadIconFont: (font) ->
|
||||
el = $("[data-icon-font=\"#{font}\"]")
|
||||
return if el.length # already loaded
|
||||
|
||||
el = $("<style data-icon-font=\"#{font}\">").appendTo('head')
|
||||
woffUrl = "assets/icon-fonts/#{font}.woff"
|
||||
css = """
|
||||
@font-face {
|
||||
font-family: '#{font}';
|
||||
src: url('#{woffUrl}');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[data-font="#{font}"] {
|
||||
font-family: '#{font}';
|
||||
}
|
||||
"""
|
||||
|
||||
el.text css
|
||||
|
||||
@loadIconFontInfo: (font, callback) ->
|
||||
$.getJSON "assets/icon-fonts/#{font}.json", (data) -> callback(data.icons)
|
||||
|
||||
@getScrollBarWidth: ->
|
||||
$outer = $('<div>').css(
|
||||
visibility: 'hidden'
|
||||
|
|
201
app/assets/javascripts/app/lib/mixins/view_helpers.coffee
Normal file
201
app/assets/javascripts/app/lib/mixins/view_helpers.coffee
Normal file
|
@ -0,0 +1,201 @@
|
|||
# Top-level shortcuts for various view template helper methods,
|
||||
# to be included in view templates via
|
||||
#
|
||||
# JST["path/to/template"](_.extend(App.ViewHelpers))
|
||||
|
||||
App.ViewHelpers =
|
||||
# define print name helper
|
||||
P: (object, attributeName, attributes, table = false) ->
|
||||
App.viewPrint(object, attributeName, attributes, table)
|
||||
|
||||
# define date format helper
|
||||
date: (time) ->
|
||||
return '' if !time
|
||||
|
||||
timeObject = new Date(time)
|
||||
d = App.Utils.formatTime(timeObject.getDate(), 2)
|
||||
m = App.Utils.formatTime(timeObject.getMonth() + 1, 2)
|
||||
y = timeObject.getFullYear()
|
||||
"#{y}-#{m}-#{d}"
|
||||
|
||||
# define datetime format helper
|
||||
datetime: (time) ->
|
||||
return '' if !time
|
||||
|
||||
timeObject = new Date(time)
|
||||
d = App.Utils.formatTime(timeObject.getDate(), 2)
|
||||
m = App.Utils.formatTime(timeObject.getMonth() + 1, 2)
|
||||
y = timeObject.getFullYear()
|
||||
S = App.Utils.formatTime(timeObject.getSeconds(), 2)
|
||||
M = App.Utils.formatTime(timeObject.getMinutes(), 2)
|
||||
H = App.Utils.formatTime(timeObject.getHours(), 2)
|
||||
"#{y}-#{m}-#{d} #{H}:#{M}:#{S}"
|
||||
|
||||
# define decimal format helper
|
||||
decimal: (data, positions = 2) ->
|
||||
App.Utils.decimal(data, positions)
|
||||
|
||||
# define time_duration / mm:ss / hh:mm:ss format helper
|
||||
time_duration: (time) ->
|
||||
return '' if !time
|
||||
return '' if isNaN(parseInt(time))
|
||||
|
||||
# Hours, minutes and seconds
|
||||
hrs = ~~parseInt((time / 3600))
|
||||
mins = ~~parseInt(((time % 3600) / 60))
|
||||
secs = parseInt(time % 60)
|
||||
|
||||
# Output like "1:01" or "4:03:59" or "123:03:59"
|
||||
mins = "0#{mins}" if mins < 10
|
||||
secs = "0#{secs}" if secs < 10
|
||||
if hrs > 0
|
||||
return "#{hrs}:#{mins}:#{secs}"
|
||||
"#{mins}:#{secs}"
|
||||
|
||||
# define mask helper
|
||||
# mask an value like 'a***********yz'
|
||||
M: (item, start = 1, end = 2) ->
|
||||
return '' if !item
|
||||
string = ''
|
||||
end = item.length - end - 1
|
||||
for n in [0..item.length-1]
|
||||
if start <= n && end >= n
|
||||
string += '*'
|
||||
else
|
||||
string += item[n]
|
||||
string
|
||||
|
||||
# define translation helper
|
||||
T: (item, args...) ->
|
||||
App.i18n.translateContent(item, args...)
|
||||
|
||||
# define translation inline helper
|
||||
Ti: (item, args...) ->
|
||||
App.i18n.translateInline(item, args...)
|
||||
|
||||
# define translation for date helper
|
||||
Tdate: (item, args...) ->
|
||||
App.i18n.translateDate(item, args...)
|
||||
|
||||
# define translation for timestamp helper
|
||||
Ttimestamp: (item, args...) ->
|
||||
App.i18n.translateTimestamp(item, args...)
|
||||
|
||||
# define linkify helper
|
||||
L: (item) ->
|
||||
if item && typeof item is 'string'
|
||||
return App.Utils.linkify(item)
|
||||
item
|
||||
|
||||
# define config helper
|
||||
C: (key) ->
|
||||
App.Config.get(key)
|
||||
|
||||
# define session helper
|
||||
S: (key) ->
|
||||
App.Session.get(key)
|
||||
|
||||
# define view helper for rendering partial views
|
||||
V: (name, params) ->
|
||||
App.view(name)(params)
|
||||
|
||||
# define address line helper
|
||||
AddressLine: (line) ->
|
||||
return '' if !line
|
||||
items = emailAddresses.parseAddressList(line)
|
||||
|
||||
# line was not parsable
|
||||
return App.Utils.htmlEscape(line) if !items
|
||||
|
||||
# set markup
|
||||
result = ''
|
||||
for item in items
|
||||
if result
|
||||
result = result + ', '
|
||||
if item.name
|
||||
item.name = item.name
|
||||
.replace(',', '')
|
||||
.replace(';', '')
|
||||
.replace('"', '')
|
||||
.replace('\'', '')
|
||||
if item.name.match(/\@|,|;|\^|\+|#|§|\$|%|&|\/|\(|\)|=|\?|\*/)
|
||||
item.name = "\"#{item.name}\""
|
||||
result = "#{result}#{App.Utils.htmlEscape(item.name)} "
|
||||
if item.address
|
||||
result = result + " <span class=\"text-muted\"><#{App.Utils.htmlEscape(item.address)}></span>"
|
||||
result
|
||||
|
||||
# define file size helper
|
||||
humanFileSize: (size) ->
|
||||
App.Utils.humanFileSize(size)
|
||||
|
||||
# define pretty/human time helper
|
||||
humanTime: (time, escalation = false, cssClass = '') ->
|
||||
timestamp = App.i18n.translateTimestamp(time)
|
||||
if escalation
|
||||
cssClass += ' escalation'
|
||||
humanTime = App.PrettyDate.humanTime(time, escalation)
|
||||
"<time class=\"humanTimeFromNow #{cssClass}\" data-time=\"#{time}\" title=\"#{timestamp}\">#{humanTime}</time>"
|
||||
|
||||
# Why not just use `Icon: App.Utils.icon`?
|
||||
# Because App.Utils isn't loaded until after this file.
|
||||
Icon: (name, className = '') ->
|
||||
App.Utils.icon(name, className)
|
||||
|
||||
fontIcon: (name, font) ->
|
||||
App.Utils.fontIcon(name, font)
|
||||
|
||||
# define richtext helper
|
||||
RichText: (string) ->
|
||||
return string if !string
|
||||
if string.match(/@T\('/)
|
||||
string = string.replace(/@T\('(.+?)'\)/g, (match, capture) ->
|
||||
App.i18n.translateContent(capture)
|
||||
)
|
||||
return marked(string)
|
||||
App.i18n.translateContent(string)
|
||||
|
||||
ContentTypeIcon: (contentType) ->
|
||||
contentType = App.Utils.contentTypeCleanup(contentType)
|
||||
icons =
|
||||
# image
|
||||
'image/jpeg': 'file-image'
|
||||
'image/jpg': 'file-image'
|
||||
'image/png': 'file-image'
|
||||
'image/svg': 'file-image'
|
||||
'image/gif': 'file-image'
|
||||
# documents
|
||||
'application/pdf': 'file-pdf'
|
||||
'application/msword': 'file-word' # .doc, .dot
|
||||
'application/vnd.ms-word': 'file-word'
|
||||
'application/vnd.oasis.opendocument.text': 'file-word'
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file-word' # .docx
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': 'file-word' # .dotx
|
||||
'application/vnd.ms-excel': 'file-excel' # .xls
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file-excel' # .xlsx
|
||||
'application/vnd.oasis.opendocument.spreadsheet': 'file-excel'
|
||||
'application/vnd.ms-powerpoint': 'file-powerpoint' # .ppt
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file-powerpoint' # .pptx
|
||||
'application/vnd.oasis.opendocument.presentation': 'file-powerpoint'
|
||||
'text/plain': 'file-text'
|
||||
'text/html': 'file-code'
|
||||
'application/json': 'file-code'
|
||||
'message/rfc822': 'file-email'
|
||||
# code
|
||||
'application/json': 'file-code'
|
||||
# text
|
||||
'text/plain': 'file-text'
|
||||
'text/rtf': 'file-text'
|
||||
# archives
|
||||
'application/gzip': 'file-archive'
|
||||
'application/zip': 'file-archive'
|
||||
return icons[contentType]
|
||||
|
||||
canDownload: (contentType) ->
|
||||
contentType = App.Utils.contentTypeCleanup(contentType)
|
||||
contentType != 'text/html'
|
||||
|
||||
canPreview: (contentType) ->
|
||||
return false if _.isEmpty(contentType)
|
||||
return true if contentType.match(/image\/(png|jpg|jpeg|gif)/i)
|
||||
false
|
|
@ -3151,6 +3151,59 @@ test("htmlImage2DataUrl", function() {
|
|||
|
||||
});
|
||||
|
||||
test('App.Utils.icon()', function() {
|
||||
// When given no arguments,
|
||||
// expect @icon() to return null
|
||||
equal(App.Utils.icon(), null, 'with no arguments')
|
||||
|
||||
// On a modern browser and when given a single argument,
|
||||
// expect @icon(name) to return an <svg> tag
|
||||
window.svgPolyfill = false
|
||||
svgTag = '<svg class="icon icon-foo "><use xlink:href="assets/images/icons.svg#icon-foo" /></svg>'
|
||||
equal(App.Utils.icon('foo'), svgTag, 'with one arg / no SVG polyfill')
|
||||
|
||||
// On a modern browser and when given two arguments,
|
||||
// expect @icon(name) to return an <svg> tag
|
||||
// with second arg as add'l class name
|
||||
window.svgPolyfill = false
|
||||
svgTag = '<svg class="icon icon-foo bar"><use xlink:href="assets/images/icons.svg#icon-foo" /></svg>'
|
||||
equal(App.Utils.icon('foo', 'bar'), svgTag, 'with two args / no SVG polyfill')
|
||||
|
||||
// On a browser requiring SVG polyfill and when given a single argument,
|
||||
// expect @icon(name, class) to return an <svg> tag
|
||||
// with pathless xlink:href attr
|
||||
window.svgPolyfill = true
|
||||
svgTag = '<svg class="icon icon-foo "><use xlink:href="#icon-foo" /></svg>'
|
||||
equal(App.Utils.icon('foo'), svgTag, 'with one arg / SVG polyfill')
|
||||
|
||||
// On a browser requiring SVG polyfill and when given two arguments,
|
||||
// expect @icon(name, class) to return an <svg> tag
|
||||
// with pathless xlink:href attr and second arg as add'l class name
|
||||
window.svgPolyfill = true
|
||||
svgTag = '<svg class="icon icon-foo bar"><use xlink:href="#icon-foo" /></svg>'
|
||||
equal(App.Utils.icon('foo', 'bar'), svgTag, 'with two args / SVG polyfill')
|
||||
|
||||
// For a left-to-right browser language and when given an argument containing '{start}' or '{end}',
|
||||
// expect @icon(name) to return an <svg> tag
|
||||
// replacing '{start}' with 'left' and '{end}' with 'right'
|
||||
window.svgPolyfill = false
|
||||
App.i18n.dir = function() { return 'ltr' }
|
||||
svgTag = '<svg class="icon icon-arrow-left "><use xlink:href="assets/images/icons.svg#icon-arrow-left" /></svg>'
|
||||
equal(App.Utils.icon('arrow-{start}'), svgTag, 'for ltr locale / name includes "{start}"')
|
||||
svgTag = '<svg class="icon icon-arrow-right "><use xlink:href="assets/images/icons.svg#icon-arrow-right" /></svg>'
|
||||
equal(App.Utils.icon('arrow-{end}'), svgTag, 'for ltr locale / name includes "{end}"')
|
||||
|
||||
// For a right-to-left browser language and when given an argument containing '{start}' or '{end}',
|
||||
// expect @icon(name) to return an <svg> tag
|
||||
// replacing '{start}' with 'left' and '{end}' with 'right'
|
||||
window.svgPolyFill = false
|
||||
App.i18n.dir = function() { return 'rtl' }
|
||||
svgTag = '<svg class="icon icon-arrow-right "><use xlink:href="assets/images/icons.svg#icon-arrow-right" /></svg>'
|
||||
equal(App.Utils.icon('arrow-{start}'), svgTag, 'for rtl locale / name includes "{start}"')
|
||||
svgTag = '<svg class="icon icon-arrow-left "><use xlink:href="assets/images/icons.svg#icon-arrow-left" /></svg>'
|
||||
equal(App.Utils.icon('arrow-{end}'), svgTag, 'for rtl locale / name includes "{end}"')
|
||||
});
|
||||
|
||||
source = '<img src="/assets/images/avatar-bg.png">some test'
|
||||
$('#image2text').html(source)
|
||||
var htmlImage2DataUrlTest = function() {
|
||||
|
|
Loading…
Reference in a new issue