chr 0.2.8 → 0.3.5

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +33 -27
  3. data/README.md +6 -1
  4. data/app/assets/javascripts/chr.coffee +18 -16
  5. data/app/assets/javascripts/chr/core/chr.coffee +41 -76
  6. data/app/assets/javascripts/chr/core/chr_router.coffee +141 -0
  7. data/app/assets/javascripts/chr/core/item.coffee +36 -49
  8. data/app/assets/javascripts/chr/core/list.coffee +26 -71
  9. data/app/assets/javascripts/chr/core/list_config.coffee +28 -13
  10. data/app/assets/javascripts/chr/core/list_pagination.coffee +56 -13
  11. data/app/assets/javascripts/chr/core/list_reorder.coffee +2 -2
  12. data/app/assets/javascripts/chr/core/list_search.coffee +2 -2
  13. data/app/assets/javascripts/chr/core/module.coffee +24 -93
  14. data/app/assets/javascripts/chr/core/utils.coffee +4 -0
  15. data/app/assets/javascripts/chr/core/view.coffee +123 -43
  16. data/app/assets/javascripts/chr/core/view_local-storage.coffee +58 -0
  17. data/app/assets/javascripts/chr/store/{_array-store.coffee → array-store.coffee} +3 -1
  18. data/app/assets/javascripts/chr/store/{_object-store.coffee → object-store.coffee} +0 -0
  19. data/app/assets/javascripts/chr/store/rails-array-store.coffee +39 -0
  20. data/app/assets/javascripts/chr/store/{mongosteen-object-store.coffee → rails-form-object-parser.coffee} +11 -20
  21. data/app/assets/javascripts/chr/store/rails-object-store.coffee +35 -0
  22. data/app/assets/javascripts/chr/store/rest-array-store.coffee +3 -5
  23. data/app/assets/javascripts/chr/store/rest-object-store.coffee +1 -1
  24. data/app/assets/javascripts/form/expandable-group.coffee +30 -0
  25. data/app/assets/javascripts/{chr/form → form}/form.coffee +3 -1
  26. data/app/assets/javascripts/{chr/form → form}/input-checkbox.coffee +1 -1
  27. data/app/assets/javascripts/{chr/form → form}/input-color.coffee +2 -0
  28. data/app/assets/javascripts/{chr/form → form}/input-date.coffee +0 -0
  29. data/app/assets/javascripts/{chr/form → form}/input-file.coffee +28 -1
  30. data/app/assets/javascripts/{chr/form → form}/input-form.coffee +14 -5
  31. data/app/assets/javascripts/{chr/form → form}/input-form_reorder.coffee +0 -0
  32. data/app/assets/javascripts/{chr/form → form}/input-hidden.coffee +9 -9
  33. data/app/assets/javascripts/{chr/form → form}/input-list.coffee +61 -53
  34. data/app/assets/javascripts/{chr/form → form}/input-list_reorder.coffee +2 -0
  35. data/app/assets/javascripts/form/input-list_typeahead.coffee +55 -0
  36. data/app/assets/javascripts/{chr/form → form}/input-password.coffee +1 -0
  37. data/app/assets/javascripts/{chr/form → form}/input-select.coffee +10 -11
  38. data/app/assets/javascripts/form/input-select2.coffee +33 -0
  39. data/app/assets/javascripts/{chr/form → form}/input-string.coffee +45 -2
  40. data/app/assets/javascripts/{chr/form → form}/input-text.coffee +6 -3
  41. data/app/assets/javascripts/input-html.coffee +8 -5
  42. data/app/assets/javascripts/input-markdown.coffee +10 -5
  43. data/app/assets/javascripts/input-redactor.coffee +1 -61
  44. data/app/assets/javascripts/redactor/input-redactor.coffee +53 -0
  45. data/app/assets/javascripts/redactor/input-redactor_character.coffee +83 -0
  46. data/app/assets/javascripts/redactor/input-redactor_images.coffee +166 -0
  47. data/app/assets/javascripts/{chr/vendor → vendor}/ace.js +0 -0
  48. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.scrollparent.js +0 -0
  49. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.textarea_autosize.js +0 -0
  50. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.typeahead.js +0 -0
  51. data/app/assets/javascripts/{chr/vendor → vendor}/marked.js +0 -0
  52. data/app/assets/javascripts/{chr/vendor → vendor}/mode-html.js +0 -0
  53. data/app/assets/javascripts/{chr/vendor → vendor}/mode-markdown.js +0 -0
  54. data/app/assets/javascripts/{chr/vendor → vendor}/redactor.fixedtoolbar.js +1 -1
  55. data/app/assets/javascripts/vendor/select2.js +5274 -0
  56. data/app/assets/javascripts/{chr/vendor → vendor}/slip.js +0 -0
  57. data/app/assets/stylesheets/_chr.scss +11 -13
  58. data/app/assets/stylesheets/_input-redactor.scss +17 -16
  59. data/app/assets/stylesheets/{core → chr}/_icons.scss +1 -1
  60. data/app/assets/stylesheets/chr/_main.scss +110 -0
  61. data/app/assets/stylesheets/{core → chr}/_mixins.scss +58 -34
  62. data/app/assets/stylesheets/{core → chr}/_settings.scss +7 -1
  63. data/app/assets/stylesheets/form/_expandable-group.scss +16 -0
  64. data/app/assets/stylesheets/form/_input-select2.scss +94 -0
  65. data/app/assets/stylesheets/form/_main.scss +180 -0
  66. data/app/assets/stylesheets/vendor/select2.css +258 -0
  67. data/bower.json +2 -2
  68. data/dist/chr.js +2292 -2030
  69. data/dist/input-ace.js +18 -8
  70. data/dist/input-redactor.js +0 -156
  71. data/docs/bootstrap.md +1 -1
  72. data/docs/rails.md +10 -10
  73. data/lib/chr.rb +1 -8
  74. data/lib/chr/version.rb +1 -1
  75. data/lib/mongoid/character.rb +30 -0
  76. data/package.json +1 -1
  77. metadata +49 -43
  78. data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +0 -55
  79. data/app/assets/stylesheets/core/_list.scss +0 -39
  80. data/app/assets/stylesheets/core/_main.scss +0 -49
  81. data/app/assets/stylesheets/core/_responsive.scss +0 -62
  82. data/app/assets/stylesheets/form/_form.scss +0 -41
  83. data/app/assets/stylesheets/form/_input-checkbox.scss +0 -18
  84. data/app/assets/stylesheets/form/_input-color.scss +0 -10
  85. data/app/assets/stylesheets/form/_input-file.scss +0 -29
  86. data/app/assets/stylesheets/form/_input-form.scss +0 -26
  87. data/app/assets/stylesheets/form/_input-list.scss +0 -36
  88. data/app/assets/stylesheets/form/_input-string.scss +0 -8
