chr 0.2.1 → 0.2.4

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/Gruntfile.coffee +50 -16
  4. data/app/assets/javascripts/chr.coffee +9 -16
  5. data/app/assets/javascripts/chr/core/chr.coffee +38 -20
  6. data/app/assets/javascripts/chr/core/item.coffee +30 -24
  7. data/app/assets/javascripts/chr/core/list.coffee +30 -60
  8. data/app/assets/javascripts/chr/core/list_config.coffee +65 -0
  9. data/app/assets/javascripts/chr/core/list_pagination.coffee +27 -0
  10. data/app/assets/javascripts/chr/core/list_reorder.coffee +75 -0
  11. data/app/assets/javascripts/chr/core/list_search.coffee +41 -0
  12. data/app/assets/javascripts/chr/core/module.coffee +55 -32
  13. data/app/assets/javascripts/chr/core/utils.coffee +34 -13
  14. data/app/assets/javascripts/chr/core/view.coffee +70 -97
  15. data/app/assets/javascripts/chr/form/form.coffee +63 -49
  16. data/app/assets/javascripts/chr/form/input-checkbox.coffee +40 -27
  17. data/app/assets/javascripts/chr/form/input-color.coffee +26 -8
  18. data/app/assets/javascripts/chr/form/input-date.coffee +0 -0
  19. data/app/assets/javascripts/chr/form/input-file.coffee +81 -46
  20. data/app/assets/javascripts/chr/form/input-form.coffee +162 -0
  21. data/app/assets/javascripts/chr/form/input-form_reorder.coffee +67 -0
  22. data/app/assets/javascripts/chr/form/input-hidden.coffee +27 -11
  23. data/app/assets/javascripts/chr/form/input-list.coffee +60 -56
  24. data/app/assets/javascripts/chr/form/input-list_reorder.coffee +37 -0
  25. data/app/assets/javascripts/chr/form/input-password.coffee +31 -0
  26. data/app/assets/javascripts/chr/form/input-select.coffee +61 -35
  27. data/app/assets/javascripts/chr/form/input-string.coffee +55 -25
  28. data/app/assets/javascripts/chr/form/input-text.coffee +22 -5
  29. data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +1 -1
  30. data/app/assets/javascripts/chr/vendor/ace.js +18280 -0
  31. data/app/assets/javascripts/chr/vendor/marked.js +1272 -0
  32. data/app/assets/javascripts/chr/vendor/mode-html.js +2436 -0
  33. data/app/assets/javascripts/chr/vendor/mode-markdown.js +2820 -0
  34. data/app/assets/javascripts/chr/vendor/redactor.fixedtoolbar.js +110 -0
  35. data/app/assets/javascripts/input-html.coffee +78 -0
  36. data/app/assets/javascripts/input-markdown.coffee +88 -0
  37. data/app/assets/javascripts/input-redactor.coffee +66 -0
  38. data/app/assets/stylesheets/_chr.scss +6 -6
  39. data/app/assets/stylesheets/_input-redactor.scss +34 -0
  40. data/app/assets/stylesheets/core/_mixins.scss +75 -0
  41. data/app/assets/stylesheets/form/_input-checkbox.scss +18 -0
  42. data/app/assets/stylesheets/form/{_input_color.scss → _input-color.scss} +0 -0
  43. data/app/assets/stylesheets/form/{_input_file.scss → _input-file.scss} +1 -0
  44. data/app/assets/stylesheets/form/{_nested_form.scss → _input-form.scss} +0 -0
  45. data/app/assets/stylesheets/form/{_input_list.scss → _input-list.scss} +0 -0
  46. data/app/assets/stylesheets/form/_input-string.scss +8 -0
  47. data/bower.json +3 -2
  48. data/{app/assets/javascripts/chr-dist.js → dist/chr.js} +1472 -1337
  49. data/dist/input-ace.js +24936 -0
  50. data/dist/input-redactor.js +156 -0
  51. data/lib/chr/version.rb +1 -1
  52. data/package.json +2 -2
  53. metadata +29 -13
  54. data/app/assets/javascripts/chr/core/list-pagination.coffee +0 -26
  55. data/app/assets/javascripts/chr/core/list-reorder.coffee +0 -70
  56. data/app/assets/javascripts/chr/core/list-search.coffee +0 -37
  57. data/app/assets/javascripts/chr/form/nested-form.coffee +0 -164
  58. data/app/assets/stylesheets/form/_input_checkbox.scss +0 -91
  59. data/app/assets/stylesheets/form/_input_string.scss +0 -8
