chr 0.2.8 → 0.3.5

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +33 -27
  3. data/README.md +6 -1
  4. data/app/assets/javascripts/chr.coffee +18 -16
  5. data/app/assets/javascripts/chr/core/chr.coffee +41 -76
  6. data/app/assets/javascripts/chr/core/chr_router.coffee +141 -0
  7. data/app/assets/javascripts/chr/core/item.coffee +36 -49
  8. data/app/assets/javascripts/chr/core/list.coffee +26 -71
  9. data/app/assets/javascripts/chr/core/list_config.coffee +28 -13
  10. data/app/assets/javascripts/chr/core/list_pagination.coffee +56 -13
  11. data/app/assets/javascripts/chr/core/list_reorder.coffee +2 -2
  12. data/app/assets/javascripts/chr/core/list_search.coffee +2 -2
  13. data/app/assets/javascripts/chr/core/module.coffee +24 -93
  14. data/app/assets/javascripts/chr/core/utils.coffee +4 -0
  15. data/app/assets/javascripts/chr/core/view.coffee +123 -43
  16. data/app/assets/javascripts/chr/core/view_local-storage.coffee +58 -0
  17. data/app/assets/javascripts/chr/store/{_array-store.coffee → array-store.coffee} +3 -1
  18. data/app/assets/javascripts/chr/store/{_object-store.coffee → object-store.coffee} +0 -0
  19. data/app/assets/javascripts/chr/store/rails-array-store.coffee +39 -0
  20. data/app/assets/javascripts/chr/store/{mongosteen-object-store.coffee → rails-form-object-parser.coffee} +11 -20
  21. data/app/assets/javascripts/chr/store/rails-object-store.coffee +35 -0
  22. data/app/assets/javascripts/chr/store/rest-array-store.coffee +3 -5
  23. data/app/assets/javascripts/chr/store/rest-object-store.coffee +1 -1
  24. data/app/assets/javascripts/form/expandable-group.coffee +30 -0
  25. data/app/assets/javascripts/{chr/form → form}/form.coffee +3 -1
  26. data/app/assets/javascripts/{chr/form → form}/input-checkbox.coffee +1 -1
  27. data/app/assets/javascripts/{chr/form → form}/input-color.coffee +2 -0
  28. data/app/assets/javascripts/{chr/form → form}/input-date.coffee +0 -0
  29. data/app/assets/javascripts/{chr/form → form}/input-file.coffee +28 -1
  30. data/app/assets/javascripts/{chr/form → form}/input-form.coffee +14 -5
  31. data/app/assets/javascripts/{chr/form → form}/input-form_reorder.coffee +0 -0
  32. data/app/assets/javascripts/{chr/form → form}/input-hidden.coffee +9 -9
  33. data/app/assets/javascripts/{chr/form → form}/input-list.coffee +61 -53
  34. data/app/assets/javascripts/{chr/form → form}/input-list_reorder.coffee +2 -0
  35. data/app/assets/javascripts/form/input-list_typeahead.coffee +55 -0
  36. data/app/assets/javascripts/{chr/form → form}/input-password.coffee +1 -0
  37. data/app/assets/javascripts/{chr/form → form}/input-select.coffee +10 -11
  38. data/app/assets/javascripts/form/input-select2.coffee +33 -0
  39. data/app/assets/javascripts/{chr/form → form}/input-string.coffee +45 -2
  40. data/app/assets/javascripts/{chr/form → form}/input-text.coffee +6 -3
  41. data/app/assets/javascripts/input-html.coffee +8 -5
  42. data/app/assets/javascripts/input-markdown.coffee +10 -5
  43. data/app/assets/javascripts/input-redactor.coffee +1 -61
  44. data/app/assets/javascripts/redactor/input-redactor.coffee +53 -0
  45. data/app/assets/javascripts/redactor/input-redactor_character.coffee +83 -0
  46. data/app/assets/javascripts/redactor/input-redactor_images.coffee +166 -0
  47. data/app/assets/javascripts/{chr/vendor → vendor}/ace.js +0 -0
  48. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.scrollparent.js +0 -0
  49. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.textarea_autosize.js +0 -0
  50. data/app/assets/javascripts/{chr/vendor → vendor}/jquery.typeahead.js +0 -0
  51. data/app/assets/javascripts/{chr/vendor → vendor}/marked.js +0 -0
  52. data/app/assets/javascripts/{chr/vendor → vendor}/mode-html.js +0 -0
  53. data/app/assets/javascripts/{chr/vendor → vendor}/mode-markdown.js +0 -0
  54. data/app/assets/javascripts/{chr/vendor → vendor}/redactor.fixedtoolbar.js +1 -1
  55. data/app/assets/javascripts/vendor/select2.js +5274 -0
  56. data/app/assets/javascripts/{chr/vendor → vendor}/slip.js +0 -0
  57. data/app/assets/stylesheets/_chr.scss +11 -13
  58. data/app/assets/stylesheets/_input-redactor.scss +17 -16
  59. data/app/assets/stylesheets/{core → chr}/_icons.scss +1 -1
  60. data/app/assets/stylesheets/chr/_main.scss +110 -0
  61. data/app/assets/stylesheets/{core → chr}/_mixins.scss +58 -34
  62. data/app/assets/stylesheets/{core → chr}/_settings.scss +7 -1
  63. data/app/assets/stylesheets/form/_expandable-group.scss +16 -0
  64. data/app/assets/stylesheets/form/_input-select2.scss +94 -0
  65. data/app/assets/stylesheets/form/_main.scss +180 -0
  66. data/app/assets/stylesheets/vendor/select2.css +258 -0
  67. data/bower.json +2 -2
  68. data/dist/chr.js +2292 -2030
  69. data/dist/input-ace.js +18 -8
  70. data/dist/input-redactor.js +0 -156
  71. data/docs/bootstrap.md +1 -1
  72. data/docs/rails.md +10 -10
  73. data/lib/chr.rb +1 -8
  74. data/lib/chr/version.rb +1 -1
  75. data/lib/mongoid/character.rb +30 -0
  76. data/package.json +1 -1
  77. metadata +49 -43
  78. data/app/assets/javascripts/chr/store/mongosteen-array-store.coffee +0 -55
  79. data/app/assets/stylesheets/core/_list.scss +0 -39
  80. data/app/assets/stylesheets/core/_main.scss +0 -49
  81. data/app/assets/stylesheets/core/_responsive.scss +0 -62
  82. data/app/assets/stylesheets/form/_form.scss +0 -41
  83. data/app/assets/stylesheets/form/_input-checkbox.scss +0 -18
  84. data/app/assets/stylesheets/form/_input-color.scss +0 -10
  85. data/app/assets/stylesheets/form/_input-file.scss +0 -29
  86. data/app/assets/stylesheets/form/_input-form.scss +0 -26
  87. data/app/assets/stylesheets/form/_input-list.scss +0 -36
  88. data/app/assets/stylesheets/form/_input-string.scss +0 -8