@@ -3,7 +3,7 @@
3
3
  # -----------------------------------------------------------------------------
4
4
  #
5
5
  # Dependencies:
6
- #= require ../vendor/slip
6
+ #= require vendor/slip
7
7
  #
8
8
  # -----------------------------------------------------------------------------
9
9
 
@@ -59,7 +59,7 @@
59
59
  objectPositionValue = _getObjectNewPosition(e.target)
60
60
  objectId = $(e.target).attr('data-id')
61
61
  value = {}
62
- value["[#{arrayStore.sortBy}"] = "#{ objectPositionValue }"
62
+ value["[#{arrayStore.sortBy}]"] = "#{ objectPositionValue }"
63
63
 
64
64
  arrayStore.update objectId, value,
65
65
  # error handling
@@ -32,7 +32,7 @@
32
32
 
33
33
  _on_search: ->
34
34
  query = @$searchInput.val()
35
- @_show_spinner()
35
+ @showSpinner()
36
36
  @config.arrayStore.search(query)
37
37
 
38
38
 
@@ -45,7 +45,7 @@
45
45
  _on_search_cancel: ->
46
46
  @$el.removeClass('list-search')
47
47
  @$searchInput.val('')
48
- @_show_spinner()
48
+ @showSpinner()
49
49
  @config.arrayStore.reset()