@@ -0,0 +1,65 @@
1
+
2
+ # -----------------------------------------------------------------------------
3
+ # LIST CONFIG
4
+ # Methods for processing:
5
+ # - @config.items
6
+ # - @config.arrayStore
7
+ # - @config.objectStore
8
+ # -----------------------------------------------------------------------------
9
+
10
+ @listConfig =
11
+ # PRIVATE ===============================================
12
+
13
+ _process_config_items: ->
14
+ for slug, config of @config.items
15
+ object = { _id: slug, _title: config.title ? slug.titleize() }
16
+
17
+ # There might be some cases when we need this:
18
+ #if config.objectStore
19
+ # $.extend(object, config.objectStore.get())
20
+
21
+ if config.items or config.arrayStore
22
+ @module.addNestedList(slug, config, this)
23
+
24
+ @_add_item("#/#{ @path }/#{ slug }", object, 0, config)
25
+ @configItemsCount += 1
26
+
27
+
28
+ _bind_config_array_store: ->
29
+ # item added
30
+ @config.arrayStore.on 'object_added', (e, data) =>
31
+ @_add_item("#/#{ @path }/view/#{ data.object._id }", data.object, data.position, @config)
32
+
33
+ if @config.objects
34
+ @config.arrayStore.addObjects(@config.objects)
35
+
36
+ # item updated
37
+ @config.arrayStore.on 'object_changed', (e, data) =>
38
+ item = @items[data.object._id]
39
+ if item then item.render() ; @_update_item_position(item, data.position)
40
+
41
+ # item removed
42
+ @config.arrayStore.on 'object_removed', (e, data) =>
43
+ item = @items[data.object_id]
44
+ if item then item.destroy() ; delete @items[data.object_id]
45
+
46
+ # items loaded
47
+ @config.arrayStore.on 'objects_added', (e, data) =>
48
+ @_hide_spinner()
49
+ @_set_active_item()
50
+
51
+ if @config.arrayStore.pagination
52
+ @_bind_pagination()
53
+
54
+ if @config.arrayStore.searchable
55
+ @_bind_search(this)
56
+
57
+ if @config.arrayStore.reorderable
58
+ @_bind_reorder(this)
59
+
60
+
61
+ _bind_config_object_store: ->
62
+
63
+
64
+
65
+
@@ -0,0 +1,27 @@
1
+ # -----------------------------------------------------------------------------
2
+ # LIST PAGINATION
3
+ # todo:
4
+ # - trigger onScroll event only when scrolling down
5
+ # -----------------------------------------------------------------------------
6
+
7
+ @listPagination =
8
+ # PRIVATE ===============================================
9
+
10
+ _bind_pagination: ->
11
+ arrayStore = @config.arrayStore
12
+ @$items.scroll (e) =>
13
+ if ! arrayStore.dataFetchLock
14
+ # TODO: update this logic as it's not reliable when items has different height
15
+ $listChildren = @$items.children()
16
+ listChildrenCount = $listChildren.length
17
+ listFirstChildHeight = $listChildren.first().outerHeight()
18
+ listHeight = listChildrenCount * listFirstChildHeight
19
+ viewHeight = @$el.height()
20
+
21
+ if listHeight < (viewHeight + e.target.scrollTop + 100)
22
+ @_show_spinner()
23
+ arrayStore.load()
24
+
25
+
26
+
27
+
@@ -0,0 +1,75 @@
1
+ # -----------------------------------------------------------------------------
2
+ # LIST REORDER
3
+ # -----------------------------------------------------------------------------
4
+ #
5
+ # Dependencies:
6
+ #= require ../vendor/slip
7
+ #
8
+ # -----------------------------------------------------------------------------
9
+
10
+ @listReorder =
11
+ # PRIVATE ===============================================
12
+
13
+ _bind_reorder: (listEl) ->
14
+ items = listEl.items
15
+ list = listEl.$items.get(0)
16
+ arrayStore = listEl.config.arrayStore
17
+
18
+ config = arrayStore.reorderable
19
+
20
+ # this is optimistic scenario when assumes that all positions are different
21
+ _getObjectNewPosition = (el) ->
22
+ $el =$ el
23
+
24
+ nextObjectId = $el.next().attr('data-id')
25
+ prevObjectId = $el.prev().attr('data-id')
26
+ nextObjectPosition = 0
27
+ prevObjectPosition = 0
28
+
29
+ if prevObjectId
30
+ prevObjectPosition = items[prevObjectId].position()
31
+
32
+ if nextObjectId
33
+ nextObjectPosition = items[nextObjectId].position()
34
+
35
+ if arrayStore.sortReverse
36
+ newPosition = nextObjectPosition + Math.abs(nextObjectPosition - prevObjectPosition) / 2.0
37
+ else
38
+ newPosition = prevObjectPosition + Math.abs(nextObjectPosition - prevObjectPosition) / 2.0
39
+
40
+ return newPosition
41
+
42
+ new Slip(list)
43
+
44
+ list.addEventListener 'slip:beforeswipe', (e) -> e.preventDefault()
45
+
46
+ list.addEventListener 'slip:beforewait', ((e) ->
47
+ if $(e.target).hasClass("icon-reorder") then e.preventDefault()
48
+ ), false
49
+
50
+ list.addEventListener 'slip:beforereorder', ((e) ->
51
+ if not $(e.target).hasClass("icon-reorder") then e.preventDefault()
52
+ ), false
53
+
54
+ list.addEventListener 'slip:reorder', ((e) =>
55
+ # when `e.detail.insertBefore` is null, item put to the end of the list.
56
+ e.target.parentNode.insertBefore(e.target, e.detail.insertBefore)
57
+
58
+ objectPositionValue = _getObjectNewPosition(e.target)
59
+ objectId = $(e.target).attr('data-id')
60
+ value = {}
61
+ value["[#{arrayStore.sortBy}"] = "#{ objectPositionValue }"
62
+
63
+ arrayStore.update objectId, value,
64
+ # error handling
65
+ onSuccess: (object) => ;
66
+ onError: (errors) => ;
67
+
68
+ return false
69
+ ), false
70
+
71
+ $(list).addClass 'reorderable'
72
+
73
+
74
+
75
+
@@ -0,0 +1,41 @@
1
+ # -----------------------------------------------------------------------------
2
+ # LIST SEARCH
3
+ # -----------------------------------------------------------------------------
4
+
5
+ @listSearch =
6
+ # PRIVATE ===============================================
7
+
8
+ _bind_search: (listEl) ->
9
+ $input = listEl.$search
10
+ arrayStore = listEl.config.arrayStore
11
+
12
+ search = (input) ->
13
+ query = $(input).val()
14
+ listEl._show_spinner()
15
+ arrayStore.search(query)
16
+
17
+ show = ->
18
+ listEl.$el.addClass 'list-search'
19
+ $input.find('input').focus()
20
+
21
+ cancel = ->
22
+ listEl.$el.removeClass 'list-search'
23
+ $input.find('input').val('')
24
+ listEl._show_spinner()
25
+ arrayStore.reset()
26
+
27
+ $input.show()
28
+
29
+ $input.on 'keyup', 'input', (e) =>
30
+ if e.keyCode == 27 # esc
31
+ return cancel()
32
+
33
+ if e.keyCode == 13 # enter
34
+ return search(e.target)
35
+
36
+ $input.on 'click', '.icon', (e) => e.preventDefault() ; show()
37
+ $input.on 'click', '.cancel', (e) => e.preventDefault() ; cancel()
38
+
39
+
40
+
41
+
@@ -8,11 +8,25 @@
8
8
 