@@ -9,6 +9,10 @@
9
9
  # -----------------------------------------------------------------------------
10
10
  # LIST ITEM
11
11
  # -----------------------------------------------------------------------------
12
+ #
13
+ # config options:
14
+ # onItemRender
15
+ #
12
16
  # public methods:
13
17
  # render()
14
18
  # destroy()
@@ -16,89 +20,72 @@
16
20
  #
17
21
  # -----------------------------------------------------------------------------
18
22
  class @Item
19
- constructor: (@module, @path, @object, @config) ->
20
- @$el =$ """<a class='item' href='#{ @path }' data-id='#{ @object._id }' data-title=''></a>"""
21
- @$el.on 'click', (e) => @_click(e)
23
+ constructor: (@module, @path, @object, @config, @type) ->
24
+ @$el =$ """<a class='item is-#{ @type }' href='#{ @path }' data-id='#{ @object._id }'></a>"""
22
25
  @render()
23
26
 
24
27
 
25
28
  # PRIVATE ===============================================
26
29
 
27
- _is_folder: ->
28
- # update this logic as it's not reliable
29
- if @object._title then true else false
30
-
31
-
32
30
  _render_title: ->
33
- title = @object._title # nested list title predefined in config (or slug based)
34
- title ?= @object[@config.itemTitleField] # based on config
35
- title ?= _firstNonEmptyValue(@object) # auto-generated: first non empty value
31
+ title = @object.__title__ # title for @config.items
32
+ title ?= @object[@config.itemTitleField]
33
+ title ?= @object['_list_item_title']
34
+ title ?= _firstNonEmptyValue(@object)
36
35
  title ?= "No Title"