50
50
 
51
51
 
@@ -11,16 +11,13 @@
11
11
  # -----------------------------------------------------------------------------
12
12
  # Config options:
13
13
  # title - title used for menu and root list header
14
+ # menuTitle - title used for the menu link
14
15
  # showNestedListsAside - show module root list on the left and all nested
15
16
  # lists on the right side for desktop
16
17
  #
17
18
  # Public methods:
18
19
  # addNestedList (listName, config, parentList)
19
- # showNestedList (listName)
20
- # hideNestedLists (exceptList)
21
- # visibleNestedListShownWithParent ()
22
- # showRootList ()
23
- # hideActiveList ()
20
+ # showList()
24
21
  # showView (object, config, title)
25
22
  # showViewByObjectId (objectId, config, title)
26
23
  # destroyView ()
@@ -36,119 +33,53 @@ class @Module
36
33
  @chr.$el.append @$el
37
34
 
38
35
  # root list
39
- @activeList = @rootList = new List(this, @name, @config)
36
+ @rootList = new List(this, "#/#{ @name }", @name, @config)
40
37
 
41
38
  # menu item + layout
42
- menuTitle = @config.menuTitle ? @config.title
43
- menuTitle ?= @name.titleize()
44
- menuPath = @name
45
-
46
- # do not hide root list layout, nested lists are shown on aside
47
- if @config.showNestedListsAside
48
- @$el.addClass 'first-list-aside'
49
- # jump to first nested list on menu click
50
- firstNestedList = _firstNonEmptyValue(@nestedLists)
51
- if ! @chr.isMobile() && firstNestedList
52
- menuPath += "/#{ firstNestedList.name }"
53
-
54
- @chr.addMenuItem(menuPath, menuTitle)
39
+ @menuTitle = @config.menuTitle ? @config.title
40
+ @menuTitle ?= @name.titleize()
55
41
 
56
42
  @config.onModuleInit?(this)
57
43
 
58
44
 
59
- # PRIVATE ===============================================
60
-
61
- # update list data if it's not visible, e.g. for update action we do not
62
- # update whole list, this method is called before active list is shown.
63
- _update_active_list_items: ->
64
- if not @activeList.isVisible()
65
- @activeList.updateItems()
66
-
67
-
68
- # returns path for the current list
69
- _view_path: ->
70
- currentList = @visibleNestedListShownWithParent() ? @activeList
71
- currentList.path
72
-
73
-
74
45
  # PUBLIC ================================================
75
46
 
76
- addNestedList: (listName, config, parentList) ->
77
- @nestedLists[listName] = new List(this, listName, config, parentList)
78
-
79
-
80
- # shows one of nested lists
81
- showNestedList: (listName) ->
82
- listToShow = @nestedLists[listName]
47
+ addNestedList: (name, config, parentList) ->
48
+ path = [ parentList.path, name ].join('/')
49
+ @nestedLists[name] = new List(this, path, name, config, parentList)
83
50
 
84
- if listToShow.showWithParent
85
- # list works as view, never becomes active
86
- listToShow.updateItems()
87
- listToShow.show => @hideNestedLists(exceptList=listName)
88
51
 
52
+ showList: (name) ->
53
+ if ! name # show root list, hide all nested
54
+ list.hide() for key, list of @nestedLists
55
+ @activeList = @rootList
89
56
  else
90
- @activeList = listToShow
91
- @_update_active_list_items()
92
- @activeList.show()
57
+ @activeList = @nestedLists[name]
93
58
 
94
- @destroyView()
59
+ @activeList.show()
95
60
 