9
9
  # -----------------------------------------------------------------------------
10
10
  # MODULE
11
- #
12
- # configuration options:
11
+ # -----------------------------------------------------------------------------
12
+ # Config options:
13
13
  # title - title used for menu and root list header
14
14
  # showNestedListsAside - show module root list on the left and all nested
15
15
  # lists on the right side for desktop
16
+ #
17
+ # Public methods:
18
+ # addNestedList (listName, config, parentList)
19
+ # showNestedList (listName, animate=false)
20
+ # hideNestedLists (exceptList)
21
+ # visibleNestedListShownWithParent ()
22
+ # showRootList()
23
+ # hideActiveList (animate=false)
24
+ # showView (object, config, title, animate=false)
25
+ # showViewByObjectId (objectId, config, title, animate=false)
26
+ # destroyView ()
27
+ # show ()
28
+ # hide (animate=false)
29
+ #
16
30
  # -----------------------------------------------------------------------------
17
31
  class @Module
18
32
  constructor: (@chr, @name, @config) ->
@@ -34,7 +48,7 @@ class @Module
34
48
  @$el.addClass 'first-list-aside'
35
49
  # jump to first nested list on menu click
36
50
  firstNestedList = _firstNonEmptyValue(@nestedLists)
37
- if ! _isMobile() && firstNestedList
51
+ if ! @chr.isMobile() && firstNestedList
38
52
  menuPath += "/#{ firstNestedList.name }"