37
36
  title = title.plainText()
38
37
 
39
38
  @$title =$ "<div class='item-title'>#{ title }</div>"
40
39
  @$el.append(@$title)
41
- @$el.attr('data-title', title)
42
40
 
43
41
 
44
42
  _render_subtitle: ->
43
+ subtitle = @object.__subtitle__ # subtitle for @config.items
44
+
45
45
  if @config.itemSubtitleField
46
- subtitle = @object[@config.itemSubtitleField]
47
- if subtitle != ''
48
- @$subtitle =$ "<div class='item-subtitle'>#{subtitle}</div>"
49
- @$el.append(@$subtitle)
50
- @$el.addClass 'has-subtitle'
46
+ subtitle ?= @object[@config.itemSubtitleField]
47
+
48
+ subtitle ?= @object['_list_item_subtitle']
49
+
50
+ if subtitle
51
+ @$subtitle =$ "<div class='item-subtitle'>#{ subtitle }</div>"
52
+ @$el.append(@$subtitle)
53
+ @$el.addClass 'has-subtitle'
51
54
 
52
55
 
53
56
  _render_thumbnail: ->
54
- if @config.itemThumbnail
55
- imageUrl = @config.itemThumbnail(@object)
56
- # carrierwave fix, check if still required
57
- if imageUrl != '' and not imageUrl.endsWith('_old_')
58
- @$thumbnail =$ "<div class='item-thumbnail'><img src='#{imageUrl}' /></div>"
57
+ imageUrl = @config.itemThumbnail?(@object)
58
+ imageUrl ?= @object[@config.itemThumbnail]
59
+ imageUrl ?= @object['_list_item_thumbnail']
60
+
61
+ if imageUrl
62
+ # RAILS carrierwave fix, check if still required
63
+ if not imageUrl.endsWith('_old_')
64
+ @$thumbnail =$ "<div class='item-thumbnail'><img src='#{ imageUrl }' /></div>"
59
65
  @$el.append(@$thumbnail)
60
66
  @$el.addClass 'has-thumbnail'
61
67
 
62
68
 
63
- # EVENTS ================================================
64
-
65
- _click: (e) ->
66
- if @.$el.hasClass('active') then e.preventDefault() ; return
67
-
68
- hash = $(e.currentTarget).attr('href')
69
- crumbs = hash.split('/')
70
- title = $(e.currentTarget).attr('data-title')
71
- id = $(e.currentTarget).attr('data-id')
72
-
73
- chr.updateHash(hash, true)
74
-
75
- # show view for a arrayStore item
76
- if crumbs[crumbs.length - 2] == 'view'
77
- return @module.showViewByObjectId(id, @config, title)
78
- # show objectStore item view
79
- if @config.objectStore
80
- return @module.showViewByObjectId('', @config, title)
81
- # show nested list
82
- @module.showNestedList(_last(crumbs))
83
-
84
-
85
69
  # PUBLIC ================================================
86
70
 
87
71
  render: ->
88
- @$el.html('').removeClass('item-folder has-subtitle has-thumbnail')
72
+ @$el.html('').removeClass('has-subtitle has-thumbnail')
73
+
89
74
  @_render_title()
75
+ @_render_subtitle()
90
76
 
91
- if @_is_folder()
92
- @$el.addClass('item-folder')
77
+ if @type == 'folder'
93
78
  @$el.append $("<div class='icon-folder'></div>")
94
- else
95
- @_render_subtitle()
79
+
80
+ if @type == 'object'
96
81
  @_render_thumbnail()
97
82
 
98
83
  if @config.arrayStore and @config.arrayStore.reorderable
99
84
  @$el.addClass('reorderable')
100
85
  @$el.append $("<div class='icon-reorder'></div>")
101
86
 
87
+ @config.onItemRender?(this)
88
+
102
89
 
103
90
  destroy: ->
104
91
  @$el.remove()
