Improved collection handling.
This commit is contained in:
parent
8bbe1c9289
commit
422ae18a2d
25 changed files with 1477 additions and 1061 deletions
|
@ -95,8 +95,8 @@ class App.Controller extends Spine.Controller
|
||||||
|
|
||||||
# if no content exists, try firstname/lastname
|
# if no content exists, try firstname/lastname
|
||||||
if !object[row.name]['name']
|
if !object[row.name]['name']
|
||||||
if object[row.name]['firstname'] || object[row.name]['lastname']
|
if object[row.name]['realname']
|
||||||
object[row.name]['name'] = (object[row.name]['firstname'] || '') + ' ' + (object[row.name]['lastname'] || '')
|
object[row.name]['name'] = object[row.name]['realname']
|
||||||
|
|
||||||
# if it isnt a object, create one
|
# if it isnt a object, create one
|
||||||
else if typeof object[row.name] isnt 'object'
|
else if typeof object[row.name] isnt 'object'
|
||||||
|
@ -112,7 +112,7 @@ class App.Controller extends Spine.Controller
|
||||||
|
|
||||||
# execute callback on content
|
# execute callback on content
|
||||||
if row.callback
|
if row.callback
|
||||||
object[row.name]['name'] = row.callback(object[row.name]['name'])
|
object[row.name]['name'] = row.callback( object[row.name]['name'] )
|
||||||
|
|
||||||
# lookup relation
|
# lookup relation
|
||||||
if !object[row.name]['name']
|
if !object[row.name]['name']
|
||||||
|
@ -120,7 +120,7 @@ class App.Controller extends Spine.Controller
|
||||||
for attribute in attributes
|
for attribute in attributes
|
||||||
if rowWithoutId is attribute.name
|
if rowWithoutId is attribute.name
|
||||||
if attribute.relation && App[attribute.relation]
|
if attribute.relation && App[attribute.relation]
|
||||||
record = App[attribute.relation].find( object[rowWithoutId] )
|
record = App.Collection.find( attribute.relation, object[rowWithoutId] )
|
||||||
object[row.name]['name'] = record.name
|
object[row.name]['name'] = record.name
|
||||||
|
|
||||||
# @log 'table', 'header', header, 'overview', data_types, 'objects', objects
|
# @log 'table', 'header', header, 'overview', data_types, 'objects', objects
|
||||||
|
@ -158,12 +158,12 @@ class App.Controller extends Spine.Controller
|
||||||
{ name: 'ticket_priority', translate: true },
|
{ name: 'ticket_priority', translate: true },
|
||||||
{ name: 'group' },
|
{ name: 'group' },
|
||||||
{ name: 'owner', class: 'user-data', data: { id: true } },
|
{ name: 'owner', class: 'user-data', data: { id: true } },
|
||||||
{ name: 'created_at', callback: @humanTime },
|
{ name: 'created_at', callback: @frontendTime },
|
||||||
{ name: 'last_contact', callback: @humanTime },
|
{ name: 'last_contact', callback: @frontendTime },
|
||||||
{ name: 'last_contact_agent', callback: @humanTime },
|
{ name: 'last_contact_agent', callback: @frontendTime },
|
||||||
{ name: 'last_contact_customer', callback: @humanTime },
|
{ name: 'last_contact_customer', callback: @frontendTime },
|
||||||
{ name: 'first_response', callback: @humanTime },
|
{ name: 'first_response', callback: @frontendTime },
|
||||||
{ name: 'close_time', callback: @humanTime },
|
{ name: 'close_time', callback: @frontendTime },
|
||||||
]
|
]
|
||||||
shown_all_attributes = []
|
shown_all_attributes = []
|
||||||
for all_attribute in all_attributes
|
for all_attribute in all_attributes
|
||||||
|
@ -190,25 +190,38 @@ class App.Controller extends Spine.Controller
|
||||||
humanTime: (time) =>
|
humanTime: (time) =>
|
||||||
current = new Date()
|
current = new Date()
|
||||||
created = new Date(time)
|
created = new Date(time)
|
||||||
diff = (current - created) / 1000
|
string = ''
|
||||||
|
diff = ( current - created ) / 1000
|
||||||
if diff >= 86400
|
if diff >= 86400
|
||||||
unit = Math.round( (diff / 86400) )
|
unit = Math.round( ( diff / 86400 ) )
|
||||||
if unit > 1
|
# if unit > 1
|
||||||
return unit + ' ' + T('days')
|
# return unit + ' ' + T('days')
|
||||||
else
|
# else
|
||||||
return unit + ' ' + T('day')
|
# return unit + ' ' + T('day')
|
||||||
|
string = unit + ' ' + T('d')
|
||||||
if diff >= 3600
|
if diff >= 3600
|
||||||
unit = Math.round( (diff / 3600) )
|
unit = Math.round( ( diff / 3600 ) % 24 )
|
||||||
if unit > 1
|
# if unit > 1
|
||||||
return unit + ' ' + T('hours')
|
# return unit + ' ' + T('hours')
|
||||||
|
# else
|
||||||
|
# return unit + ' ' + T('hour')
|
||||||
|
if string isnt ''
|
||||||
|
string = string + ' ' + unit + ' ' + T('h')
|
||||||
|
return string
|
||||||
else
|
else
|
||||||
return unit + ' ' + T('hour')
|
string = unit + ' ' + T('h')
|
||||||
if diff <= 3600
|
if diff <= 86400
|
||||||
unit = Math.round( (diff / 60) )
|
unit = Math.round( ( diff / 60 ) % 60 )
|
||||||
if unit > 1
|
# if unit > 1
|
||||||
return unit + ' ' + T('minutes')
|
# return unit + ' ' + T('minutes')
|
||||||
|
# else
|
||||||
|
# return unit + ' ' + T('minute')
|
||||||
|
if string isnt ''
|
||||||
|
string = string + ' ' + unit + ' ' + T('m')
|
||||||
|
return string
|
||||||
else
|
else
|
||||||
return unit + ' ' + T('minute')
|
string = unit + ' ' + T('m')
|
||||||
|
return string
|
||||||
|
|
||||||
userInfo: (data) =>
|
userInfo: (data) =>
|
||||||
# start customer info controller
|
# start customer info controller
|
||||||
|
@ -230,6 +243,21 @@ class App.Controller extends Spine.Controller
|
||||||
@navigate '#login'
|
@navigate '#login'
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
frontendTime: (timestamp) ->
|
||||||
|
'<span class="humanTimeFromNow" data-time="' + timestamp + '">?</span>'
|
||||||
|
|
||||||
|
frontendTimeUpdate: ->
|
||||||
|
update = =>
|
||||||
|
ui = @
|
||||||
|
$('.humanTimeFromNow').each( ->
|
||||||
|
# console.log('rewrite frontendTimeUpdate', this)
|
||||||
|
timestamp = $(this).data('time')
|
||||||
|
time = ui.humanTime( timestamp )
|
||||||
|
$(this).attr( 'title', Ts(timestamp) )
|
||||||
|
$(this).text( time )
|
||||||
|
)
|
||||||
|
@interval( update, 30000, 'frontendTimeUpdate' )
|
||||||
|
|
||||||
clearInterval: (interval_id) =>
|
clearInterval: (interval_id) =>
|
||||||
# check global var
|
# check global var
|
||||||
if !@intervalID
|
if !@intervalID
|
||||||
|
@ -237,13 +265,13 @@ class App.Controller extends Spine.Controller
|
||||||
|
|
||||||
clearInterval( @intervalID[interval_id] ) if @intervalID[interval_id]
|
clearInterval( @intervalID[interval_id] ) if @intervalID[interval_id]
|
||||||
|
|
||||||
interval: (action, interval, interval_id) =>
|
interval: (callback, interval, interval_id) =>
|
||||||
|
|
||||||
# check global var
|
# check global var
|
||||||
if !@intervalID
|
if !@intervalID
|
||||||
@intervalID = {}
|
@intervalID = {}
|
||||||
|
|
||||||
action()
|
callback()
|
||||||
|
|
||||||
# auto save
|
# auto save
|
||||||
every = (ms, cb) -> setInterval cb, ms
|
every = (ms, cb) -> setInterval cb, ms
|
||||||
|
@ -253,7 +281,7 @@ class App.Controller extends Spine.Controller
|
||||||
|
|
||||||
# request new data
|
# request new data
|
||||||
@intervalID[interval_id] = every interval, () =>
|
@intervalID[interval_id] = every interval, () =>
|
||||||
action()
|
callback()
|
||||||
|
|
||||||
userPopups: (position = 'right') ->
|
userPopups: (position = 'right') ->
|
||||||
|
|
||||||
|
@ -267,11 +295,11 @@ class App.Controller extends Spine.Controller
|
||||||
placement: position,
|
placement: position,
|
||||||
title: ->
|
title: ->
|
||||||
user_id = $(@).data('id')
|
user_id = $(@).data('id')
|
||||||
user = App.User.find(user_id)
|
user = App.Collection.find( 'User', user_id )
|
||||||
(user.firstname || '') + ' ' + (user.lastname || '')
|
user.realname
|
||||||
content: ->
|
content: ->
|
||||||
user_id = $(@).data('id')
|
user_id = $(@).data('id')
|
||||||
user = App.User.find(user_id)
|
user = App.Collection.find( 'User', user_id )
|
||||||
|
|
||||||
# get display data
|
# get display data
|
||||||
data = []
|
data = []
|
||||||
|
@ -322,9 +350,8 @@ class App.Controller extends Spine.Controller
|
||||||
type = $(@).filter('[data-type]').data('type')
|
type = $(@).filter('[data-type]').data('type')
|
||||||
data = tickets[type] || []
|
data = tickets[type] || []
|
||||||
|
|
||||||
for ticket in data
|
|
||||||
|
|
||||||
# set human time
|
# set human time
|
||||||
|
for ticket in data
|
||||||
ticket.humanTime = controller.humanTime(ticket.created_at)
|
ticket.humanTime = controller.humanTime(ticket.created_at)
|
||||||
|
|
||||||
# insert data
|
# insert data
|
||||||
|
@ -333,129 +360,6 @@ class App.Controller extends Spine.Controller
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
loadCollection: (params) ->
|
|
||||||
|
|
||||||
# remember in store if not already requested
|
|
||||||
if !params.localStorage
|
|
||||||
if params.type == 'User'
|
|
||||||
for user_id, user of params.data
|
|
||||||
data = {}
|
|
||||||
data[params.type] = {}
|
|
||||||
data[params.type][ user_id ] = user
|
|
||||||
App.Store.write( 'collection::' + params.type + '::' + user_id, { type: params.type, localStorage: true, collections: data } )
|
|
||||||
else
|
|
||||||
for object in params.data
|
|
||||||
data = {}
|
|
||||||
data[params.type] = [ object ]
|
|
||||||
App.Store.write( 'collection::' + params.type + '::' + object.id, { type: params.type, localStorage: true, collections: data } )
|
|
||||||
|
|
||||||
# users
|
|
||||||
if params.type == 'User'
|
|
||||||
for user_id, user of params.data
|
|
||||||
|
|
||||||
# set socal media links
|
|
||||||
if user['accounts']
|
|
||||||
for account of user['accounts']
|
|
||||||
if account == 'twitter'
|
|
||||||
user['accounts'][account]['link'] = 'http://twitter.com/' + user['accounts'][account]['username']
|
|
||||||
if account == 'facebook'
|
|
||||||
user['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + user['accounts'][account]['uid']
|
|
||||||
|
|
||||||
# set image url
|
|
||||||
if user && !user['image']
|
|
||||||
user['image'] = 'http://placehold.it/48x48'
|
|
||||||
|
|
||||||
# set realname
|
|
||||||
user['realname'] = ''
|
|
||||||
if user['firstname']
|
|
||||||
user['realname'] = user['firstname']
|
|
||||||
if user['lastname']
|
|
||||||
if user['realname'] isnt ''
|
|
||||||
user['realname'] = user['realname'] + ' '
|
|
||||||
user['realname'] = user['realname'] + user['lastname']
|
|
||||||
|
|
||||||
# load in collection if needed
|
|
||||||
if !params.collection
|
|
||||||
App.User.refresh( user, options: { clear: true } )
|
|
||||||
|
|
||||||
# tickets
|
|
||||||
else if params.type == 'Ticket'
|
|
||||||
for ticket in params.data
|
|
||||||
|
|
||||||
# set human time
|
|
||||||
ticket.humanTime = @humanTime(ticket.created_at)
|
|
||||||
|
|
||||||
# priority
|
|
||||||
ticket.ticket_priority = App.TicketPriority.find(ticket.ticket_priority_id)
|
|
||||||
|
|
||||||
# state
|
|
||||||
ticket.ticket_state = App.TicketState.find(ticket.ticket_state_id)
|
|
||||||
|
|
||||||
# group
|
|
||||||
ticket.group = App.Group.find(ticket.group_id)
|
|
||||||
|
|
||||||
# customer
|
|
||||||
if ticket.customer_id and App.User.exists(ticket.customer_id)
|
|
||||||
user = App.User.find(ticket.customer_id)
|
|
||||||
ticket.customer = user
|
|
||||||
|
|
||||||
# owner
|
|
||||||
if ticket.owner_id and App.User.exists(ticket.owner_id)
|
|
||||||
user = App.User.find(ticket.owner_id)
|
|
||||||
ticket.owner = user
|
|
||||||
|
|
||||||
# load in collection if needed
|
|
||||||
if !params.collection
|
|
||||||
App.Ticket.refresh( ticket, options: { clear: true } )
|
|
||||||
|
|
||||||
# articles
|
|
||||||
else if params.type == 'TicketArticle'
|
|
||||||
for article in params.data
|
|
||||||
|
|
||||||
# add user
|
|
||||||
article.created_by = App.User.find(article.created_by_id)
|
|
||||||
|
|
||||||
# set human time
|
|
||||||
article.humanTime = @humanTime(article.created_at)
|
|
||||||
|
|
||||||
# add possible actions
|
|
||||||
article.article_type = App.TicketArticleType.find( article.ticket_article_type_id )
|
|
||||||
article.article_sender = App.TicketArticleSender.find( article.ticket_article_sender_id )
|
|
||||||
|
|
||||||
# load in collection if needed
|
|
||||||
if !params.collection
|
|
||||||
App.TicketArticle.refresh( article, options: { clear: true } )
|
|
||||||
|
|
||||||
# history
|
|
||||||
else if params.type == 'History'
|
|
||||||
for histroy in params.data
|
|
||||||
|
|
||||||
# add user
|
|
||||||
histroy.created_by = App.User.find(histroy.created_by_id)
|
|
||||||
|
|
||||||
# set human time
|
|
||||||
histroy.humanTime = @humanTime(histroy.created_at)
|
|
||||||
|
|
||||||
# add possible actions
|
|
||||||
if histroy.history_attribute_id
|
|
||||||
histroy.attribute = App.HistoryAttribute.find( histroy.history_attribute_id )
|
|
||||||
if histroy.history_type_id
|
|
||||||
histroy.type = App.HistoryType.find( histroy.history_type_id )
|
|
||||||
if histroy.history_object_id
|
|
||||||
histroy.object = App.HistoryObject.find( histroy.history_object_id )
|
|
||||||
|
|
||||||
# load in collection if needed
|
|
||||||
if !params.collection
|
|
||||||
App.History.refresh( histroy, options: { clear: true } )
|
|
||||||
|
|
||||||
# all the rest
|
|
||||||
else
|
|
||||||
for object in params.data
|
|
||||||
|
|
||||||
# load in collection if needed
|
|
||||||
if !params.collection
|
|
||||||
App[params.type].refresh( object, options: { clear: true } )
|
|
||||||
|
|
||||||
ws_send: (data) ->
|
ws_send: (data) ->
|
||||||
Spine.trigger( 'ws:send', JSON.stringify(data) )
|
Spine.trigger( 'ws:send', JSON.stringify(data) )
|
||||||
|
|
||||||
|
|
|
@ -418,7 +418,8 @@ class App.ControllerForm extends App.Controller
|
||||||
# if record.name.toString() is attribute.value.toString()
|
# if record.name.toString() is attribute.value.toString()
|
||||||
# record.selected = 'selected'
|
# record.selected = 'selected'
|
||||||
# record.checked = 'checked'
|
# record.checked = 'checked'
|
||||||
if ( attribute.value && record.value && _.include(attribute.value, record.value) ) || ( attribute.value && record.name && _.include(attribute.value, record.name) )
|
|
||||||
|
else if ( attribute.value && record.value && _.include(attribute.value, record.value) ) || ( attribute.value && record.name && _.include(attribute.value, record.name) )
|
||||||
record.selected = 'selected'
|
record.selected = 'selected'
|
||||||
record.checked = 'checked'
|
record.checked = 'checked'
|
||||||
|
|
||||||
|
|
|
@ -37,13 +37,13 @@ class App.DashboardActivityStream extends App.Controller
|
||||||
items = data.activity_stream
|
items = data.activity_stream
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
App.Collection.load( type: 'Ticket', data: data.tickets )
|
||||||
|
|
||||||
# load article collection
|
# load article collection
|
||||||
@loadCollection( type: 'TicketArticle', data: data.articles )
|
App.Collection.load( type: 'TicketArticle', data: data.articles )
|
||||||
|
|
||||||
@render(items)
|
@render(items)
|
||||||
|
|
||||||
|
@ -51,15 +51,15 @@ class App.DashboardActivityStream extends App.Controller
|
||||||
|
|
||||||
# load user data
|
# load user data
|
||||||
for item in items
|
for item in items
|
||||||
item.created_by = App.User.find( item.created_by_id )
|
item.created_by = App.Collection.find( 'User', item.created_by_id )
|
||||||
|
|
||||||
# load ticket data
|
# load ticket data
|
||||||
for item in items
|
for item in items
|
||||||
item.data = {}
|
item.data = {}
|
||||||
if item.history_object is 'Ticket'
|
if item.history_object is 'Ticket'
|
||||||
item.data.title = App.Ticket.find( item.o_id ).title
|
item.data.title = App.Collection.find( 'Ticket', item.o_id ).title
|
||||||
if item.history_object is 'Ticket::Article'
|
if item.history_object is 'Ticket::Article'
|
||||||
article = App.TicketArticle.find( item.o_id )
|
article = App.Collection.find( 'TicketArticle', item.o_id )
|
||||||
item.history_object = 'Article'
|
item.history_object = 'Article'
|
||||||
item.sub_o_id = article.id
|
item.sub_o_id = article.id
|
||||||
item.o_id = article.ticket_id
|
item.o_id = article.ticket_id
|
||||||
|
|
|
@ -24,10 +24,10 @@ class App.DashboardRecentViewed extends App.Controller
|
||||||
@items = data.recent_viewed
|
@items = data.recent_viewed
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
App.Collection.load( type: 'Ticket', data: data.tickets )
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
)
|
)
|
||||||
|
@ -36,13 +36,11 @@ class App.DashboardRecentViewed extends App.Controller
|
||||||
|
|
||||||
# load user data
|
# load user data
|
||||||
for item in @items
|
for item in @items
|
||||||
# @log 'load', item.created_by_id
|
item.created_by = App.Collection.find( 'User', item.created_by_id )
|
||||||
item.created_by = App.User.find(item.created_by_id)
|
|
||||||
|
|
||||||
# load ticket data
|
# load ticket data
|
||||||
for item in @items
|
for item in @items
|
||||||
# @log 'load', item.o_id
|
item.ticket = App.Collection.find( 'User', item.o_id )
|
||||||
item.ticket = App.Ticket.find(item.o_id)
|
|
||||||
|
|
||||||
html = App.view('dashboard/recent_viewed')(
|
html = App.view('dashboard/recent_viewed')(
|
||||||
head: 'Recent Viewed',
|
head: 'Recent Viewed',
|
||||||
|
|
|
@ -84,7 +84,7 @@ class App.DashboardTicket extends App.Controller
|
||||||
while i < end
|
while i < end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if @ticket_list[ i - 1 ]
|
if @ticket_list[ i - 1 ]
|
||||||
@tickets_in_table.push App.Ticket.find( @ticket_list[ i - 1 ] )
|
@tickets_in_table.push App.Collection.find( 'Ticket', @ticket_list[ i - 1 ] )
|
||||||
|
|
||||||
shown_all_attributes = @ticketTableAttributes( App.Overview.find(@overview.id).view.d.overview )
|
shown_all_attributes = @ticketTableAttributes( App.Overview.find(@overview.id).view.d.overview )
|
||||||
table = @table(
|
table = @table(
|
||||||
|
@ -102,6 +102,9 @@ class App.DashboardTicket extends App.Controller
|
||||||
html.find('.table-overview').append(table)
|
html.find('.table-overview').append(table)
|
||||||
@html html
|
@html html
|
||||||
|
|
||||||
|
# show frontend times
|
||||||
|
@frontendTimeUpdate()
|
||||||
|
|
||||||
# start user popups
|
# start user popups
|
||||||
@userPopups()
|
@userPopups()
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Index extends App.Controller
|
||||||
@edit_form = cache.edit_form
|
@edit_form = cache.edit_form
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: cache.users )
|
App.Collection.load( type: 'User', data: cache.users )
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
else
|
else
|
||||||
|
@ -59,18 +59,18 @@ class Index extends App.Controller
|
||||||
@edit_form = data.edit_form
|
@edit_form = data.edit_form
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
if data.ticket && data.articles
|
if data.ticket && data.articles
|
||||||
@loadCollection( type: 'Ticket', data: [data.ticket] )
|
App.Collection.load( type: 'Ticket', data: [data.ticket] )
|
||||||
|
|
||||||
# load article collections
|
# load article collections
|
||||||
@loadCollection( type: 'TicketArticle', data: data.articles || [] )
|
App.Collection.load( type: 'TicketArticle', data: data.articles || [] )
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
t = App.Ticket.find(params.ticket_id).attributes()
|
t = App.Collection.find( 'Ticket', params.ticket_id ).attributes()
|
||||||
a = App.TicketArticle.find(params.article_id)
|
a = App.Collection.find( 'TicketArticle', params.article_id )
|
||||||
|
|
||||||
# reset owner
|
# reset owner
|
||||||
t.owner_id = 0
|
t.owner_id = 0
|
||||||
|
@ -86,9 +86,9 @@ class Index extends App.Controller
|
||||||
# set defaults
|
# set defaults
|
||||||
defaults = template['options'] || {}
|
defaults = template['options'] || {}
|
||||||
if !( 'ticket_state_id' of defaults )
|
if !( 'ticket_state_id' of defaults )
|
||||||
defaults['ticket_state_id'] = App.TicketState.findByAttribute( 'name', 'new' ).id
|
defaults['ticket_state_id'] = App.Collection.findByAttribute( 'TicketState', 'name', 'new' ).id
|
||||||
if !( 'ticket_priority_id' of defaults )
|
if !( 'ticket_priority_id' of defaults )
|
||||||
defaults['ticket_priority_id'] = App.TicketPriority.findByAttribute( 'name', '2 normal' ).id
|
defaults['ticket_priority_id'] = App.Collection.findByAttribute( 'TicketPriority', 'name', '2 normal' ).id
|
||||||
|
|
||||||
# remember customers
|
# remember customers
|
||||||
if $('#create_customer_id').val()
|
if $('#create_customer_id').val()
|
||||||
|
@ -163,10 +163,10 @@ class Index extends App.Controller
|
||||||
@log 'updateAttributes', params
|
@log 'updateAttributes', params
|
||||||
|
|
||||||
# find sender_id
|
# find sender_id
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
|
sender = App.Collection.findByAttribute( 'TicketArticleSender', 'name', 'Customer' )
|
||||||
type = App.TicketArticleType.findByAttribute( 'name', 'phone' )
|
type = App.Collection.findByAttribute( 'TicketArticleType', 'name', 'phone' )
|
||||||
if params.group_id
|
if params.group_id
|
||||||
group = App.Group.find(params.group_id)
|
group = App.Collection.find( 'Group', params.group_id )
|
||||||
|
|
||||||
# create article
|
# create article
|
||||||
params['article'] = {
|
params['article'] = {
|
||||||
|
@ -248,7 +248,7 @@ class UserNew extends App.ControllerModal
|
||||||
user = new App.User
|
user = new App.User
|
||||||
|
|
||||||
# find role_id
|
# find role_id
|
||||||
role = App.Role.findByAttribute( 'name', 'Customer' )
|
role = App.Collection.findByAttribute( 'Role', 'name', 'Customer' )
|
||||||
params.role_ids = role.id
|
params.role_ids = role.id
|
||||||
@log 'updateAttributes', params
|
@log 'updateAttributes', params
|
||||||
user.load(params)
|
user.load(params)
|
||||||
|
|
|
@ -14,23 +14,23 @@ class App.TicketHistory extends App.ControllerModal
|
||||||
@ticket = data.ticket
|
@ticket = data.ticket
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: [data.ticket] )
|
App.Collection.load( type: 'Ticket', data: [data.ticket] )
|
||||||
|
|
||||||
# load history_type collections
|
# load history_type collections
|
||||||
@loadCollection( type: 'HistoryType', data: data.history_types )
|
App.Collection.load( type: 'HistoryType', data: data.history_types )
|
||||||
|
|
||||||
# load history_object collections
|
# load history_object collections
|
||||||
@loadCollection( type: 'HistoryObject', data: data.history_objects )
|
App.Collection.load( type: 'HistoryObject', data: data.history_objects )
|
||||||
|
|
||||||
# load history_attributes collections
|
# load history_attributes collections
|
||||||
@loadCollection( type: 'HistoryAttribute', data: data.history_attributes )
|
App.Collection.load( type: 'HistoryAttribute', data: data.history_attributes )
|
||||||
|
|
||||||
# load history collections
|
# load history collections
|
||||||
App.History.deleteAll()
|
App.Collection.deleteAll( 'History' )
|
||||||
@loadCollection( type: 'History', data: data.history )
|
App.Collection.load( type: 'History', data: data.history )
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
@render()
|
@render()
|
||||||
|
@ -39,7 +39,7 @@ class App.TicketHistory extends App.ControllerModal
|
||||||
render: ->
|
render: ->
|
||||||
|
|
||||||
@html App.view('agent_ticket_history')(
|
@html App.view('agent_ticket_history')(
|
||||||
objects: App.History.all(),
|
objects: App.Collection.all( 'History' ),
|
||||||
)
|
)
|
||||||
|
|
||||||
@modalShow()
|
@modalShow()
|
||||||
|
|
|
@ -24,13 +24,16 @@ class App.TicketMerge extends App.ControllerModal
|
||||||
success: (data, status, xhr) =>
|
success: (data, status, xhr) =>
|
||||||
|
|
||||||
if data['result'] is 'success'
|
if data['result'] is 'success'
|
||||||
@loadCollection( type: 'Ticket', data: [data.master_ticket] )
|
|
||||||
@loadCollection( type: 'Ticket', data: [data.slave_ticket] )
|
|
||||||
|
|
||||||
|
# update collection
|
||||||
|
App.Collection.load( type: 'Ticket', data: [data.master_ticket] )
|
||||||
|
App.Collection.load( type: 'Ticket', data: [data.slave_ticket] )
|
||||||
|
|
||||||
|
# hide dialog
|
||||||
@modalHide()
|
@modalHide()
|
||||||
|
|
||||||
# view ticket
|
# view ticket
|
||||||
@log 'nav...', App.Ticket.find( data.master_ticket['id'] )
|
@log 'nav...', App.Collection.find( 'Ticket', data.master_ticket['id'] )
|
||||||
@navigate '#ticket/zoom/' + data.master_ticket['id']
|
@navigate '#ticket/zoom/' + data.master_ticket['id']
|
||||||
|
|
||||||
# notify UI
|
# notify UI
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Index extends App.Controller
|
||||||
|
|
||||||
@ticket_list_show = []
|
@ticket_list_show = []
|
||||||
for ticket_id in @ticket_list
|
for ticket_id in @ticket_list
|
||||||
@ticket_list_show.push App.Ticket.find(ticket_id)
|
@ticket_list_show.push App.Collection.find( 'Ticket', ticket_id )
|
||||||
|
|
||||||
# remeber bulk attributes
|
# remeber bulk attributes
|
||||||
@bulk = data.bulk
|
@bulk = data.bulk
|
||||||
|
@ -159,6 +159,9 @@ class Index extends App.Controller
|
||||||
# start user popups
|
# start user popups
|
||||||
@userPopups()
|
@userPopups()
|
||||||
|
|
||||||
|
# show frontend times
|
||||||
|
@frontendTimeUpdate()
|
||||||
|
|
||||||
# start bulk action observ
|
# start bulk action observ
|
||||||
@el.find('.bulk-action').append( @bulk_form() )
|
@el.find('.bulk-action').append( @bulk_form() )
|
||||||
|
|
||||||
|
|
|
@ -54,28 +54,26 @@ class Index extends App.Controller
|
||||||
@edit_form = data.edit_form
|
@edit_form = data.edit_form
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: [data.ticket] )
|
App.Collection.load( type: 'Ticket', data: [data.ticket] )
|
||||||
|
|
||||||
# load article collections
|
# load article collections
|
||||||
@loadCollection( type: 'TicketArticle', data: data.articles || [] )
|
App.Collection.load( type: 'TicketArticle', data: data.articles )
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
render: =>
|
render: =>
|
||||||
|
|
||||||
return if !App.Ticket.exists(@ticket_id)
|
|
||||||
|
|
||||||
# get data
|
# get data
|
||||||
if !@ticket
|
if !@ticket
|
||||||
@ticket = App.Ticket.find(@ticket_id)
|
@ticket = App.Collection.find( 'Ticket', @ticket_id )
|
||||||
if !@articles
|
if !@articles
|
||||||
@articles = []
|
@articles = []
|
||||||
for article_id in @ticket.article_ids
|
for article_id in @ticket.article_ids
|
||||||
article = App.TicketArticle.find(article_id)
|
article = App.Collection.find( 'TicketArticle', article_id )
|
||||||
@articles.push article
|
@articles.push article
|
||||||
|
|
||||||
# rework articles
|
# rework articles
|
||||||
|
@ -146,6 +144,9 @@ class Index extends App.Controller
|
||||||
# show ticket action row
|
# show ticket action row
|
||||||
@ticket_action_row()
|
@ticket_action_row()
|
||||||
|
|
||||||
|
# show frontend times
|
||||||
|
@frontendTimeUpdate()
|
||||||
|
|
||||||
# scrall to article if given
|
# scrall to article if given
|
||||||
if @article_id
|
if @article_id
|
||||||
offset = document.getElementById( 'article-' + @article_id ).offsetTop
|
offset = document.getElementById( 'article-' + @article_id ).offsetTop
|
||||||
|
@ -345,11 +346,11 @@ class Index extends App.Controller
|
||||||
|
|
||||||
# find sender_id
|
# find sender_id
|
||||||
if @isRole('Customer')
|
if @isRole('Customer')
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
|
sender = App.Collection.findByAttribute( 'TicketArticleSender', 'name', 'Customer' )
|
||||||
type = App.TicketArticleType.findByAttribute( 'name', 'web' )
|
type = App.Collection.findByAttribute( 'TicketArticleType', 'name', 'web' )
|
||||||
params['ticket_article_type_id'] = type.id
|
params['ticket_article_type_id'] = type.id
|
||||||
else
|
else
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Agent' )
|
sender = App.Collection.findByAttribute( 'TicketArticleSender', 'name', 'Agent' )
|
||||||
params.ticket_article_sender_id = sender.id
|
params.ticket_article_sender_id = sender.id
|
||||||
@log 'updateAttributes', params, sender, sender.id
|
@log 'updateAttributes', params, sender, sender.id
|
||||||
article.load(params)
|
article.load(params)
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Index extends App.Controller
|
||||||
@edit_form = cache.edit_form
|
@edit_form = cache.edit_form
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: cache.users )
|
App.Collection.load( type: 'User', data: cache.users )
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
else
|
else
|
||||||
|
@ -53,18 +53,18 @@ class Index extends App.Controller
|
||||||
@edit_form = data.edit_form
|
@edit_form = data.edit_form
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
if data.ticket && data.articles
|
if data.ticket && data.articles
|
||||||
@loadCollection( type: 'Ticket', data: [data.ticket] )
|
App.Collection.load( type: 'Ticket', data: [data.ticket] )
|
||||||
|
|
||||||
# load article collections
|
# load article collections
|
||||||
@loadCollection( type: 'TicketArticle', data: data.articles || [] )
|
App.Collection.load( type: 'TicketArticle', data: data.articles || [] )
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
t = App.Ticket.find(params.ticket_id).attributes()
|
t = App.Collection.find( 'Ticket', params.ticket_id ).attributes()
|
||||||
a = App.TicketArticle.find(params.article_id)
|
a = App.Collection.find( 'TicketArticle', params.article_id )
|
||||||
|
|
||||||
# reset owner
|
# reset owner
|
||||||
t.owner_id = 0
|
t.owner_id = 0
|
||||||
|
@ -80,9 +80,9 @@ class Index extends App.Controller
|
||||||
# set defaults
|
# set defaults
|
||||||
defaults = template['options'] || {}
|
defaults = template['options'] || {}
|
||||||
if !( 'ticket_state_id' of defaults )
|
if !( 'ticket_state_id' of defaults )
|
||||||
defaults['ticket_state_id'] = App.TicketState.findByAttribute( 'name', 'new' )
|
defaults['ticket_state_id'] = App.Collection.findByAttribute( 'TicketState', 'name', 'new' )
|
||||||
if !( 'ticket_priority_id' of defaults )
|
if !( 'ticket_priority_id' of defaults )
|
||||||
defaults['ticket_priority_id'] = App.TicketPriority.findByAttribute( 'name', '2 normal' )
|
defaults['ticket_priority_id'] = App.Collection.findByAttribute( 'TicketPriority', 'name', '2 normal' )
|
||||||
|
|
||||||
# generate form
|
# generate form
|
||||||
configure_attributes = [
|
configure_attributes = [
|
||||||
|
@ -126,11 +126,11 @@ class Index extends App.Controller
|
||||||
params.customer_id = Session['id']
|
params.customer_id = Session['id']
|
||||||
|
|
||||||
# set prio
|
# set prio
|
||||||
priority = App.TicketPriority.findByAttribute( 'name', '2 normal' )
|
priority = App.Collection.findByAttribute( 'TicketPriority', 'name', '2 normal' )
|
||||||
params.ticket_state_id = priority.id
|
params.ticket_state_id = priority.id
|
||||||
|
|
||||||
# set state
|
# set state
|
||||||
state = App.TicketState.findByAttribute( 'name', 'new' )
|
state = App.Collection.findByAttribute( 'TicketState', 'name', 'new' )
|
||||||
params.ticket_priority_id = state.id
|
params.ticket_priority_id = state.id
|
||||||
|
|
||||||
# fillup params
|
# fillup params
|
||||||
|
@ -142,10 +142,10 @@ class Index extends App.Controller
|
||||||
@log 'updateAttributes', params
|
@log 'updateAttributes', params
|
||||||
|
|
||||||
# find sender_id
|
# find sender_id
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
|
sender = App.Collection.findByAttribute( 'TicketArticleSender', 'name', 'Customer' )
|
||||||
type = App.TicketArticleType.findByAttribute( 'name', 'web' )
|
type = App.Collection.findByAttribute( 'TicketArticleType', 'name', 'web' )
|
||||||
if params.group_id
|
if params.group_id
|
||||||
group = App.Group.find(params.group_id)
|
group = App.Collection.find( 'Group', params.group_id )
|
||||||
|
|
||||||
# create article
|
# create article
|
||||||
params['article'] = {
|
params['article'] = {
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Index extends App.Controller
|
||||||
@master_user = data.master_user
|
@master_user = data.master_user
|
||||||
|
|
||||||
# load group collection
|
# load group collection
|
||||||
@loadCollection( type: 'Group', data: data.groups )
|
App.Collection.load( type: 'Group', data: data.groups )
|
||||||
|
|
||||||
# render page
|
# render page
|
||||||
@render()
|
@render()
|
||||||
|
@ -80,7 +80,7 @@ class Index extends App.Controller
|
||||||
@params.invite = true
|
@params.invite = true
|
||||||
|
|
||||||
# find agent role
|
# find agent role
|
||||||
role = App.Role.findByAttribute('name', 'Agent')
|
role = App.Collection.findByAttribute( 'Role', 'name', 'Agent' )
|
||||||
if role
|
if role
|
||||||
@params.role_ids = role.id
|
@params.role_ids = role.id
|
||||||
else
|
else
|
||||||
|
|
|
@ -26,10 +26,10 @@ class App.LinkInfo extends App.Controller
|
||||||
@links = data.links
|
@links = data.links
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
App.Collection.load( type: 'Ticket', data: data.tickets )
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,7 @@ class App.LinkInfo extends App.Controller
|
||||||
list[ item['link_type'] ] = []
|
list[ item['link_type'] ] = []
|
||||||
|
|
||||||
if item['link_object'] is 'Ticket'
|
if item['link_object'] is 'Ticket'
|
||||||
ticket = App.Ticket.find( item['link_object_value'] )
|
ticket = App.Collection.find( 'Ticket', item['link_object_value'] )
|
||||||
if ticket.ticket_state.name is 'merged'
|
if ticket.ticket_state.name is 'merged'
|
||||||
ticket.css = 'merged'
|
ticket.css = 'merged'
|
||||||
list[ item['link_type'] ].push ticket
|
list[ item['link_type'] ].push ticket
|
||||||
|
|
|
@ -170,10 +170,10 @@ class App.Navigation extends App.Controller
|
||||||
items = data.recent_viewed
|
items = data.recent_viewed
|
||||||
|
|
||||||
# load user collection
|
# load user collection
|
||||||
@loadCollection( type: 'User', data: data.users )
|
App.Collection.load( type: 'User', data: data.users )
|
||||||
|
|
||||||
# load ticket collection
|
# load ticket collection
|
||||||
@loadCollection( type: 'Ticket', data: data.tickets )
|
App.Collection.load( type: 'Ticket', data: data.tickets )
|
||||||
|
|
||||||
# remove old views
|
# remove old views
|
||||||
for key of Config.NavBarRight
|
for key of Config.NavBarRight
|
||||||
|
@ -190,7 +190,7 @@ class App.Navigation extends App.Controller
|
||||||
if prio is 8000
|
if prio is 8000
|
||||||
divider = true
|
divider = true
|
||||||
navheader = 'Recent Viewed'
|
navheader = 'Recent Viewed'
|
||||||
ticket = App.Ticket.find(item.o_id)
|
ticket = App.Collection.find( 'Ticket', item.o_id )
|
||||||
prio++
|
prio++
|
||||||
Config.NavBarRight['RecendViewed::' + ticket.id + '-' + prio ] = {
|
Config.NavBarRight['RecendViewed::' + ticket.id + '-' + prio ] = {
|
||||||
prio: prio,
|
prio: prio,
|
||||||
|
|
|
@ -11,7 +11,7 @@ class App.TemplateUI extends App.Controller
|
||||||
|
|
||||||
# fetch item on demand
|
# fetch item on demand
|
||||||
fetch_needed = 1
|
fetch_needed = 1
|
||||||
if App.Template.count() > 0
|
if App.Collection.count( 'Template' ) > 0
|
||||||
fetch_needed = 0
|
fetch_needed = 0
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class App.TemplateUI extends App.Controller
|
||||||
@log 'loading....'
|
@log 'loading....'
|
||||||
@render()
|
@render()
|
||||||
App.Template.unbind 'refresh'
|
App.Template.unbind 'refresh'
|
||||||
App.Template.fetch()
|
App.Collection.fetch( 'Template' )
|
||||||
|
|
||||||
render: =>
|
render: =>
|
||||||
@configure_attributes = [
|
@configure_attributes = [
|
||||||
|
@ -32,7 +32,7 @@ class App.TemplateUI extends App.Controller
|
||||||
|
|
||||||
template = {}
|
template = {}
|
||||||
if @template_id
|
if @template_id
|
||||||
template = App.Template.find(@template_id)
|
template = App.Collection.find( 'Template', @template_id )
|
||||||
|
|
||||||
# insert data
|
# insert data
|
||||||
@html App.view('template')(
|
@html App.view('template')(
|
||||||
|
@ -49,7 +49,7 @@ class App.TemplateUI extends App.Controller
|
||||||
|
|
||||||
# get params
|
# get params
|
||||||
params = @formParam(e.target)
|
params = @formParam(e.target)
|
||||||
template = App.Template.find( params['template_id'] )
|
template = App.Collection.find( 'Template', params['template_id'] )
|
||||||
if confirm('Sure?')
|
if confirm('Sure?')
|
||||||
template.destroy()
|
template.destroy()
|
||||||
@template_id = undefined
|
@template_id = undefined
|
||||||
|
@ -61,7 +61,7 @@ class App.TemplateUI extends App.Controller
|
||||||
# get params
|
# get params
|
||||||
params = @formParam(e.target)
|
params = @formParam(e.target)
|
||||||
|
|
||||||
template = App.Template.find( params['template_id'] )
|
template = App.Collection.find( 'Template', params['template_id'] )
|
||||||
Spine.trigger 'ticket_create_rerender', template.attributes()
|
Spine.trigger 'ticket_create_rerender', template.attributes()
|
||||||
|
|
||||||
create: (e) =>
|
create: (e) =>
|
||||||
|
@ -72,7 +72,7 @@ class App.TemplateUI extends App.Controller
|
||||||
name = params['template_name']
|
name = params['template_name']
|
||||||
# delete params['template_name']
|
# delete params['template_name']
|
||||||
|
|
||||||
template = App.Template.findByAttribute( 'name', name )
|
template = App.Collection.findByAttribute( 'Template', 'name', name )
|
||||||
if !template
|
if !template
|
||||||
template = new App.Template
|
template = new App.Template
|
||||||
|
|
||||||
|
|
|
@ -6,29 +6,9 @@ class App.UserInfo extends App.Controller
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
App.Collection.find( 'User', @user_id, @render )
|
||||||
|
|
||||||
# fetch item on demand
|
render: (user) =>
|
||||||
fetch_needed = 1
|
|
||||||
if App.User.exists(@user_id)
|
|
||||||
@log 'exists.user...', @user_id
|
|
||||||
fetch_needed = 0
|
|
||||||
@render(@user_id)
|
|
||||||
|
|
||||||
if fetch_needed
|
|
||||||
@reload(@user_id)
|
|
||||||
|
|
||||||
reload: (user_id) =>
|
|
||||||
App.User.bind 'refresh', =>
|
|
||||||
@log 'loading.user...', user_id
|
|
||||||
App.User.unbind 'refresh'
|
|
||||||
@render(user_id)
|
|
||||||
App.User.fetch( id: user_id )
|
|
||||||
|
|
||||||
render: (user_id) ->
|
|
||||||
|
|
||||||
# load user collection
|
|
||||||
user = App.User.find(user_id)
|
|
||||||
@loadCollection( type: 'User', data: { new: user }, collection: true )
|
|
||||||
|
|
||||||
# get display data
|
# get display data
|
||||||
data = []
|
data = []
|
||||||
|
@ -39,20 +19,20 @@ class App.UserInfo extends App.Controller
|
||||||
|
|
||||||
# insert data
|
# insert data
|
||||||
@html App.view('user_info')(
|
@html App.view('user_info')(
|
||||||
user: App.User.find(user_id),
|
user: user,
|
||||||
data: data,
|
data: data,
|
||||||
)
|
)
|
||||||
|
|
||||||
@userTicketPopups(
|
@userTicketPopups(
|
||||||
selector: '.user-tickets',
|
selector: '.user-tickets',
|
||||||
user_id: user_id,
|
user_id: user.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
update: (e) =>
|
update: (e) =>
|
||||||
|
|
||||||
# update changes
|
# update changes
|
||||||
note = $(e.target).parent().find('[data-type=edit]').val()
|
note = $(e.target).parent().find('[data-type=edit]').val()
|
||||||
user = App.User.find(@user_id)
|
user = App.Collection.find( 'User', @user_id )
|
||||||
if user.note isnt note
|
if user.note isnt note
|
||||||
user.updateAttributes( note: note )
|
user.updateAttributes( note: note )
|
||||||
@log 'update', e, note, user
|
@log 'update', e, note, user
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#= require ./lib/bootstrap-tab.js
|
#= require ./lib/bootstrap-tab.js
|
||||||
#= require ./lib/bootstrap-transition.js
|
#= require ./lib/bootstrap-transition.js
|
||||||
|
|
||||||
#= require ./lib/underscore.coffee
|
#= require ./lib/underscore-1.3.3.js
|
||||||
#= require ./lib/ba-linkify.js
|
#= require ./lib/ba-linkify.js
|
||||||
#= require ./lib/jquery.tagsinput.js
|
#= require ./lib/jquery.tagsinput.js
|
||||||
#= require ./lib/jquery.noty.js
|
#= require ./lib/jquery.noty.js
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
#= require ./lib/auth.js.coffee
|
#= require ./lib/auth.js.coffee
|
||||||
#= require ./lib/i18n.js.coffee
|
#= require ./lib/i18n.js.coffee
|
||||||
#= require ./lib/store.js.coffee
|
#= require ./lib/store.js.coffee
|
||||||
|
#= require ./lib/collection.js.coffee
|
||||||
#= require_tree ./models
|
#= require_tree ./models
|
||||||
#= require_tree ./controllers
|
#= require_tree ./controllers
|
||||||
#= require_tree ./views
|
#= require_tree ./views
|
||||||
|
|
|
@ -65,9 +65,8 @@ class App.Auth
|
||||||
App.WebSocket.auth()
|
App.WebSocket.auth()
|
||||||
|
|
||||||
# refresh/load default collections
|
# refresh/load default collections
|
||||||
controller = new App.Controller
|
|
||||||
for key, value of data.default_collections
|
for key, value of data.default_collections
|
||||||
controller.loadCollection( type: key, data: value )
|
App.Collection.load( type: key, data: value )
|
||||||
|
|
||||||
# rebuild navbar with new navbar items
|
# rebuild navbar with new navbar items
|
||||||
Spine.trigger 'navrebuild', data.session
|
Spine.trigger 'navrebuild', data.session
|
||||||
|
|
222
app/assets/javascripts/app/lib/collection.js.coffee
Normal file
222
app/assets/javascripts/app/lib/collection.js.coffee
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
class App.Collection
|
||||||
|
_instance = undefined
|
||||||
|
|
||||||
|
@init: ->
|
||||||
|
_instance = new _Singleton
|
||||||
|
|
||||||
|
@load: ( args ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.load( args )
|
||||||
|
|
||||||
|
@find: ( type, id, callback ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.find( type, id, callback )
|
||||||
|
|
||||||
|
@get: ( args ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.get( args )
|
||||||
|
|
||||||
|
@all: ( type ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.all( type )
|
||||||
|
|
||||||
|
@deleteAll: ( type ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.deleteAll( type )
|
||||||
|
|
||||||
|
@findByAttribute: ( type, key, value ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.findByAttribute( type, key, value )
|
||||||
|
|
||||||
|
@count: ( type ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.count( type )
|
||||||
|
|
||||||
|
@fetch: ( type ) ->
|
||||||
|
if _instance == undefined
|
||||||
|
_instance ?= new _Singleton
|
||||||
|
_instance.fetch( type )
|
||||||
|
|
||||||
|
class _Singleton
|
||||||
|
|
||||||
|
constructor: (@args) ->
|
||||||
|
|
||||||
|
# add trigger - bind new events
|
||||||
|
Spine.bind 'loadCollection', (data) =>
|
||||||
|
|
||||||
|
# load collections
|
||||||
|
if data.collections
|
||||||
|
for type of data.collections
|
||||||
|
|
||||||
|
console.log 'loadCollection:trigger', type, data.collections[type]
|
||||||
|
@load( localStorage: data.localStorage, type: type, data: data.collections[type] )
|
||||||
|
|
||||||
|
# find collections to load
|
||||||
|
@_loadCollectionAll()
|
||||||
|
|
||||||
|
_loadCollectionAll: ->
|
||||||
|
list = App.Store.list()
|
||||||
|
for key in list
|
||||||
|
parts = key.split('::')
|
||||||
|
if parts[0] is 'collection'
|
||||||
|
data = App.Store.get( key )
|
||||||
|
if data && data.localStorage
|
||||||
|
console.log('load INIT', data)
|
||||||
|
@load( data )
|
||||||
|
|
||||||
|
load: (params) ->
|
||||||
|
console.log( 'load', params )
|
||||||
|
|
||||||
|
return if _.isEmpty( params.data )
|
||||||
|
|
||||||
|
if _.isArray( params.data )
|
||||||
|
for object in params.data
|
||||||
|
console.log( 'load ARRAY', object)
|
||||||
|
App[params.type].refresh( object, options: { clear: true } )
|
||||||
|
|
||||||
|
# remember in store if not already requested from local storage
|
||||||
|
if !params.localStorage
|
||||||
|
App.Store.write( 'collection::' + params.type + '::' + object.id, { type: params.type, localStorage: true, data: [ object ] } )
|
||||||
|
return
|
||||||
|
|
||||||
|
# if _.isObject( params.data )
|
||||||
|
for key, object of params.data
|
||||||
|
console.log( 'load OB', object)
|
||||||
|
App[params.type].refresh( object, options: { clear: true } )
|
||||||
|
|
||||||
|
# remember in store if not already requested from local storage
|
||||||
|
if !params.localStorage
|
||||||
|
App.Store.write( 'collection::' + params.type + '::' + object.id, { type: params.type, localStorage: true, data: [ object ] } )
|
||||||
|
|
||||||
|
find: ( type, id, callback ) ->
|
||||||
|
|
||||||
|
console.log( 'find', type, id )
|
||||||
|
# if App[type].exists( id ) && !callback
|
||||||
|
if App[type].exists( id )
|
||||||
|
console.log( 'find exists', type, id )
|
||||||
|
data = App[type].find( id )
|
||||||
|
if callback
|
||||||
|
callback( data )
|
||||||
|
else
|
||||||
|
console.log( 'find not loaded!', type, id )
|
||||||
|
if callback
|
||||||
|
App[type].bind 'refresh', ->
|
||||||
|
console.log 'loaded..' + type + '..', id
|
||||||
|
App[type].unbind 'refresh'
|
||||||
|
data = App.Collection.find( type, id )
|
||||||
|
callback( data )
|
||||||
|
console.log 'loading..' + type + '..', id
|
||||||
|
App[type].fetch( id: id )
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
# users
|
||||||
|
if type == 'User'
|
||||||
|
|
||||||
|
# set socal media links
|
||||||
|
if data['accounts']
|
||||||
|
for account of data['accounts']
|
||||||
|
if account == 'twitter'
|
||||||
|
data['accounts'][account]['link'] = 'http://twitter.com/' + data['accounts'][account]['username']
|
||||||
|
if account == 'facebook'
|
||||||
|
data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
|
||||||
|
|
||||||
|
# set image url
|
||||||
|
if data && !data['image']
|
||||||
|
data['image'] = 'http://placehold.it/48x48'
|
||||||
|
|
||||||
|
# set realname
|
||||||
|
data['realname'] = ''
|
||||||
|
if data['firstname']
|
||||||
|
data['realname'] = data['firstname']
|
||||||
|
if data['lastname']
|
||||||
|
if data['realname'] isnt ''
|
||||||
|
data['realname'] = data['realname'] + ' '
|
||||||
|
data['realname'] = data['realname'] + data['lastname']
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
# tickets
|
||||||
|
else if type == 'Ticket'
|
||||||
|
|
||||||
|
# priority
|
||||||
|
data.ticket_priority = @find( 'TicketPriority', data.ticket_priority_id )
|
||||||
|
|
||||||
|
# state
|
||||||
|
data.ticket_state = @find( 'TicketState', data.ticket_state_id )
|
||||||
|
|
||||||
|
# group
|
||||||
|
data.group = @find( 'Group', data.group_id )
|
||||||
|
|
||||||
|
# customer
|
||||||
|
if data.customer_id
|
||||||
|
data.customer = @find( 'User', data.customer_id )
|
||||||
|
|
||||||
|
# owner
|
||||||
|
if data.owner_id
|
||||||
|
data.owner = @find( 'User', data.owner_id )
|
||||||
|
|
||||||
|
# add created & updated
|
||||||
|
if data.created_by_id
|
||||||
|
data.created_by = @find( 'User', data.created_by_id )
|
||||||
|
if data.updated_by_id
|
||||||
|
data.updated_by = @find( 'User', data.updated_by_id )
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
# articles
|
||||||
|
else if type == 'TicketArticle'
|
||||||
|
|
||||||
|
# add created & updated
|
||||||
|
data.created_by = @find( 'User', data.created_by_id )
|
||||||
|
|
||||||
|
# add possible actions
|
||||||
|
data.article_type = @find( 'TicketArticleType', data.ticket_article_type_id )
|
||||||
|
data.article_sender = @find( 'TicketArticleSender', data.ticket_article_sender_id )
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
# history
|
||||||
|
else if type == 'History'
|
||||||
|
|
||||||
|
# add user
|
||||||
|
data.created_by = @find( 'User', data.created_by_id )
|
||||||
|
|
||||||
|
# add possible actions
|
||||||
|
if data.history_attribute_id
|
||||||
|
data.attribute = @find( 'HistoryAttribute', data.history_attribute_id )
|
||||||
|
if data.history_type_id
|
||||||
|
data.type = @find( 'HistoryType', data.history_type_id )
|
||||||
|
if data.history_object_id
|
||||||
|
data.object = @find( 'HistoryObject', data.history_object_id )
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
else
|
||||||
|
return data
|
||||||
|
|
||||||
|
get: (params) ->
|
||||||
|
console.log('get')
|
||||||
|
App[params.type].refresh( object, options: { clear: true } )
|
||||||
|
|
||||||
|
all: (type) ->
|
||||||
|
App[type].all()
|
||||||
|
|
||||||
|
deleteAll: (type) ->
|
||||||
|
App[type].deleteAll()
|
||||||
|
|
||||||
|
findByAttribute: ( type, key, value ) ->
|
||||||
|
App[type].findByAttribute( key, value )
|
||||||
|
|
||||||
|
count: ( type ) ->
|
||||||
|
App[type].count()
|
||||||
|
|
||||||
|
fetch: ( type ) ->
|
||||||
|
App[type].fetch()
|
|
@ -5,36 +5,26 @@ class App.Run extends App.Controller
|
||||||
@log 'RUN app'
|
@log 'RUN app'
|
||||||
@el = $('#app')
|
@el = $('#app')
|
||||||
|
|
||||||
|
# init collections
|
||||||
|
App.Collection.init()
|
||||||
|
|
||||||
# create web socket connection
|
# create web socket connection
|
||||||
App.WebSocket.connect()
|
App.WebSocket.connect()
|
||||||
|
|
||||||
# init of i18n
|
# init of i18n
|
||||||
new App.i18n
|
new App.i18n
|
||||||
|
|
||||||
# bind new events
|
|
||||||
Spine.bind 'loadCollection', (data) =>
|
|
||||||
|
|
||||||
# load collections
|
|
||||||
if data.collections
|
|
||||||
for key of data.collections
|
|
||||||
|
|
||||||
@log 'loadCollection', key, data.collections[key]
|
|
||||||
@loadCollection( localStorage: data.localStorage, type: key, data: data.collections[key] )
|
|
||||||
|
|
||||||
# load collections
|
|
||||||
App.Store.load()
|
|
||||||
|
|
||||||
# start navigation controller
|
# start navigation controller
|
||||||
new App.Navigation( el: @el.find('#navigation') );
|
new App.Navigation( el: @el.find('#navigation') )
|
||||||
|
|
||||||
# check if session already exists/try to get session data from server
|
# check if session already exists/try to get session data from server
|
||||||
App.Auth.loginCheck()
|
App.Auth.loginCheck()
|
||||||
|
|
||||||
# start notify controller
|
# start notify controller
|
||||||
new App.Notify( el: @el.find('#notify') );
|
new App.Notify( el: @el.find('#notify') )
|
||||||
|
|
||||||
# start content
|
# start content
|
||||||
new App.Content( el: @el.find('#content') );
|
new App.Content( el: @el.find('#content') )
|
||||||
|
|
||||||
# bind to fill selected text into
|
# bind to fill selected text into
|
||||||
$(@el).bind('mouseup', =>
|
$(@el).bind('mouseup', =>
|
||||||
|
|
|
@ -3,10 +3,6 @@ class App.Store
|
||||||
@renew: ->
|
@renew: ->
|
||||||
_instance = new _Singleton
|
_instance = new _Singleton
|
||||||
|
|
||||||
@load: ->
|
|
||||||
if _instance == undefined
|
|
||||||
_instance ?= new _Singleton
|
|
||||||
|
|
||||||
@write: (key, value) ->
|
@write: (key, value) ->
|
||||||
if _instance == undefined
|
if _instance == undefined
|
||||||
_instance ?= new _Singleton
|
_instance ?= new _Singleton
|
||||||
|
@ -22,10 +18,10 @@ class App.Store
|
||||||
_instance ?= new _Singleton
|
_instance ?= new _Singleton
|
||||||
_instance.delete(args)
|
_instance.delete(args)
|
||||||
|
|
||||||
@clear: (args) ->
|
@clear: ->
|
||||||
if _instance == undefined
|
if _instance == undefined
|
||||||
_instance ?= new _Singleton
|
_instance ?= new _Singleton
|
||||||
_instance.clear(args)
|
_instance.clear()
|
||||||
|
|
||||||
@list: () ->
|
@list: () ->
|
||||||
if _instance == undefined
|
if _instance == undefined
|
||||||
|
@ -34,94 +30,32 @@ class App.Store
|
||||||
|
|
||||||
# The actual Singleton class
|
# The actual Singleton class
|
||||||
class _Singleton
|
class _Singleton
|
||||||
store: {}
|
|
||||||
|
|
||||||
constructor: (@args) ->
|
# write to local storage
|
||||||
|
write: (key, value) ->
|
||||||
|
localStorage.setItem( key, JSON.stringify( value ) )
|
||||||
|
|
||||||
# find collections to load
|
# get item
|
||||||
@_loadCollectionAll()
|
get: (key) ->
|
||||||
@_loadCollectionType('TicketPriority')
|
value = localStorage.getItem( key )
|
||||||
@_loadCollectionType('TicketStateType')
|
return if !value
|
||||||
@_loadCollectionType('TicketState')
|
object = JSON.parse( value )
|
||||||
@_loadCollectionType('TicketArticleSender')
|
return object
|
||||||
@_loadCollectionType('TicketArticleType')
|
|
||||||
@_loadCollectionType('Group')
|
|
||||||
@_loadCollectionType('Role')
|
|
||||||
@_loadCollectionType('Organization')
|
|
||||||
@_loadCollectionType('User')
|
|
||||||
@_loadCollectionType()
|
|
||||||
|
|
||||||
_loadCollectionAll: ->
|
# delete item
|
||||||
@all = {}
|
delete: (key) ->
|
||||||
@rest = {}
|
localStorage.removeItem( key )
|
||||||
|
|
||||||
|
# clear local storage
|
||||||
|
clear: ->
|
||||||
|
localStorage.clear()
|
||||||
|
|
||||||
|
# return list of all keys
|
||||||
|
list: ->
|
||||||
|
list = []
|
||||||
logLength = localStorage.length-1;
|
logLength = localStorage.length-1;
|
||||||
for count in [0..logLength]
|
for count in [0..logLength]
|
||||||
key = localStorage.key( count )
|
key = localStorage.key( count )
|
||||||
if key
|
if key
|
||||||
value = localStorage.getItem( key )
|
|
||||||
data = JSON.parse( value )
|
|
||||||
@all[key] = data
|
|
||||||
|
|
||||||
_loadCollectionType: (type) ->
|
|
||||||
# console.log('STORE NEW' + logLength)
|
|
||||||
toGo = @all
|
|
||||||
if !_.isEmpty( @rest )
|
|
||||||
toGo = _.clone( @rest )
|
|
||||||
@rest = {}
|
|
||||||
for key, data of toGo
|
|
||||||
# console.log('STORE NEW' + count + '-' + key, data)
|
|
||||||
if data['collections']
|
|
||||||
data['localStorage'] = true
|
|
||||||
|
|
||||||
if type
|
|
||||||
if data['type'] is type
|
|
||||||
@_loadCollection(data)
|
|
||||||
else
|
|
||||||
@rest[key] = data
|
|
||||||
else
|
|
||||||
@_loadCollection(data)
|
|
||||||
|
|
||||||
_loadCollection: (data) ->
|
|
||||||
console.log('fire', 'loadCollection', data )
|
|
||||||
Spine.trigger( 'loadCollection', data )
|
|
||||||
|
|
||||||
write: (key, value) ->
|
|
||||||
|
|
||||||
# write to instance
|
|
||||||
@store[ key ] = value
|
|
||||||
|
|
||||||
# write to local storage
|
|
||||||
localStorage.setItem( key, JSON.stringify( value ) )
|
|
||||||
|
|
||||||
get: (key) ->
|
|
||||||
|
|
||||||
# return from instance
|
|
||||||
return @store[ key ] if @store[ key ]
|
|
||||||
|
|
||||||
# if not, return from local storage
|
|
||||||
value = localStorage.getItem( key )
|
|
||||||
object = JSON.parse( value )
|
|
||||||
return object if object
|
|
||||||
|
|
||||||
# return undefined if not in storage
|
|
||||||
return undefined
|
|
||||||
|
|
||||||
delete: (key) ->
|
|
||||||
delete @store[ key ]
|
|
||||||
|
|
||||||
clear: (action) ->
|
|
||||||
|
|
||||||
console.log 'Store:clear', action
|
|
||||||
|
|
||||||
# clear instance data
|
|
||||||
@store = {}
|
|
||||||
|
|
||||||
# clear local storage
|
|
||||||
if action is 'all'
|
|
||||||
localStorage.clear()
|
|
||||||
|
|
||||||
list: () ->
|
|
||||||
list = []
|
|
||||||
for key of @store
|
|
||||||
list.push key
|
list.push key
|
||||||
list
|
list
|
1059
app/assets/javascripts/app/lib/underscore-1.3.3.js
Normal file
1059
app/assets/javascripts/app/lib/underscore-1.3.3.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,682 +0,0 @@
|
||||||
# **Underscore.coffee
|
|
||||||
# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.**
|
|
||||||
# Underscore is freely distributable under the terms of the
|
|
||||||
# [MIT license](http://en.wikipedia.org/wiki/MIT_License).
|
|
||||||
# Portions of Underscore are inspired by or borrowed from
|
|
||||||
# [Prototype.js](http://prototypejs.org/api), Oliver Steele's
|
|
||||||
# [Functional](http://osteele.com), and John Resig's
|
|
||||||
# [Micro-Templating](http://ejohn.org).
|
|
||||||
# For all details and documentation:
|
|
||||||
# http://documentcloud.github.com/underscore/
|
|
||||||
|
|
||||||
|
|
||||||
# Baseline setup
|
|
||||||
# --------------
|
|
||||||
|
|
||||||
# Establish the root object, `window` in the browser, or `global` on the server.
|
|
||||||
root = this
|
|
||||||
|
|
||||||
|
|
||||||
# Save the previous value of the `_` variable.
|
|
||||||
previousUnderscore = root._
|
|
||||||
|
|
||||||
|
|
||||||
# Establish the object that gets thrown to break out of a loop iteration.
|
|
||||||
# `StopIteration` is SOP on Mozilla.
|
|
||||||
breaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
|
|
||||||
|
|
||||||
|
|
||||||
# Helper function to escape **RegExp** contents, because JS doesn't have one.
|
|
||||||
escapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
|
|
||||||
|
|
||||||
|
|
||||||
# Save bytes in the minified (but not gzipped) version:
|
|
||||||
ArrayProto = Array.prototype
|
|
||||||
ObjProto = Object.prototype
|
|
||||||
|
|
||||||
|
|
||||||
# Create quick reference variables for speed access to core prototypes.
|
|
||||||
slice = ArrayProto.slice
|
|
||||||
unshift = ArrayProto.unshift
|
|
||||||
toString = ObjProto.toString
|
|
||||||
hasOwnProperty = ObjProto.hasOwnProperty
|
|
||||||
propertyIsEnumerable = ObjProto.propertyIsEnumerable
|
|
||||||
|
|
||||||
|
|
||||||
# All **ECMA5** native implementations we hope to use are declared here.
|
|
||||||
nativeForEach = ArrayProto.forEach
|
|
||||||
nativeMap = ArrayProto.map
|
|
||||||
nativeReduce = ArrayProto.reduce
|
|
||||||
nativeReduceRight = ArrayProto.reduceRight
|
|
||||||
nativeFilter = ArrayProto.filter
|
|
||||||
nativeEvery = ArrayProto.every
|
|
||||||
nativeSome = ArrayProto.some
|
|
||||||
nativeIndexOf = ArrayProto.indexOf
|
|
||||||
nativeLastIndexOf = ArrayProto.lastIndexOf
|
|
||||||
nativeIsArray = Array.isArray
|
|
||||||
nativeKeys = Object.keys
|
|
||||||
|
|
||||||
|
|
||||||
# Create a safe reference to the Underscore object for use below.
|
|
||||||
_ = (obj) -> new wrapper(obj)
|
|
||||||
|
|
||||||
|
|
||||||
# Export the Underscore object for **CommonJS**.
|
|
||||||
if typeof(exports) != 'undefined' then exports._ = _
|
|
||||||
|
|
||||||
|
|
||||||
# Export Underscore to global scope.
|
|
||||||
root._ = _
|
|
||||||
|
|
||||||
|
|
||||||
# Current version.
|
|
||||||
_.VERSION = '1.1.0'
|
|
||||||
|
|
||||||
|
|
||||||
# Collection Functions
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# The cornerstone, an **each** implementation.
|
|
||||||
# Handles objects implementing **forEach**, arrays, and raw objects.
|
|
||||||
_.each = (obj, iterator, context) ->
|
|
||||||
try
|
|
||||||
if nativeForEach and obj.forEach is nativeForEach
|
|
||||||
obj.forEach iterator, context
|
|
||||||
else if _.isNumber obj.length
|
|
||||||
iterator.call context, obj[i], i, obj for i in [0...obj.length]
|
|
||||||
else
|
|
||||||
iterator.call context, val, key, obj for own key, val of obj
|
|
||||||
catch e
|
|
||||||
throw e if e isnt breaker
|
|
||||||
obj
|
|
||||||
|
|
||||||
|
|
||||||
# Return the results of applying the iterator to each element. Use JavaScript
|
|
||||||
# 1.6's version of **map**, if possible.
|
|
||||||
_.map = (obj, iterator, context) ->
|
|
||||||
return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
|
|
||||||
results = []
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
results.push iterator.call context, value, index, list
|
|
||||||
results
|
|
||||||
|
|
||||||
|
|
||||||
# **Reduce** builds up a single result from a list of values. Also known as
|
|
||||||
# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.
|
|
||||||
_.reduce = (obj, iterator, memo, context) ->
|
|
||||||
if nativeReduce and obj.reduce is nativeReduce
|
|
||||||
iterator = _.bind iterator, context if context
|
|
||||||
return obj.reduce iterator, memo
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
memo = iterator.call context, memo, value, index, list
|
|
||||||
memo
|
|
||||||
|
|
||||||
|
|
||||||
# The right-associative version of **reduce**, also known as **foldr**. Uses
|
|
||||||
# JavaScript 1.8's version of **reduceRight**, if available.
|
|
||||||
_.reduceRight = (obj, iterator, memo, context) ->
|
|
||||||
if nativeReduceRight and obj.reduceRight is nativeReduceRight
|
|
||||||
iterator = _.bind iterator, context if context
|
|
||||||
return obj.reduceRight iterator, memo
|
|
||||||
reversed = _.clone(_.toArray(obj)).reverse()
|
|
||||||
_.reduce reversed, iterator, memo, context
|
|
||||||
|
|
||||||
|
|
||||||
# Return the first value which passes a truth test.
|
|
||||||
_.detect = (obj, iterator, context) ->
|
|
||||||
result = null
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
if iterator.call context, value, index, list
|
|
||||||
result = value
|
|
||||||
_.breakLoop()
|
|
||||||
result
|
|
||||||
|
|
||||||
|
|
||||||
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
|
||||||
# **filter**, if it exists.
|
|
||||||
_.filter = (obj, iterator, context) ->
|
|
||||||
return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
|
|
||||||
results = []
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
results.push value if iterator.call context, value, index, list
|
|
||||||
results
|
|
||||||
|
|
||||||
|
|
||||||
# Return all the elements for which a truth test fails.
|
|
||||||
_.reject = (obj, iterator, context) ->
|
|
||||||
results = []
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
results.push value if not iterator.call context, value, index, list
|
|
||||||
results
|
|
||||||
|
|
||||||
|
|
||||||
# Determine whether all of the elements match a truth test. Delegate to
|
|
||||||
# JavaScript 1.6's **every**, if it is present.
|
|
||||||
_.every = (obj, iterator, context) ->
|
|
||||||
iterator ||= _.identity
|
|
||||||
return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
|
|
||||||
result = true
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
_.breakLoop() unless (result = result and iterator.call(context, value, index, list))
|
|
||||||
result
|
|
||||||
|
|
||||||
|
|
||||||
# Determine if at least one element in the object matches a truth test. Use
|
|
||||||
# JavaScript 1.6's **some**, if it exists.
|
|
||||||
_.some = (obj, iterator, context) ->
|
|
||||||
iterator ||= _.identity
|
|
||||||
return obj.some iterator, context if nativeSome and obj.some is nativeSome
|
|
||||||
result = false
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
_.breakLoop() if (result = iterator.call(context, value, index, list))
|
|
||||||
result
|
|
||||||
|
|
||||||
|
|
||||||
# Determine if a given value is included in the array or object,
|
|
||||||
# based on `===`.
|
|
||||||
_.include = (obj, target) ->
|
|
||||||
return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
|
|
||||||
return true for own key, val of obj when val is target
|
|
||||||
false
|
|
||||||
|
|
||||||
|
|
||||||
# Invoke a method with arguments on every item in a collection.
|
|
||||||
_.invoke = (obj, method) ->
|
|
||||||
args = _.rest arguments, 2
|
|
||||||
(if method then val[method] else val).apply(val, args) for val in obj
|
|
||||||
|
|
||||||
|
|
||||||
# Convenience version of a common use case of **map**: fetching a property.
|
|
||||||
_.pluck = (obj, key) ->
|
|
||||||
_.map(obj, (val) -> val[key])
|
|
||||||
|
|
||||||
|
|
||||||
# Return the maximum item or (item-based computation).
|
|
||||||
_.max = (obj, iterator, context) ->
|
|
||||||
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
|
|
||||||
result = computed: -Infinity
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
computed = if iterator then iterator.call(context, value, index, list) else value
|
|
||||||
computed >= result.computed and (result = {value: value, computed: computed})
|
|
||||||
result.value
|
|
||||||
|
|
||||||
|
|
||||||
# Return the minimum element (or element-based computation).
|
|
||||||
_.min = (obj, iterator, context) ->
|
|
||||||
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
|
|
||||||
result = computed: Infinity
|
|
||||||
_.each obj, (value, index, list) ->
|
|
||||||
computed = if iterator then iterator.call(context, value, index, list) else value
|
|
||||||
computed < result.computed and (result = {value: value, computed: computed})
|
|
||||||
result.value
|
|
||||||
|
|
||||||
|
|
||||||
# Sort the object's values by a criterion produced by an iterator.
|
|
||||||
_.sortBy = (obj, iterator, context) ->
|
|
||||||
_.pluck(((_.map obj, (value, index, list) ->
|
|
||||||
{value: value, criteria: iterator.call(context, value, index, list)}
|
|
||||||
).sort((left, right) ->
|
|
||||||
a = left.criteria; b = right.criteria
|
|
||||||
if a < b then -1 else if a > b then 1 else 0
|
|
||||||
)), 'value')
|
|
||||||
|
|
||||||
|
|
||||||
# Use a comparator function to figure out at what index an object should
|
|
||||||
# be inserted so as to maintain order. Uses binary search.
|
|
||||||
_.sortedIndex = (array, obj, iterator) ->
|
|
||||||
iterator ||= _.identity
|
|
||||||
low = 0
|
|
||||||
high = array.length
|
|
||||||
while low < high
|
|
||||||
mid = (low + high) >> 1
|
|
||||||
if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid
|
|
||||||
low
|
|
||||||
|
|
||||||
|
|
||||||
# Convert anything iterable into a real, live array.
|
|
||||||
_.toArray = (iterable) ->
|
|
||||||
return [] if (!iterable)
|
|
||||||
return iterable.toArray() if (iterable.toArray)
|
|
||||||
return iterable if (_.isArray(iterable))
|
|
||||||
return slice.call(iterable) if (_.isArguments(iterable))
|
|
||||||
_.values(iterable)
|
|
||||||
|
|
||||||
|
|
||||||
# Return the number of elements in an object.
|
|
||||||
_.size = (obj) -> _.toArray(obj).length
|
|
||||||
|
|
||||||
|
|
||||||
# Array Functions
|
|
||||||
# ---------------
|
|
||||||
|
|
||||||
# Get the first element of an array. Passing `n` will return the first N
|
|
||||||
# values in the array. Aliased as **head**. The `guard` check allows it to work
|
|
||||||
# with **map**.
|
|
||||||
_.first = (array, n, guard) ->
|
|
||||||
if n and not guard then slice.call(array, 0, n) else array[0]
|
|
||||||
|
|
||||||
|
|
||||||
# Returns everything but the first entry of the array. Aliased as **tail**.
|
|
||||||
# Especially useful on the arguments object. Passing an `index` will return
|
|
||||||
# the rest of the values in the array from that index onward. The `guard`
|
|
||||||
# check allows it to work with **map**.
|
|
||||||
_.rest = (array, index, guard) ->
|
|
||||||
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
|
||||||
|
|
||||||
|
|
||||||
# Get the last element of an array.
|
|
||||||
_.last = (array) -> array[array.length - 1]
|
|
||||||
|
|
||||||
|
|
||||||
# Trim out all falsy values from an array.
|
|
||||||
_.compact = (array) -> item for item in array when item
|
|
||||||
|
|
||||||
|
|
||||||
# Return a completely flattened version of an array.
|
|
||||||
_.flatten = (array) ->
|
|
||||||
_.reduce array, (memo, value) ->
|
|
||||||
return memo.concat(_.flatten(value)) if _.isArray value
|
|
||||||
memo.push value
|
|
||||||
memo
|
|
||||||
, []
|
|
||||||
|
|
||||||
|
|
||||||
# Return a version of the array that does not contain the specified value(s).
|
|
||||||
_.without = (array) ->
|
|
||||||
values = _.rest arguments
|
|
||||||
val for val in _.toArray(array) when not _.include values, val
|
|
||||||
|
|
||||||
|
|
||||||
# Produce a duplicate-free version of the array. If the array has already
|
|
||||||
# been sorted, you have the option of using a faster algorithm.
|
|
||||||
_.uniq = (array, isSorted) ->
|
|
||||||
memo = []
|
|
||||||
for el, i in _.toArray array
|
|
||||||
memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
|
|
||||||
memo
|
|
||||||
|
|
||||||
|
|
||||||
# Produce an array that contains every item shared between all the
|
|
||||||
# passed-in arrays.
|
|
||||||
_.intersect = (array) ->
|
|
||||||
rest = _.rest arguments
|
|
||||||
_.select _.uniq(array), (item) ->
|
|
||||||
_.all rest, (other) ->
|
|
||||||
_.indexOf(other, item) >= 0
|
|
||||||
|
|
||||||
|
|
||||||
# Zip together multiple lists into a single array -- elements that share
|
|
||||||
# an index go together.
|
|
||||||
_.zip = ->
|
|
||||||
length = _.max _.pluck arguments, 'length'
|
|
||||||
results = new Array length
|
|
||||||
for i in [0...length]
|
|
||||||
results[i] = _.pluck arguments, String i
|
|
||||||
results
|
|
||||||
|
|
||||||
|
|
||||||
# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),
|
|
||||||
# we need this function. Return the position of the first occurrence of an
|
|
||||||
# item in an array, or -1 if the item is not included in the array.
|
|
||||||
_.indexOf = (array, item) ->
|
|
||||||
return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
|
|
||||||
i = 0; l = array.length
|
|
||||||
while l - i
|
|
||||||
if array[i] is item then return i else i++
|
|
||||||
-1
|
|
||||||
|
|
||||||
|
|
||||||
# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,
|
|
||||||
# if possible.
|
|
||||||
_.lastIndexOf = (array, item) ->
|
|
||||||
return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
|
|
||||||
i = array.length
|
|
||||||
while i
|
|
||||||
if array[i] is item then return i else i--
|
|
||||||
-1
|
|
||||||
|
|
||||||
|
|
||||||
# Generate an integer Array containing an arithmetic progression. A port of
|
|
||||||
# [the native Python **range** function](http://docs.python.org/library/functions.html#range).
|
|
||||||
_.range = (start, stop, step) ->
|
|
||||||
a = arguments
|
|
||||||
solo = a.length <= 1
|
|
||||||
i = start = if solo then 0 else a[0]
|
|
||||||
stop = if solo then a[0] else a[1]
|
|
||||||
step = a[2] or 1
|
|
||||||
len = Math.ceil((stop - start) / step)
|
|
||||||
return [] if len <= 0
|
|
||||||
range = new Array len
|
|
||||||
idx = 0
|
|
||||||
loop
|
|
||||||
return range if (if step > 0 then i - stop else stop - i) >= 0
|
|
||||||
range[idx] = i
|
|
||||||
idx++
|
|
||||||
i+= step
|
|
||||||
|
|
||||||
|
|
||||||
# Function Functions
|
|
||||||
# ------------------
|
|
||||||
|
|
||||||
# Create a function bound to a given object (assigning `this`, and arguments,
|
|
||||||
# optionally). Binding with arguments is also known as **curry**.
|
|
||||||
_.bind = (func, obj) ->
|
|
||||||
args = _.rest arguments, 2
|
|
||||||
-> func.apply obj or root, args.concat arguments
|
|
||||||
|
|
||||||
|
|
||||||
# Bind all of an object's methods to that object. Useful for ensuring that
|
|
||||||
# all callbacks defined on an object belong to it.
|
|
||||||
_.bindAll = (obj) ->
|
|
||||||
funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
|
||||||
_.each funcs, (f) -> obj[f] = _.bind obj[f], obj
|
|
||||||
obj
|
|
||||||
|
|
||||||
|
|
||||||
# Delays a function for the given number of milliseconds, and then calls
|
|
||||||
# it with the arguments supplied.
|
|
||||||
_.delay = (func, wait) ->
|
|
||||||
args = _.rest arguments, 2
|
|
||||||
setTimeout((-> func.apply(func, args)), wait)
|
|
||||||
|
|
||||||
|
|
||||||
# Memoize an expensive function by storing its results.
|
|
||||||
_.memoize = (func, hasher) ->
|
|
||||||
memo = {}
|
|
||||||
hasher or= _.identity
|
|
||||||
->
|
|
||||||
key = hasher.apply this, arguments
|
|
||||||
return memo[key] if key of memo
|
|
||||||
memo[key] = func.apply this, arguments
|
|
||||||
|
|
||||||
|
|
||||||
# Defers a function, scheduling it to run after the current call stack has
|
|
||||||
# cleared.
|
|
||||||
_.defer = (func) ->
|
|
||||||
_.delay.apply _, [func, 1].concat _.rest arguments
|
|
||||||
|
|
||||||
|
|
||||||
# Returns the first function passed as an argument to the second,
|
|
||||||
# allowing you to adjust arguments, run code before and after, and
|
|
||||||
# conditionally execute the original function.
|
|
||||||
_.wrap = (func, wrapper) ->
|
|
||||||
-> wrapper.apply wrapper, [func].concat arguments
|
|
||||||
|
|
||||||
|
|
||||||
# Returns a function that is the composition of a list of functions, each
|
|
||||||
# consuming the return value of the function that follows.
|
|
||||||
_.compose = ->
|
|
||||||
funcs = arguments
|
|
||||||
->
|
|
||||||
args = arguments
|
|
||||||
for i in [funcs.length - 1..0] by -1
|
|
||||||
args = [funcs[i].apply(this, args)]
|
|
||||||
args[0]
|
|
||||||
|
|
||||||
|
|
||||||
# Object Functions
|
|
||||||
# ----------------
|
|
||||||
|
|
||||||
# Retrieve the names of an object's properties.
|
|
||||||
_.keys = nativeKeys or (obj) ->
|
|
||||||
return _.range 0, obj.length if _.isArray(obj)
|
|
||||||
key for key, val of obj
|
|
||||||
|
|
||||||
|
|
||||||
# Retrieve the values of an object's properties.
|
|
||||||
_.values = (obj) ->
|
|
||||||
_.map obj, _.identity
|
|
||||||
|
|
||||||
|
|
||||||
# Return a sorted list of the function names available in Underscore.
|
|
||||||
_.functions = (obj) ->
|
|
||||||
_.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
|
|
||||||
|
|
||||||
|
|
||||||
# Extend a given object with all of the properties in a source object.
|
|
||||||
_.extend = (obj) ->
|
|
||||||
for source in _.rest(arguments)
|
|
||||||
obj[key] = val for key, val of source
|
|
||||||
obj
|
|
||||||
|
|
||||||
|
|
||||||
# Create a (shallow-cloned) duplicate of an object.
|
|
||||||
_.clone = (obj) ->
|
|
||||||
return obj.slice 0 if _.isArray obj
|
|
||||||
_.extend {}, obj
|
|
||||||
|
|
||||||
|
|
||||||
# Invokes interceptor with the obj, and then returns obj.
|
|
||||||
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
|
||||||
_.tap = (obj, interceptor) ->
|
|
||||||
interceptor obj
|
|
||||||
obj
|
|
||||||
|
|
||||||
|
|
||||||
# Perform a deep comparison to check if two objects are equal.
|
|
||||||
_.isEqual = (a, b) ->
|
|
||||||
# Check object identity.
|
|
||||||
return true if a is b
|
|
||||||
# Different types?
|
|
||||||
atype = typeof(a); btype = typeof(b)
|
|
||||||
return false if atype isnt btype
|
|
||||||
# Basic equality test (watch out for coercions).
|
|
||||||
return true if `a == b`
|
|
||||||
# One is falsy and the other truthy.
|
|
||||||
return false if (!a and b) or (a and !b)
|
|
||||||
# One of them implements an `isEqual()`?
|
|
||||||
return a.isEqual(b) if a.isEqual
|
|
||||||
# Check dates' integer values.
|
|
||||||
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
|
|
||||||
# Both are NaN?
|
|
||||||
return false if _.isNaN(a) and _.isNaN(b)
|
|
||||||
# Compare regular expressions.
|
|
||||||
if _.isRegExp(a) and _.isRegExp(b)
|
|
||||||
return a.source is b.source and
|
|
||||||
a.global is b.global and
|
|
||||||
a.ignoreCase is b.ignoreCase and
|
|
||||||
a.multiline is b.multiline
|
|
||||||
# If a is not an object by this point, we can't handle it.
|
|
||||||
return false if atype isnt 'object'
|
|
||||||
# Check for different array lengths before comparing contents.
|
|
||||||
return false if a.length and (a.length isnt b.length)
|
|
||||||
# Nothing else worked, deep compare the contents.
|
|
||||||
aKeys = _.keys(a); bKeys = _.keys(b)
|
|
||||||
# Different object sizes?
|
|
||||||
return false if aKeys.length isnt bKeys.length
|
|
||||||
# Recursive comparison of contents.
|
|
||||||
return false for key, val of a when !(key of b) or !_.isEqual(val, b[key])
|
|
||||||
true
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given array or object empty?
|
|
||||||
_.isEmpty = (obj) ->
|
|
||||||
return obj.length is 0 if _.isArray(obj) or _.isString(obj)
|
|
||||||
return false for own key of obj
|
|
||||||
true
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value a DOM element?
|
|
||||||
_.isElement = (obj) -> obj and obj.nodeType is 1
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value an array?
|
|
||||||
_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given variable an arguments object?
|
|
||||||
_.isArguments = (obj) -> obj and obj.callee
|
|
||||||
|
|
||||||
|
|
||||||
# Is the given value a function?
|
|
||||||
_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
|
|
||||||
|
|
||||||
|
|
||||||
# Is the given value a string?
|
|
||||||
_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value a number?
|
|
||||||
_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value a boolean?
|
|
||||||
_.isBoolean = (obj) -> obj is true or obj is false
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value a Date?
|
|
||||||
_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
|
||||||
|
|
||||||
|
|
||||||
# Is the given value a regular expression?
|
|
||||||
_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
|
|
||||||
|
|
||||||
|
|
||||||
# Is the given value NaN -- this one is interesting. `NaN != NaN`, and
|
|
||||||
# `isNaN(undefined) == true`, so we make sure it's a number first.
|
|
||||||
_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj)
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given value equal to null?
|
|
||||||
_.isNull = (obj) -> obj is null
|
|
||||||
|
|
||||||
|
|
||||||
# Is a given variable undefined?
|
|
||||||
_.isUndefined = (obj) -> typeof obj is 'undefined'
|
|
||||||
|
|
||||||
|
|
||||||
# Utility Functions
|
|
||||||
# -----------------
|
|
||||||
|
|
||||||
# Run Underscore.js in noConflict mode, returning the `_` variable to its
|
|
||||||
# previous owner. Returns a reference to the Underscore object.
|
|
||||||
_.noConflict = ->
|
|
||||||
root._ = previousUnderscore
|
|
||||||
this
|
|
||||||
|
|
||||||
|
|
||||||
# Keep the identity function around for default iterators.
|
|
||||||
_.identity = (value) -> value
|
|
||||||
|
|
||||||
|
|
||||||
# Run a function `n` times.
|
|
||||||
_.times = (n, iterator, context) ->
|
|
||||||
iterator.call context, i for i in [0...n]
|
|
||||||
|
|
||||||
|
|
||||||
# Break out of the middle of an iteration.
|
|
||||||
_.breakLoop = -> throw breaker
|
|
||||||
|
|
||||||
|
|
||||||
# Add your own custom functions to the Underscore object, ensuring that
|
|
||||||
# they're correctly added to the OOP wrapper as well.
|
|
||||||
_.mixin = (obj) ->
|
|
||||||
for name in _.functions(obj)
|
|
||||||
addToWrapper name, _[name] = obj[name]
|
|
||||||
|
|
||||||
|
|
||||||
# Generate a unique integer id (unique within the entire client session).
|
|
||||||
# Useful for temporary DOM ids.
|
|
||||||
idCounter = 0
|
|
||||||
_.uniqueId = (prefix) ->
|
|
||||||
(prefix or '') + idCounter++
|
|
||||||
|
|
||||||
|
|
||||||
# By default, Underscore uses **ERB**-style template delimiters, change the
|
|
||||||
# following template settings to use alternative delimiters.
|
|
||||||
_.templateSettings = {
|
|
||||||
start: '<%'
|
|
||||||
end: '%>'
|
|
||||||
interpolate: /<%=(.+?)%>/g
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# JavaScript templating a-la **ERB**, pilfered from John Resig's
|
|
||||||
# *Secrets of the JavaScript Ninja*, page 83.
|
|
||||||
# Single-quote fix from Rick Strahl.
|
|
||||||
# With alterations for arbitrary delimiters, and to preserve whitespace.
|
|
||||||
_.template = (str, data) ->
|
|
||||||
c = _.templateSettings
|
|
||||||
endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
|
|
||||||
fn = new Function 'obj',
|
|
||||||
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
|
||||||
'with(obj||{}){p.push(\'' +
|
|
||||||
str.replace(/\r/g, '\\r')
|
|
||||||
.replace(/\n/g, '\\n')
|
|
||||||
.replace(/\t/g, '\\t')
|
|
||||||
.replace(endMatch,"✄")
|
|
||||||
.split("'").join("\\'")
|
|
||||||
.split("✄").join("'")
|
|
||||||
.replace(c.interpolate, "',$1,'")
|
|
||||||
.split(c.start).join("');")
|
|
||||||
.split(c.end).join("p.push('") +
|
|
||||||
"');}return p.join('');"
|
|
||||||
if data then fn(data) else fn
|
|
||||||
|
|
||||||
|
|
||||||
# Aliases
|
|
||||||
# -------
|
|
||||||
|
|
||||||
_.forEach = _.each
|
|
||||||
_.foldl = _.inject = _.reduce
|
|
||||||
_.foldr = _.reduceRight
|
|
||||||
_.select = _.filter
|
|
||||||
_.all = _.every
|
|
||||||
_.any = _.some
|
|
||||||
_.contains = _.include
|
|
||||||
_.head = _.first
|
|
||||||
_.tail = _.rest
|
|
||||||
_.methods = _.functions
|
|
||||||
|
|
||||||
|
|
||||||
# Setup the OOP Wrapper
|
|
||||||
# ---------------------
|
|
||||||
|
|
||||||
# If Underscore is called as a function, it returns a wrapped object that
|
|
||||||
# can be used OO-style. This wrapper holds altered versions of all the
|
|
||||||
# underscore functions. Wrapped objects may be chained.
|
|
||||||
wrapper = (obj) ->
|
|
||||||
this._wrapped = obj
|
|
||||||
this
|
|
||||||
|
|
||||||
|
|
||||||
# Helper function to continue chaining intermediate results.
|
|
||||||
result = (obj, chain) ->
|
|
||||||
if chain then _(obj).chain() else obj
|
|
||||||
|
|
||||||
|
|
||||||
# A method to easily add functions to the OOP wrapper.
|
|
||||||
addToWrapper = (name, func) ->
|
|
||||||
wrapper.prototype[name] = ->
|
|
||||||
args = _.toArray arguments
|
|
||||||
unshift.call args, this._wrapped
|
|
||||||
result func.apply(_, args), this._chain
|
|
||||||
|
|
||||||
|
|
||||||
# Add all ofthe Underscore functions to the wrapper object.
|
|
||||||
_.mixin _
|
|
||||||
|
|
||||||
|
|
||||||
# Add all mutator Array functions to the wrapper.
|
|
||||||
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
|
|
||||||
method = Array.prototype[name]
|
|
||||||
wrapper.prototype[name] = ->
|
|
||||||
method.apply(this._wrapped, arguments)
|
|
||||||
result(this._wrapped, this._chain)
|
|
||||||
|
|
||||||
|
|
||||||
# Add all accessor Array functions to the wrapper.
|
|
||||||
_.each ['concat', 'join', 'slice'], (name) ->
|
|
||||||
method = Array.prototype[name]
|
|
||||||
wrapper.prototype[name] = ->
|
|
||||||
result(method.apply(this._wrapped, arguments), this._chain)
|
|
||||||
|
|
||||||
|
|
||||||
# Start chaining a wrapped Underscore object.
|
|
||||||
wrapper::chain = ->
|
|
||||||
this._chain = true
|
|
||||||
this
|
|
||||||
|
|
||||||
|
|
||||||
# Extracts the result from a wrapped and chained object.
|
|
||||||
wrapper::value = -> this._wrapped
|
|
|
@ -21,7 +21,7 @@
|
||||||
<%= @ticket.group.name %> -
|
<%= @ticket.group.name %> -
|
||||||
<%- T(@ticket.ticket_state.name) %> -
|
<%- T(@ticket.ticket_state.name) %> -
|
||||||
<%- T(@ticket.ticket_priority.name) %> -
|
<%- T(@ticket.ticket_priority.name) %> -
|
||||||
<%- @ticket.humanTime %>
|
<span class="humanTimeFromNow" data-time="<%- @ticket.created_at %>">?</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
<a href="<%= action.href %>" data-type="<%= action.type %>" class="<% if action.class: %><%= action.class %><% end %>"><%= T(action.name) %></a>
|
<a href="<%= action.href %>" data-type="<%= action.type %>" class="<% if action.class: %><%= action.class %><% end %>"><%= T(action.name) %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
- <%- article.humanTime %> <%- T('ago') %>
|
- <span class="humanTimeFromNow" data-time="<%- article.created_at %>">?</span>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if article.to: %>
|
<% if article.to: %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% for ticket in @tickets: %>
|
<% for ticket in @tickets: %>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="customer-info"><a href="#ticket/zoom/<%= ticket.id %>" title="<%= ticket.title %>">T:<%= ticket.number %></a> <%= ticket.humanTime %><br/><%= ticket.title %></div>
|
<div class="customer-info"><a href="#ticket/zoom/<%= ticket.id %>" title="<%= ticket.title %>">T:<%= ticket.number %></a> <span title="<%= ticket.created_at_short %>"><%= ticket.humanTime %></span><br/><%= ticket.title %></div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
Loading…
Reference in a new issue