96
61
 
97
- hideNestedLists: (exceptList) ->
98
- @activeList = @rootList
99
- list.hide() for key, list of @nestedLists when key isnt exceptList
62
+ showView: (objectId, config) ->
63
+ @view = new View(this, config, @activeList.path, @activeList.name)
64
+ @$el.append(@view.$el)
65
+ @view.show(objectId)
100
66
 
101
67
 
102
- visibleNestedListShownWithParent: ->
103
- for key, list of @nestedLists
104
- if list.isVisible() && list.showWithParent then return list
68
+ show: ->
69
+ @$el.show()
70
+ @showList()
105
71
 
106
72
 
107
- showRootList: ->
73
+ hide: ->
108
74
  @destroyView()
109
- while @activeList != @rootList
110
- @hideActiveList()
111
-
112
-
113
- hideActiveList: ->
114
- @activeList.$el.hide()
115
- @activeList = @activeList.parentList
116
-
117
-
118
- showView: (object, config, title) ->
119
- newView = new View(this, config, @_view_path(), object, title)
120
- @chr.$el.append(newView.$el)
121
-
122
- newView.show =>
123
- @destroyView()
124
- @view = newView
125
-
126
-
127
- showViewByObjectId: (objectId, config, title) ->
128
- onSuccess = (object) => @showView(object, config, title)
129
- onError = -> chr.showError("can\'t show view for requested object")
130
-
131
- if objectId == ''
132
- config.objectStore.loadObject({ onSuccess: onSuccess, onError: onError })
133
- else
134
- config.arrayStore.loadObject(objectId, { onSuccess: onSuccess, onError: onError })
75
+ @$el.hide()
135
76
 
136
77
 
137
78
  destroyView: ->
138
79
  @view?.destroy()
80
+ @view = null
139
81
 
140
82
 
141
- show: ->
142
- @_update_active_list_items()
143
- @$el.show()
144
- @activeList.show()
145
-
146
-
147
- hide: ->
148
- @hideNestedLists()
149
- @destroyView()
150
- @$el.hide()
151
-
152
83
 
153
84
 
154
85
 
@@ -10,6 +10,7 @@
10
10
  # UTILS
11
11
  # -----------------------------------------------------------------------------
12
12
  # Public methods:
13
+ # _any(array)
13
14
  # _last(array)
14
15
  # _first(array)
15
16
  # _firstNonEmptyValue(hash)
@@ -22,6 +23,9 @@
22
23
  # include(class, hash)
23
24
  # -----------------------------------------------------------------------------
24
25
 
26
+ # _any(array)
27
+ @_any = (array) -> array.length > 0
28
+
25
29
  # _last(array)
26
30
  @_last = (array) -> array[array.length - 1]
27
31
 
@@ -8,8 +8,9 @@
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
  # VIEW
11
+ # -----------------------------------------------------------------------------
11
12
  #
12
- # config options:
13
+ # Config options:
13
14
  # formClass - custom form class to be used
14
15
  # formSchema - form schema for object, autogenerated if missing
15
16
  # disableDelete - do not add delete button below the form
@@ -17,78 +18,93 @@
17
18
  # fullsizeView — use fullsize layout in desktop mode
18
19
  # onViewShow - on show callback
19
20
  #
20
- # public methods:
21
- # show(callback)
21
+ # Public methods:
22
+ # show(objectId)
22
23
  # destroy()
24
+ # showSpinner()
25
+ # hideSpinner()
26
+ #
27
+ # Dependencies:
28
+ #= require ./view_local-storage
23
29
  #
24
30
  # -----------------------------------------------------------------------------
25
31
  class @View
26
- constructor: (@module, @config, @closePath, @object, @title) ->
32
+ constructor: (@module, @config, @closePath, @listName) ->
27
33
  @store = @config.arrayStore ? @config.objectStore
34
+ @path = window.location.hash
28
35
 