@@ -11,6 +11,8 @@
11
11
  # -----------------------------------------------------------------------------
12
12
  #
13
13
  # Configuration options:
14
+ # title - list title
15
+ # subtitle - list subtitle
14
16
  # itemClass - item class to be used instead of default one
15
17
  # itemTitleField - object attributes name for list item title
16
18
  # itemSubtitleField - object attributes name for list item subtitle
@@ -19,8 +21,11 @@
19
21
  # onListInit - callback on list is initialized
20
22
  # onListShow - callback on list is shown
21
23
  # objects - objects array to be added to the store on start
24
+ # showWithParent - show list on a aside from parent
22
25
  #
23
26
  # Public methods:
27
+ # showSpinner()
28
+ # hideSpinner()
24
29
  # hide() - hide list
25
30
  # show() - show list
26
31
  # updateItems() - update list items (sync through store with backend)
@@ -35,54 +40,47 @@
35
40
  # -----------------------------------------------------------------------------
36
41
 
37
42
  class @List
38
- constructor: (@module, @name, @config, @parentList) ->
39
- @configItemsCount = 0
40
- @path = @_path()
41
- @items = {}
42
- @title = @config.title ? @name.titleize()
43
- @itemClass = @config.itemClass ? Item
44
-
45
- @showWithParent = false
46
- if @parentList
47
- @showWithParent = @parentList.config.showNestedListsAside || false
43
+ constructor: (@module, @path, @name, @config, @parentList) ->
44
+ @items = {}
45
+ @title = @config.title ? @name.titleize()
46
+ @itemClass = @config.itemClass ? Item
47
+
48
+ @_config_items_count = 0
48
49
 
49
- @config.showListWithParent ? false
50
+ @showWithParent = @config.showWithParent ? false
50
51
 
51
- @$el =$ "<div class='list #{ @name }'>"
52
+ @$el =$ "<div class='list #{ @name }' style='display:none;'>"
52
53
  @module.$el.append @$el
53
54
 
54
- # hide all nested lists
55
- if @parentList then @$el.hide()
55
+ if @showWithParent
56
+ @$el.addClass('list-aside')
56
57
 
57
58
  # items
58
59
  @$items =$ "<div class='items'>"
59
60
  @$el.append @$items
60
61
 
61
62
  # header
62
- @$header =$ "<header></header>"
63
+ @$header =$ "<header class='header'></header>"
63
64
  @$el.append @$header
64
65
 
65
- # back button
66
+ # back
66
67
  if @parentList
67
- @$backBtn =$ "<a href='#/#{ @parentList.path }' class='back silent'></a>"
68
- @$backBtn.on 'click', (e) => @_back(e)
68
+ @$backBtn =$ "<a href='#{ @parentList.path }' class='back'>Close</a>"
69
69
  else
70
- @$backBtn =$ "<a href='#/' class='back'></a>"
70
+ @$backBtn =$ "<a href='#/' class='back'>Close</a>"
71
71
  @$header.prepend @$backBtn
72
72
 
73
73
  # spinner & title
74
74
  @$header.append "<div class='spinner'></div>"
75
75
  @$header.append "<span class='title'>#{ @title }</span>"
76
76
 
77
- # new item button
77
+ # new item
78
78
  if not @config.disableNewItems and @config.formSchema
79
- @$newBtn =$ "<a href='#/#{ @path }/new' class='new silent'></a>"
80
- @$newBtn.on 'click', (e) => @_new(e)
79
+ @$newBtn =$ "<a href='#{ @path }/new' class='new'></a>"
81
80
  @$header.append @$newBtn
82
81
 
83
82
  if @config.items then @_process_config_items()
84
83
  if @config.arrayStore then @_bind_config_array_store()
85
- if @config.objectStore then @_bind_config_object_store()
86
84
 
87
85
  @_bind_hashchange()
88
86
 
@@ -104,62 +102,22 @@ class @List
104
102
  return $(a).addClass('active')
105
103
 
106
104
 
