chr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/CONTRIBUTING.md +24 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +21 -0
  6. data/README.md +18 -0
  7. data/Rakefile +3 -0
  8. data/app/assets/javascripts/chr.coffee +41 -0
  9. data/app/assets/javascripts/chr/core/_chr.coffee +89 -0
  10. data/app/assets/javascripts/chr/core/_item.coffee +85 -0
  11. data/app/assets/javascripts/chr/core/_list.coffee +154 -0
  12. data/app/assets/javascripts/chr/core/_listReorder.coffee +70 -0
  13. data/app/assets/javascripts/chr/core/_listScroll.coffee +23 -0
  14. data/app/assets/javascripts/chr/core/_listSearch.coffee +28 -0
  15. data/app/assets/javascripts/chr/core/_module.coffee +98 -0
  16. data/app/assets/javascripts/chr/core/_utils.coffee +50 -0
  17. data/app/assets/javascripts/chr/core/_view.coffee +121 -0
  18. data/app/assets/javascripts/chr/form/_form.coffee +205 -0
  19. data/app/assets/javascripts/chr/form/_inputCheckbox.coffee +70 -0
  20. data/app/assets/javascripts/chr/form/_inputColor.coffee +35 -0
  21. data/app/assets/javascripts/chr/form/_inputFile.coffee +82 -0
  22. data/app/assets/javascripts/chr/form/_inputHidden.coffee +41 -0
  23. data/app/assets/javascripts/chr/form/_inputList.coffee +142 -0
  24. data/app/assets/javascripts/chr/form/_inputSelect.coffee +59 -0
  25. data/app/assets/javascripts/chr/form/_inputString.coffee +87 -0
  26. data/app/assets/javascripts/chr/form/_inputText.coffee +23 -0
  27. data/app/assets/javascripts/chr/form/_nestedForm.coffee +164 -0
  28. data/app/assets/javascripts/chr/store/_store.coffee +104 -0
  29. data/app/assets/javascripts/chr/store/_storeRails.coffee +167 -0
  30. data/app/assets/javascripts/chr/vendor/jquery.scrollparent.js +14 -0
  31. data/app/assets/javascripts/chr/vendor/jquery.textarea_autosize.js +55 -0
  32. data/app/assets/javascripts/chr/vendor/jquery.typeahead.js +1782 -0
  33. data/app/assets/javascripts/chr/vendor/slip.js +804 -0
  34. data/app/assets/stylesheets/_chr.scss +7 -0
  35. data/app/assets/stylesheets/core/_icons.scss +124 -0
  36. data/app/assets/stylesheets/core/_list.scss +44 -0
  37. data/app/assets/stylesheets/core/_main.scss +89 -0
  38. data/app/assets/stylesheets/core/_responsive.scss +41 -0
  39. data/app/assets/stylesheets/form/_form.scss +50 -0
  40. data/app/assets/stylesheets/form/_input_checkbox.scss +87 -0
  41. data/app/assets/stylesheets/form/_input_color.scss +10 -0
  42. data/app/assets/stylesheets/form/_input_file.scss +28 -0
  43. data/app/assets/stylesheets/form/_input_list.scss +36 -0
  44. data/app/assets/stylesheets/form/_input_string.scss +8 -0
  45. data/app/assets/stylesheets/form/_input_text.scss +48 -0
  46. data/app/assets/stylesheets/form/_nested_form.scss +26 -0
  47. data/chr.gemspec +34 -0
  48. data/lib/chr.rb +15 -0
  49. data/lib/chr/engine.rb +5 -0
  50. data/lib/chr/version.rb +3 -0
  51. metadata +152 -0
