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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gruntfile.coffee +50 -16
- data/app/assets/javascripts/chr.coffee +9 -16
- data/app/assets/javascripts/chr/core/chr.coffee +38 -20
- data/app/assets/javascripts/chr/core/item.coffee +30 -24
- data/app/assets/javascripts/chr/core/list.coffee +30 -60
- data/app/assets/javascripts/chr/core/list_config.coffee +65 -0
- data/app/assets/javascripts/chr/core/list_pagination.coffee +27 -0
- data/app/assets/javascripts/chr/core/list_reorder.coffee +75 -0
- data/app/assets/javascripts/chr/core/list_search.coffee +41 -0
- data/app/assets/javascripts/chr/core/module.coffee +55 -32
- data/app/assets/javascripts/chr/core/utils.coffee +34 -13
- data/app/assets/javascripts/chr/core/view.coffee +70 -97
- data/app/assets/javascripts/chr/form/form.coffee +63 -49
- data/app/assets/javascripts/chr/form/input-checkbox.coffee +40 -27
- data/app/assets/javascripts/chr/form/input-color.coffee +26 -8
- data/app/assets/javascripts/chr/form/input-date.coffee +0 -0
- data/app/assets/javascripts/chr/form/input-file.coffee +81 -46
- data/app/assets/javascripts/chr/form/input-form.coffee +162 -0
- data/app/assets/javascripts/chr/form/input-form_reorder.coffee +67 -0
- data/app/assets/javascripts/chr/form/input-hidden.coffee +27 -11
- data/app/assets/javascripts/chr/form/input-list.coffee +60 -56
- data/app/assets/javascripts/chr/form/input-list_reorder.coffee +37 -0
- data/app/assets/javascripts/chr/form/input-password.coffee +31 -0
- data/app/assets/javascripts/chr/form/input-select.coffee +61 -35
- data/app/assets/javascripts/chr/form/input-string.coffee +55 -25
- data/app/assets/javascripts/chr/form/input-text.coffee +22 -5
- data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +1 -1
- data/app/assets/javascripts/chr/vendor/ace.js +18280 -0
- data/app/assets/javascripts/chr/vendor/marked.js +1272 -0
- data/app/assets/javascripts/chr/vendor/mode-html.js +2436 -0
- data/app/assets/javascripts/chr/vendor/mode-markdown.js +2820 -0
- data/app/assets/javascripts/chr/vendor/redactor.fixedtoolbar.js +110 -0
- data/app/assets/javascripts/input-html.coffee +78 -0
- data/app/assets/javascripts/input-markdown.coffee +88 -0
- data/app/assets/javascripts/input-redactor.coffee +66 -0
- data/app/assets/stylesheets/_chr.scss +6 -6
- data/app/assets/stylesheets/_input-redactor.scss +34 -0
- data/app/assets/stylesheets/core/_mixins.scss +75 -0
- data/app/assets/stylesheets/form/_input-checkbox.scss +18 -0
- data/app/assets/stylesheets/form/{_input_color.scss → _input-color.scss} +0 -0
- data/app/assets/stylesheets/form/{_input_file.scss → _input-file.scss} +1 -0
- data/app/assets/stylesheets/form/{_nested_form.scss → _input-form.scss} +0 -0
- data/app/assets/stylesheets/form/{_input_list.scss → _input-list.scss} +0 -0
- data/app/assets/stylesheets/form/_input-string.scss +8 -0
- data/bower.json +3 -2
- data/{app/assets/javascripts/chr-dist.js → dist/chr.js} +1472 -1337
- data/dist/input-ace.js +24936 -0
- data/dist/input-redactor.js +156 -0
- data/lib/chr/version.rb +1 -1
- data/package.json +2 -2
- metadata +29 -13
- data/app/assets/javascripts/chr/core/list-pagination.coffee +0 -26
- data/app/assets/javascripts/chr/core/list-reorder.coffee +0 -70
- data/app/assets/javascripts/chr/core/list-search.coffee +0 -37
- data/app/assets/javascripts/chr/form/nested-form.coffee +0 -164
- data/app/assets/stylesheets/form/_input_checkbox.scss +0 -91
- 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
|
-
#
|
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 !
|
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 = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' }
|
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 =
|
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 =
|
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
|
-
|
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
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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
|
50
|
+
# close
|
41
51
|
@$closeBtn =$ "<a href='#/#{ @closePath }' class='close silent'>Close</a>"
|
42
|
-
@$closeBtn.on 'click', (e) => @
|
52
|
+
@$closeBtn.on 'click', (e) => @_close(e)
|
43
53
|
@$header.append @$closeBtn
|
44
54
|
|
45
|
-
# save
|
55
|
+
# save
|
46
56
|
unless @config.disableSave
|
47
57
|
@$saveBtn =$ "<a href='#' class='save'>Save</a>"
|
48
|
-
@$saveBtn.on 'click', (e) => @
|
58
|
+
@$saveBtn.on 'click', (e) => @_save(e)
|
49
59
|
@$header.append @$saveBtn
|
50
60
|
|
51
|
-
|
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
|
-
|
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
|
-
|
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) => @
|
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
|
-
|
100
|
-
|
101
|
-
|
84
|
+
_save_success: ->
|
85
|
+
@$el.removeClass('view-saving')
|
86
|
+
@_set_title(true)
|
87
|
+
@form.updateValues(@object)
|
102
88
|
|
103
89
|
|
104
|
-
|
105
|
-
|
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(
|
131
|
-
|
93
|
+
@form.showValidationErrors(validationErrors)
|
132
94
|
|
133
|
-
_is_new: ->
|
134
|
-
! @object
|
135
95
|
|
96
|
+
# EVENTS ================================================
|
136
97
|
|
137
|
-
|
138
|
-
|
98
|
+
_close: (e) ->
|
99
|
+
@onCloseAnimation(=> @destroy())
|
139
100
|
|
140
101
|
|
141
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
121
|
+
_delete: (e) ->
|
152
122
|
e.preventDefault()
|
153
123
|
if confirm("Are you sure?")
|
154
|
-
@store.remove
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
164
|
-
@_initialize_form_plugins()
|
165
|
-
callback?())
|
138
|
+
@onShowAnimation(=> after_show())
|
166
139
|
else
|
167
|
-
@$el.show
|
140
|
+
@$el.show(0, => after_show())
|
168
141
|
|
169
142
|
|
170
143
|
destroy: ->
|
171
|
-
@form
|
144
|
+
@form.destroy()
|
172
145
|
@$el.remove()
|
173
146
|
|
174
147
|
|