107
- _path: ->
108
- crumbs = [] ; l = this
109
- while l.parentList
110
- crumbs.push(l.name) ; l = l.parentList
111
- @module.name + ( if crumbs.length > 0 then '/' + crumbs.reverse().join('/') else '' )
112
-
113
-
114
- _add_item: (path, object, position, config) ->
115
- item = new @itemClass(@module, path, object, config)
116
- @items[object._id] = item
117
- @_update_item_position(item, position)
118
-
119
-
120
- _update_item_position: (item, position) ->
121
- position = @configItemsCount + position
122
- if position == 0
123
- @$items.prepend(item.$el)
124
- else
125
- @$items.append(item.$el.hide())
126
- $(@$items.children()[position - 1]).after(item.$el.show())
127
-
105
+ # PUBLIC ================================================
128
106
 
129
- _show_spinner: ->
107
+ showSpinner: ->
130
108
  @$el.addClass('show-spinner')
131
109
 
132
110
 
133
- _hide_spinner: ->
111
+ hideSpinner: ->
134
112
  @$el.removeClass('show-spinner')
135
113
 
136
114
 
137
- # EVENTS ================================================
138
-
139
- _back: (e) ->
140
- @module.chr.unsetActiveListItems()
141
- @module.destroyView()
142
-
143
- if @showWithParent
144
- @hide()
145
- else
146
- @module.hideActiveList()
147
-
148
-
149
- _new: (e) ->
150
- chr.updateHash($(e.currentTarget).attr('href'), true)
151
- @module.showView(null, @config, 'New')
152
-
153
-
154
- # PUBLIC ================================================
155
-
156
115
  hide: ->
157
116
  @$el.hide()
158
117
 
159
118
 
160
119
  show: (callback) ->
161
120
  @$el.show 0, =>
162
- @$items.scrollTop(0)
163
121
  @config.onListShow?(@)
164
122
  callback?()
165
123
 
@@ -167,14 +125,11 @@ class @List
167
125
  updateItems: ->
168
126
  if not @config.disableUpdateItems
169
127
  if @config.arrayStore
170
- @_show_spinner()
128
+ @showSpinner()
129
+ @$items.scrollTop(0)
171
130
  @config.arrayStore.reset()
172
131
 
173
132
 
174
- isVisible: ->
175
- @$el.is(':visible')
176
-
177
-
178
133
  include(List, listConfig)
179
134
  include(List, listPagination)
180
135
  include(List, listReorder)
@@ -4,31 +4,49 @@
4
4
  # Methods for processing:
5
5
  # - @config.items
6
6
  # - @config.arrayStore
7
- # - @config.objectStore
8
7
  # -----------------------------------------------------------------------------
9
8
 
10
9
  @listConfig =
11
10
  # PRIVATE ===============================================
12
11
 
12
+ _add_item: (path, object, position, config, type) ->
13
+ item = new @itemClass(@module, path, object, config, type)
14
+ @items[object._id] = item
15
+ @_update_item_position(item, position)
16
+
17
+
18
+ _update_item_position: (item, position) ->
19
+ # skip static items in the head of list
20
+ position = @_config_items_count + position
21
+ if position == 0
22
+ @$items.prepend(item.$el)
23
+ else
24
+ @$items.append(item.$el.hide())
25
+ $(@$items.children()[position - 1]).after(item.$el.show())
26
+
27
+
13
28
  _process_config_items: ->
14
29
  for slug, config of @config.items
15
- object = { _id: slug, _title: config.title ? slug.titleize() }
30
+ object =
31
+ _id: slug
32
+ __title__: config.title ? slug.titleize()
33
+ __subtitle__: config.subtitle ? false
16
34
 
17
- # There might be some cases when we need this:
18
- #if config.objectStore
19
- # $.extend(object, config.objectStore.get())
35
+ item_type = 'nested_object'
20
36
 
21
- if config.items or config.arrayStore
37
+ if config.items || config.arrayStore
38
+ item_type = 'folder'
22
39
  @module.addNestedList(slug, config, this)
23
40
 
24
- @_add_item("#/#{ @path }/#{ slug }", object, 0, config)
25
- @configItemsCount += 1
41
+ @_add_item("#{ @path }/#{ slug }", object, 0, config, item_type)
42
+ @_config_items_count += 1
26
43
 
27
44
 
28
45
  _bind_config_array_store: ->
29
46
  # item added