29
- @$el =$ "<section class='view #{ @module.name }' style='display:none;'>"
36
+ @$el =$ "<section class='view #{ @listName }'>"
30
37
 
31
38
  # fullsize
32
39
  if @config.fullsizeView
33
40
  @$el.addClass 'fullsize'
34
41
 
35
42
  # header
36
- @$header =$ "<header></header>"
37
- @$title =$ "<div class='title'></div>"
43
+ @$header =$ "<header class='header'></header>"
44
+ @$spinner =$ "<div class='spinner'></div>"
45
+ @$title =$ "<div class='title'></div>"
46
+ @$header.append @$spinner
38
47
  @$header.append @$title
39
48
  @$el.append @$header
40
- @_set_title()
41
49
 
42
50
  # close
43
- @$closeBtn =$ "<a href='#/#{ @closePath }' class='close silent'>Close</a>"
51
+ @$closeBtn =$ "<a href='#{ @closePath }' class='close'>Close</a>"
44
52
  @$closeBtn.on 'click', (e) => @_close(e)
45
53
  @$header.append @$closeBtn
46
54
 
47
- # save
48
- unless @config.disableSave
49
- @$saveBtn =$ "<a href='#' class='save'>Save</a>"
50
- @$saveBtn.on 'click', (e) => @_save(e)
51
- @$header.append @$saveBtn
52
-
53
- # form
54
- @form = new (@config.formClass ? Form)(@object, @config)
55
- @$el.append @form.$el
56
- @_add_form_delete_button()
55
+ # content
56
+ @$content =$ "<div class='content'></div>"
57
+ @$el.append @$content
57
58
 
58
59
 
59
60
  # PRIVATE ===============================================
60
61
 
61
- _set_title: (reset=false) ->
62
- if reset && @config.arrayStore then @title = null
63
- title = @title
64
- title ?= @object[@config.itemTitleField] if @config.itemTitleField
65
- title ?= _firstNonEmptyValue(@object)
62
+ _set_title: ->
63
+ if ! @object
64
+ title = "New"
65
+
66
+ else if @config.objectStore
67
+ title = @config.title
68
+ title ?= _firstNonEmptyValue(@object)
69
+
70
+ else
71
+ if @config.itemTitleField
72
+ title = @object[@config.itemTitleField]
73
+ title ?= @object['_list_item_title']
74
+ title ?= _firstNonEmptyValue(@object)
75
+
66
76
  @$title.html(title.plainText())
67
77
 
68
78
 
69
- _add_form_delete_button: ->
79
+ _add_delete_button: ->
70
80
  unless @config.disableDelete or @config.objectStore or (! @object)
71
- @$deleteBtn =$ "<a href='#' class='delete'>Delete</a>"
81
+ @$deleteBtn =$ "<a href='#' class='view-delete'>Delete</a>"
72
82
  @$deleteBtn.on 'click', (e) => @_delete(e)
73
- @form.$el.append @$deleteBtn
83
+ @$content.append @$deleteBtn
74
84
 
75
85
 
76
86
  _save_success: ->
77
87
  @$el.removeClass('view-saving')
78
- @_set_title(true)
88
+ @_set_title()
89
+ @form.hideValidationErrors()
79
90
  @form.updateValues(@object)
91
+ @_clear_local_storage_cache()
80
92
 
81
93
 
82
94
  _save_error: (message, validationErrors) ->
83
95
  @$el.removeClass('view-saving')
84
- chr.showError(message)
85
96
  @form.showValidationErrors(validationErrors)
97
+ chr.showError(message)
86
98
 
87
99
 
88
100
  # EVENTS ================================================
89
101
 
90
102
  _close: (e) ->
91
- @destroy()
103
+ if @_changes_not_saved()
104
+ if confirm('Your changes are not saved, still want to close?')
105
+ @_clear_local_storage_cache()
106
+ else
107
+ e.preventDefault()
92
108
 
93
109
 
94
110
  _save: (e) ->
