initial github commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import Ember from 'ember'
|
||||
import DS from 'ember-data'
|
||||
import config from 'ember-get-config'
|
||||
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'
|
||||
|
||||
export default DS.JSONAPIAdapter.extend DataAdapterMixin,
|
||||
namespace: config.apiNamespace
|
||||
host: config.apiHost
|
||||
authorizer: 'authorizer:devise'
|
||||
#pathForType: (type)->
|
||||
# type.underscore().pluralize()
|
||||
|
||||
# Code for createRecord and updateRecord taken from ember data v2.0.0 restadapter
|
||||
createRecord: (store, type, snapshot) ->
|
||||
data = {}
|
||||
#var serializer = store.serializerFor(type.modelName);
|
||||
serializer = Ember.get(snapshot.record, 'store').serializerFor('create-and-update')
|
||||
url = @buildURL(type.modelName, null, snapshot, 'createRecord')
|
||||
|
||||
serializer.serializeIntoHash data, type, snapshot, includeId: true
|
||||
|
||||
@ajax url, 'POST', data: data
|
||||
|
||||
# return pluralized underscored version
|
||||
pathForType: (modelName) ->
|
||||
base = Ember.String.underscore(modelName)
|
||||
base = base.replace(/\./g, '/')
|
||||
#base.split(/\//).map( (str) -> Ember.String.pluralize(str) ).join('/') # all parts pluralized
|
||||
Ember.String.pluralize(base)
|
||||
|
||||
updateRecord: (store, type, snapshot) ->
|
||||
data = {}
|
||||
#var serializer = store.serializerFor(type.modelName);
|
||||
serializer = Ember.get(snapshot.record, 'store').serializerFor('create-and-update')
|
||||
id = snapshot.id
|
||||
url = @buildURL(type.modelName, id, snapshot, 'updateRecord')
|
||||
serializer.serializeIntoHash data, type, snapshot, includeId: true
|
||||
@ajax url, 'PUT', data: data
|
||||
|
||||
handleResponse: (status, headers, payload) ->
|
||||
switch status
|
||||
when 401, 403
|
||||
application_route = Ember.getOwner(@).lookup('route:application')
|
||||
application_route.set 'router.globals.flash_message', "Unauthorized action"
|
||||
#@get('session')?.invalidate()
|
||||
#application_route.set 'router.globals.current_user', null
|
||||
#application_route.transitionTo('login')
|
||||
else
|
||||
@_super arguments...
|
||||
|
||||
#buildURL: (modelName, id, snapshot, requestType, query)->
|
||||
# response = @_super arguments...
|
||||
# debugger
|
||||
# response
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import Ember from 'ember'
|
||||
import layout from '../templates/components/collapsible-content'
|
||||
|
||||
Component = Ember.Component.extend
|
||||
layout: layout
|
||||
collapsed: true
|
||||
classNames: ['collapsible-content-container']
|
||||
|
||||
title: Ember.computed 'titleSpec', ->
|
||||
title = "#{@get('titleSpec')}"
|
||||
options = {}
|
||||
|
||||
# allow modelPath as argument, useful for page-title 'action.new_model' modelPath='models.projects.site' etc
|
||||
if modelPath = @get('modelPath')
|
||||
options.model = t(modelPath)
|
||||
|
||||
# interpret the given argument as a translation string if it matches the characteristics
|
||||
title = tspan(title, options) if is_translation_path(title)
|
||||
|
||||
title.htmlSafe()
|
||||
|
||||
actions:
|
||||
toggleCollapsed: -> @toggleProperty('collapsed')
|
||||
|
||||
Component.reopenClass
|
||||
positionalParams: ['titleSpec']
|
||||
|
||||
export default Component
|
||||
@@ -0,0 +1,33 @@
|
||||
import Ember from 'ember'
|
||||
import layout from '../templates/components/editable-attribute'
|
||||
|
||||
export default Ember.Component.extend
|
||||
layout: layout
|
||||
attributeName: 'name'
|
||||
can_make_editable: true
|
||||
isEditing: false
|
||||
validation_message: ''
|
||||
make_editable: (-> @set 'isEditing', true ).on 'doubleClick'
|
||||
init: ->
|
||||
@_super arguments...
|
||||
@set 'isEditing', true if @get('model.isNew')
|
||||
actions:
|
||||
destroyRecord: ->
|
||||
@set 'validation_message', ''
|
||||
model = @get('model')
|
||||
model.rollbackAttributes()
|
||||
model.destroyRecord()
|
||||
editRecord: -> @set('isEditing', true)
|
||||
saveRecord: ->
|
||||
model = @get('model')
|
||||
@set 'validation_message', ''
|
||||
if validate = @get('validate')
|
||||
@set 'validation_message', 'Must be given' if validate is 'presence' and not @get('model.name')
|
||||
unless @get 'validation_message'
|
||||
@set 'isEditing', false
|
||||
model.save()
|
||||
rollbackRecord: ->
|
||||
@set 'validation_message', ''
|
||||
model = @get('model')
|
||||
model.rollbackAttributes()
|
||||
@set 'isEditing', false
|
||||
@@ -0,0 +1,28 @@
|
||||
import Ember from 'ember'
|
||||
#import layout from '../templates/components/error-handling'
|
||||
|
||||
export default Ember.Component.extend
|
||||
#classNames: ['dunlop-global-error', 'ui', 'negative', 'message']
|
||||
#classNameBindings: ['show_error:active:hidden']
|
||||
#layout: layout
|
||||
error: {}
|
||||
show_error: true
|
||||
show_as_list: Ember.computed.gt 'error.errors.length', 1
|
||||
|
||||
error_header: Ember.computed 'error.message', ->
|
||||
@get('error.message') || 'Oops'
|
||||
|
||||
error_body: Ember.computed 'show_as_list', ->
|
||||
''
|
||||
single_error: Ember.computed 'error.message', ->
|
||||
Ember.Object.create
|
||||
title: @get('error.message')
|
||||
status: @get('error.status')
|
||||
detail: @get('error.backtrace')
|
||||
|
||||
actions:
|
||||
reload_page: -> window.location.reload()
|
||||
clear_error: -> @set 'show_error', false
|
||||
clear_error_and_go_home: ->
|
||||
@set 'show_error', false
|
||||
Ember.getOwner(this).lookup('route:application').transitionTo('index')
|
||||
@@ -0,0 +1,18 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
# = number-input numericValue=path.to.numeric_value, taken from mozo
|
||||
export default Ember.TextField.extend
|
||||
type: 'number'
|
||||
attributeBindings: ['min', 'max', 'step']
|
||||
classNames: ['number-input']
|
||||
focusIn: -> @$().select()
|
||||
init: ->
|
||||
@_super arguments...
|
||||
value = @get 'numericValue'
|
||||
# value? returns true on 0
|
||||
@set 'value', if value? then String(value) else ''
|
||||
handle_value_change: Ember.observer 'value', ->
|
||||
string_value = @get('value')
|
||||
setValue = parseFloat(string_value)
|
||||
@set "numericValue", if Number.isFinite(setValue) then setValue else null
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
Component = Ember.Component.extend
|
||||
tagName: 'h2'
|
||||
classNames: ['page-title', 'ui', 'header']
|
||||
back: null
|
||||
|
||||
title: Ember.computed 'titleSpec', ->
|
||||
title = "#{@get('titleSpec')}"
|
||||
options = {}
|
||||
|
||||
# allow modelPath as argument, useful for page-title 'action.new_model' modelPath='models.projects.site' etc
|
||||
if modelPath = @get('modelPath')
|
||||
options.model = t(modelPath)
|
||||
|
||||
# interpret the given argument as a translation string if it matches the characteristics
|
||||
title = tspan(title, options) if is_translation_path(title)
|
||||
|
||||
title.htmlSafe()
|
||||
|
||||
Component.reopenClass
|
||||
positionalParams: ['titleSpec']
|
||||
|
||||
export default Component
|
||||
@@ -0,0 +1,16 @@
|
||||
import Ember from 'ember'
|
||||
import layout from '../templates/components/pagination-progress'
|
||||
|
||||
export default Ember.Component.extend
|
||||
tagName: 'span'
|
||||
layout: layout
|
||||
progressText: Ember.computed 'content.page', 'content.content.length', 'content.perPage', 'content.total_count', ->
|
||||
per_page = @get('content.perPage') || 10
|
||||
current_page = @get('content.page') || 1
|
||||
records_displayed = @get('content.content.length') || 0
|
||||
total_count = @get('content.meta.total_count') || 0
|
||||
|
||||
from_item = (current_page - 1) * per_page + 1
|
||||
to_item = Math.min(from_item + records_displayed - 1, total_count)
|
||||
from_item = 0 unless total_count
|
||||
"#{from_item} - #{to_item} / #{total_count}"
|
||||
@@ -0,0 +1,39 @@
|
||||
import Ember from 'ember'
|
||||
import layout from '../templates/components/push-action'
|
||||
|
||||
export default Ember.Component.extend
|
||||
layout: layout
|
||||
tagName: 'button'
|
||||
type: ''
|
||||
size: ''
|
||||
model: null
|
||||
classNames: ['push-action', 'ui', 'icon', 'button']
|
||||
classNameBindings: ['type', 'size', 'colorClass']
|
||||
click: ->
|
||||
@sendAction 'action', @get('model')
|
||||
|
||||
colorClass: Ember.computed 'type', ->
|
||||
return unless type = @get('type')
|
||||
switch type
|
||||
when 'save' then 'primary'
|
||||
when 'destroy' then 'negative'
|
||||
when 'new' then 'grey'
|
||||
else ''
|
||||
|
||||
|
||||
iconClass: Ember.computed 'type', ->
|
||||
return unless type = @get('type')
|
||||
switch type
|
||||
when 'edit' then 'pencil'
|
||||
when 'new' then 'plus'
|
||||
when 'show' then 'eye'
|
||||
when 'sort' then 'sort'
|
||||
when 'rollback' then 'undo'
|
||||
when 'save' then 'save'
|
||||
when 'destroy' then 'trash'
|
||||
when 'authorization' then 'users' # 'unlock alternate'
|
||||
when 'constraints' then 'connectdevelop'
|
||||
when 'position' then 'random'
|
||||
when 'contract' then 'file text'
|
||||
when 'raster' then 'grid layout'
|
||||
else type
|
||||
@@ -0,0 +1,24 @@
|
||||
import Ember from 'ember'
|
||||
import UiCalendarComponent from 'ember-semantic-ui-calendar/components/ui-calendar'
|
||||
{ computed, inject, merge } = Ember
|
||||
export default UiCalendarComponent.extend
|
||||
momentValue: null
|
||||
firstDayOfWeek: null
|
||||
is_date: false
|
||||
|
||||
# Provide the Date object that is used by ui-calendar
|
||||
date: computed('momentValue', ->
|
||||
if momentValue = @get('momentValue') then momentValue.toDate() else null
|
||||
).readOnly()
|
||||
|
||||
willInitSemantic: (settings) ->
|
||||
settings.firstDayOfWeek = if Number.isFinite(@get('firstDayOfWeek')) then @get('firstDayOfWeek') else (if Number.isFinite(@get('globals.first_day_of_week')) then @get('globals.first_day_of_week') else 1)
|
||||
@_super arguments...
|
||||
@set('is_date', true) if @get('momentValue._f') is 'YYYY-MM-DD' or @get('momentValue._ambigTime') or @get('type') is 'date'
|
||||
merge settings,
|
||||
onChange: (date) ->
|
||||
# Wraps the Date in a moment object an triggers the onChange action
|
||||
momentDate = if date then moment(date) else null
|
||||
momentDate._ambigTime = true if momentDate and @get('is_date')
|
||||
@sendAction 'onChange', momentDate
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import Ember from 'ember'
|
||||
import layout from '../templates/components/ui-markdown-popup'
|
||||
import UiPopup from 'semantic-ui-ember/components/ui-popup'
|
||||
import showdown from 'showdown'
|
||||
|
||||
export default UiPopup.extend
|
||||
extensions: []
|
||||
init: ->
|
||||
markdown = @get('markdown')
|
||||
converter = new showdown.Converter(@get('extensions'))
|
||||
html = converter.makeHtml(markdown)
|
||||
@attrs.html = html
|
||||
@_super arguments...
|
||||
layout: layout
|
||||
@@ -0,0 +1,191 @@
|
||||
Qstorage = localStorage
|
||||
window.day_minutes_to_time = (minutes)->
|
||||
return "" unless minutes
|
||||
[("0" + Math.floor(minutes/60)).substr(-2,2), ("0" + Math.floor(minutes%60)).substr(-2,2)].join(":")
|
||||
|
||||
# This is the same as setTimeout but then
|
||||
# with switched arguments for coffeescript friendlyness.
|
||||
# @a = 3
|
||||
# delay =>
|
||||
# console.log "The value of a is #{@a}"
|
||||
# or to delay not a second but a self defined amount of time in milliseconds:
|
||||
# delay 10000, =>
|
||||
# console.log "The value of a is #{@a}"
|
||||
window.delay = (time = 1000, fn, args...) ->
|
||||
setTimeout fn, time, args...
|
||||
|
||||
# based on http://www.thecodeship.com/web-development/alternative-to-javascript-evil-setinterval/
|
||||
# can be used as:
|
||||
# to keep scope and execute every second:
|
||||
# @a = 3
|
||||
# interval =>
|
||||
# console.log "Hi there with a is: #{@a}"
|
||||
# to log every 200 milliseconds without conservation of this scope:
|
||||
# interval 200, -> console.log "Hi there"
|
||||
# to execute a maximum number (30) of times:
|
||||
# interval 200, 30, -> console.log "Hi there"
|
||||
window.interval = (wait = 1000, extra_args..., func)->
|
||||
times = extra_args[0]
|
||||
interv = do (wait, times)->
|
||||
->
|
||||
return if times? and times-- <= 0
|
||||
setTimeout interv, wait
|
||||
try
|
||||
func.call null
|
||||
catch e
|
||||
times = 0
|
||||
throw e.toString()
|
||||
setTimeout interv, wait
|
||||
|
||||
window.is_translation_path = (path_or_value) ->
|
||||
return true if path_or_value is 'application_name'
|
||||
String(path_or_value).match /^[a-z0-9_]+\.[a-z0-9\._]+$/
|
||||
|
||||
window.ttry = (path, vars={})->
|
||||
t(path, $.extend(vars, emptyWhenNotFound: true))
|
||||
|
||||
# return translation in the form
|
||||
# <span data-t="models.table">Tafel</span>
|
||||
window.tspan = (path, vars={}) ->
|
||||
result = t(path, vars)
|
||||
return '' if not result and vars.emptyWhenNotFound
|
||||
"<span data-t='#{path}' class='translation' data-t-attributes='#{JSON.stringify(vars)}'>#{result}</span>"
|
||||
|
||||
window.t = (path, vars={}) ->
|
||||
#result = undefined
|
||||
#m = undefined
|
||||
#translatable = undefined
|
||||
#isafety = undefined
|
||||
#replacable = undefined
|
||||
if vars.scope
|
||||
path = "#{vars.scope}.#{path}"
|
||||
delete vars.scope
|
||||
if vars.suffix
|
||||
path = "#{path}.#{vars.suffix}"
|
||||
delete vars.suffix
|
||||
locale = Qstorage.getItem('locale') || 'en'
|
||||
parts = path.split(".")
|
||||
#accessor = "$translations.#{$locale}[\"#{parts.join("\"][\"")}\"]"
|
||||
result = $translations[locale]
|
||||
try
|
||||
result = result[part] for part in parts
|
||||
catch err # when nesting gets too deep
|
||||
console.log "[TRANSLATION] Cannot find translation: #{path}"
|
||||
return vars.default if vars.default? # questionmark is important to allow {default: ''} setting
|
||||
result = parts[parts.length - 1].capitalize()
|
||||
return if vars.emptyWhenNotFound then "" else result #parts[parts.length - 1].capitalize()
|
||||
unless result?
|
||||
console.log "[TRANSLATION] Cannot find translation: #{path}"
|
||||
return vars.default if vars.default? and not result # questionmark is important to allow {default: ''}. This is however valid for all non truthy result values, so no question mark here
|
||||
return "" if result is ""
|
||||
return '' if not result? and vars.emptyWhenNotFound # When latest translation part cannot be found and empty result is required
|
||||
return parts[parts.length - 1].capitalize() unless result
|
||||
|
||||
|
||||
# allow t('test', {var: 'Benjamin'}) statements
|
||||
# with $translations.nl.test = "Hello %{var}"
|
||||
for variable, value of vars
|
||||
# allow t 'action.select_model' modelPath='models.project'
|
||||
if match = String(variable).match(/^(\w+)Path$/)
|
||||
value = window.t value
|
||||
variable = match[1]
|
||||
result = result.replace("%{#{variable}}", value)
|
||||
|
||||
isafety = 0
|
||||
try
|
||||
while result.indexOf("${") > -1
|
||||
m = result.match(/\${([\w\.]+(\|\w+)?)}/)
|
||||
if m[2]
|
||||
translatable = m[1].replace(m[2], "")
|
||||
operation = $transformation_mappings[m[2].substr(1) or m[2].substr(1)]
|
||||
else
|
||||
translatable = m[1]
|
||||
operation = null
|
||||
replacable = t(translatable)
|
||||
replacable = replacable[operation]() if operation
|
||||
result = result.replace(m[0], replacable)
|
||||
break if isafety > 10 # referencing other translations may cause infinite loops
|
||||
isafety += 1
|
||||
catch err
|
||||
console.log "translation #{result} cannot be interpolated"
|
||||
result
|
||||
|
||||
window.setLocale = (locale, options={}) ->
|
||||
locale ||= Qstorage.getItem('locale') || options.default || 'en'
|
||||
Qstorage.setItem "locale", locale
|
||||
window.$locale = locale
|
||||
setTranslations()
|
||||
|
||||
window.setTranslations = (selector) ->
|
||||
#list = $("#top-navigation-list")
|
||||
locale = Qstorage.getItem('locale') || 'en'
|
||||
selector = $( selector || document)
|
||||
selector.find(".locale-select").show()
|
||||
selector.find(".locale-select-" + locale).hide()
|
||||
moment.locale locale
|
||||
if selector
|
||||
selector.find("[data-t]").each ->
|
||||
$(this).html t($(this).data("t"), $(this).data("tAttributes"))
|
||||
|
||||
selector.find("*[data-time]").each ->
|
||||
$(this).text moment($(this).data("time")).format($(this).data("timeFormat") or "dd D MMM HH:mm")
|
||||
|
||||
else
|
||||
$("[data-t]").each ->
|
||||
$(this).html t($(this).data("t"), $(this).data("tAttributes"))
|
||||
|
||||
$("*[data-time]").each ->
|
||||
$(this).text moment($(this).data("time")).format($(this).data("timeFormat") or "dd D MMM HH:mm")
|
||||
|
||||
# jQuery UI datepicker support
|
||||
$(".datepicker").datepicker "option", $.datepicker.regional[locale] if $.fn.datepicker
|
||||
|
||||
# pickadate support
|
||||
if $.fn.pickadate
|
||||
if selector.hasClass('datepicker')
|
||||
datepicker_object = selector
|
||||
selector.siblings('.pickadate-display').remove()
|
||||
else
|
||||
datepicker_object = selector.find('.datepicker')
|
||||
selector.find('.pickadate-display').remove()
|
||||
datepicker_object.pickadate('stop') if datepicker_object.data('pickadate')
|
||||
$.extend( $.fn.pickadate.defaults, $pickadate_translations[locale] ) if $pickadate_translations?[locale]
|
||||
window.pickadate_options ||= {}
|
||||
datepicker_object.pickadate(window.pickadate_options)
|
||||
datepicker_object.change()
|
||||
|
||||
# use like
|
||||
# error_message
|
||||
# attribute: t('attributes.section.title')
|
||||
# message: 'blank'
|
||||
window.error_message = (options = {})->
|
||||
locale = options.locale || Qstorage.getItem('locale') || 'en'
|
||||
#message = $translations[locale].errors.messages[options.message]
|
||||
message = t "errors.messages.#{options.message}", options
|
||||
t 'errors.format',
|
||||
attribute: options.attribute
|
||||
message: message
|
||||
|
||||
window.setupTranslations = (options = {})->
|
||||
locale = options.locale || Qstorage.getItem('locale') || 'en'
|
||||
if $.fn.pickadate
|
||||
$(document).on 'change', '.datepicker', ->
|
||||
input = $(@)
|
||||
input.next().remove() if input.next().hasClass('pickadate-display')
|
||||
if input.data('pickadate')
|
||||
if input.val()
|
||||
#display_format = $pickadate_translations[$locale].displayFormat
|
||||
#display_date = input.data('pickadate').get('select', display_format)
|
||||
display_date = input.data('pickadate').get('select')
|
||||
display_date = $('<span></span>').text(" #{display_date}") # add space between the icon and the date
|
||||
else
|
||||
display_date = $('<span></span>').data('t', 'datepicker.no_date').text(t('datepicker.no_date'))
|
||||
display_tag = $('<span></span>').addClass('pickadate-display').append('<span class="fa fa-calendar fa-lg"></span>').append(display_date)
|
||||
#display_tag.click (e)->(e.preventDefault();input.click().focus();false )
|
||||
display_tag.click (e)->(e.preventDefault();input.pickadate('open');false )
|
||||
$(@).after(display_tag)
|
||||
setLocale(locale)
|
||||
$('.datepicker').change().hide()
|
||||
window.$transformation_mappings =
|
||||
downcase: "toLowerCase"
|
||||
upcase: "toUpperCase"
|
||||
@@ -0,0 +1,16 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
## This function receives the params `params, hash`
|
||||
#export anyPermissionStartsWith = (params) ->
|
||||
# return params
|
||||
|
||||
#export default Ember.Helper.helper anyPermissionStartsWith
|
||||
export default Ember.Helper.extend
|
||||
globals: Ember.inject.service('globals')
|
||||
compute: ([expression], options) ->
|
||||
return false unless @get('globals.current_user')
|
||||
return true if @get('globals.current_user.admin')
|
||||
regexp = new RegExp("^\\w+-\\w+-#{expression}")
|
||||
roles = @get('globals.current_user.role_names')
|
||||
roles.some (role) ->
|
||||
regexp.test role
|
||||
@@ -0,0 +1,13 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
export default Ember.Helper.extend
|
||||
globals: Ember.inject.service('globals')
|
||||
custom_compute: (authorization, type, target, roles) ->
|
||||
false
|
||||
compute: ([authorization, type, target], options) ->
|
||||
return false unless @get('globals.current_user')
|
||||
return true if @get('globals.current_user.admin')
|
||||
roles = @get('globals.current_user.role_names') || []
|
||||
return true if "manage-#{type}-#{target}" in roles
|
||||
return true if "#{authorization}-#{type}-#{target}" in roles
|
||||
@custom_compute.call(@, authorization, type, target, roles)
|
||||
@@ -0,0 +1,9 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
export formatDate = ([date], options={}) ->
|
||||
return '' unless date
|
||||
return date if typeof date is 'string'
|
||||
date = moment(date) unless date._isAMomentObject
|
||||
date.format(options.format || 'll')
|
||||
|
||||
export default Ember.Helper.helper formatDate
|
||||
@@ -0,0 +1,17 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
## This function receives the params `params, hash`
|
||||
#export hasPermissionOfType = (params) ->
|
||||
# return params
|
||||
|
||||
#export default Ember.Helper.helper hasPermissionOfType
|
||||
export default Ember.Helper.extend
|
||||
globals: Ember.inject.service('globals')
|
||||
compute: ([permission_type], options) ->
|
||||
return false unless permission_type
|
||||
return false unless @get('globals.current_user')
|
||||
return true if @get('globals.current_user.admin')
|
||||
regexp = new RegExp("^\\w+-#{permission_type}-")
|
||||
roles = @get('globals.current_user.role_names')
|
||||
roles.some (role) ->
|
||||
regexp.test role
|
||||
@@ -0,0 +1,13 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
# This function receives the params `params, hash`
|
||||
export minuteOfDay = ([number]) ->
|
||||
return '-' unless number?
|
||||
hour = Math.floor(number / 60)
|
||||
minute = number % 60
|
||||
hour_string = "0#{hour}".slice(-2)
|
||||
minute_string = "0#{minute}".slice(-2)
|
||||
"#{hour_string}:#{minute_string}"
|
||||
|
||||
export default Ember.Helper.helper minuteOfDay
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
headers = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
"AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV",
|
||||
"AW", "AX", "AY", "AZ", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BK", "BL", "BM", "BN", "BO", "BP", "BQ", "BR",
|
||||
"BS", "BT", "BU", "BV", "BW", "BX", "BY", "BZ", "CA", "CB", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CJ", "CK", "CL", "CM", "CN",
|
||||
"CO", "CP", "CQ", "CR", "CS", "CT", "CU", "CV", "CW", "CX", "CY", "CZ", "DA", "DB", "DC", "DD", "DE", "DF", "DG", "DH", "DI", "DJ",
|
||||
"DK", "DL", "DM", "DN", "DO", "DP", "DQ", "DR", "DS", "DT", "DU", "DV", "DW", "DX", "DY", "DZ", "EA", "EB", "EC", "ED", "EE", "EF",
|
||||
"EG", "EH", "EI", "EJ", "EK", "EL", "EM", "EN", "EO", "EP", "EQ", "ER", "ES", "ET", "EU", "EV", "EW", "EX", "EY", "EZ", "FA", "FB",
|
||||
"FC", "FD", "FE", "FF", "FG", "FH", "FI", "FJ", "FK", "FL", "FM", "FN", "FO", "FP", "FQ", "FR", "FS", "FT", "FU", "FV", "FW", "FX",
|
||||
"FY", "FZ", "GA", "GB", "GC", "GD", "GE", "GF", "GG", "GH", "GI", "GJ", "GK", "GL", "GM", "GN", "GO", "GP", "GQ", "GR", "GS", "GT",
|
||||
"GU", "GV", "GW", "GX", "GY", "GZ", "HA", "HB", "HC", "HD", "HE", "HF", "HG", "HH", "HI", "HJ", "HK", "HL", "HM", "HN", "HO", "HP",
|
||||
"HQ", "HR", "HS", "HT", "HU", "HV", "HW", "HX", "HY", "HZ", "IA", "IB", "IC", "ID", "IE", "IF", "IG", "IH", "II", "IJ", "IK", "IL",
|
||||
"IM", "IN", "IO", "IP", "IQ", "IR", "IS", "IT", "IU", "IV", "IW", "IX", "IY", "IZ", "JA", "JB", "JC", "JD", "JE", "JF", "JG", "JH",
|
||||
"JI", "JJ", "JK", "JL", "JM", "JN", "JO", "JP", "JQ", "JR", "JS", "JT", "JU", "JV", "JW", "JX", "JY", "JZ", "KA", "KB", "KC", "KD",
|
||||
"KE", "KF", "KG", "KH", "KI", "KJ", "KK", "KL", "KM", "KN", "KO", "KP", "KQ", "KR", "KS", "KT", "KU", "KV", "KW", "KX", "KY", "KZ",
|
||||
"LA", "LB", "LC", "LD", "LE", "LF", "LG", "LH", "LI", "LJ", "LK", "LL", "LM", "LN", "LO", "LP", "LQ", "LR", "LS", "LT", "LU", "LV",
|
||||
"LW", "LX", "LY", "LZ", "MA", "MB", "MC", "MD", "ME", "MF", "MG", "MH", "MI", "MJ", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR",
|
||||
"MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NB", "NC", "ND", "NE", "NF", "NG", "NH", "NI", "NJ", "NK", "NL", "NM", "NN",
|
||||
"NO", "NP", "NQ", "NR", "NS", "NT", "NU", "NV", "NW", "NX", "NY", "NZ", "OA", "OB", "OC", "OD", "OE", "OF", "OG", "OH", "OI", "OJ",
|
||||
"OK", "OL", "OM", "ON", "OO", "OP", "OQ", "OR", "OS", "OT", "OU", "OV", "OW", "OX", "OY", "OZ", "PA", "PB", "PC", "PD", "PE", "PF",
|
||||
"PG", "PH", "PI", "PJ", "PK", "PL", "PM", "PN", "PO", "PP", "PQ", "PR", "PS", "PT", "PU", "PV", "PW", "PX", "PY", "PZ", "QA", "QB",
|
||||
"QC", "QD", "QE", "QF", "QG", "QH", "QI", "QJ", "QK", "QL", "QM", "QN", "QO", "QP", "QQ", "QR", "QS", "QT", "QU", "QV", "QW", "QX",
|
||||
"QY", "QZ", "RA", "RB", "RC", "RD", "RE", "RF", "RG", "RH", "RI", "RJ", "RK", "RL", "RM", "RN", "RO", "RP", "RQ", "RR", "RS", "RT",
|
||||
"RU", "RV", "RW", "RX", "RY", "RZ", "SA", "SB", "SC", "SD", "SE", "SF", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SP",
|
||||
"SQ", "SR", "SS", "ST", "SU", "SV", "SW", "SX", "SY", "SZ", "TA", "TB", "TC", "TD", "TE", "TF", "TG", "TH", "TI", "TJ", "TK", "TL",
|
||||
"TM", "TN", "TO", "TP", "TQ", "TR", "TS", "TT", "TU", "TV", "TW", "TX", "TY", "TZ", "UA", "UB", "UC", "UD", "UE", "UF", "UG", "UH",
|
||||
"UI", "UJ", "UK", "UL", "UM", "UN", "UO", "UP", "UQ", "UR", "US", "UT", "UU", "UV", "UW", "UX", "UY", "UZ", "VA", "VB", "VC", "VD",
|
||||
"VE", "VF", "VG", "VH", "VI", "VJ", "VK", "VL", "VM", "VN", "VO", "VP", "VQ", "VR", "VS", "VT", "VU", "VV", "VW", "VX", "VY", "VZ",
|
||||
"WA", "WB", "WC", "WD", "WE", "WF", "WG", "WH", "WI", "WJ", "WK", "WL", "WM", "WN", "WO", "WP", "WQ", "WR", "WS", "WT", "WU", "WV",
|
||||
"WW", "WX", "WY", "WZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG", "XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ", "XR",
|
||||
"XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "YA", "YB", "YC", "YD", "YE", "YF", "YG", "YH", "YI", "YJ", "YK", "YL", "YM", "YN",
|
||||
"YO", "YP", "YQ", "YR", "YS", "YT", "YU", "YV", "YW", "YX", "YY", "YZ", "ZA", "ZB", "ZC", "ZD", "ZE", "ZF", "ZG", "ZH", "ZI", "ZJ",
|
||||
"ZK", "ZL", "ZM", "ZN", "ZO", "ZP", "ZQ", "ZR", "ZS", "ZT", "ZU", "ZV", "ZW", "ZX", "ZY", "ZZ"
|
||||
]
|
||||
# This function receives the params `params, hash`
|
||||
export numberToXlsHeader = ([index], options) ->
|
||||
headers[index]
|
||||
|
||||
export default Ember.Helper.helper numberToXlsHeader
|
||||
@@ -0,0 +1,13 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
# This function receives the params `params, hash`
|
||||
export translate = (params, options) ->
|
||||
path = params[0]
|
||||
toptions = options
|
||||
if params.length > 1 and typeof(params[1].toJSON) is 'function'
|
||||
$.extend toptions, params[1].toJSON()
|
||||
text = t(path, toptions)
|
||||
tag = if options.bare then text else "<span data-t='#{path}' class='translation' data-t-attributes='#{JSON.stringify(toptions)}'>#{text}</span>"
|
||||
tag.htmlSafe()
|
||||
|
||||
export default Ember.Helper.helper translate
|
||||
@@ -0,0 +1,18 @@
|
||||
import ComponentOverrides from 'ember-cli-dunlop/overrides/component'
|
||||
import ControllerOverrides from 'ember-cli-dunlop/overrides/controller'
|
||||
import ObjectOverrides from 'ember-cli-dunlop/overrides/object'
|
||||
import RouteOverrides from 'ember-cli-dunlop/overrides/route'
|
||||
import DunlopTranslationFunctions from 'ember-cli-dunlop/dunlop-translations/global-functions'
|
||||
#included: function(app) {
|
||||
# this._super.included(app);
|
||||
# app.import('ember-cli-dunlop/overrides/component');
|
||||
# app.import('ember-cli-dunlop/overrides/controller');
|
||||
#}
|
||||
# Takes two parameters: container and application
|
||||
export initialize = () ->
|
||||
# application.register 'route', 'foo', 'service:foo'
|
||||
|
||||
export default {
|
||||
name: 'ember-cli-dunlop-overrides'
|
||||
initialize: initialize
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import Ember from 'ember'
|
||||
import config from 'ember-get-config'
|
||||
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin'
|
||||
|
||||
export default Ember.Mixin.create ApplicationRouteMixin,
|
||||
current_user_loaded: (user) ->
|
||||
beforeModel: (transition) ->
|
||||
@_loadCurrentUser arguments... unless transition?.targetName is 'login'
|
||||
|
||||
sessionAuthenticated: ->
|
||||
@_super arguments...
|
||||
@_loadCurrentUser()
|
||||
|
||||
_loadCurrentUser: (transition) ->
|
||||
#if userId = @get('session.data.authenticated.user_id')
|
||||
return if transition?.targetName is 'login'
|
||||
@get('store').queryRecord('user', me: true).then (user) =>
|
||||
@set 'router.globals.current_user', user
|
||||
@current_user_loaded user # overloadable
|
||||
, =>
|
||||
@set 'router.globals.current_user', null
|
||||
#@transitionTo('login').then (loginRoute) -> loginRoute.set('router.globals.flash.notice', 'Please login')
|
||||
@transitionTo('login')
|
||||
|
||||
actions:
|
||||
error: (error) ->
|
||||
return unless @connections?.length # cannot use outlet at this moment
|
||||
console.log error
|
||||
container = Ember.getOwner(@)
|
||||
if status = error.status || error?.errors?[0].status
|
||||
switch parseInt(status)
|
||||
when 401, 403
|
||||
@set 'router.globals.current_user', null
|
||||
@transitionTo 'login'
|
||||
return
|
||||
controller = container.lookup "component:error-handling"
|
||||
return unless controller?.set
|
||||
controller.set 'error', error
|
||||
@render 'global/error-messages',
|
||||
into: 'application'
|
||||
outlet: 'error-handling'
|
||||
controller: controller
|
||||
#@set "router.globals.flash_message", "Oops! Something went wrong!<br>#{error.message}".htmlSafe()
|
||||
|
||||
clear_error: ->
|
||||
#@disconnectOutlet # https://github.com/emberjs/ember-inspector/issues/625
|
||||
# outlet: 'error-handling'
|
||||
# parentView: 'application'
|
||||
@render 'modals/nothing',
|
||||
outlet: 'error-handling'
|
||||
into: 'application'
|
||||
controller: 'application'
|
||||
@set 'router.globals.modal_active', false
|
||||
|
||||
clear_error_and_go_home: ->
|
||||
@send 'clear_error'
|
||||
@transitionTo 'index'
|
||||
|
||||
didTransition: ->
|
||||
@_super arguments...
|
||||
Ember.run.next =>
|
||||
return unless path = @get('controller.currentPath')
|
||||
@set 'router.globals.current_route', Ember.Object.create
|
||||
path: path # a.b.c
|
||||
node: path.replace(/.*\./, '') # c
|
||||
primary: '.' in path
|
||||
|
||||
openModal: (modalName, options={}) ->
|
||||
if typeof options is 'function'
|
||||
console.warn "Modal #{modalName} call has function as argument in stead of options!"
|
||||
return
|
||||
else if options.promise? and options.content
|
||||
options = {model: options.content}
|
||||
else if typeof options isnt 'object'
|
||||
options = {model: options}
|
||||
options.model = options.model.content if options.model?.promise? and options.model.content
|
||||
container = Ember.getOwner(@)
|
||||
|
||||
modalNameParts = modalName.split('/')
|
||||
partialName = modalNameParts.pop()
|
||||
|
||||
controller = container.lookup "component:modals/#{modalName}"
|
||||
if not controller and modalNameParts.length
|
||||
controller = container.lookup "component:#{modalNameParts.join('/')}/modals/#{partialName}"
|
||||
controller ||= container.lookup "component:#{modalName}"
|
||||
controller ||= container.lookup('component:modals/base')
|
||||
#try
|
||||
# controller = @controllerFor("modals/#{modalName}")
|
||||
#catch error
|
||||
# controller = @controllerFor("modals/base")
|
||||
#controller ||= @controllerFor("modals/base")
|
||||
controller.set 'model', options.model
|
||||
defaultModalOptions =
|
||||
closeOnOverlay: true
|
||||
closeOnModalClick: false
|
||||
modal_options = Ember.Object.create($.extend(defaultModalOptions, options))
|
||||
controller.set 'modal_options', modal_options
|
||||
|
||||
# use root level partials for wizards and elementary use case
|
||||
# Try to resolve the modalName traversing up the hierarchy so a modalName
|
||||
# of wizard/create-distribution-point/copper/edit-distribution-point will look for partials:
|
||||
#
|
||||
# wizard/create-distribution-point/copper/-edit-distribution-point
|
||||
# wizard/create-distribution-point/copper/modals/-edit-distribution-point
|
||||
# modals/wizard/create-distribution-point/copper/-edit-distribution-point
|
||||
#
|
||||
# wizard/create-distribution-point/-edit-distribution-point
|
||||
# wizard/create-distribution-point/modals/-edit-distribution-point
|
||||
# modals/wizard/create-distribution-point/-edit-distribution-point
|
||||
#
|
||||
# wizard/-edit-distribution-point
|
||||
# wizard/modals/-edit-distribution-point
|
||||
# modals/wizard/-edit-distribution-point
|
||||
#
|
||||
# -edit-distribution-point
|
||||
# modals/-edit-distribution-point
|
||||
#
|
||||
partialPath = null
|
||||
while not partialPath and modalNameParts.length
|
||||
if container.lookup "template:#{modalNameParts.join('/')}/-#{partialName}"
|
||||
partialPath = "#{modalNameParts.join('/')}/#{partialName}" # without the dash
|
||||
else if container.lookup "template:#{modalNameParts.join('/')}/modals/-#{partialName}"
|
||||
partialPath = "#{modalNameParts.join('/')}/modals/#{partialName}" # without the dash
|
||||
else if container.lookup "template:modals/#{modalNameParts.join('/')}/modals/-#{partialName}"
|
||||
partialPath = "modals/#{modalNameParts.join('/')}/modals/#{partialName}" # without the dash
|
||||
else if container.lookup "template:components/#{modalNameParts.join('/')}/-#{partialName}"
|
||||
partialPath = "components/#{modalNameParts.join('/')}/#{partialName}" # without the dash
|
||||
modalNameParts.pop()
|
||||
partialPath ||= partialName if container.lookup("template:-#{partialName}") # can be root template
|
||||
partialPath ||= "modals/#{partialName}" if container.lookup("template:modals/-#{partialName}") # can be root template
|
||||
unless partialPath
|
||||
message = "Cannot find template for modal '#{modalName}'"
|
||||
console.log message
|
||||
@set "router.globals.flash_message", message
|
||||
return
|
||||
|
||||
@incrementProperty 'router.globals.modals_counter'
|
||||
controller.set 'partialName', partialPath
|
||||
controller.willOpenModal() # can be implemented to setup stuff before rendering and hooks
|
||||
@render 'modals/layout',
|
||||
into: 'application'
|
||||
outlet: 'modal'
|
||||
controller: controller
|
||||
@set 'router.globals.modal_active', true
|
||||
false # return false since this result will in most cases the the last statement of an action. So prevent bubbling
|
||||
|
||||
closeModal: ->
|
||||
#@disconnectOutlet # https://github.com/emberjs/ember-inspector/issues/625
|
||||
# outlet: 'modal'
|
||||
# parentView: 'application'
|
||||
@render 'modals/nothing',
|
||||
outlet: 'modal'
|
||||
into: 'application'
|
||||
controller: 'application'
|
||||
@set 'router.globals.modal_active', false
|
||||
|
||||
signOut: ->
|
||||
@set 'router.globals.current_user', null
|
||||
@get('session').invalidate() if @get('session.isAuthenticated')
|
||||
@transitionTo 'login'
|
||||
try
|
||||
$.ajax
|
||||
url: "#{config.apiHost}/users/sign_out"
|
||||
type: 'DELETE'
|
||||
dataType: 'json'
|
||||
@@ -0,0 +1,121 @@
|
||||
import Ember from 'ember'
|
||||
export default Ember.Component.extend
|
||||
alert_message: ""
|
||||
modal_options: {}
|
||||
layoutName: 'modals/layout'
|
||||
willOpenModal: -> # can be implemented, is fired after loading attributes, before rendering
|
||||
title: (->
|
||||
# return title if directly set by options
|
||||
return @get('modal_options.title') if @get('modal_options.title')
|
||||
# return translated title_path if directly set by controller
|
||||
translation_params = {}
|
||||
if title_params = @get('modal_options.title_params')
|
||||
translation_params = title_params
|
||||
else if model = @get('model')
|
||||
if model.toJSON
|
||||
translation_params = model.toJSON()
|
||||
else if model.__changeset__ and model._content?.toJSON
|
||||
translation_params = model._content.toJSON()
|
||||
|
||||
# return translated title_path if directly set by options
|
||||
# return translated title_path if directly set by controller
|
||||
if title_path = @get('modal_options.title_path') || @get('title_path')
|
||||
return tspan(title_path, translation_params).htmlSafe()
|
||||
|
||||
translation_params.emptyWhenNotFound = true # allow for fallbacks
|
||||
|
||||
partial_title_path = "#{@get('partialName').replace(/-/g, '_').replace(/\W/g, '.')}.title"
|
||||
return partialNameTranslation.htmlSafe() if partialNameTranslation = tspan(partial_title_path, translation_params)
|
||||
|
||||
# @toString() => <frontend@component:workflow-action-definition/modals/edit-workflow-action-definition::ember873>
|
||||
underscored = @toString().replace(/.*(controller|component):/, '').replace(/::ember.*$/, '').replace(/\W+$/, '').underscore().replace(/\//g, '.')
|
||||
return convention_translation.htmlSafe() if convention_translation = tspan("#{underscored}.title", translation_params)
|
||||
|
||||
# Return a kind of humanized version of the title
|
||||
underscored.replace(/.*\./,'').capitalize().replace(/_/, ' ')
|
||||
).property('model.id', 'modal_options.title_path')
|
||||
body: (->
|
||||
@get('modal_options.body')
|
||||
).property('modal_options.body')
|
||||
save_error: (error)->
|
||||
if typeof error is 'string'
|
||||
@set 'alert_message', error
|
||||
else if error?.status is 403 or error?.status is 401
|
||||
@set 'alert_message', 'Unauthorized action'
|
||||
else
|
||||
@set 'alert_message', 'Something went wrong'
|
||||
save_success: ->
|
||||
@set 'alert_message', ''
|
||||
pre_hook_modals_count = @get('globals.modals_counter')
|
||||
if save_callback = @get('modal_options.save')
|
||||
save_callback.apply(@, arguments)
|
||||
after_hook_modals_count = @get('globals.modals_counter')
|
||||
@send 'closeModal' unless after_hook_modals_count > pre_hook_modals_count # when modals count changed, another modal is opened by the hook. Do not close
|
||||
|
||||
actions:
|
||||
close: ->
|
||||
pre_hook_modals_count = @get('globals.modals_counter')
|
||||
if close = @get('modal_options.close')
|
||||
close.apply(@)
|
||||
after_hook_modals_count = @get('globals.modals_counter')
|
||||
unless @preventClose or after_hook_modals_count > pre_hook_modals_count # when modals count changed, another modal is opened by the hook. Do not close
|
||||
@set 'alert_message', ''
|
||||
@send 'closeModal'
|
||||
closeOnOverlay: ->
|
||||
@send('rollback_and_close') if @get('modal_options.closeOnOverlay')
|
||||
false
|
||||
modalClick: ->
|
||||
@send('close') if @get('modal_options.closeOnModalClick')
|
||||
false
|
||||
ok: ->
|
||||
if ok = @get('modal_options.ok')
|
||||
ok.call(@)
|
||||
@set 'alert_message', ''
|
||||
@send 'closeModal' unless @preventClose
|
||||
confirm: -> @send('ok')
|
||||
saveRecord: ->
|
||||
model = @get("model")
|
||||
if typeof model.validate is 'function'
|
||||
model.validate() # validate if method exists
|
||||
if changeset = model.get('changeset')
|
||||
changeset.validate()
|
||||
if changeset.get('isValid')
|
||||
changeset.save().then @save_success.bind(@), @save_error.bind(@)
|
||||
else
|
||||
if model.get('isValid')
|
||||
model.save().then @save_success.bind(@), @save_error.bind(@)
|
||||
#@send 'closeModal' unless @preventClose
|
||||
else
|
||||
console.log "[VALIDATION ERRORS]"
|
||||
console.log model.get("errors")
|
||||
rollback_and_close: ->
|
||||
if model = @get("model")
|
||||
if model.__changeset__
|
||||
model.rollback()
|
||||
if model.get("_content.isNew") # delete new records on modal rollback
|
||||
model.get("_content").deleteRecord() #NOTE: maybe this should be unloadRecord --force=true (state/dirty tracking issues)
|
||||
else
|
||||
model.rollbackAttributes() if typeof model.rollbackAttributes is 'function'
|
||||
@send 'close'
|
||||
destroyRecord: ->
|
||||
my_scope = @
|
||||
destroy_callback = @get('modal_options.destroy_callback') || ->
|
||||
# transition to top level route if current is nested inside a model.show route
|
||||
if modelName = my_scope.get('model.constructor.modelName')
|
||||
flat_model_name = modelName.replace(/^\w+\//, '').replace('/', '.') # panda/projec => project
|
||||
if my_scope.get('router.globals.current_route.path')?.match("^#{flat_model_name}.show")
|
||||
my_scope.get('router').transitionTo flat_model_name
|
||||
|
||||
@modal 'confirm',
|
||||
title_path: @get('modal_options.destroy_text_path') || 'general.destroy.are_you_sure'
|
||||
model: @get('model')
|
||||
ok: ->
|
||||
model = @get("model")
|
||||
model = model.get("_content") if model.__changeset__
|
||||
model.destroyRecord().then -> destroy_callback.call(my_scope, model)
|
||||
@send 'closeModal' unless @preventClose
|
||||
|
||||
closeModal: ->
|
||||
container = Ember.getOwner(@)
|
||||
ar = container.lookup('route:application')
|
||||
ar.send 'closeModal'
|
||||
@@ -0,0 +1,8 @@
|
||||
import DS from 'ember-data'
|
||||
|
||||
export default DS.Model.extend
|
||||
nickname: DS.attr('string')
|
||||
email: DS.attr('string')
|
||||
admin: DS.attr('boolean', defaultValue: false)
|
||||
role_names: DS.attr(defaultValue: -> [])
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import Ember from 'ember'
|
||||
export default Ember.Component.reopen
|
||||
modal: (name, options={}) ->
|
||||
target = Ember.getOwner(@).lookup('route:application')
|
||||
target.send "openModal", name, options
|
||||
@@ -0,0 +1,4 @@
|
||||
import Ember from 'ember'
|
||||
export default Ember.Controller.reopen
|
||||
modal: (name, options={})->
|
||||
@send "openModal", name, options
|
||||
@@ -0,0 +1,20 @@
|
||||
import Ember from 'ember'
|
||||
export default Ember.Object.reopen
|
||||
getPropertyValues: (names...) ->
|
||||
result = Ember.A()
|
||||
for name in names
|
||||
result.push @get(name)
|
||||
result
|
||||
|
||||
toProperties: ->
|
||||
result = {}
|
||||
for key, value of @
|
||||
continue unless @hasOwnProperty(key)
|
||||
continue if key is 'changeset'
|
||||
if typeof(value?.format) is 'function'
|
||||
value = value.format()
|
||||
else if typeof(value?.toISOString) is 'function'
|
||||
value = value.toISOString()
|
||||
result[key] = value
|
||||
result
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import Ember from 'ember'
|
||||
|
||||
Ember.Route.reopen
|
||||
modal: (name, options = {})->
|
||||
@send "openModal", name, options
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ActiveModelSerializer } from 'active-model-adapter'
|
||||
export default ActiveModelSerializer.extend #DS.EmbeddedRecordsMixin,
|
||||
|
||||
#serializeIntoHash: ->
|
||||
# debugger
|
||||
# @_super arguments...
|
||||
# debugger
|
||||
# 3
|
||||
|
||||
#payloadKeyFromModelName: (modelName) ->
|
||||
# key = @_super(modelName)
|
||||
# key.replace(/\//g, '.')
|
||||
|
||||
serializeBelongsTo: (snapshot, json, relationship)->
|
||||
payloadKey = @keyForRelationship(relationship.key, 'belongsTo', 'serialize')
|
||||
belongsToId = snapshot.record.get("#{relationship.key}.id")
|
||||
belongsToId?= null
|
||||
json[payloadKey] = belongsToId
|
||||
# the super method looks up the inverse (workflow_step_definition) in stead of the constraint_workflow_step_definition id
|
||||
#debugger
|
||||
#@_super(arguments...)
|
||||
|
||||
# If a relationship has configuration serialize: 'ids' then add the association's ids
|
||||
# comments: DS.hasMany('comment', serialize: 'ids')
|
||||
serializeHasMany: (snapshot, json, relationship)->
|
||||
if relationship.options.serialize is 'ids'
|
||||
json["#{relationship.key.singularize()}_ids"] = Ember.get(snapshot.record, relationship.key).mapBy('id').toArray()
|
||||
@@ -0,0 +1,69 @@
|
||||
.pointable
|
||||
cursor: pointer
|
||||
|
||||
.comma-separated-items
|
||||
> *
|
||||
&:after
|
||||
content: ", "
|
||||
&:last-child
|
||||
&:after
|
||||
content: ""
|
||||
|
||||
.pull-right
|
||||
float: right
|
||||
|
||||
.unpadded
|
||||
padding: 0 !important
|
||||
|
||||
.ui.table
|
||||
td
|
||||
&.compact
|
||||
padding: 0.5em 0.7em
|
||||
|
||||
input:disabled
|
||||
background-color: #ccc !important
|
||||
|
||||
// .ui.grid might end up with columns having a negative bottom margin that captures the click event of the svg below it
|
||||
.without-bottom-margin
|
||||
margin-bottom: 0 !important
|
||||
|
||||
.between-brackets
|
||||
&:before
|
||||
content: '('
|
||||
&:after
|
||||
content: ')'
|
||||
&.curly
|
||||
&:before
|
||||
content: '{'
|
||||
&:after
|
||||
content: '}'
|
||||
&.angle
|
||||
&:before
|
||||
content: '<'
|
||||
&:after
|
||||
content: '>'
|
||||
&.square
|
||||
&:before
|
||||
content: '['
|
||||
&:after
|
||||
content: ']'
|
||||
&.spaced
|
||||
&:before
|
||||
content: '( '
|
||||
&:after
|
||||
content: ' )'
|
||||
&.curly
|
||||
&:before
|
||||
content: '{ '
|
||||
&:after
|
||||
content: ' }'
|
||||
&.angle
|
||||
&:before
|
||||
content: '< '
|
||||
&:after
|
||||
content: ' >'
|
||||
&.square
|
||||
&:before
|
||||
content: '[ '
|
||||
&:after
|
||||
content: ' ]'
|
||||
@@ -0,0 +1,17 @@
|
||||
$non-default-attention-color: #f2711c;
|
||||
|
||||
@import "./mixins/animations";
|
||||
|
||||
@import './convenience-helpers';
|
||||
|
||||
@import "./components/collapsible-content";
|
||||
@import "./components/error-handling";
|
||||
@import "./components/modal";
|
||||
@import "./components/flash-message";
|
||||
@import "./components/forms";
|
||||
@import "./components/page-title";
|
||||
@import "./components/pagination";
|
||||
@import "./components/table-filtering";
|
||||
@import "./components/title-box";
|
||||
@import "./components/ui-calendar-additions";
|
||||
@import "./components/ui-markdown-popup";
|
||||
@@ -0,0 +1,3 @@
|
||||
.collapsible-content-container
|
||||
.header
|
||||
cursor: pointer
|
||||
@@ -0,0 +1,3 @@
|
||||
.dunlop-global-error
|
||||
&.hidden
|
||||
display: none
|
||||
@@ -0,0 +1,26 @@
|
||||
.flash-message
|
||||
position: fixed
|
||||
top: 60px
|
||||
right: -440px
|
||||
background-color: rgba(200,200,200,0.8)
|
||||
padding: 20px
|
||||
width: 400px
|
||||
border-radius: 9999px
|
||||
font-weight: bold
|
||||
font-size: 1.4
|
||||
z-index: 8976
|
||||
+animation(flashMessageInactive 0.5s)
|
||||
&.active
|
||||
+animation(flashMessageActive 0.5s)
|
||||
right: 20px
|
||||
|
||||
+keyframes(flashMessageActive)
|
||||
from
|
||||
right: -440px
|
||||
to
|
||||
right: 20px
|
||||
+keyframes(flashMessageInactive)
|
||||
from
|
||||
right: 20px
|
||||
to
|
||||
right: -440px
|
||||
@@ -0,0 +1,19 @@
|
||||
.ui.form
|
||||
.form-label
|
||||
display: inline-block
|
||||
vertical-align: top
|
||||
width: 35%
|
||||
.form-field
|
||||
display: inline-block
|
||||
width: 64%
|
||||
@media screen and (max-width: 723px)
|
||||
.form-label
|
||||
width: 99%
|
||||
.form-field
|
||||
width: 99%
|
||||
|
||||
.form-actions
|
||||
clear: both
|
||||
margin-top: 12px
|
||||
padding-top: 8px
|
||||
border-top: 1px solid #ddd
|
||||
@@ -0,0 +1,74 @@
|
||||
.modal
|
||||
margin: 10px auto
|
||||
width: 1122px
|
||||
max-width: 100%
|
||||
background-color: #fff
|
||||
padding: 1em
|
||||
padding-bottom: 0
|
||||
max-height: calc(100% - 20px)
|
||||
overflow-y: scroll
|
||||
z-index: 6524
|
||||
.modal-body
|
||||
// Add space for modal-actions
|
||||
padding-bottom: 86px
|
||||
position: relative
|
||||
min-height: 392px
|
||||
.modal-rollback
|
||||
//+button($bg: $secondary-color)
|
||||
//@extend .ui, .button
|
||||
.modal-close
|
||||
//+button($bg: #ddd)
|
||||
//@extend .ui, .button
|
||||
.modal-confirm, .modal-ok, .modal-save, .modal-add
|
||||
//+button
|
||||
//@extend .primary
|
||||
.modal-destroy
|
||||
//+button($bg: $alert-color)
|
||||
//@extend .negative
|
||||
margin-right: 8px
|
||||
.modal-edit
|
||||
//+button($bg: $warning-color)
|
||||
margin-right: 8px
|
||||
.done
|
||||
text-decoration: line-through
|
||||
.modal-actions
|
||||
border-top: 1px solid #999
|
||||
padding-top: 7px
|
||||
padding-bottom: 7px
|
||||
margin-bottom: -86px
|
||||
> a
|
||||
margin-bottom: 0
|
||||
&.sticky
|
||||
position: absolute
|
||||
margin-bottom: 0
|
||||
box-shadow: 0 -3px 9px -2px rgba(0, 0, 0, 0.8)
|
||||
background-color: #eee
|
||||
padding-left: 1em
|
||||
padding-right: 1em
|
||||
bottom: 0
|
||||
width: calc(100% + 2em)
|
||||
//Width: 100%
|
||||
margin-left: -1em
|
||||
//margin-right: -1em
|
||||
border-top: 1px solid #999
|
||||
.pull-right
|
||||
.ui.action.input
|
||||
margin-right: 122px
|
||||
.ui.message
|
||||
margin: 0
|
||||
.modal-alert
|
||||
//@extend .alert-box
|
||||
//@extend .alert
|
||||
//color: $alert-color
|
||||
|
||||
.overlay
|
||||
height: 100%
|
||||
width: 100%
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
background-color: rgba(0, 0, 0, 0.5)
|
||||
z-index: 6522
|
||||
|
||||
.flush--top
|
||||
margin-top: 0
|
||||
@@ -0,0 +1,6 @@
|
||||
.page-title
|
||||
&.ui.header
|
||||
margin-top: 12px
|
||||
.between-brackets
|
||||
&:before
|
||||
margin-left: 1rem
|
||||
@@ -0,0 +1,33 @@
|
||||
ul.pagination
|
||||
padding: 0
|
||||
list-style: none
|
||||
a
|
||||
cursor: pointer
|
||||
&.disabled
|
||||
cursor: default
|
||||
li
|
||||
display: inline
|
||||
&.arrow
|
||||
a
|
||||
padding: 0 6px
|
||||
&.next
|
||||
padding-left: 3px // subtract margin-right from li
|
||||
&.page-number
|
||||
margin-right: 3px
|
||||
a
|
||||
background-color: #ccc
|
||||
padding: 4px
|
||||
color: #666
|
||||
border-radius: 3px
|
||||
&.active
|
||||
a
|
||||
color: white
|
||||
background-color: black
|
||||
font-weight: bold
|
||||
|
||||
.ui.top-right.pagination.menu
|
||||
float: right
|
||||
border-bottom-left-radius: 0
|
||||
border-bottom-right-radius: 0
|
||||
margin-top: 8px
|
||||
border-bottom-width: 0
|
||||
@@ -0,0 +1,81 @@
|
||||
// ransack componennt integration
|
||||
table thead tr.table-filters-row, table.ui.table thead tr.table-filters-row
|
||||
input
|
||||
margin: 0
|
||||
margin-top: 3px
|
||||
padding: 3px
|
||||
button
|
||||
margin: 0
|
||||
margin-top: 3px
|
||||
th
|
||||
padding: 0 4px
|
||||
&.active-filter
|
||||
background-color: rgba(242, 112, 28, 0.15)
|
||||
color: $non-default-attention-color
|
||||
.active-button
|
||||
background-color: $non-default-attention-color
|
||||
color: white
|
||||
font-weight: bold
|
||||
i, i.ui.icon
|
||||
cursor: pointer
|
||||
pointer-events: auto
|
||||
// Allow force by means of extra class
|
||||
&.action
|
||||
cursor: pointer !important
|
||||
pointer-events: auto !important
|
||||
&.filter-date-range
|
||||
.menu
|
||||
.divider + .item
|
||||
padding-top: 0 !important
|
||||
.item + .divider
|
||||
margin-top: 0
|
||||
.select-week
|
||||
input
|
||||
width: 72px
|
||||
.select-year
|
||||
input
|
||||
width: 92px
|
||||
.ui.horizontal.divider
|
||||
// Without this css it renders a double border if text is used
|
||||
border-top: 0
|
||||
|
||||
|
||||
|
||||
// semantic-ui implementation
|
||||
table
|
||||
td, th
|
||||
&.actions
|
||||
// semantic-ui .collapsing
|
||||
width: 1px
|
||||
white-space: nowrap
|
||||
text-align: right
|
||||
.ui.icon.button
|
||||
padding: 0.4rem
|
||||
table.ui.table
|
||||
thead
|
||||
// custom
|
||||
tr.filters
|
||||
th
|
||||
padding: 0
|
||||
i.close.icon
|
||||
cursor: pointer
|
||||
margin-left: 6px
|
||||
.active-filter
|
||||
.active-button
|
||||
background-color: $non-default-attention-color
|
||||
color: white
|
||||
font-weight: bold
|
||||
tr.table-filters-row
|
||||
.filter-dropdown-select-multiple
|
||||
.ui.action.input
|
||||
margin-right: 49px // space for action button
|
||||
input
|
||||
margin-top: 0
|
||||
.ui.icon.button
|
||||
i
|
||||
// This one is somehow needed for the right filter icon fit
|
||||
padding-top: 0
|
||||
.ui.header
|
||||
&.active-filter
|
||||
color: $non-default-attention-color
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* Based on the bootstrap 3+ panel, of course under a different name.
|
||||
The conversion is done based on:
|
||||
http://stackoverflow.com/questions/19220027/can-you-use-panels-in-bootstrap-2-3
|
||||
.title-box
|
||||
padding: 15px
|
||||
margin-bottom: 20px
|
||||
background-color: #ffffff
|
||||
border: 1px solid #dddddd
|
||||
border-radius: 4px
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05)
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05)
|
||||
|
||||
.title-box-heading
|
||||
padding: 10px 15px
|
||||
margin: -15px -15px 15px
|
||||
font-size: 17.5px
|
||||
font-weight: 500
|
||||
background-color: #f5f5f5
|
||||
border-bottom: 1px solid #dddddd
|
||||
border-top-right-radius: 3px
|
||||
border-top-left-radius: 3px
|
||||
|
||||
.title-box-footer
|
||||
padding: 10px 15px
|
||||
margin: 15px -15px -15px
|
||||
background-color: #f5f5f5
|
||||
border-top: 1px solid #dddddd
|
||||
border-bottom-right-radius: 3px
|
||||
border-bottom-left-radius: 3px
|
||||
|
||||
.title-box-primary
|
||||
border-color: #428bca
|
||||
.title-box-heading
|
||||
color: #ffffff
|
||||
background-color: #428bca
|
||||
border-color: #428bca
|
||||
|
||||
.title-box-success
|
||||
border-color: #d6e9c6
|
||||
|
||||
.title-box-heading
|
||||
color: #468847
|
||||
background-color: #dff0d8
|
||||
border-color: #d6e9c6
|
||||
|
||||
.title-box-warning
|
||||
border-color: #fbeed5
|
||||
|
||||
.title-box-heading
|
||||
color: #c09853
|
||||
background-color: #fcf8e3
|
||||
border-color: #fbeed5
|
||||
|
||||
.title-box-danger
|
||||
border-color: #eed3d7
|
||||
|
||||
.title-box-heading
|
||||
color: #b94a48
|
||||
background-color: #f2dede
|
||||
border-color: #eed3d7
|
||||
|
||||
.title-box-info
|
||||
border-color: #bce8f1
|
||||
|
||||
.title-box-heading
|
||||
color: #3a87ad
|
||||
background-color: #d9edf7
|
||||
border-color: #bce8f1
|
||||
|
||||
//State specific additions
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// This fixes the issue of ember-semantic-ui-calendar not functioning inside a definition table
|
||||
.ui.definition.table .ui.calendar
|
||||
padding: 0
|
||||
table
|
||||
//border: 1px solid rgba(34,36,38,.15)
|
||||
border-radius: .28571429rem
|
||||
box-shadow: none
|
||||
color: rgba(0,0,0,.87)
|
||||
tr
|
||||
th
|
||||
pointer-events: all
|
||||
font-weight: bold
|
||||
color: rgba(0,0,0,.87)
|
||||
background-color: #F9FAFB
|
||||
&:nth-child(2)
|
||||
border-left: 0
|
||||
td
|
||||
pointer-events: all
|
||||
&:first-child:not(.ignored)
|
||||
background-color: white
|
||||
font-weight: normal
|
||||
&.disabled
|
||||
color: rgba(40, 40, 40, 0.3)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
body .ui.popup
|
||||
z-index: 9021
|
||||
>
|
||||
ul, ol
|
||||
margin-top: 4px
|
||||
margin-bottom: 4px
|
||||
padding-left: 1em
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
=animation($animate...)
|
||||
$max: length($animate)
|
||||
$animations: ""
|
||||
|
||||
@for $i from 1 through $max
|
||||
$animations: #{$animations + nth($animate, $i)}
|
||||
|
||||
@if $i < $max
|
||||
$animations: #{$animations + ", "}
|
||||
|
||||
-webkit-animation: $animations
|
||||
-moz-animation: $animations
|
||||
-o-animation: $animations
|
||||
animation: $animations
|
||||
|
||||
=keyframes($animationName)
|
||||
@-webkit-keyframes #{$animationName}
|
||||
@content
|
||||
|
||||
@-moz-keyframes #{$animationName}
|
||||
@content
|
||||
|
||||
@-o-keyframes #{$animationName}
|
||||
@content
|
||||
|
||||
@keyframes #{$animationName}
|
||||
@content
|
||||
@@ -0,0 +1,9 @@
|
||||
if collapsed
|
||||
.ui.header{action 'toggleCollapsed'}
|
||||
i.arrow.right.icon
|
||||
.content= title
|
||||
else
|
||||
.ui.header{action 'toggleCollapsed'}
|
||||
i.arrow.down.icon
|
||||
.content= title
|
||||
= yield
|
||||
@@ -0,0 +1,26 @@
|
||||
/.ui.form
|
||||
.ui.right.floated.icon.buttons
|
||||
if isEditing
|
||||
= push-action type='rollback' action='rollbackRecord'
|
||||
= push-action type='destroy' action='destroyRecord'
|
||||
= push-action type='save' action='saveRecord'
|
||||
else
|
||||
if can_make_editable
|
||||
/= push-action type='edit' action='editRecord'
|
||||
if isEditing
|
||||
= input value=model.name enter='saveRecord'
|
||||
else
|
||||
span= model.name
|
||||
if isEditing
|
||||
.ui.action.input
|
||||
= input value=model.name enter='saveRecord'
|
||||
= push-action type='rollback' action='rollbackRecord'
|
||||
= push-action type='destroy' action='destroyRecord'
|
||||
= push-action type='save' action='saveRecord'
|
||||
if validation_message
|
||||
br
|
||||
.ui.pointing.red.basic.label= validation_message
|
||||
else
|
||||
span= model.name
|
||||
= yield
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
i.close.icon
|
||||
.header= error_header
|
||||
= yield
|
||||
@@ -0,0 +1,3 @@
|
||||
= progressText
|
||||
= yield
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
i.icon class=iconClass
|
||||
if hasBlock
|
||||
span.push-action-content= yield
|
||||
@@ -0,0 +1 @@
|
||||
= yield
|
||||
@@ -0,0 +1 @@
|
||||
import './freeze-moment'
|
||||
@@ -0,0 +1,11 @@
|
||||
window.freezeMoment = (time, callback) ->
|
||||
#time = "#{time}Z" if typeof time is 'string' and time.slice(-1) isnt 'Z' # work with UTC dates?
|
||||
time = moment(time) unless time._isAMomentObject
|
||||
originalMomentNow = moment.now
|
||||
date = time.toDate()
|
||||
moment.now = -> date
|
||||
callback.call null, time
|
||||
moment.now = originalMomentNow
|
||||
time
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import DS from 'ember-data'
|
||||
import moment from 'moment'
|
||||
|
||||
# mozo magick, better than ember-cli-moment-transform, since it preserves dates through time zones!!!!
|
||||
export default DS.Transform.extend
|
||||
deserialize: (serialized)->
|
||||
return serialized unless serialized
|
||||
m = moment(serialized)
|
||||
m.type = 'date'
|
||||
m
|
||||
|
||||
serialize: (deserialized, options = {})->
|
||||
return deserialized unless deserialized
|
||||
deserialized = moment(deserialized) unless moment.isMoment(deserialized)
|
||||
deserialized.format(options.format || 'YYYY-MM-DD')
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import DS from 'ember-data'
|
||||
import moment from 'moment'
|
||||
|
||||
# mozo magick, better than ember-cli-moment-transform, since it preserves dates through time zones!!!!
|
||||
export default DS.Transform.extend
|
||||
deserialize: (serialized)->
|
||||
return serialized unless serialized
|
||||
m = moment(serialized)
|
||||
m.type = 'datetime'
|
||||
m
|
||||
|
||||
serialize: (deserialized, options = {})->
|
||||
return deserialized unless deserialized
|
||||
deserialized = moment(deserialized) unless moment.isMoment(deserialized)
|
||||
deserialized.format(options.format)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
dateSort = (date_a, date_b) ->
|
||||
if date_a
|
||||
a = if date_a.toISOString then date_a.toISOString() else date_a
|
||||
else
|
||||
a = null
|
||||
if date_b
|
||||
b = if date_b.toISOString then date_b.toISOString() else date_b
|
||||
else
|
||||
b = null
|
||||
Ember.compare a, b
|
||||
|
||||
create_date_sort = (attribute) ->
|
||||
(object_a, object_b) ->
|
||||
dateSort object_a.get(attribute), object_b.get(attribute)
|
||||
export { create_date_sort }
|
||||
export default dateSort
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
getMaxSuffix = (array) ->
|
||||
max = 0
|
||||
return 0 unless array
|
||||
array.forEach (element)->
|
||||
return unless element
|
||||
max = Math.max(match[0], max) if match = String(element).match /\d+$/
|
||||
max
|
||||
|
||||
window.get_max_suffix = getMaxSuffix # make globally available
|
||||
export default getMaxSuffix
|
||||
@@ -0,0 +1,11 @@
|
||||
# based on https://github.com/alisdair/horses-and-chickens
|
||||
import Ember from 'ember'
|
||||
|
||||
export default (attribute) ->
|
||||
Ember.computed attribute,
|
||||
get: ->
|
||||
@get(attribute).toString()
|
||||
|
||||
set: (key, value) ->
|
||||
@set(attribute, parseInt(value, 10))
|
||||
return value
|
||||
@@ -0,0 +1,46 @@
|
||||
mapSum = (target, property) ->
|
||||
Ember.computed "#{target}.@each.#{property}", ->
|
||||
return 0 unless ary_result = @get(target).mapBy(property)
|
||||
return 0 unless ary_result.length
|
||||
ary_result.reduce( ((sum, item) -> sum + item), 0)
|
||||
|
||||
mapFilter = (target, property, filter_by_key) ->
|
||||
Ember.computed "#{target}.@each.#{property}", filter_by_key, ->
|
||||
@get(target).filterBy(property, @get(filter_by_key))
|
||||
|
||||
mapHasMany = (target, has_many_relation) ->
|
||||
Ember.computed "#{target}.@each.#{has_many_relation}", ->
|
||||
result = Ember.A()
|
||||
Ember.RSVP.all(@get(target).mapBy(has_many_relation)).then (targets_has_many_associations) ->
|
||||
Ember.run ->
|
||||
targets_has_many_associations.forEach (target_has_many_records) ->
|
||||
result.pushObjects target_has_many_records.toArray()
|
||||
result
|
||||
|
||||
mapMany = (target, many_attribute) ->
|
||||
Ember.computed "#{target}.@each.#{many_attribute}", ->
|
||||
result = Ember.A()
|
||||
Ember.run =>
|
||||
targets = @get(target)
|
||||
if Ember.isArray(targets)
|
||||
targets.forEach (record) ->
|
||||
result.pushObjects record.get(many_attribute)
|
||||
else
|
||||
targets.then (records) ->
|
||||
records.forEach (record) ->
|
||||
result.pushObjects record.get(many_attribute)
|
||||
result
|
||||
|
||||
mapMax = (target, property, options={}) ->
|
||||
Ember.computed "#{target}.@each.#{property}", ->
|
||||
return 0 unless ary_result = @get(target).mapBy(property)
|
||||
return 0 unless ary_result.length
|
||||
ary_result.reduce (max, item) ->
|
||||
Math.max(max, item)
|
||||
, -Infinity
|
||||
|
||||
export { mapSum, mapFilter, mapHasMany, mapMany, mapMax }
|
||||
export default Ember.Object.create
|
||||
mapSum: mapSum
|
||||
mapFilter: mapFilter
|
||||
mapHasMany: mapHasMany
|
||||
@@ -0,0 +1,12 @@
|
||||
# This is an objects merge helper but in stead of
|
||||
# mergin into the first object, it merges into the last object.
|
||||
# This is a lot safer and better in coffeescript environments, since the
|
||||
# last argument is the indented subject(focus) of configuration.
|
||||
# Since the attributes of these have prevalence, this merger comes last
|
||||
# in the used Ember.assign
|
||||
mergeObjects = (objects..., target) ->
|
||||
result = {}
|
||||
Ember.assign result, objects..., target
|
||||
result
|
||||
window.merge_objects = mergeObjects
|
||||
export default mergeObjects
|
||||
@@ -0,0 +1,17 @@
|
||||
export default Ember.Object.create
|
||||
# go from string:
|
||||
# 19, 21-27, 98-101
|
||||
# to
|
||||
# [19,21,22,23,24,25,26,27,98,99,100,101]
|
||||
parse: (identifier) ->
|
||||
ids = []
|
||||
return ids unless identifier
|
||||
String(identifier).replace(/\s/g, '').split(',').forEach (id_spec) ->
|
||||
[id_start, id_end] = id_spec.split('-')
|
||||
id_start = parseInt(id_start)
|
||||
if id_end
|
||||
range_result = [id_start..id_end]
|
||||
ids = ids.concat range_result
|
||||
else
|
||||
ids.push id_start
|
||||
ids
|
||||
@@ -0,0 +1,12 @@
|
||||
sortNumeric = (target, keys...) ->
|
||||
Ember.computed.sort target, (a, b) ->
|
||||
[int_a, int_b] = [parseInt(a.get(keys[0])), parseInt(b.get(keys[0]))]
|
||||
if int_a > int_b
|
||||
1
|
||||
else if int_a < int_b
|
||||
-1
|
||||
else
|
||||
0
|
||||
|
||||
export { sortNumeric }
|
||||
export default sortNumeric
|
||||
@@ -0,0 +1,34 @@
|
||||
import Ember from 'ember'
|
||||
import buildMessage from 'ember-changeset-validations/utils/validation-errors'
|
||||
|
||||
_isPresent = (value) ->
|
||||
if (value instanceof Ember.ObjectProxy || value instanceof Ember.ArrayProxy)
|
||||
return Ember._isPresent(Ember.get(value, 'content'))
|
||||
Ember.isPresent(value)
|
||||
|
||||
_testPresence = (key, value, presence, context = {}) ->
|
||||
if presence
|
||||
return Ember._isPresent(value) || buildMessage(key, 'present', value, context)
|
||||
else
|
||||
return Ember.isBlank(value) || buildMessage(key, 'blank', value, context)
|
||||
|
||||
export default (opts)->
|
||||
(key, value, oldValue, changes, content) =>
|
||||
if typeof opts is "function"
|
||||
presence = true
|
||||
condition = opts
|
||||
else
|
||||
presence = if "presence" in opts then opts.presence else true
|
||||
condition = opts.condition
|
||||
|
||||
# since contentn is the model we have to emulate the changes for now
|
||||
condition_object = Ember.assign(content.toJSON(), changes)
|
||||
if condition.call(condition_object) # perform validation when condition returns true
|
||||
if presence
|
||||
_isPresent(value) || buildMessage(key, 'present', value, opts)
|
||||
else
|
||||
isBlank(value) || buildMessage(key, 'blank', value, opts)
|
||||
else
|
||||
true
|
||||
#return _testPresence(key, value, opts)
|
||||
|
||||
Reference in New Issue
Block a user