@@ -0,0 +1,70 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT CHECKBOX
3
+ # -----------------------------------------------------------------------------
4
+ class @InputCheckbox extends InputString
5
+ _safeValue: ->
6
+ if not @value or @value == 'false' or @value == 0 or @value == '0'
7
+ return false
8
+ else
9
+ return true
10
+
11
+ _addInput: ->
12
+ # NOTE: for boolean checkbox to be serialized correctly we need a hidden false
13
+ # value which is used by default and overriden by checked value
14
+ @$false_hidden_input =$ "<input type='hidden' name='#{ @name }' value='false' />"
15
+ @$el.append @$false_hidden_input
16
+
17
+ @$input =$ "<input type='checkbox' name='#{ @name }' id='#{ @name }' value='true' #{ if @_safeValue() then 'checked' else '' } />"
18
+ @$el.append @$input
19
+
20
+ #
21
+ # PUBLIC
22
+ #
23
+
24
+ constructor: (@name, @value, @config, @object) ->
25
+ @$el =$ "<label for='#{ @name }' class='input-#{ @config.type } input-#{ @config.klass } #{ @config.klassName }'>"
26
+
27
+ @_addInput()
28
+ @_addLabel()
29
+
30
+ return this
31
+
32
+ updateValue: (@value) ->
33
+ @$input.prop('checked', @_safeValue())
34
+
35
+ hash: (hash={}) ->
36
+ hash[@config.klassName] = @$input.prop('checked')
37
+ return hash
38
+
39
+ _chrFormInputs['checkbox'] = InputCheckbox
40
+
41
+ # -----------------------------------------------------------------------------
42
+ # INPUT CHECKBOX SWITCH
43
+ # -----------------------------------------------------------------------------
44
+ class @InputCheckboxSwitch extends InputCheckbox
45
+ _addInput: ->
46
+ @$switch =$ "<div class='switch'>"
47
+ @$el.append @$switch
48
+
49
+ @$false_hidden_input =$ "<input type='hidden' name='#{ @name }' value='false' />"
50
+ @$switch.append @$false_hidden_input
51
+
52
+ @$input =$ "<input type='checkbox' name='#{ @name }' id='#{ @name }' value='true' #{ if @_safeValue() then 'checked' else '' } />"
53
+ @$switch.append @$input
54
+
55
+ @$checkbox =$ "<div class='checkbox'>"
56
+ @$switch.append @$checkbox
57
+
58
+ constructor: (@name, @value, @config, @object) ->
59
+ @$el =$ "<label for='#{ @name }' class='input-#{ @config.type } input-#{ @config.klass } #{ @config.klassName }'>"
60
+
61
+ @_addLabel()
62
+ @_addInput()
63
+
64
+ return this
65
+
66
+ _chrFormInputs['switch'] = InputCheckboxSwitch
67
+
68
+
69
+
70
+
@@ -0,0 +1,35 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT COLOR
3
+ # -----------------------------------------------------------------------------
4
+ class @InputColor extends InputString
5
+ _addColorPreview: ->
6
+ @$colorPreview =$ "<div class='preview'>"
7
+ @$el.append @$colorPreview
8
+
9
+ _updateColorPreview: ->
10
+ @$colorPreview.css { 'background-color': "##{ @$input.val() }" }
11
+
12
+ _validateInputValue: ->
13
+ if (/^(?:[0-9a-f]{3}){1,2}$/i).test(@$input.val())
14
+ @hideErrorMessage()
15
+ else
16
+ @showErrorMessage('Invalid hex value')
17
+
18
+ initialize: ->
19
+ @_addColorPreview()
20
+ @_updateColorPreview()
21
+
22
+ @$input.on 'change keyup', (e) =>
23
+ @hideErrorMessage()
24
+ @_validateInputValue()
25
+ @_updateColorPreview()
26
+
27
+ @config.onInitialize?(this)
28
+
29
+
30
+ _chrFormInputs['color'] = InputColor
31
+
32
+
33
+
34
+
35
+
@@ -0,0 +1,82 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT FILE
3
+ # -----------------------------------------------------------------------------
4
+ class @InputFile extends InputString
5
+ _addInput: ->
6
+ @$el.addClass 'empty'
7
+ if @filename
8
+ @$link =$ "<a href='#{ @value.url }' target='_blank' title='#{ @filename }'>#{ @filename }</a>"
9
+ @$el.append @$link
10
+ @$el.removeClass 'empty'
11
+
12
+ @$input =$ "<input type='file' name='#{ @name }' id='#{ @name }' />"
13
+ @$el.append @$input
14
+
15
+ _addRemoveCheckbox: ->
16
+ # NOTE: this is Rails (CarrierWave) approach to remove files, might not be
17
+ # generic, so we should consider to move it to store.
18
+ if @filename
19
+ removeInputName = @removeName()
20
+
21
+ @$removeLabel =$ "<label for='#{ removeInputName }'>Remove</label>"
22
+ @$link.after @$removeLabel
23
+
24
+ @$hiddenRemoveInput =$ "<input type='hidden' name='#{ removeInputName }' value='false'>"
25
+ @$removeInput =$ "<input type='checkbox' name='#{ removeInputName }' id='#{ removeInputName }' value='true'>"
26
+
27
+ @$link.after @$removeInput
28
+ @$link.after @$hiddenRemoveInput
29
+
30
+ constructor: (@name, @value, @config, @object) ->
31
+ @$el =$ "<div class='input-#{ @config.type } input-#{ @config.klass } #{ @config.klassName }'>"
32
+
33
+ # NOTE: carrierwave filename workaround
34
+ @filename = null
35
+ if @value.url
36
+ @filename = _last(@value.url.split('/'))
37
+ if @filename == '_old_' then @filename = null
38
+
39
+ @_addLabel()
40
+ @_addInput()
41
+ @_addRemoveCheckbox()
42
+
43
+ return this
44
+
45
+ removeName: -> @name.reverse().replace('[', '[remove_'.reverse()).reverse()
46
+
47
+ updateValue: (@value) ->
48
+ # TODO: this method required to enable version switch for objects history
49
+
50
+
51
+ _chrFormInputs['file'] = InputFile
52
+
53
+ # -----------------------------------------------------------------------------
54
+ # INPUT FILE IMAGE
55
+ # -----------------------------------------------------------------------------
56
+ class @InputFileImage extends InputFile
57
+ _addInput: ->
58
+ @$el.addClass 'empty'
59
+ if @filename
60
+ @$link =$ "<a href='#{ @value.url }' target='_blank' title='#{ @filename }'>#{ @filename }</a>"
61
+ @$el.append @$link
62
+
63
+ thumbnailImageUrl = @value.url
64
+ thumbnailImage = @value[@config.thumbnailFieldName]
65
+
66
+ if thumbnailImage
67
+ thumbnailImageUrl = thumbnailImage.url
68
+
69
+ @$thumb =$ "<img src='#{ thumbnailImageUrl }' />"
70
+ @$el.append @$thumb
71
+
72
+ @$el.removeClass 'empty'
73
+
74
+ @$input =$ "<input type='file' name='#{ @name }' id='#{ @name }' />"
75
+ @$el.append @$input
76
+
77
+
78
+ _chrFormInputs['image'] = InputFileImage
79
+
80
+
81
+
82
+
@@ -0,0 +1,41 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT HIDDEN
3
+ # -----------------------------------------------------------------------------
4
+ class @InputHidden
5
+ constructor: (@name, @value, @config, @object) ->
6
+ @$el = $("<input type='hidden' name='#{ @name }' value='#{ @_valueSafe() }' id='#{ @name }' />")
7
+
8
+ return this
9
+
10
+ _valueSafe: ->
11
+ if typeof(@value) == 'object'
12
+ JSON.stringify(@value)
13
+ else
14
+ _escapeHtml(@value)
15
+
16
+ #
17
+ # PUBLIC
18
+ #
19
+
20
+ initialize: ->
21
+ @config.onInitialize?(this)
22
+
23
+ updateValue: (@value) ->
24
+ @$el.val(@_valueSafe())
25
+
26
+ hash: (hash={}) ->
27
+ hash[@config.klassName] = @$el.val()
28
+ return hash
29
+
30
+ showErrorMessage: (message) ->
31
+ ;
32
+
33
+ hideErrorMessage: ->
34
+ ;
35
+
36
+
37
+ _chrFormInputs['hidden'] = InputHidden
38
+
39
+
40
+
41
+
@@ -0,0 +1,142 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT LIST
3
+ # Allows to create/delete/reorder list items connected to dynamic or static
4
+ # collection. Value should be an array of objects.
5
+ #
6
+ # Dependencies:
7
+ # - jquery.typeahead
8
+ # - slip
9
+ # -----------------------------------------------------------------------------
10
+ class @InputList extends InputString
11
+ _updateInputValue: ->
12
+ ids = []
13
+ @$items.children('li').each (i, el)->
14
+ ids.push $(el).attr('data-id')
15
+ value = ids.join(',')
16
+ @$input.val(value)
17
+
18
+ _removeItem: ($el) ->
19
+ id = $el.attr('data-id')
20
+ delete @objects[id]
21
+
22
+ $el.parent().remove()
23
+ @_updateInputValue()
24
+
25
+ _addItem: (o) ->
26
+ id = o['_id']
27
+
28
+ @objects[id] = o
29
+
30
+ if @config.itemTemplate
31
+ item = @config.itemTemplate(o)
32
+ else
33
+ item = o[@config.titleFieldName]
34
+
35
+ listItem =$ """<li data-id='#{ id }'>
36
+ <span class='icon-reorder' data-container-class='#{ @reorderContainerClass }'></span>
37
+ #{ item }
38
+ <a href='#' class='action_remove'>Remove</a>
39
+ </li>"""
40
+ @$items.append listItem
41
+ @_updateInputValue()
42
+
43
+ _addItems: ->
44
+ @reorderContainerClass = @config.klassName
45
+ @objects = {}
46
+ @$items =$ "<ul class='#{ @reorderContainerClass }'></ul>"
47
+
48
+ for o in @value
49
+ @_addItem(o)
50
+
51
+ @typeaheadInput.before @$items
52
+
53
+ _addInput: ->
54
+ # hidden input that stores ids
55
+ # NOTE: we use __LIST__ prefix to identify ARRAY input type and
56
+ # process it's value while form submission.
57
+ name = if @config.namePrefix then "#{@config.namePrefix}[__LIST__#{@config.target}]" else "[__LIST__#{@config.target}]"
58
+
59
+ @$input =$ "<input type='hidden' name='#{ name }' value='' />"
60
+ @$el.append @$input
61
+
62
+ # NOTE: other options might be added here (static collection)
63
+ if @config.typeahead
64
+ # typeahead input for adding new items
65
+ placeholder = @config.typeahead.placeholder
66
+ @typeaheadInput =$ "<input type='text' placeholder='#{ placeholder }' />"
67
+ @$el.append @typeaheadInput
68
+
69
+ @_addItems()
70
+ @_updateInputValue()
71
+
72
+ #
73
+ # PUBLIC
74
+ #
75
+
76
+ initialize: ->
77
+ # typeahead
78
+ if @config.typeahead
79
+ limit = @config.typeahead.limit || 5
80
+ dataSource = new Bloodhound
81
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(@config.titleFieldName)
82
+ queryTokenizer: Bloodhound.tokenizers.whitespace
83
+ remote: @config.typeahead.url
84
+ limit: limit
85
+
86
+ dataSource.initialize()
87
+
88
+ @typeaheadInput.typeahead({
89
+ hint: false
90
+ highlight: true
91
+ }, {
92
+ name: @config.klassName,
93
+ displayKey: @config.titleFieldName,
94
+ source: dataSource.ttAdapter()
95
+ })
96
+
97
+ @typeaheadInput.on 'typeahead:selected', (e, object, dataset) =>
98
+ @_addItem(object)
99
+ @typeaheadInput.typeahead('val', '')
100
+
101
+ # events
102
+ @$items.on 'click', '.action_remove', (e) =>
103
+ e.preventDefault()
104
+ if confirm('Are you sure?')
105
+ @_removeItem($(e.currentTarget))
106
+
107
+ # reorder
108
+ list = @$items.get(0)
109
+ new Slip(list)
110
+
111
+ list.addEventListener 'slip:beforeswipe', (e) -> e.preventDefault()
112
+
113
+ list.addEventListener 'slip:beforewait', ((e) ->
114
+ if $(e.target).hasClass("icon-reorder") then e.preventDefault()
115
+ ), false
116
+
117
+ list.addEventListener 'slip:beforereorder', ((e) ->
118
+ if not $(e.target).hasClass("icon-reorder") then e.preventDefault()
119
+ ), false
120
+
121
+ list.addEventListener 'slip:reorder', ((e) =>
122
+ e.target.parentNode.insertBefore(e.target, e.detail.insertBefore)
123
+ @_updateInputValue()
124
+ return false
125
+ ), false
126
+
127
+ @config.onInitialize?(this)
128
+
129
+ # TODO: add support
130
+ updateValue: (@value) ->
131
+
132
+ hash: (hash={}) ->
133
+ hash[@config.klassName] = []
134
+ ids = @$input.val().split(',')
135
+ hash[@config.klassName].push(@objects[id]) for id in ids
136
+ return hash
137
+
138
+ _chrFormInputs['list'] = InputList
139
+
140
+
141
+
142
+
@@ -0,0 +1,59 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT SELECT
3
+ # -----------------------------------------------------------------------------
4
+ class @InputSelect extends InputString
5
+ _createEl: ->
6
+ @$el =$ "<div class='input-#{ @config.type } input-#{ @config.klass } #{ @config.klassName }'>"
7
+
8
+ _addOption: (title, value) ->
9
+ selected = if @value == value then 'selected' else ''
10
+ $option =$ """<option value='#{ value }' #{ selected }>#{ title }</option>"""
11
+ @$input.append $option
12
+
13
+ _addListOptions: ->
14
+ data = @config.optionsList
15
+ for o in data
16
+ @_addOption(o, o)
17
+
18
+ _addHashOptions: ->
19
+ data = @config.optionsHash
20
+ for value, title of data
21
+ @_addOption(title, value)
22
+
23
+ _addCollectionOptions: ->
24
+ data = @config.collection.data
25
+ valueField = @config.collection.valueField
26
+ titleField = @config.collection.titleField
27
+
28
+ for o in data
29
+ title = o[titleField]
30
+ value = o[valueField]
31
+ @_addOption(title, value)
32
+
33
+ _addOptions: ->
34
+ if @config.collection
35
+ @_addCollectionOptions()
36
+ else if @config.optionsList
37
+ @_addListOptions()
38
+ else if @config.optionsHash
39
+ @_addHashOptions()
40
+
41
+ _addInput: ->
42
+ @$input =$ """<select name='#{ @name }' id='#{ @name }'></select>"""
43
+ @$el.append @$input
44
+
45
+ if @config.optionsHashFieldName
46
+ @value = String(@value)
47
+ if @object
48
+ @config.optionsHash = @object[@config.optionsHashFieldName]
49
+ else
50
+ @config.optionsHash = { '': '--' }
51
+
52
+ @_addOptions()
53
+
54
+
55
+ _chrFormInputs['select'] = InputSelect
56
+
57
+
58
+
59
+
@@ -0,0 +1,87 @@
1
+ # -----------------------------------------------------------------------------
2
+ # INPUT STRING
3
+ # -----------------------------------------------------------------------------
4
+ class @InputString
5
+ constructor: (@name, @value, @config, @object) ->
6
+ @_createEl()
7
+ @_addLabel()
8
+ @_addInput()
9
+ @_addPlaceholder()
10
+
11
+ return this
12
+
13
+ _createEl: ->
14
+ @$el =$ "<label for='#{ @name }' class='input-#{ @config.type } input-#{ @config.klass } #{ @config.klassName }'>"
15
+
16
+ _addLabel: ->
17
+ if @config.klass in [ 'inline', 'stacked' ]
18
+ @$label =$ "<span class='label'>#{ @config.label }</span>"
19
+ @$el.append @$label
20
+
21
+ @$errorMessage =$ "<span class='error-message'></span>"
22
+ @$label.append @$errorMessage
23
+
24
+ _addInput: ->
25
+ @$input =$ "<input type='text' name='#{ @name }' value='#{ @_valueSafe() }' id='#{ @name }' />"
26
+ @$el.append @$input
27
+
28
+ if @config.options and $.isArray(@config.options)
29
+ data = new Bloodhound
30
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value')
31
+ queryTokenizer: Bloodhound.tokenizers.whitespace
32
+ local: $.map @config.options, (opt) -> { value: opt }
33
+
34
+ data.initialize()
35
+
36
+ @$input.typeahead({
37
+ hint: true
38
+ highlight: true
39
+ minLength: 1
40
+ },
41
+ {
42
+ name: 'options'
43
+ displayKey: 'value'
44
+ source: data.ttAdapter()
45
+ })
46
+
47
+ _valueSafe: ->
48
+ if typeof(@value) == 'object'
49
+ JSON.stringify(@value)
50
+ else
51
+ _escapeHtml(@value)
52
+
53
+ _addPlaceholder: ->
54
+ if @config.klass in [ 'placeholder', 'stacked' ]
55
+ @$input.attr 'placeholder', @config.label
56
+
57
+ if @config.placeholder
58
+ @$input.attr 'placeholder', @config.placeholder
59
+
60
+ #
61
+ # PUBLIC
62
+ #
63
+
64
+ initialize: ->
65
+ @config.onInitialize?(this)
66
+
67
+ hash: (hash={}) ->
68
+ hash[@config.klassName] = @$input.val()
69
+ return hash
70
+
71
+ updateValue: (@value) ->
72
+ @$input.val(@value)
73
+
74
+ showErrorMessage: (message) ->
75
+ @$el.addClass 'error'
76
+ @$errorMessage.html(message)
77
+
78
+ hideErrorMessage: ->
79
+ @$el.removeClass 'error'
80
+ @$errorMessage.html('')
81
+
82
+
83
+ _chrFormInputs['string'] = InputString
84
+
85
+
86
+
87
+