39
53
 
40
54
  @chr.addMenuItem(menuPath, menuTitle)
@@ -42,6 +56,8 @@ class @Module
42
56
  @config.onModuleInit?(this)
43
57
 
44
58
 
59
+ # PRIVATE ===============================================
60
+
45
61
  # update list data if it's not visible, e.g. for update action we do not
46
62
  # update whole list, this method is called before active list is shown.
47
63
  _update_active_list_items: ->
@@ -55,10 +71,46 @@ class @Module
55
71
  currentList.path
56
72
 
57
73
 
74
+ # PUBLIC ================================================
75
+
58
76
  addNestedList: (listName, config, parentList) ->
59
77
  @nestedLists[listName] = new List(this, listName, config, parentList)
60
78
 
61
79
 
80
+ # shows one of nested lists, with or without animation
81
+ showNestedList: (listName, animate=false) ->
82
+ listToShow = @nestedLists[listName]
83
+
84
+ if listToShow.showWithParent
85
+ # list works as view, never becomes active
86
+ listToShow.updateItems()
87
+ listToShow.show animate, => @hideNestedLists(exceptList=listName)
88
+
89
+ else
90
+ @activeList = listToShow
91
+ @_update_active_list_items()
92
+ @activeList.show(animate)
93
+
94
+ # hide view
95
+ if animate and @view then @view.$el.fadeOut $.fx.speeds._default, => @destroyView()
96
+
97
+
98
+ hideNestedLists: (exceptList) ->
99
+ @activeList = @rootList
100
+ list.hide() for key, list of @nestedLists when key isnt exceptList
101
+
102
+
103
+ visibleNestedListShownWithParent: ->
104
+ for key, list of @nestedLists
105
+ if list.isVisible() && list.showWithParent then return list
106
+
107
+
108
+ showRootList: () ->
109
+ @destroyView()
110
+ while @activeList != @rootList
111
+ @hideActiveList(false)
112
+
113
+
62
114
  hideActiveList: (animate=false)->
63
115
  if animate then @activeList.$el.fadeOut() else @activeList.$el.hide()
64
116
  @activeList = @activeList.parentList
@@ -105,34 +157,5 @@ class @Module
105
157
  @$el.hide()
106
158
 
107
159
 
108
- # shows one of nested lists, with or without animation
109
- showNestedList: (listName, animate=false) ->
110
- listToShow = @nestedLists[listName]
111
-
112
- if listToShow.showWithParent
113
- # list works as view, never becomes active
114
- listToShow.updateItems()
115
- listToShow.show animate, => @hideNestedLists(exceptList=listName)
116
-
117
- else
118
- @activeList = listToShow
119
- @_update_active_list_items()
120
- @activeList.show(animate)
121
-
122
- # hide view
123
- if animate and @view then @view.$el.fadeOut $.fx.speeds._default, => @destroyView()
124
-
125
-
126
- hideNestedLists: (exceptList) ->
127
- @activeList = @rootList
128
- list.hide() for key, list of @nestedLists when key isnt exceptList
129
-
130
-
131
- # returns visible nested list that acts as view
132
- visibleNestedListShownWithParent: ->
133
- for key, list of @nestedLists
134
- if list.isVisible() && list.showWithParent then return list
135
-
136
-
137
160
 
