chr 0.2.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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