Split of _application_controller_form.js.coffee into single files for each ui element.
This commit is contained in:
parent
79666ba324
commit
971321b369
20 changed files with 1206 additions and 1150 deletions
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,221 @@
|
|||
class App.UiElement.ApplicationUiElement
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions: (attribute) ->
|
||||
|
||||
# skip sorting if it is disabled by config
|
||||
return if attribute.sortBy == null
|
||||
|
||||
return if !attribute.options
|
||||
|
||||
if _.isArray( attribute.options )
|
||||
# reverse if we have to exit early, if configured
|
||||
if attribute.order
|
||||
if attribute.order == 'DESC'
|
||||
attribute.options = attribute.options.reverse()
|
||||
return
|
||||
|
||||
options_by_name = []
|
||||
for i in attribute.options
|
||||
options_by_name.push i['name'].toString().toLowerCase()
|
||||
options_by_name = options_by_name.sort()
|
||||
|
||||
options_new = []
|
||||
options_new_used = {}
|
||||
for i in options_by_name
|
||||
for ii, vv in attribute.options
|
||||
if !options_new_used[ ii['value'] ] && i.toString().toLowerCase() is ii['name'].toString().toLowerCase()
|
||||
options_new_used[ ii['value'] ] = 1
|
||||
options_new.push ii
|
||||
attribute.options = options_new
|
||||
|
||||
# do a final reverse, if configured
|
||||
if attribute.order
|
||||
if attribute.order == 'DESC'
|
||||
attribute.options = attribute.options.reverse()
|
||||
|
||||
@addNullOption: (attribute) ->
|
||||
return if !attribute.options
|
||||
return if !attribute.nulloption
|
||||
if _.isArray( attribute.options )
|
||||
attribute.options.unshift( { name: '-', value: '' } )
|
||||
else
|
||||
attribute.options[''] = '-'
|
||||
|
||||
@getConfigOptionList: (attribute) ->
|
||||
return if !attribute.options
|
||||
selection = attribute.options
|
||||
attribute.options = []
|
||||
if _.isArray( selection )
|
||||
for row in selection
|
||||
if attribute.translate
|
||||
row.name = App.i18n.translateInline( row.name )
|
||||
attribute.options.push row
|
||||
else
|
||||
order = _.sortBy(
|
||||
_.keys(selection), (item) ->
|
||||
selection[item].toString().toLowerCase()
|
||||
)
|
||||
for key in order
|
||||
name_new = selection[key]
|
||||
if attribute.translate
|
||||
name_new = App.i18n.translateInline( name_new )
|
||||
attribute.options.push {
|
||||
name: name_new
|
||||
value: key
|
||||
}
|
||||
|
||||
@getRelationOptionList: (attribute, params) ->
|
||||
|
||||
# build options list based on relation
|
||||
return if !attribute.relation
|
||||
return if !App[attribute.relation]
|
||||
|
||||
attribute.options = []
|
||||
list = []
|
||||
if attribute.filter
|
||||
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter', attribute.filter
|
||||
|
||||
# function based filter
|
||||
if typeof attribute.filter is 'function'
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-function'
|
||||
|
||||
all = App[ attribute.relation ].search( sortBy: attribute.sortBy )
|
||||
|
||||
list = attribute.filter( all, 'collection', params )
|
||||
|
||||
# data based filter
|
||||
else if attribute.filter[ attribute.name ]
|
||||
filter = attribute.filter[ attribute.name ]
|
||||
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-data', filter
|
||||
|
||||
# check all records
|
||||
for record in App[ attribute.relation ].search( sortBy: attribute.sortBy )
|
||||
|
||||
# check all filter attributes
|
||||
for key in filter
|
||||
|
||||
# check all filter values as array
|
||||
# if it's matching, use it for selection
|
||||
if record['id'] is key
|
||||
list.push record
|
||||
|
||||
# data based filter
|
||||
else if attribute.filter && _.isArray attribute.filter
|
||||
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-array', attribute.filter
|
||||
|
||||
# check all records
|
||||
for record in App[ attribute.relation ].search( sortBy: attribute.sortBy )
|
||||
|
||||
# check all filter attributes
|
||||
for key in attribute.filter
|
||||
|
||||
# check all filter values as array
|
||||
# if it's matching, use it for selection
|
||||
if record['id'] is key || ( record['id'] && key && record['id'].toString() is key.toString() )
|
||||
list.push record
|
||||
|
||||
# check if current value need to be added
|
||||
if params[ attribute.name ]
|
||||
hit = false
|
||||
for value in list
|
||||
if value['id'].toString() is params[ attribute.name ].toString()
|
||||
hit = true
|
||||
if !hit
|
||||
currentRecord = App[ attribute.relation ].find( params[ attribute.name ] )
|
||||
list.push currentRecord
|
||||
|
||||
# no data filter matched
|
||||
else
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-data no filter matched'
|
||||
list = App[ attribute.relation ].search( sortBy: attribute.sortBy )
|
||||
else
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList:filter-no filter defined'
|
||||
list = App[ attribute.relation ].search( sortBy: attribute.sortBy )
|
||||
|
||||
App.Log.debug 'ControllerForm', '_getRelationOptionList', attribute, list
|
||||
|
||||
# build options list
|
||||
@buildOptionList( list, attribute )
|
||||
|
||||
# build options list
|
||||
@buildOptionList: (list, attribute) ->
|
||||
|
||||
for item in list
|
||||
|
||||
# if active or if active doesn't exist
|
||||
if item.active || !( 'active' of item )
|
||||
name_new = '?'
|
||||
if item.displayName
|
||||
name_new = item.displayName()
|
||||
else if item.name
|
||||
name_new = item.name
|
||||
if attribute.translate
|
||||
name_new = App.i18n.translateInline(name_new)
|
||||
attribute.options.push {
|
||||
name: name_new,
|
||||
value: item.id,
|
||||
note: item.note,
|
||||
}
|
||||
|
||||
# execute filter
|
||||
@filterOption: (attribute) ->
|
||||
return if !attribute.filter
|
||||
return if !attribute.options
|
||||
|
||||
return if typeof attribute.filter isnt 'function'
|
||||
App.Log.debug 'ControllerForm', '_filterOption:filter-function'
|
||||
|
||||
attribute.options = attribute.filter( attribute.options, attribute )
|
||||
|
||||
# set selected attributes
|
||||
@selectedOptions: (attribute) ->
|
||||
return if !attribute.options
|
||||
|
||||
# check if selected / checked need to be set
|
||||
check = (value, record) ->
|
||||
if typeof value is 'string' || typeof value is 'number' || typeof value is 'boolean'
|
||||
|
||||
# if name or value is matching
|
||||
if record.value.toString() is value.toString() || record.name.toString() is value.toString()
|
||||
record.selected = 'selected'
|
||||
record.checked = 'checked'
|
||||
|
||||
else if ( value && record.value && _.include( value, record.value ) ) || ( value && record.name && _.include( value, record.name ) )
|
||||
record.selected = 'selected'
|
||||
record.checked = 'checked'
|
||||
|
||||
# lookup of any record
|
||||
for record in attribute.options
|
||||
|
||||
if _.isArray( attribute.value )
|
||||
for value in attribute.value
|
||||
check( value, record )
|
||||
|
||||
if typeof attribute.value is 'string' || typeof attribute.value is 'number' || typeof attribute.value is 'boolean'
|
||||
check( attribute.value, record )
|
||||
|
||||
# if noting is selected / checked, use default as selected / checked
|
||||
selected = false
|
||||
for record in attribute.options
|
||||
if record.selected || record.checked
|
||||
selected = true
|
||||
if !selected
|
||||
for record in attribute.options
|
||||
if typeof attribute.default is 'string' || typeof attribute.default is 'number' || typeof attribute.default is 'boolean'
|
||||
check( attribute.default, record )
|
||||
|
||||
# set disabled attributes
|
||||
@disabledOptions: (attribute) ->
|
||||
|
||||
return if !attribute.options
|
||||
return if !_.isArray( attribute.options )
|
||||
|
||||
for record in attribute.options
|
||||
if record.disable is true
|
||||
record.disabled = 'disabled'
|
||||
else
|
||||
record.disabled = ''
|
|
@ -0,0 +1,28 @@
|
|||
class App.UiElement.active extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute, params) ->
|
||||
|
||||
# set attributes
|
||||
attribute.null = false
|
||||
attribute.translation = true
|
||||
|
||||
# build options list
|
||||
attribute.options = [
|
||||
{ name: 'active', value: true }
|
||||
{ name: 'inactive', value: false }
|
||||
]
|
||||
|
||||
# set data type
|
||||
if attribute.name
|
||||
attribute.name = '{boolean}' + attribute.name
|
||||
|
||||
# build options list based on config
|
||||
@getConfigOptionList( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
# return item
|
||||
$( App.view('generic/select')( attribute: attribute ) )
|
|
@ -0,0 +1,51 @@
|
|||
class App.UiElement.autocompletion
|
||||
@render: (attribute, params) ->
|
||||
|
||||
if params[ attribute.name + '_autocompletion_value_shown' ]
|
||||
attribute.valueShown = params[ attribute.name + '_autocompletion_value_shown' ]
|
||||
|
||||
item = $( App.view('generic/autocompletion')( attribute: attribute ) )
|
||||
|
||||
a = =>
|
||||
local_attribute = '#' + attribute.id
|
||||
local_attribute_full = '#' + attribute.id + '_autocompletion'
|
||||
@callback = attribute.callback
|
||||
|
||||
# call calback on init
|
||||
if @callback && attribute.value && @params
|
||||
@callback( @params )
|
||||
|
||||
b = (event, item) =>
|
||||
# set html form attribute
|
||||
$(local_attribute).val(item.id).trigger('change')
|
||||
$(local_attribute + '_autocompletion_value_shown').val(item.value)
|
||||
|
||||
# call calback
|
||||
if @callback
|
||||
params = App.ControllerForm.params(form)
|
||||
@callback( params )
|
||||
###
|
||||
$(@local_attribute_full).tagsInput(
|
||||
autocomplete_url: '/users/search',
|
||||
height: '30px',
|
||||
width: '530px',
|
||||
auto: {
|
||||
source: '/users/search',
|
||||
minLength: 2,
|
||||
select: ( event, ui ) ->
|
||||
#@log 'notice', 'selected', event, ui
|
||||
b(event, ui.item)
|
||||
}
|
||||
)
|
||||
###
|
||||
source = attribute.source
|
||||
if typeof(source) is 'string'
|
||||
source = source.replace('#{@apiPath}', App.Config.get('api_path') );
|
||||
$(local_attribute_full).autocomplete(
|
||||
source: source,
|
||||
minLength: attribute.minLengt || 3,
|
||||
select: ( event, ui ) =>
|
||||
b(event, ui.item)
|
||||
)
|
||||
App.Delay.set( a, 280, undefined, 'form_autocompletion' )
|
||||
item
|
|
@ -0,0 +1,25 @@
|
|||
class App.UiElement.boolean extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute, params) ->
|
||||
|
||||
# build options list
|
||||
if _.isEmpty(attribute.options)
|
||||
attribute.options = [
|
||||
{ name: 'yes', value: true }
|
||||
{ name: 'no', value: false }
|
||||
]
|
||||
|
||||
# set data type
|
||||
if attribute.name
|
||||
attribute.name = '{boolean}' + attribute.name
|
||||
|
||||
# build options list based on config
|
||||
@getConfigOptionList( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
# return item
|
||||
$( App.view('generic/select')( attribute: attribute ) )
|
|
@ -0,0 +1,25 @@
|
|||
class App.UiElement.checkbox extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute, params) ->
|
||||
|
||||
# build options list based on config
|
||||
@getConfigOptionList( attribute, params )
|
||||
|
||||
# build options list based on relation
|
||||
@getRelationOptionList( attribute, params )
|
||||
|
||||
# add null selection if needed
|
||||
@addNullOption( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
# disable item of list
|
||||
@disabledOptions( attribute, params )
|
||||
|
||||
# filter attributes
|
||||
@filterOption( attribute, params )
|
||||
|
||||
$( App.view('generic/checkbox')( attribute: attribute ) )
|
|
@ -0,0 +1,139 @@
|
|||
class App.UiElement.date
|
||||
@render: (attribute) ->
|
||||
|
||||
# set data type
|
||||
if attribute.name
|
||||
attribute.nameRaw = attribute.name
|
||||
attribute.name = '{date}' + attribute.name
|
||||
if attribute.value
|
||||
if typeof( attribute.value ) is 'string'
|
||||
unixtime = new Date( Date.parse( "#{attribute.value}T00:00:00Z" ) )
|
||||
else
|
||||
unixtime = new Date( attribute.value )
|
||||
year = unixtime.getUTCFullYear()
|
||||
month = unixtime.getUTCMonth() + 1
|
||||
day = unixtime.getUTCDate()
|
||||
hour = unixtime.getUTCHours()
|
||||
minute = unixtime.getUTCMinutes()
|
||||
item = $( App.view('generic/date')(
|
||||
attribute: attribute
|
||||
year: year
|
||||
month: month
|
||||
day: day
|
||||
) )
|
||||
|
||||
setNewTime = (diff, el, reset) ->
|
||||
name = $(el).closest('.form-group').find('[data-name]').attr('data-name')
|
||||
|
||||
# remove old validation
|
||||
item.find('.has-error').removeClass('has-error')
|
||||
item.closest('.form-group').find('.help-inline').html('')
|
||||
|
||||
day = item.closest('.form-group').find("[name=\"{date}#{name}___day\"]").val()
|
||||
month = item.closest('.form-group').find("[name=\"{date}#{name}___month\"]").val()
|
||||
year = item.closest('.form-group').find("[name=\"{date}#{name}___year\"]").val()
|
||||
format = (number) ->
|
||||
if parseInt(number) < 10
|
||||
number = "0#{number}"
|
||||
number
|
||||
if !reset && (year isnt '' && month isnt '' && day isnt '')
|
||||
time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T00:00:00Z" ) )
|
||||
time.setMinutes( time.getMinutes() + diff + time.getTimezoneOffset() )
|
||||
else
|
||||
time = new Date()
|
||||
time.setMinutes( time.getMinutes() + diff )
|
||||
item.closest('.form-group').find("[name=\"{date}#{name}___day\"]").val( time.getDate() )
|
||||
item.closest('.form-group').find("[name=\"{date}#{name}___month\"]").val( time.getMonth()+1 )
|
||||
item.closest('.form-group').find("[name=\"{date}#{name}___year\"]").val( time.getFullYear() )
|
||||
|
||||
item.find('.js-today').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(0, @, true)
|
||||
)
|
||||
item.find('.js-plus-day').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(60 * 24, @)
|
||||
)
|
||||
item.find('.js-minus-day').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(-60 * 24, @)
|
||||
)
|
||||
item.find('.js-plus-week').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(60 * 24 * 7, @)
|
||||
)
|
||||
item.find('.js-minus-week').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(-60 * 24 * 7, @)
|
||||
)
|
||||
|
||||
item.find('input').bind('keyup blur focus change', (e) ->
|
||||
|
||||
# do validation
|
||||
name = $(@).attr('name')
|
||||
if name
|
||||
fieldPrefix = name.split('___')[0]
|
||||
|
||||
# remove old validation
|
||||
item.find('.has-error').removeClass('has-error')
|
||||
item.closest('.form-group').find('.help-inline').html('')
|
||||
|
||||
day = item.closest('.form-group').find("[name=\"#{fieldPrefix}___day\"]").val()
|
||||
month = item.closest('.form-group').find("[name=\"#{fieldPrefix}___month\"]").val()
|
||||
year = item.closest('.form-group').find("[name=\"#{fieldPrefix}___year\"]").val()
|
||||
|
||||
# validate exists
|
||||
errors = {}
|
||||
if !day
|
||||
errors.day = 'missing'
|
||||
if !month
|
||||
errors.month = 'missing'
|
||||
if !year
|
||||
errors.year = 'missing'
|
||||
|
||||
# ranges
|
||||
if day
|
||||
daysInMonth = 31
|
||||
if month && year
|
||||
daysInMonth = new Date(year, month, 0).getDate();
|
||||
|
||||
if parseInt(day).toString() is 'NaN'
|
||||
errors.day = 'invalid'
|
||||
else if parseInt(day) > daysInMonth || parseInt(day) < 1
|
||||
errors.day = 'invalid'
|
||||
|
||||
if month
|
||||
if parseInt(month).toString() is 'NaN'
|
||||
errors.month = 'invalid'
|
||||
else if parseInt(month) > 12 || parseInt(month) < 1
|
||||
errors.month = 'invalid'
|
||||
|
||||
if year
|
||||
if parseInt(year).toString() is 'NaN'
|
||||
errors.year = 'invalid'
|
||||
else if parseInt(year) > 2100 || parseInt(year) < 2001
|
||||
errors.year = 'invalid'
|
||||
|
||||
if !_.isEmpty(errors)
|
||||
|
||||
# if field is required, if not do not show error
|
||||
if year is '' && day is '' && month
|
||||
if attribute.null
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return
|
||||
else
|
||||
item.closest('.form-group').find('.help-inline').text( 'is required' )
|
||||
|
||||
# show invalid options
|
||||
for key, value of errors
|
||||
item.closest('.form-group').addClass('has-error')
|
||||
item.closest('.form-group').find("[name=\"#{fieldPrefix}___#{key}\"]").addClass('has-error')
|
||||
#item.closest('.form-group').find('.help-inline').text( value )
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return
|
||||
)
|
||||
|
||||
item
|
|
@ -0,0 +1,172 @@
|
|||
class App.UiElement.datetime
|
||||
@render: (attribute) ->
|
||||
|
||||
# set data type
|
||||
if attribute.name
|
||||
attribute.nameRaw = attribute.name
|
||||
attribute.name = '{datetime}' + attribute.name
|
||||
if attribute.value
|
||||
if typeof( attribute.value ) is 'string'
|
||||
unixtime = new Date( Date.parse( attribute.value ) )
|
||||
else
|
||||
unixtime = new Date( attribute.value )
|
||||
year = unixtime.getFullYear()
|
||||
month = unixtime.getMonth() + 1
|
||||
day = unixtime.getDate()
|
||||
hour = unixtime.getHours()
|
||||
minute = unixtime.getMinutes()
|
||||
item = $( App.view('generic/datetime')(
|
||||
attribute: attribute
|
||||
year: year
|
||||
month: month
|
||||
day: day
|
||||
hour: hour
|
||||
minute: minute
|
||||
) )
|
||||
|
||||
setNewTime = (diff, el, reset) ->
|
||||
name = $(el).closest('.form-group').find('[data-name]').attr('data-name')
|
||||
|
||||
# remove old validation
|
||||
item.find('.has-error').removeClass('has-error')
|
||||
item.closest('.form-group').find('.help-inline').html('')
|
||||
|
||||
day = item.closest('.form-group').find("[name=\"{datetime}#{name}___day\"]").val()
|
||||
month = item.closest('.form-group').find("[name=\"{datetime}#{name}___month\"]").val()
|
||||
year = item.closest('.form-group').find("[name=\"{datetime}#{name}___year\"]").val()
|
||||
hour = item.closest('.form-group').find("[name=\"{datetime}#{name}___hour\"]").val()
|
||||
minute = item.closest('.form-group').find("[name=\"{datetime}#{name}___minute\"]").val()
|
||||
format = (number) ->
|
||||
if parseInt(number) < 10
|
||||
number = "0#{number}"
|
||||
number
|
||||
if !reset && (year isnt '' && month isnt '' && day isnt '' && hour isnt '' && day isnt '')
|
||||
time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T#{format(hour)}:#{format(minute)}:00Z" ) )
|
||||
time.setMinutes( time.getMinutes() + diff + time.getTimezoneOffset() )
|
||||
else
|
||||
time = new Date()
|
||||
time.setMinutes( time.getMinutes() + diff )
|
||||
#console.log('T', time, time.getHours(), time.getMinutes())
|
||||
item.closest('.form-group').find("[name=\"{datetime}#{name}___day\"]").val( time.getDate() )
|
||||
item.closest('.form-group').find("[name=\"{datetime}#{name}___month\"]").val( time.getMonth()+1 )
|
||||
item.closest('.form-group').find("[name=\"{datetime}#{name}___year\"]").val( time.getFullYear() )
|
||||
item.closest('.form-group').find("[name=\"{datetime}#{name}___hour\"]").val( time.getHours() )
|
||||
item.closest('.form-group').find("[name=\"{datetime}#{name}___minute\"]").val( time.getMinutes() )
|
||||
|
||||
item.find('.js-today').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(0, @, true)
|
||||
)
|
||||
item.find('.js-plus-hour').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(60, @)
|
||||
)
|
||||
item.find('.js-minus-hour').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(-60, @)
|
||||
)
|
||||
item.find('.js-plus-day').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(60 * 24, @)
|
||||
)
|
||||
item.find('.js-minus-day').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(-60 * 24, @)
|
||||
)
|
||||
item.find('.js-plus-week').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(60 * 24 * 7, @)
|
||||
)
|
||||
item.find('.js-minus-week').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
setNewTime(-60 * 24 * 7, @)
|
||||
)
|
||||
|
||||
item.find('input').bind('keyup blur focus change', (e) ->
|
||||
|
||||
# do validation
|
||||
name = $(@).attr('name')
|
||||
if name
|
||||
fieldPrefix = name.split('___')[0]
|
||||
|
||||
# remove old validation
|
||||
item.find('.has-error').removeClass('has-error')
|
||||
item.closest('.form-group').find('.help-inline').html('')
|
||||
|
||||
day = item.closest('.form-group').find("[name=\"#{fieldPrefix}___day\"]").val()
|
||||
month = item.closest('.form-group').find("[name=\"#{fieldPrefix}___month\"]").val()
|
||||
year = item.closest('.form-group').find("[name=\"#{fieldPrefix}___year\"]").val()
|
||||
hour = item.closest('.form-group').find("[name=\"#{fieldPrefix}___hour\"]").val()
|
||||
minute = item.closest('.form-group').find("[name=\"#{fieldPrefix}___minute\"]").val()
|
||||
|
||||
# validate exists
|
||||
errors = {}
|
||||
if !day
|
||||
errors.day = 'missing'
|
||||
if !month
|
||||
errors.month = 'missing'
|
||||
if !year
|
||||
errors.year = 'missing'
|
||||
if !hour
|
||||
errors.hour = 'missing'
|
||||
if !minute
|
||||
errors.minute = 'missing'
|
||||
|
||||
# ranges
|
||||
if day
|
||||
daysInMonth = 31
|
||||
if month && year
|
||||
daysInMonth = new Date(year, month, 0).getDate();
|
||||
|
||||
if parseInt(day).toString() is 'NaN'
|
||||
errors.day = 'invalid'
|
||||
else if parseInt(day) > daysInMonth || parseInt(day) < 1
|
||||
errors.day = 'invalid'
|
||||
|
||||
if month
|
||||
if parseInt(month).toString() is 'NaN'
|
||||
errors.month = 'invalid'
|
||||
else if parseInt(month) > 12 || parseInt(month) < 1
|
||||
errors.month = 'invalid'
|
||||
|
||||
if year
|
||||
if parseInt(year).toString() is 'NaN'
|
||||
errors.year = 'invalid'
|
||||
else if parseInt(year) > 2100 || parseInt(year) < 2001
|
||||
errors.year = 'invalid'
|
||||
|
||||
if hour
|
||||
if parseInt(hour).toString() is 'NaN'
|
||||
errors.hour = 'invalid'
|
||||
else if parseInt(hour) > 23 || parseInt(hour) < 0
|
||||
errors.hour = 'invalid'
|
||||
|
||||
if minute
|
||||
if parseInt(minute).toString() is 'NaN'
|
||||
errors.minute = 'invalid'
|
||||
else if parseInt(minute) > 59
|
||||
errors.minute = 'invalid'
|
||||
|
||||
if !_.isEmpty(errors)
|
||||
|
||||
# if field is required, if not do not show error
|
||||
if year is '' && day is '' && month is '' && hour is '' && minute is ''
|
||||
if attribute.null
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return
|
||||
else
|
||||
item.closest('.form-group').find('.help-inline').text( 'is required' )
|
||||
|
||||
# show invalid options
|
||||
for key, value of errors
|
||||
item.closest('.form-group').addClass('has-error')
|
||||
item.closest('.form-group').find("[name=\"#{fieldPrefix}___#{key}\"]").addClass('has-error')
|
||||
#item.closest('.form-group').find('.help-inline').text( value )
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return
|
||||
)
|
||||
|
||||
item
|
|
@ -0,0 +1,176 @@
|
|||
class App.UiElement.postmaster_match
|
||||
@render: (attribute, params, form_controller) ->
|
||||
addItem = (key, displayName, el, defaultValue = '') =>
|
||||
add = { name: key, display: displayName, tag: 'input', null: false, default: defaultValue }
|
||||
itemInput = $( form_controller.formGenItem( add ).append('<svg class="icon icon-diagonal-cross remove"><use xlink:href="#icon-diagonal-cross"></use></svg>' ) )
|
||||
|
||||
# remove on click
|
||||
itemInput.find('.remove').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
key = $(e.target).closest('.form-group').find('select, input').attr('name')
|
||||
return if !key
|
||||
$(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').show()
|
||||
$(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
|
||||
$(e.target).closest('.form-group').remove()
|
||||
)
|
||||
|
||||
# add new item
|
||||
control = el.closest('.postmaster_match')
|
||||
control.find('.list').append(itemInput)
|
||||
control.find('.addSelection select').val('')
|
||||
control.find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
|
||||
control.find('.addSelection select option[value="' + key + '"]').hide()
|
||||
|
||||
# scaffold of match elements
|
||||
item = $('
|
||||
<div class="postmaster_match">
|
||||
<hr>
|
||||
<div class="list"></div>
|
||||
<hr>
|
||||
<div>
|
||||
<div class="addSelection"></div>
|
||||
<svg class="icon icon-plus add"><use xlink:href="#icon-plus"></use></svg>
|
||||
</div>
|
||||
</div>')
|
||||
|
||||
# select shown attributes
|
||||
loopData = [
|
||||
{
|
||||
value: 'from'
|
||||
name: 'From'
|
||||
},
|
||||
{
|
||||
value: 'to'
|
||||
name: 'To'
|
||||
},
|
||||
{
|
||||
value: 'cc'
|
||||
name: 'Cc'
|
||||
},
|
||||
{
|
||||
value: 'subject'
|
||||
name: 'Subject'
|
||||
},
|
||||
{
|
||||
value: 'body'
|
||||
name: 'Body'
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '-'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: 'x-any-recipient'
|
||||
name: 'Any Recipient'
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '-'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '- ' + App.i18n.translateInline('expert settings') + ' -'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '-'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: 'x-spam-flag'
|
||||
name: 'X-Spam-Flag'
|
||||
},
|
||||
{
|
||||
value: 'x-spam-level'
|
||||
name: 'X-Spam-Level'
|
||||
},
|
||||
{
|
||||
value: 'x-spam-score'
|
||||
name: 'X-Spam-Score'
|
||||
},
|
||||
{
|
||||
value: 'x-spam-status'
|
||||
name: 'X-Spam-Status'
|
||||
},
|
||||
{
|
||||
value: 'importance'
|
||||
name: 'Importance'
|
||||
},
|
||||
{
|
||||
value: 'x-priority'
|
||||
name: 'X-Priority'
|
||||
},
|
||||
|
||||
{
|
||||
value: 'organization'
|
||||
name: 'Organization'
|
||||
},
|
||||
|
||||
{
|
||||
value: 'x-original-to'
|
||||
name: 'X-Original-To'
|
||||
},
|
||||
{
|
||||
value: 'delivered-to'
|
||||
name: 'Delivered-To'
|
||||
},
|
||||
{
|
||||
value: 'envelope-to'
|
||||
name: 'Envelope-To'
|
||||
},
|
||||
{
|
||||
value: 'return-path'
|
||||
name: 'Return-Path'
|
||||
},
|
||||
{
|
||||
value: 'mailing-list'
|
||||
name: 'Mailing-List'
|
||||
},
|
||||
{
|
||||
value: 'list-id'
|
||||
name: 'List-Id'
|
||||
},
|
||||
{
|
||||
value: 'list-archive'
|
||||
name: 'List-Archive'
|
||||
},
|
||||
{
|
||||
value: 'mailing-list'
|
||||
name: 'Mailing-List'
|
||||
},
|
||||
{
|
||||
value: 'auto-submitted'
|
||||
name: 'Auto-Submitted'
|
||||
},
|
||||
{
|
||||
value: 'x-loop'
|
||||
name: 'X-Loop'
|
||||
},
|
||||
]
|
||||
for listItem in loopData
|
||||
listItem.value = "#{ attribute.name }::#{listItem.value}"
|
||||
add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
|
||||
item.find('.addSelection').append( form_controller.formGenItem( add ) )
|
||||
|
||||
# bind add click
|
||||
item.find('.add').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
name = $(@).closest('.controls').find('.addSelection').find('select').val()
|
||||
displayName = $(@).closest('.controls').find('.addSelection').find('select option:selected').html()
|
||||
return if !name
|
||||
addItem( name, displayName, $(@) )
|
||||
)
|
||||
|
||||
# show default values
|
||||
loopDataValue = {}
|
||||
if attribute.value
|
||||
for key, value of attribute.value
|
||||
displayName = key
|
||||
for listItem in loopData
|
||||
if listItem.value is "#{ attribute.name }::#{key}"
|
||||
addItem( "#{ attribute.name }::#{key}", listItem.name, item.find('.add'), value )
|
||||
|
||||
item
|
|
@ -0,0 +1,124 @@
|
|||
class App.UiElement.postmaster_set
|
||||
@render: (attribute, params, form_controller) ->
|
||||
addItem = (key, displayName, el, defaultValue = '') =>
|
||||
collection = undefined
|
||||
for listItem in loopData
|
||||
if listItem.value is key
|
||||
collection = listItem
|
||||
if collection.relation
|
||||
add = { name: key, display: displayName, tag: 'select', multiple: false, null: false, nulloption: true, relation: collection.relation, translate: true, default: defaultValue }
|
||||
else if collection.options
|
||||
add = { name: key, display: displayName, tag: 'select', multiple: false, null: false, nulloption: true, options: collection.options, translate: true, default: defaultValue }
|
||||
else
|
||||
add = { name: key, display: displayName, tag: 'input', null: false, default: defaultValue }
|
||||
itemInput = $( form_controller.formGenItem( add ).append('<svg class="icon icon-diagonal-cross remove"><use xlink:href="#icon-diagonal-cross"></use></svg>' ) )
|
||||
|
||||
# remove on click
|
||||
itemInput.find('.remove').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
key = $(e.target).closest('.form-group').find('select, input').attr('name')
|
||||
return if !key
|
||||
$(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').show()
|
||||
$(e.target).closest('.controls').find('.addSelection select option[value="' + key + '"]').prop('disabled', false)
|
||||
$(e.target).closest('.form-group').remove()
|
||||
)
|
||||
|
||||
# add new item
|
||||
control = el.closest('.perform_set')
|
||||
control.find('.list').append(itemInput)
|
||||
control.find('.addSelection select').val('')
|
||||
control.find('.addSelection select option[value="' + key + '"]').prop('disabled', true)
|
||||
control.find('.addSelection select option[value="' + key + '"]').hide()
|
||||
|
||||
# scaffold of perform elements
|
||||
item = $('
|
||||
<div class="perform_set">
|
||||
<hr>
|
||||
<div class="list"></div>
|
||||
<hr>
|
||||
<div>
|
||||
<div class="addSelection"></div>
|
||||
<svg class="icon icon-plus add"><use xlink:href="#icon-plus"></use></svg>
|
||||
</div>
|
||||
</div>')
|
||||
|
||||
|
||||
# select shown attributes
|
||||
loopData = [
|
||||
{
|
||||
value: 'x-zammad-ticket-priority_id'
|
||||
name: 'Ticket Priority'
|
||||
relation: 'TicketPriority'
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-ticket-state_id'
|
||||
name: 'Ticket State'
|
||||
relation: 'TicketState'
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-ticket-customer'
|
||||
name: 'Ticket Customer'
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-ticket-group_id'
|
||||
name: 'Ticket Group'
|
||||
relation: 'Group'
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-ticket-owner'
|
||||
name: 'Ticket Owner'
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '-'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-article-internal'
|
||||
name: 'Article Internal'
|
||||
options: { true: 'Yes', false: 'No'}
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-article-type_id'
|
||||
name: 'Article Type'
|
||||
relation: 'TicketArticleType'
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-article-sender_id'
|
||||
name: 'Article Sender'
|
||||
relation: 'TicketArticleSender'
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
name: '-'
|
||||
disable: true
|
||||
},
|
||||
{
|
||||
value: 'x-zammad-ignore'
|
||||
name: 'Ignore Message'
|
||||
options: { true: 'Yes', false: 'No'}
|
||||
},
|
||||
]
|
||||
for listItem in loopData
|
||||
listItem.value = "#{ attribute.name }::#{listItem.value}"
|
||||
add = { name: '', display: '', tag: 'select', multiple: false, null: false, nulloption: true, options: loopData, translate: true, required: false }
|
||||
item.find('.addSelection').append( form_controller.formGenItem( add ) )
|
||||
|
||||
item.find('.add').bind('click', (e) ->
|
||||
e.preventDefault()
|
||||
name = $(@).closest('.controls').find('.addSelection').find('select').val()
|
||||
displayName = $(@).closest('.controls').find('.addSelection').find('select option:selected').html()
|
||||
return if !name
|
||||
addItem( name, displayName, $(@) )
|
||||
)
|
||||
|
||||
# show default values
|
||||
loopDataValue = {}
|
||||
if attribute.value
|
||||
for key, value of attribute.value
|
||||
displayName = key
|
||||
for listItem in loopData
|
||||
if listItem.value is "#{ attribute.name }::#{key}"
|
||||
addItem( "#{ attribute.name }::#{key}", listItem.name, item.find('.add'), value )
|
||||
|
||||
item
|
|
@ -0,0 +1,25 @@
|
|||
class App.UiElement.radio extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute, params) ->
|
||||
|
||||
# build options list based on config
|
||||
@getConfigOptionList( attribute, params )
|
||||
|
||||
# build options list based on relation
|
||||
@getRelationOptionList( attribute, params )
|
||||
|
||||
# add null selection if needed
|
||||
@addNullOption( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
# disable item of list
|
||||
@disabledOptions( attribute, params )
|
||||
|
||||
# filter attributes
|
||||
@filterOption( attribute, params )
|
||||
|
||||
$( App.view('generic/radio')( attribute: attribute ) )
|
|
@ -0,0 +1,100 @@
|
|||
class App.UiElement.richtext
|
||||
@render: (attribute) ->
|
||||
|
||||
item = $( App.view('generic/richtext')( attribute: attribute ) )
|
||||
item.find('[contenteditable]').ce(
|
||||
mode: attribute.type
|
||||
maxlength: attribute.maxlength
|
||||
)
|
||||
if attribute.upload
|
||||
item.append( $( App.view('generic/attachment')( attribute: attribute ) ) )
|
||||
|
||||
renderAttachment = (file) =>
|
||||
item.find('.attachments').append( App.view('generic/attachment_item')(
|
||||
fileName: file.filename
|
||||
fileSize: @humanFileSize( file.size )
|
||||
store_id: file.store_id
|
||||
))
|
||||
item.on(
|
||||
'click'
|
||||
"[data-id=#{file.store_id}]", (e) =>
|
||||
@attachments = _.filter(
|
||||
@attachments,
|
||||
(item) ->
|
||||
return if item.id isnt file.store_id
|
||||
item
|
||||
)
|
||||
store_id = $(e.currentTarget).data('id')
|
||||
|
||||
# delete attachment from storage
|
||||
App.Ajax.request(
|
||||
type: 'DELETE'
|
||||
url: App.Config.get('api_path') + '/ticket_attachment_upload'
|
||||
data: JSON.stringify( { store_id: store_id } ),
|
||||
processData: false
|
||||
success: (data, status, xhr) =>
|
||||
)
|
||||
|
||||
# remove attachment from dom
|
||||
element = $(e.currentTarget).closest('.attachments')
|
||||
$(e.currentTarget).closest('.attachment').remove()
|
||||
# empty .attachment (remove spaces) to keep css working, thanks @mrflix :-o
|
||||
if element.find('.attachment').length == 0
|
||||
element.empty()
|
||||
)
|
||||
|
||||
@attachments = []
|
||||
@progressBar = item.find('.attachmentUpload-progressBar')
|
||||
@progressText = item.find('.js-percentage')
|
||||
@attachmentPlaceholder = item.find('.attachmentPlaceholder')
|
||||
@attachmentUpload = item.find('.attachmentUpload')
|
||||
@attachmentsHolder = item.find('.attachments')
|
||||
@cancelContainer = item.find('.js-cancel')
|
||||
|
||||
u = => html5Upload.initialize(
|
||||
uploadUrl: App.Config.get('api_path') + '/ticket_attachment_upload',
|
||||
dropContainer: item.closest('form').get(0),
|
||||
cancelContainer: @cancelContainer,
|
||||
inputField: item.find( 'input' ).get(0),
|
||||
key: 'File',
|
||||
data: { form_id: @form_id },
|
||||
maxSimultaneousUploads: 1,
|
||||
onFileAdded: (file) =>
|
||||
|
||||
file.on(
|
||||
|
||||
onStart: =>
|
||||
@attachmentPlaceholder.addClass('hide')
|
||||
@attachmentUpload.removeClass('hide')
|
||||
@cancelContainer.removeClass('hide')
|
||||
console.log('upload start')
|
||||
|
||||
onAborted: =>
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
|
||||
# Called after received response from the server
|
||||
onCompleted: (response) =>
|
||||
|
||||
response = JSON.parse(response)
|
||||
@attachments.push response.data
|
||||
|
||||
@attachmentPlaceholder.removeClass('hide')
|
||||
@attachmentUpload.addClass('hide')
|
||||
|
||||
renderAttachment(response.data)
|
||||
console.log('upload complete', response.data )
|
||||
|
||||
# Called during upload progress, first parameter
|
||||
# is decimal value from 0 to 100.
|
||||
onProgress: (progress, fileSize, uploadedBytes) =>
|
||||
@progressBar.width(parseInt(progress) + "%")
|
||||
@progressText.text(parseInt(progress))
|
||||
# hide cancel on 90%
|
||||
if parseInt(progress) >= 90
|
||||
@cancelContainer.addClass('hide')
|
||||
console.log('uploadProgress ', parseInt(progress))
|
||||
)
|
||||
)
|
||||
App.Delay.set( u, 100, undefined, 'form_upload' )
|
||||
item
|
|
@ -0,0 +1,3 @@
|
|||
class App.UiElement.searchable_select
|
||||
@render: (attribute) ->
|
||||
new App.SearchableSelect( attribute: attribute ).element()
|
|
@ -0,0 +1,32 @@
|
|||
class App.UiElement.select extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute, params, form_controller) ->
|
||||
|
||||
# set multible option
|
||||
if attribute.multiple
|
||||
attribute.multiple = 'multiple'
|
||||
else
|
||||
attribute.multiple = ''
|
||||
|
||||
# build options list based on config
|
||||
@getConfigOptionList( attribute, params )
|
||||
|
||||
# build options list based on relation
|
||||
@getRelationOptionList( attribute, params )
|
||||
|
||||
# add null selection if needed
|
||||
@addNullOption( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
# disable item of list
|
||||
@disabledOptions( attribute, params )
|
||||
|
||||
# filter attributes
|
||||
@filterOption( attribute, params )
|
||||
|
||||
# return item
|
||||
$( App.view('generic/select')( attribute: attribute ) )
|
|
@ -0,0 +1,4 @@
|
|||
class App.UiElement.sla_times
|
||||
@render: (attribute) ->
|
||||
|
||||
$( App.view('generic/sla_times')( attribute: attribute ) )
|
|
@ -0,0 +1,8 @@
|
|||
class App.UiElement.tag
|
||||
@render: (attribute) ->
|
||||
item = $( App.view('generic/input')( attribute: attribute ) )
|
||||
a = =>
|
||||
$('#' + attribute.id ).tokenfield()
|
||||
$('#' + attribute.id ).parent().css('height', 'auto')
|
||||
App.Delay.set( a, 120, undefined, 'tags' )
|
||||
item
|
|
@ -0,0 +1,41 @@
|
|||
class App.UiElement.textarea
|
||||
@render: (attribute) ->
|
||||
fileUploaderId = 'file-uploader-' + new Date().getTime() + '-' + Math.floor( Math.random() * 99999 )
|
||||
item = $( App.view('generic/textarea')( attribute: attribute ) + '<div class="file-uploader ' + attribute.class + '" id="' + fileUploaderId + '"></div>' )
|
||||
|
||||
a = =>
|
||||
visible = $( item[0] ).is(":visible")
|
||||
if visible && !$( item[0] ).expanding('active')
|
||||
$( item[0] ).expanding()
|
||||
$( item[0] ).on('focus', ->
|
||||
visible = $( item[0] ).is(":visible")
|
||||
if visible && !$( item[0] ).expanding('active')
|
||||
$( item[0] ).expanding().focus()
|
||||
)
|
||||
App.Delay.set( a, 80 )
|
||||
|
||||
if attribute.upload
|
||||
|
||||
# add file uploader
|
||||
u = =>
|
||||
# only add upload item if html element exists
|
||||
if @el.find('#' + fileUploaderId )[0]
|
||||
@el.find('#' + fileUploaderId ).fineUploader(
|
||||
request:
|
||||
endpoint: App.Config.get('api_path') + '/ticket_attachment_upload'
|
||||
params:
|
||||
form_id: @form_id
|
||||
text:
|
||||
uploadButton: '<i class="glyphicon glyphicon-paperclip"></i>'
|
||||
template: '<div class="qq-uploader">' +
|
||||
'<pre class="btn qq-upload-icon qq-upload-drop-area"><span>{dragZoneText}</span></pre>' +
|
||||
'<div class="btn btn-default qq-upload-icon2 qq-upload-button pull-right" style="">{uploadButtonText}</div>' +
|
||||
'<ul class="qq-upload-list span5" style="margin-top: 10px;"></ul>' +
|
||||
'</div>',
|
||||
classes:
|
||||
success: ''
|
||||
fail: ''
|
||||
debug: false
|
||||
)
|
||||
App.Delay.set( u, 100, undefined, 'form_upload' )
|
||||
item
|
|
@ -0,0 +1,25 @@
|
|||
class App.UiElement.timezone extends App.UiElement.ApplicationUiElement
|
||||
@render: (attribute) ->
|
||||
|
||||
attribute.options = []
|
||||
timezones = App.Config.get('timezones')
|
||||
|
||||
# build list based on config
|
||||
for timezone_value, timezone_diff of timezones
|
||||
if timezone_diff > 0
|
||||
timezone_diff = '+' + timezone_diff
|
||||
item =
|
||||
name: "#{timezone_value} (GMT#{timezone_diff})"
|
||||
value: timezone_value
|
||||
attribute.options.push item
|
||||
|
||||
# add null selection if needed
|
||||
@addNullOption( attribute, params )
|
||||
|
||||
# sort attribute.options
|
||||
@sortOptions( attribute, params )
|
||||
|
||||
# finde selected/checked item of list
|
||||
@selectedOptions( attribute, params )
|
||||
|
||||
$( App.view('generic/select')( attribute: attribute ) )
|
|
@ -0,0 +1,3 @@
|
|||
class App.UiElement.user_autocompletion
|
||||
@render: (attribute) ->
|
||||
new App.UserOrganizationAutocompletion( attribute: attribute ).element()
|
|
@ -254,4 +254,6 @@ class App extends Spine.Controller
|
|||
JST["app/views/#{name}"](params)
|
||||
template
|
||||
|
||||
class App.UiElement
|
||||
|
||||
window.App = App
|
||||
|
|
Loading…
Reference in a new issue