30
47
  @config.arrayStore.on 'object_added', (e, data) =>
31
- @_add_item("#/#{ @path }/view/#{ data.object._id }", data.object, data.position, @config)
48
+
49
+ @_add_item("#{ @path }/view/#{ data.object._id }", data.object, data.position, @config, 'object')
32
50
 
33
51
  if @config.objects
34
52
  @config.arrayStore.addObjects(@config.objects)
@@ -45,7 +63,7 @@
45
63
 
46
64
  # items loaded
47
65
  @config.arrayStore.on 'objects_added', (e, data) =>
48
- @_hide_spinner()
66
+ @hideSpinner()
49
67
  @_set_active_item()
50
68
 
51
69
  if @config.arrayStore.pagination
@@ -58,8 +76,5 @@
58
76
  @_bind_reorder()
59
77
 
60
78
 
61
- _bind_config_object_store: ->
62
-
63
-
64
79
 
65
80
 
@@ -3,32 +3,75 @@
3
3
  # -----------------------------------------------------------------------------
4
4
 
5
5
  @listPagination =
6
-
7
6
  # PRIVATE ===============================================
8
7
 
9
8
  _bind_pagination: ->
9
+ if chr.isMobile()
10
+ chr._bind_mobile_scroll()
11
+
12
+ else
13
+ @_bind_desktop_scroll()
14
+
15
+
16
+ _bind_desktop_scroll: ->
10
17
  @lastScrollTop = 0
18
+ $viewport = @$el
11
19
 
12
20
  @$items.scroll (e) =>
21
+ scroll_top = @$items.scrollTop()
22
+
23
+ # trigger next page loading only when scrolling to bottom
24
+ if @lastScrollTop < scroll_top
25
+ chr._load_next_page($viewport, this, scroll_top)
26
+
27
+ @lastScrollTop = scroll_top
28
+
29
+
30
+ chr._bind_mobile_scroll = ->
31
+ if ! @_mobile_scroll_binded
32
+ @lastScrollTop = 0
33
+ $viewport = $(window)
34
+
35
+ $viewport.scroll (e) =>
36
+ if ! @module then return
37
+
38
+ if @module.view then return
39
+
40
+ scroll_top = $viewport.scrollTop()
41
+ @module.activeList.scrollCache = scroll_top
42
+
43
+ # TODO: updated this for iphone (test with no mouse on safari),
44
+ # scenario: list is loaded and do not fill the page size
45
+
13
46
  # trigger next page loading only when scrolling to bottom
14
- if @lastScrollTop < e.target.scrollTop
15
- if ! @config.arrayStore.dataFetchLock
47
+ if @lastScrollTop < scroll_top
48
+ chr._load_next_page($viewport, @module.activeList, scroll_top)
49
+
50
+ @lastScrollTop = scroll_top
51
+
52
+ @_mobile_scroll_binded = true
53
+
16
54
 
17
- listViewHeight = @$el.height()
18
- listItemsHeight = 0
19
- @$items.children().each -> listItemsHeight += $(this).height()
55
+ chr._load_next_page = ($viewport, list, scroll_top) ->
56
+ $items = list.$items
57
+ store = list.config.arrayStore
20
58
 
21
- if listItemsHeight < (listViewHeight + e.target.scrollTop + 100)
59
+ if store.dataFetchLock then return
22
60
 
23
- if ! @config.arrayStore.lastPageLoaded
61
+ if store.lastPageLoaded then return
24
62
 
25
- @_show_spinner()
63
+ # check if scroll is near bottom of the $viewport
64
+ viewport_height = $viewport.height()
65
+ list_items_height = 0
66
+ $items.children().each -> list_items_height += $(this).height()
26
67
 
27
- @config.arrayStore.load false,
28
- onSuccess: => ;
29
- onError: => chr.showAlert("Can't load next page, server error 500.")
68
+ if list_items_height - scroll_top - 100 > viewport_height
69
+ return
30
70
 
31
- @lastScrollTop = e.target.scrollTop
71
+ list.showSpinner()
72
+ store.load false,
73
+ onSuccess: => ;
74
+ onError: => chr.showAlert("Can't load next page, server error 500.")
32
75
 
33
76
 
34
77