138
161
 
@@ -1,5 +1,26 @@
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
+
1
9
  # -----------------------------------------------------------------------------
2
10
  # UTILS
11
+ # -----------------------------------------------------------------------------
12
+ # Public methods:
13
+ # _last(array)
14
+ # _first(array)
15
+ # _firstNonEmptyValue(hash)
16
+ # _escapeHtml(string)
17
+ # String.titleize()
18
+ # String.reverse()
19
+ # String.startsWith(str)
20
+ # String.endsWith(str)
21
+ # String.plainText()
22
+ # include(class, hash)
23
+ # -----------------------------------------------------------------------------
3
24
 
4
25
  # _last(array)
5
26
  @_last = (array) -> array[array.length - 1]
@@ -10,20 +31,17 @@
10
31
  # _firstNonEmptyValue(hash)
11
32
  @_firstNonEmptyValue = (o) -> ((return v if k[0] != '_' and v and v != '') for k, v of o) ; return null
12
33
 
13
- # _stripHtml(string)
14
- @_stripHtml = (string) -> String(string).replace(/<\/?[^>]+(>|$)/g, "")
15
-
16
34
  # _escapeHtml(string)
17
35
  @_entityMap = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': '&quot;', "'": '&#39;', "/": '&#x2F;' }
18
36
  @_escapeHtml = (string) -> String(string).replace /[&<>"'\/]/g, (s) -> _entityMap[s]
19
37
 
20
38
  # String.titleize
21
39
  if typeof String.prototype.titleize != 'function'
22
- String.prototype.titleize = () -> return this.replace(/_/g, ' ').replace(/\b./g, ((m) -> m.toUpperCase()))
40
+ String.prototype.titleize = -> return this.replace(/_/g, ' ').replace(/\b./g, ((m) -> m.toUpperCase()))
23
41
 
24
42
  # String.reverse
25
43
  if typeof String.prototype.reverse != 'function'
26
- String.prototype.reverse = (str) -> return this.split("").reverse().join("")
44
+ String.prototype.reverse = -> return this.split("").reverse().join("")
27
45
 
28
46
  # String.startsWith
29
47
  if typeof String.prototype.startsWith != 'function'
@@ -33,17 +51,20 @@ if typeof String.prototype.startsWith != 'function'
33
51
  if typeof String.prototype.endsWith != 'function'
34
52
  String.prototype.endsWith = (str) -> return this.slice(this.length - str.length, this.length) == str
35
53
 
36
- # -----------------------------------------------------------------------------
37
- # HELPERS
38
-
39
- # Helps to figure out how many list items fits screen height
40
- @_itemsPerScreen = -> itemHeight = 60 ; return Math.ceil($(window).height() / itemHeight)
41
- @_itemsPerPageRequest = _itemsPerScreen() * 2
54
+ # String.plainText
55
+ if typeof String.prototype.plainText != 'function'
56
+ String.prototype.plainText = () -> return $("<div>#{ this }</div>").text()
42
57
 
43
- # Check if running on mobile
44
- @_isMobile = -> $(window).width() < 760
45
58
 
46
59
  # -----------------------------------------------------------------------------
60
+ # Mixins: http://arcturo.github.io/library/coffeescript/03_classes.html
61
+ # -----------------------------------------------------------------------------
62
+ @extend = (obj, mixin) ->
63
+ obj[name] = method for name, method of mixin
64
+ return obj
65
+
66
+ @include = (klass, mixin) ->
67
+ extend(klass.prototype, mixin)
47
68
 
48
69
 
49
70
 
@@ -9,11 +9,15 @@
9
9
  # -----------------------------------------------------------------------------
10
10
  # VIEW
11
11
  #
12
- # configuration options:
13
- # formSchema - form schema for object, autogenerated if missing
14
- # disableDelete - do not add delete button below the form
15
- # disableSave - do not add save button in header
16
- # fullsizeView — use fullsize layout in desktop mode
12
+ # config options:
13
+ # formClass - custom form class to be used
14
+ # formSchema - form schema for object, autogenerated if missing
15
+ # disableDelete - do not add delete button below the form
16
+ # disableSave - do not add save button in header
17
+ # fullsizeView — use fullsize layout in desktop mode
18
+ # onViewShow - on show callback
19
+ # onShowAnimation - animation method to be used on show
20
+ # onCloseAnimation - animation method to be used on close
17
21
  #
18
22
  # public methods:
19
23
  # show(animate, callback)
@@ -24,151 +28,120 @@ class @View
24
28
  constructor: (@module, @config, @closePath, @object, @title) ->
25
29
  @store = @config.arrayStore ? @config.objectStore
26
30
 
27
- @$el =$ "<section class='view #{ @module.name }'>"
28
- @$el.hide()
31
+ @$el =$ "<section class='view #{ @module.name }' style='display:none;'>"
29
32
 
33
+ # fullsize
30
34
  if @config.fullsizeView
31
35
  @$el.addClass 'fullsize'
32
36
 
37
+ # animations
38
+ @onShowAnimation = @config.onShowAnimation
39
+ @onCloseAnimation = @config.onCloseAnimation
40
+ @onShowAnimation ?= (callback) => @$el.fadeIn $.fx.speeds._default, -> callback()
41
+ @onCloseAnimation ?= (callback) => @$el.fadeOut $.fx.speeds._default, -> callback()
42
+
33
43
  # header
34
44
  @$header =$ "<header></header>"
35
- @$el.append @$header
36
-
37
45
  @$title =$ "<div class='title'></div>"
38
46
  @$header.append @$title
47
+ @$el.append @$header
48
+ @_set_title()
39
49
 
40
- # close button
50
+ # close
41
51
  @$closeBtn =$ "<a href='#/#{ @closePath }' class='close silent'>Close</a>"
42
- @$closeBtn.on 'click', (e) => @_on_close(e)
52
+ @$closeBtn.on 'click', (e) => @_close(e)
43
53
  @$header.append @$closeBtn
44
54
 
45
- # save button
55
+ # save
46
56
  unless @config.disableSave
47
57
  @$saveBtn =$ "<a href='#' class='save'>Save</a>"
48
- @$saveBtn.on 'click', (e) => @_on_save(e)
58
+ @$saveBtn.on 'click', (e) => @_save(e)
49
59
  @$header.append @$saveBtn
50
60
 
51
- @_render()
52
-
61
+ # form
62
+ @form = new (@config.formClass ? Form)(@object, @config)
63
+ @$el.append @form.$el
64
+ @_add_form_delete_button()
53
65
 
54
- _render: ->
55
- @_update_title()
56
- @_render_form()
57
66
 
67
+ # PRIVATE ===============================================
58
68
 
59
- _update_title: ->
69
+ _set_title: (reset=false) ->
70
+ if reset && @config.arrayStore then @title = null
60
71
  title = @title
61
72
  title ?= @object[@config.itemTitleField] if @config.itemTitleField
62
73
  title ?= _firstNonEmptyValue(@object)
74
+ @$title.html(title.plainText())
63
75
 
64
- if title == "" then title = "No Title"
65
-
66
- # remove html tags from title to do not break layout
67
- titleText = $("<div>#{ title }</div>").text()
68
- @$title.html(titleText)
69
76
 
70
-
71
- _render_form: ->
72
- @form?.destroy()
73
- @form = new Form(@object, @config)
74
-
75
- # delete button
76
- unless @config.disableDelete or @config.objectStore or @_is_new()
77
+ _add_form_delete_button: ->
78
+ unless @config.disableDelete or @config.objectStore or (! @object)
77
79
  @$deleteBtn =$ "<a href='#' class='delete'>Delete</a>"
78
- @$deleteBtn.on 'click', (e) => @_on_delete(e)
80
+ @$deleteBtn.on 'click', (e) => @_delete(e)
79
81
  @form.$el.append @$deleteBtn
80
82
 
81
- @$el.append @form.$el
82
-
83
-
84
- _update_object: (value) ->
85
- @_start_saving()
86
- @store.update @object._id, value,
87
- onSuccess: (@object) =>
88
- # add a note here for this line, it's not obvious why it's here,
89
- # looks like some logic related to title update
90
- if @config.arrayStore then @title = null
91
-
92
- formScrollPosition = @form.$el.scrollTop()
93
- @_render()
94
- @_initialize_form_plugins()
95
- @form.$el.scrollTop(formScrollPosition)
96
-
97
- @_stop_saving()
98
83
 
99
- onError: (errors) =>
100
- @_validation_errors('Changes were not saved.', errors)
101
- @_stop_saving()
84
+ _save_success: ->
85
+ @$el.removeClass('view-saving')
86
+ @_set_title(true)
87
+ @form.updateValues(@object)
102
88
 
103
89
 
104
- _create_object: (value) ->
105
- @_start_saving()
106
- # refactor this to subscribe to list event: item_added
107
- @store.push value,
108
- onSuccess: (object) =>
109
- # we need to know when list item is added
110
- location.hash = "#/#{ @closePath }/view/#{ object._id }"
111
- onError: (errors) =>
112
- @_validation_errors('Item were not created.', errors)
113
- @_stop_saving()
114
-
115
- _start_saving: ->
116
- @$el.addClass('view-saving')
117
-
118
-
119
- _stop_saving: ->
120
- setTimeout ( => @$el.removeClass('view-saving') ), 250
121
-
122
-
123
- _initialize_form_plugins: ->
124
- @form.initializePlugins()
125
- @config.onViewShow?(@)
126
-
127
-
128
- _validation_errors: (message, errors) ->
90
+ _save_error: (message, validationErrors) ->
91
+ @$el.removeClass('view-saving')
129
92
  chr.showError(message)
130
- @form.showValidationErrors(errors)
131
-
93
+ @form.showValidationErrors(validationErrors)
132
94
 
133
- _is_new: ->
134
- ! @object
135
95
 
96
+ # EVENTS ================================================
136
97
 
137
- _on_close: (e) ->
138
- @$el.fadeOut $.fx.speeds._default, => @destroy()
98
+ _close: (e) ->
99
+ @onCloseAnimation(=> @destroy())
139
100
 
140
101
 
141
- _on_save: (e) ->
102
+ _save: (e) ->
142
103
  e.preventDefault()
104
+ @$el.addClass('view-saving')
105
+
143
106
  serializedFormObj = @form.serialize()
144
107
 
145
108
  if @object
146
- @_update_object(serializedFormObj)
109
+ @store.update @object._id, serializedFormObj,
110
+ onSuccess: (@object) => @_save_success()
111
+ onError: (errors) => @_save_error('Changes were not saved.', errors)
147
112
  else
148
- @_create_object(serializedFormObj)
113
+ @store.push serializedFormObj,
114
+ onSuccess: (@object) =>
115
+ @_save_success()
116
+ @_add_form_delete_button()
117
+ chr.updateHash("#/#{ @closePath }/view/#{ @object._id }", true)
118
+ onError: (errors) => @_save_error('Item were not created.', errors)
149
119
 
150
120
 
151
- _on_delete: (e) ->
121
+ _delete: (e) ->
152
122
  e.preventDefault()
153
123
  if confirm("Are you sure?")
154
- @store.remove(@object._id)
155
- @$el.fadeOut $.fx.speeds._default, =>
156
- window._skipHashchange = true
157
- location.hash = "#/#{ @closePath }"
158
- @destroy()
124
+ @store.remove @object._id,
125
+ onSuccess: => @onCloseAnimation( => chr.updateHash("#/#{ @closePath }", true) ; @destroy() )
126
+ onError: -> chr.showError('Can\'t delete object.')
127
+
159
128
 
129
+ # PUBLIC ================================================
160
130
 
161
131
  show: (animate, callback) ->
132
+ after_show = =>
133
+ callback?()
134
+ @form.initializePlugins()
135
+ @config.onViewShow?(@)
136
+
162
137
  if animate
163
- @$el.fadeIn($.fx.speeds._default, =>
164
- @_initialize_form_plugins()
165
- callback?())
138
+ @onShowAnimation(=> after_show())
166
139
  else
167
- @$el.show 0, => @_initialize_form_plugins() ; callback?()
140
+ @$el.show(0, => after_show())
168
141
 
169
142
 
170
143
  destroy: ->
171
- @form?.destroy()
144
+ @form.destroy()
172
145
  @$el.remove()
173
146
 
174
147