chr 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gruntfile.coffee +6 -3
- data/LICENSE.md +1 -1
- data/README.md +286 -7
- data/app/assets/javascripts/chr-dist.js +1031 -580
- data/app/assets/javascripts/chr.coffee +7 -4
- data/app/assets/javascripts/chr/core/chr.coffee +88 -37
- data/app/assets/javascripts/chr/core/item.coffee +57 -35
- data/app/assets/javascripts/chr/core/{list-scroll.coffee → list-pagination.coffee} +6 -3
- data/app/assets/javascripts/chr/core/list-reorder.coffee +3 -3
- data/app/assets/javascripts/chr/core/list-search.coffee +20 -11
- data/app/assets/javascripts/chr/core/list.coffee +163 -89
- data/app/assets/javascripts/chr/core/module.coffee +75 -35
- data/app/assets/javascripts/chr/core/view.coffee +117 -61
- data/app/assets/javascripts/chr/store/{store.coffee → _array-store.coffee} +53 -106
- data/app/assets/javascripts/chr/store/_object-store.coffee +28 -0
- data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +199 -0
- data/app/assets/javascripts/chr/store/mongosteen-object-store.coffee +52 -0
- data/app/assets/javascripts/chr/store/rest-array-store.coffee +142 -0
- data/app/assets/javascripts/chr/store/rest-object-store.coffee +79 -0
- data/app/assets/stylesheets/core/_list.scss +20 -25
- data/app/assets/stylesheets/core/_main.scss +3 -4
- data/app/assets/stylesheets/core/_mixins.scss +33 -2
- data/app/assets/stylesheets/core/_responsive.scss +30 -9
- data/app/assets/stylesheets/form/_input_checkbox.scss +18 -14
- data/bower.json +3 -2
- data/chr.gemspec +1 -1
- data/docs/assets.md +0 -0
- data/docs/basics.md +0 -0
- data/docs/bootstrap-data.md +0 -0
- data/docs/custom-inputs.md +0 -0
- data/docs/form.md +0 -0
- data/docs/internals.md +0 -0
- data/docs/nested-forms.md +0 -0
- data/docs/redactor-js.md +0 -0
- data/docs/scopes.md +0 -0
- data/lib/chr/version.rb +1 -1
- data/package.json +1 -1
- metadata +19 -7
- data/app/assets/javascripts/chr/store/store-mongosteen.coffee +0 -111
- 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
|
+
|