chr 0.1.5 → 0.2.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +6 -3
  3. data/LICENSE.md +1 -1
  4. data/README.md +286 -7
  5. data/app/assets/javascripts/chr-dist.js +1031 -580
  6. data/app/assets/javascripts/chr.coffee +7 -4
  7. data/app/assets/javascripts/chr/core/chr.coffee +88 -37
  8. data/app/assets/javascripts/chr/core/item.coffee +57 -35
  9. data/app/assets/javascripts/chr/core/{list-scroll.coffee → list-pagination.coffee} +6 -3
  10. data/app/assets/javascripts/chr/core/list-reorder.coffee +3 -3
  11. data/app/assets/javascripts/chr/core/list-search.coffee +20 -11
  12. data/app/assets/javascripts/chr/core/list.coffee +163 -89
  13. data/app/assets/javascripts/chr/core/module.coffee +75 -35
  14. data/app/assets/javascripts/chr/core/view.coffee +117 -61
  15. data/app/assets/javascripts/chr/store/{store.coffee → _array-store.coffee} +53 -106
  16. data/app/assets/javascripts/chr/store/_object-store.coffee +28 -0
  17. data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +199 -0
  18. data/app/assets/javascripts/chr/store/mongosteen-object-store.coffee +52 -0
  19. data/app/assets/javascripts/chr/store/rest-array-store.coffee +142 -0
  20. data/app/assets/javascripts/chr/store/rest-object-store.coffee +79 -0
  21. data/app/assets/stylesheets/core/_list.scss +20 -25
  22. data/app/assets/stylesheets/core/_main.scss +3 -4
  23. data/app/assets/stylesheets/core/_mixins.scss +33 -2
  24. data/app/assets/stylesheets/core/_responsive.scss +30 -9
  25. data/app/assets/stylesheets/form/_input_checkbox.scss +18 -14
  26. data/bower.json +3 -2
  27. data/chr.gemspec +1 -1
  28. data/docs/assets.md +0 -0
  29. data/docs/basics.md +0 -0
  30. data/docs/bootstrap-data.md +0 -0
  31. data/docs/custom-inputs.md +0 -0
  32. data/docs/form.md +0 -0
  33. data/docs/internals.md +0 -0
  34. data/docs/nested-forms.md +0 -0
  35. data/docs/redactor-js.md +0 -0
  36. data/docs/scopes.md +0 -0
  37. data/lib/chr/version.rb +1 -1
  38. data/package.json +1 -1
  39. metadata +19 -7
  40. data/app/assets/javascripts/chr/store/store-mongosteen.coffee +0 -111
  41. data/app/assets/javascripts/chr/store/store-rest.coffee +0 -90