@@ -99,39 +115,103 @@ class @View
99
115
 
100
116
  if @object
101
117
  @store.update @object._id, serializedFormObj,
102
- onSuccess: (@object) => @_save_success()
103
- onError: (errors) => @_save_error('Changes were not saved.', errors)
118
+ onSuccess: (@object) =>
119
+ @_save_success()
120
+ onError: (errors) => @_save_error('Changes are not saved.', errors)
104
121
  else
105
122
  @store.push serializedFormObj,
106
123
  onSuccess: (@object) =>
107
124
  @_save_success()
108
- @_add_form_delete_button()
109
- chr.updateHash("#/#{ @closePath }/view/#{ @object._id }", true)
110
- onError: (errors) => @_save_error('Item were not created.', errors)
125
+ @_add_delete_button()
126
+ chr.updateHash("#{ @closePath }/view/#{ @object._id }", true)
127
+ @path = window.location.hash
128
+ @config.onViewShow?(@)
129
+ onError: (errors) => @_save_error('Document is not created due to an error.', errors)
111
130
 
112
131
 
113
132
  _delete: (e) ->
114
133
  e.preventDefault()
115
134
  if confirm("Are you sure?")
116
135
  @store.remove @object._id,
117
- onSuccess: => chr.updateHash("#/#{ @closePath }", true) ; @destroy()
118
- onError: -> chr.showError('Can\'t delete object.')
136
+ onSuccess: =>
137
+ @_clear_local_storage_cache()
138
+ chr.updateHash("#{ @closePath }", true)
139
+ @destroy()
140
+ chr.mobileListLock(false)
141
+ onError: -> chr.showError('Can\'t delete document.')
142
+
143
+
144
+ _render_form: ->
145
+ @_set_title()
146
+
147
+ @hideSpinner()
148
+
149
+ # save
150
+ unless @config.disableSave
151
+ @$saveBtn =$ "<a href='#' class='save'>Save</a>"
152
+ @$saveBtn.on 'click', (e) => @_save(e)
153
+ @$header.append @$saveBtn
154
+
155
+ # sync with local storage cache
156
+ @_update_object_from_local_storage()
157
+
158
+ # form
159
+ @form = new (@config.formClass ? Form)(@object, @config)
160
+ @$content.append @form.$el
161
+ @form.initializePlugins()
162
+
163
+ @_add_delete_button()
164
+ @config.onViewShow?(@)
165
+
166
+ # enable local storage caching
167
+ @_bind_form_change()
168
+
169
+
170
+ _show_error: ->
171
+ @hideSpinner()
172
+ chr.showError("can\'t show view for requested object, application error 500")
119
173
 
120
174
 
121
175
  # PUBLIC ================================================
122
176
 
123
- show: (callback) ->
124
- @$el.show 0, =>
125
- callback?()
126
- @form.initializePlugins()
127
- @config.onViewShow?(@)
177
+
178
+ showSpinner: ->
179
+ @$el.addClass('show-spinner')
180
+
181
+
182
+ hideSpinner: ->
183
+ @$el.removeClass('show-spinner')
128
184
 
129
185
 
130
186
  destroy: ->
131
- @form.destroy()
187
+ @form?.destroy()
132
188
  @$el.remove()
133
189
 
134
190
 
191
+ show: (objectId) ->
192
+ callbacks =
193
+ onSuccess: (@object) => @_render_form()
194
+ onError: => @_show_error()
195
+
196
+ @showSpinner()
197
+
198
+ # new for array store
199
+ if objectId == null
200
+ @object = null
201
+ @_render_form()
202
+
203
+ # object store
204
+ else if objectId == ''
205
+ @_set_title()
206
+ @store.loadObject(callbacks)
207
+
208
+ # array store
209
+ else
210
+ @store.loadObject(objectId, callbacks)
211
+
212
+
213
+ include(View, viewLocalStorage)
214
+
135
215
 
136
216
 
137
217