@@ -0,0 +1,28 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # OBJECT STORE
11
+ # -----------------------------------------------------------------------------
12
+ class @ObjectStore
13
+ constructor: (@config={}) ->
14
+ @_initialize_database()
15
+
16
+ _initialize_database: ->
17
+ @_data = @config.data
18
+
19
+ loadObject: ->
20
+ @_data
21
+
22
+ update: (id, value, callback) ->
23
+ $.extend(@_data, value)
24
+ callback?(@_data)
25
+
26
+
27
+
28
+
@@ -0,0 +1,199 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # MONGOSTEEN (RAILS) ARRAY/COLLECTION STORE IMPLEMENTATION
11
+ # this store implementation talks to Mongosteen powered Rails api, supports
12
+ # features:
13
+ #
14
+ # - pagination
15
+ # `sortBy` & `sortReverse` options should be set same as on
16
+ # backend model with `default_scope` method (default), e.g:
17
+ # - frontend: `{ sortBy: 'created_at', sortReverse: true }`
18
+ # - backend: `default_scope -> { desc(:created_at) }`
19
+ #
20
+ # - search
21
+ # backend model configuration required, e.g: `search_in :title`
22
+ #
23
+ # configuration options:
24
+ # @config.pagination - enable pagination, default `true`
25
+ # @config.searchable - enable search, default `false`
26
+ #
27
+ # -----------------------------------------------------------------------------
28
+ class @MongosteenArrayStore extends RestArrayStore
29
+ # initial store configuration
30
+ _initialize_database: ->
31
+ @dataFetchLock = false
32
+ @ajaxConfig =
33
+ processData: false
34
+ contentType: false
35
+
36
+ @searchable = @config.searchable ? false
37
+ @searchQuery = ''
38
+
39
+ @pagination = @config.pagination ? true
40
+ @nextPage = 1
41
+ @objectsPerPage = _itemsPerPageRequest ? 20
42
+
43
+ if @pagination
44
+ @_bind_pagination_sync()
45
+
46
+
47
+ # ---------------------------------------------------------
48
+ # workarounds to have consistency between arrayStore and
49
+ # database while loading next page
50
+ # ---------------------------------------------------------
51
+ _bind_pagination_sync: ->
52
+ @lastPageLoaded = false
53
+
54
+ # when object's added to the end of the list & not on the last page,
55
+ # we don't know it's position on the backend, so remove it from store
56
+ $(this).on 'object_added', (e, data) =>
57
+ if ! @lastPageLoaded
58
+ new_object = data.object
59
+ new_object_position = data.position
60
+
61
+ # check if object added to the end of the list
62
+ if new_object_position >= @objectsNumberForLoadedPages
63
+ e.stopImmediatePropagation()
64
+
65
+ @_remove_data_object(new_object._id)
66
+
67
+ # when object's added to the end of the list & not on the last page,
68
+ # we don't know it's position on the backend, so remove it from store
69
+ $(this).on 'object_changed', (e, data) =>
70
+ if ! @lastPageLoaded
71
+ new_object = data.object
72
+ new_object_position = data.position
73
+
74
+ # check if object added to the end of the list
75
+ if new_object_position >= @objectsNumberForLoadedPages - 1
76
+ e.stopImmediatePropagation()
77
+
78
+ @_remove_data_object(new_object._id)
79
+
80
+ # load current page again after item delete to sync, last item on the page
81
+ $(this).on 'object_removed', (e, data) =>
82
+ if ! @lastPageLoaded
83
+ @_reload_current_page()
84
+
85
+
86
+ _reload_current_page: ->
87
+ @nextPage -= 1 ; @load()
88
+
89
+
90
+ _udpate_next_page: (data) ->
91
+ if @pagination
92
+ if data.length > 0
93
+ @lastPageLoaded = true
94
+
95
+ if data.length == @objectsPerPage
96
+ @nextPage += 1
97
+ @lastPageLoaded = false
98
+
99
+ else
100
+ @lastPageLoaded = true
101
+
102
+ @objectsNumberForLoadedPages = (@nextPage - 1) * @objectsPerPage
103
+
104
+
105
+ # generate resource api url
106
+ _resource_url: (type, id) ->
107
+ objectPath = if id then "/#{ id }" else ''
108
+ url = "#{ @config.path }#{ objectPath }.json"
109
+
110
+ if @config.urlParams
111
+ extraParamsString = $.param(@config.urlParams)
112
+ url = "#{ url }?#{ extraParamsString }"
113
+
114
+ return url
115
+
116
+
117
+ # get form data object from serialized form object,
118
+ # it uses special format for object names for support of:
119
+ # files, lists, nested objects
120
+ _parse_form_object: (serializedFormObject) ->
121
+ formDataObject = new FormData()
122
+
123
+ for attr_name, attr_value of serializedFormObject
124
+
125
+ # special case for LIST inputs, values separated with comma
126
+ if attr_name.indexOf('[__LIST__') > -1
127
+ attr_name = attr_name.replace('__LIST__', '')
128
+ values = attr_value.split(',')
129
+
130
+ for value in values
131
+ formDataObject.append("#{ @config.resource }#{ attr_name }[]", value)
132
+
133
+ else
134
+ # special case for FILE inputs
135
+ if attr_name.startsWith('__FILE__')
136
+ attr_name = attr_name.replace('__FILE__', '')
137
+
138
+ formDataObject.append("#{ @config.resource }#{ attr_name }", attr_value)
139
+
140
+ return formDataObject
141
+
142
+
143
+ # load results for search query
144
+ search: (@searchQuery) ->
145
+ @nextPage = 1
146
+ @_reset_data()
147
+ @load()
148
+
149
+
150
+ # load next page objects from database, when finished
151
+ # trigger 'objects_added' event
152
+ load: (callbacks={}) ->
153
+ callbacks.onSuccess ?= $.noop
154
+ callbacks.onError ?= $.noop
155
+
156
+ params = {}
157
+
158
+ if @pagination
159
+ params.page = @nextPage
160
+ params.perPage = @objectsPerPage
161
+
162
+ if @searchable && @searchQuery.length > 0
163
+ params.search = @searchQuery
164
+
165
+ params = $.param(params)
166
+
167
+ @_ajax 'GET', null, params, ((data) =>
168
+ @_udpate_next_page(data)
169
+ @_add_data_object(o) for o in data
170
+
171
+ callbacks.onSuccess(data)
172
+
173
+ $(this).trigger('objects_added', { objects: data })
174
+ ), callbacks.onError
175
+
176
+
177
+ # reset data and load first page
178
+ reset: ->
179
+ @searchQuery = ''
180
+ @nextPage = 1
181
+ params = {}
182
+
183
+ if @pagination
184
+ @lastPageLoaded = false
185
+ params.page = @nextPage
186
+ params.perPage = @objectsPerPage
187
+
188
+ params = $.param(params)
189
+
190
+ @_ajax 'GET', null, params, ((data) =>
191
+ @_udpate_next_page(data)
192
+ @_sync_with_data_objects(data)
193
+
194
+ $(this).trigger('objects_added', { objects: data })
195
+ ), -> chr.showError('Error while loading data.')
196
+
197
+
198
+
199
+
@@ -0,0 +1,52 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # MONGOSTEEN (RAILS) OBJECT STORE IMPLEMENTATION
11
+ # -----------------------------------------------------------------------------
12
+ class @MongosteenObjectStore extends RestObjectStore
13
+ # initial store configuration
14
+ _initialize_database: ->
15
+ @dataFetchLock = false
16
+ @ajaxConfig =
17
+ processData: false
18
+ contentType: false
19
+
20
+
21
+ # generate rest url for resource
22
+ _resource_url: -> "#{ @config.path }.json"
23
+
24
+
25
+ # get form data object from serialized form object,
26
+ # it uses special format for object names for support of:
27
+ # files, lists, nested objects
28
+ _parse_form_object: (serializedFormObject) ->
29
+ formDataObject = new FormData()
30
+
31
+ for attr_name, attr_value of serializedFormObject
32
+
33
+ # special case for LIST inputs, values separated with comma
34
+ if attr_name.indexOf('[__LIST__') > -1
35
+ attr_name = attr_name.replace('__LIST__', '')
36
+ values = attr_value.split(',')
37
+
38
+ for value in values
39
+ formDataObject.append("#{ @config.resource }#{ attr_name }[]", value)
40
+
41
+ else
42
+ # special case for FILE inputs
43
+ if attr_name.startsWith('__FILE__')
44
+ attr_name = attr_name.replace('__FILE__', '')
45
+
46
+ formDataObject.append("#{ @config.resource }#{ attr_name }", attr_value)
47
+
48
+ return formDataObject
49
+
50
+
51
+
52
+
@@ -0,0 +1,142 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # REST ARRAY STORE
11
+ # -----------------------------------------------------------------------------
12
+ class @RestArrayStore extends ArrayStore
13
+ # initial store configuration
14
+ _initialize_database: ->
15
+ @dataFetchLock = false
16
+ @ajaxConfig = {}
17
+
18
+
19
+ # generate rest url for resource
20
+ _resource_url: (type, id) ->
21
+ objectPath = if id then "/#{ id }" else ''
22
+ "#{ @config.path }#{ objectPath }"
23
+
24
+
25
+ # do requests to database api
26
+ _ajax: (type, id, data, success, error) ->
27
+ options = $.extend @ajaxConfig,
28
+ url: @_resource_url(type, id)
29
+ type: type
30
+ data: data
31
+ success: (data, textStatus, jqXHR) =>
32
+ success?(data)
33
+ setTimeout ( => @dataFetchLock = false ), 350
34
+ #@dataFetchLock = false
35
+ error: (jqXHR, textStatus, errorThrown ) =>
36
+ error?(jqXHR.responseJSON)
37
+ @dataFetchLock = false
38
+
39
+ @dataFetchLock = true
40
+ $.ajax options
41
+
42
+
43
+ # check how this works with sorting enabled
44
+ _sync_with_data_objects: (objects) ->
45
+ if objects.length == 0 then return @_reset_data()
46
+ if @_data.length == 0 then return ( @_add_data_object(o) for o in objects )
47
+
48
+ objectsMap = {}
49
+ (o = @_normalize_object_id(o) ; objectsMap[o._id] = o) for o in objects
50
+
51
+ objectIds = $.map objects, (o) -> o._id
52
+ dataObjectIds = $.map @_data, (o) -> o._id
53
+
54
+ addObjectIds = $(objectIds).not(dataObjectIds).get()
55
+ updateDataObjectIds = $(objectIds).not(addObjectIds).get()
56
+ removeDataObjectIds = $(dataObjectIds).not(objectIds).get()
57
+
58
+ for id in removeDataObjectIds
59
+ @_remove_data_object(id)
60
+
61
+ for id in addObjectIds
62
+ @_add_data_object(objectsMap[id])
63
+
64
+ for id in updateDataObjectIds
65
+ @_update_data_object(id, objectsMap[id])
66
+
67
+
68
+ # load a single object, this is used in view when
69
+ # store has not required item
70
+ loadObject: (id, callbacks={}) ->
71
+ callbacks.onSuccess ?= $.noop
72
+ callbacks.onError ?= $.noop
73
+
74
+ @_ajax 'GET', id, {}, ((data) =>
75
+ callbacks.onSuccess(data)
76
+ ), callbacks.onError
77
+
78
+
79
+ # load objects from database, when finished
80
+ # trigger 'objects_added' event
81
+ load: (callbacks={}) ->
82
+ callbacks.onSuccess ?= $.noop
83
+ callbacks.onError ?= $.noop
84
+
85
+ @_ajax 'GET', null, {}, ((data) =>
86
+ if data.length > 0
87
+ for o in data
88
+ @_add_data_object(o)
89
+
90
+ callbacks.onSuccess(data)
91
+
92
+ $(this).trigger('objects_added', { objects: data })
93
+ ) callbacks.onError
94
+
95
+
96
+ # add new object
97
+ push: (serializedFormObject, callbacks={}) ->
98
+ callbacks.onSuccess ?= $.noop
99
+ callbacks.onError ?= $.noop
100
+
101
+ obj = @_parse_form_object(serializedFormObject)
102
+
103
+ @_ajax 'POST', null, obj, ((data) =>
104
+ @_add_data_object(data)
105
+ callbacks.onSuccess(data)
106
+ ), callbacks.onError
107
+
108
+
109
+ # update objects attributes
110
+ update: (id, serializedFormObject, callbacks={}) ->
111
+ callbacks.onSuccess ?= $.noop
112
+ callbacks.onError ?= $.noop
113
+
114
+ obj = @_parse_form_object(serializedFormObject)
115
+
116
+ @_ajax 'PUT', id, obj, ((data) =>
117
+ @_update_data_object(id, data)
118
+ callbacks.onSuccess(data)
119
+ ), callbacks.onError
120
+
121
+
122
+ # delete object
123
+ remove: (id, callbacks={}) ->
124
+ callbacks.onSuccess ?= $.noop
125
+ callbacks.onError ?= $.noop
126
+
127
+ @_ajax 'DELETE', id, {}, ( =>
128
+ @_remove_data_object(id)
129
+ callbacks.onSuccess()
130
+ ), callbacks.onError
131
+
132
+
133
+ # reset data and load it again
134
+ reset: ->
135
+ @_ajax 'GET', null, {}, ((data) =>
136
+ @_sync_with_data_objects(data)
137
+ $(this).trigger('objects_added', { objects: data })
138
+ ), -> chr.showError('Error while loading data.')
139
+
140
+
141
+
142
+
@@ -0,0 +1,79 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # REST OBJECT STORE
11
+ # -----------------------------------------------------------------------------
12
+ class @RestObjectStore extends ObjectStore
13
+ _initialize_database: ->
14
+ @dataFetchLock = false
15
+ @ajaxConfig = {}
16
+
17
+
18
+ # generate rest url for resource
19
+ _resource_url: -> @config.path
20
+
21
+
22
+ # get regular javascript object from serialized form object,
23
+ # which uses special format for object names for support of:
24
+ # - files
25
+ # - lists
26
+ # - nested objects
27
+ _parse_form_object: (serializedFormObject) ->
28
+ # this is very basic and have to be expanded to support all form inputs:
29
+ # - lists, files, nested objects
30
+ object = {}
31
+ for key, value of serializedFormObject
32
+ fieldName = key.replace('[', '').replace(']', '')
33
+ object[fieldName] = value
34
+ return object
35
+
36
+
37
+ # do requests to database api
38
+ _ajax: (type, data, success, error) ->
39
+ options = $.extend @ajaxConfig,
40
+ url: @_resource_url()
41
+ type: type
42
+ data: data
43
+ success: (data, textStatus, jqXHR) =>
44
+ success?(data)
45
+ @dataFetchLock = false
46
+ error: (jqXHR, textStatus, errorThrown ) =>
47
+ error?(jqXHR.responseJSON)
48
+ @dataFetchLock = false
49
+
50
+ @dataFetchLock = true
51
+ $.ajax options
52
+
53
+
54
+ # load a single object, this is used in view when
55
+ # store has not required item
56
+ loadObject: (callbacks={}) ->
57
+ callbacks.onSuccess ?= $.noop
58
+ callbacks.onError ?= $.noop
59
+
60
+ @_ajax 'GET', {}, ((data) =>
61
+ callbacks.onSuccess(data)
62
+ ), callbacks.onError
63
+
64
+
65
+ # update objects attributes
66
+ update: (id, serializedFormObject, callbacks={}) ->
67
+ callbacks.onSuccess ?= $.noop
68
+ callbacks.onError ?= $.noop
69
+
70
+ obj = @_parse_form_object(serializedFormObject)
71
+
72
+ @_ajax 'PUT', obj, ((data) =>
73
+ @_data = data
74
+ callbacks.onSuccess(data)
75
+ ), callbacks.onError
76
+
77
+
78
+
79
+