flipper-ui 0.2.0.beta2 → 0.2.0.beta3

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -11
  3. data/README.md +5 -7
  4. data/examples/basic.ru +27 -26
  5. data/flipper-ui.gemspec +5 -3
  6. data/lib/flipper-ui.rb +1 -0
  7. data/lib/flipper/ui.rb +11 -2
  8. data/lib/flipper/ui/action.rb +45 -4
  9. data/lib/flipper/ui/actions/actors_gate.rb +48 -0
  10. data/lib/flipper/ui/actions/add_feature.rb +20 -0
  11. data/lib/flipper/ui/actions/boolean_gate.rb +26 -0
  12. data/lib/flipper/ui/actions/feature.rb +32 -0
  13. data/lib/flipper/ui/actions/features.rb +23 -3
  14. data/lib/flipper/ui/actions/file.rb +1 -1
  15. data/lib/flipper/ui/actions/gate.rb +11 -117
  16. data/lib/flipper/ui/actions/groups_gate.rb +43 -0
  17. data/lib/flipper/ui/actions/{index.rb → home.rb} +3 -3
  18. data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +27 -0
  19. data/lib/flipper/ui/actions/percentage_of_time_gate.rb +27 -0
  20. data/lib/flipper/ui/actor.rb +13 -0
  21. data/lib/flipper/ui/assets/javascripts/application.coffee +0 -305
  22. data/lib/flipper/ui/assets/stylesheets/_bootstrap-compass.scss +9 -0
  23. data/lib/flipper/ui/assets/stylesheets/_bootstrap-mincer.scss +19 -0
  24. data/lib/flipper/ui/assets/stylesheets/_bootstrap-sprockets.scss +9 -0
  25. data/lib/flipper/ui/assets/stylesheets/_bootstrap.scss +50 -0
  26. data/lib/flipper/ui/assets/stylesheets/application.scss +33 -216
  27. data/lib/flipper/ui/assets/stylesheets/bootstrap/_alerts.scss +73 -0
  28. data/lib/flipper/ui/assets/stylesheets/bootstrap/_badges.scss +68 -0
  29. data/lib/flipper/ui/assets/stylesheets/bootstrap/_breadcrumbs.scss +26 -0
  30. data/lib/flipper/ui/assets/stylesheets/bootstrap/_button-groups.scss +243 -0
  31. data/lib/flipper/ui/assets/stylesheets/bootstrap/_buttons.scss +160 -0
  32. data/lib/flipper/ui/assets/stylesheets/bootstrap/_carousel.scss +269 -0
  33. data/lib/flipper/ui/assets/stylesheets/bootstrap/_close.scss +36 -0
  34. data/lib/flipper/ui/assets/stylesheets/bootstrap/_code.scss +69 -0
  35. data/lib/flipper/ui/assets/stylesheets/bootstrap/_component-animations.scss +37 -0
  36. data/lib/flipper/ui/assets/stylesheets/bootstrap/_dropdowns.scss +214 -0
  37. data/lib/flipper/ui/assets/stylesheets/bootstrap/_forms.scss +578 -0
  38. data/lib/flipper/ui/assets/stylesheets/bootstrap/_glyphicons.scss +305 -0
  39. data/lib/flipper/ui/assets/stylesheets/bootstrap/_grid.scss +84 -0
  40. data/lib/flipper/ui/assets/stylesheets/bootstrap/_input-groups.scss +166 -0
  41. data/lib/flipper/ui/assets/stylesheets/bootstrap/_jumbotron.scss +50 -0
  42. data/lib/flipper/ui/assets/stylesheets/bootstrap/_labels.scss +66 -0
  43. data/lib/flipper/ui/assets/stylesheets/bootstrap/_list-group.scss +124 -0
  44. data/lib/flipper/ui/assets/stylesheets/bootstrap/_media.scss +61 -0
  45. data/lib/flipper/ui/assets/stylesheets/bootstrap/_mixins.scss +39 -0
  46. data/lib/flipper/ui/assets/stylesheets/bootstrap/_modals.scss +150 -0
  47. data/lib/flipper/ui/assets/stylesheets/bootstrap/_navbar.scss +662 -0
  48. data/lib/flipper/ui/assets/stylesheets/bootstrap/_navs.scss +242 -0
  49. data/lib/flipper/ui/assets/stylesheets/bootstrap/_normalize.scss +427 -0
  50. data/lib/flipper/ui/assets/stylesheets/bootstrap/_pager.scss +54 -0
  51. data/lib/flipper/ui/assets/stylesheets/bootstrap/_pagination.scss +88 -0
  52. data/lib/flipper/ui/assets/stylesheets/bootstrap/_panels.scss +265 -0
  53. data/lib/flipper/ui/assets/stylesheets/bootstrap/_popovers.scss +135 -0
  54. data/lib/flipper/ui/assets/stylesheets/bootstrap/_print.scss +107 -0
  55. data/lib/flipper/ui/assets/stylesheets/bootstrap/_progress-bars.scss +87 -0
  56. data/lib/flipper/ui/assets/stylesheets/bootstrap/_responsive-embed.scss +35 -0
  57. data/lib/flipper/ui/assets/stylesheets/bootstrap/_responsive-utilities.scss +177 -0
  58. data/lib/flipper/ui/assets/stylesheets/bootstrap/_scaffolding.scss +162 -0
  59. data/lib/flipper/ui/assets/stylesheets/bootstrap/_tables.scss +234 -0
  60. data/lib/flipper/ui/assets/stylesheets/bootstrap/_theme.scss +273 -0
  61. data/lib/flipper/ui/assets/stylesheets/bootstrap/_thumbnails.scss +38 -0
  62. data/lib/flipper/ui/assets/stylesheets/bootstrap/_tooltip.scss +102 -0
  63. data/lib/flipper/ui/assets/stylesheets/bootstrap/_type.scss +298 -0
  64. data/lib/flipper/ui/assets/stylesheets/bootstrap/_utilities.scss +55 -0
  65. data/lib/flipper/ui/assets/stylesheets/bootstrap/_variables.scss +866 -0
  66. data/lib/flipper/ui/assets/stylesheets/bootstrap/_wells.scss +29 -0
  67. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_alerts.scss +14 -0
  68. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_background-variant.scss +11 -0
  69. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_border-radius.scss +18 -0
  70. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_buttons.scss +52 -0
  71. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_center-block.scss +7 -0
  72. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_clearfix.scss +22 -0
  73. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_forms.scss +88 -0
  74. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_gradients.scss +58 -0
  75. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_grid-framework.scss +81 -0
  76. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_grid.scss +122 -0
  77. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_hide-text.scss +21 -0
  78. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_image.scss +33 -0
  79. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_labels.scss +12 -0
  80. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_list-group.scss +31 -0
  81. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_nav-divider.scss +10 -0
  82. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_nav-vertical-align.scss +9 -0
  83. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_opacity.scss +8 -0
  84. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_pagination.scss +23 -0
  85. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_panels.scss +24 -0
  86. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_progress-bar.scss +10 -0
  87. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_reset-filter.scss +8 -0
  88. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_resize.scss +6 -0
  89. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_responsive-visibility.scss +21 -0
  90. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_size.scss +10 -0
  91. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_tab-focus.scss +9 -0
  92. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_table-row.scss +28 -0
  93. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_text-emphasis.scss +11 -0
  94. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_text-overflow.scss +8 -0
  95. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +222 -0
  96. data/lib/flipper/ui/assets/stylesheets/primer/.scss-lint.yml +446 -0
  97. data/lib/flipper/ui/assets/stylesheets/primer/_alerts.scss +106 -0
  98. data/lib/flipper/ui/assets/stylesheets/primer/_avatars.scss +36 -0
  99. data/lib/flipper/ui/assets/stylesheets/primer/_base.scss +40 -0
  100. data/lib/flipper/ui/assets/stylesheets/primer/_blankslate.scss +96 -0
  101. data/lib/flipper/ui/assets/stylesheets/primer/_buttons.scss +404 -0
  102. data/lib/flipper/ui/assets/stylesheets/primer/_counter.scss +10 -0
  103. data/lib/flipper/ui/assets/stylesheets/primer/_filter-list.scss +68 -0
  104. data/lib/flipper/ui/assets/stylesheets/primer/_flex-table.scss +20 -0
  105. data/lib/flipper/ui/assets/stylesheets/primer/_forms.scss +756 -0
  106. data/lib/flipper/ui/assets/stylesheets/primer/_layout.scss +69 -0
  107. data/lib/flipper/ui/assets/stylesheets/primer/_menu.scss +113 -0
  108. data/lib/flipper/ui/assets/stylesheets/primer/_mixins.scss +53 -0
  109. data/lib/flipper/ui/assets/stylesheets/primer/_normalize.scss +425 -0
  110. data/lib/flipper/ui/assets/stylesheets/primer/_states.scss +32 -0
  111. data/lib/flipper/ui/assets/stylesheets/primer/_tabnav.scss +65 -0
  112. data/lib/flipper/ui/assets/stylesheets/primer/_tooltips.scss +255 -0
  113. data/lib/flipper/ui/assets/stylesheets/primer/_truncate.scss +27 -0
  114. data/lib/flipper/ui/assets/stylesheets/primer/_type.scss +92 -0
  115. data/lib/flipper/ui/assets/stylesheets/primer/_utility.scss +73 -0
  116. data/lib/flipper/ui/assets/stylesheets/primer/_variables.scss +34 -0
  117. data/lib/flipper/ui/assets/stylesheets/primer/primer.scss +39 -0
  118. data/lib/flipper/ui/decorators/feature.rb +37 -4
  119. data/lib/flipper/ui/middleware.rb +12 -3
  120. data/lib/flipper/ui/public/css/application.css +2563 -144
  121. data/lib/flipper/ui/public/css/primer.css +1 -0
  122. data/lib/flipper/ui/public/css/primer/primer.css +1933 -0
  123. data/lib/flipper/ui/public/css/scss/primer.css +1933 -0
  124. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
  125. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.svg +288 -0
  126. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  127. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
  128. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.woff2 +0 -0
  129. data/lib/flipper/ui/public/js/application.js +0 -539
  130. data/lib/flipper/ui/public/js/bootstrap-sprockets.js +12 -0
  131. data/lib/flipper/ui/public/js/bootstrap.js +2317 -0
  132. data/lib/flipper/ui/public/js/bootstrap.min.js +7 -0
  133. data/lib/flipper/ui/public/js/bootstrap/affix.js +162 -0
  134. data/lib/flipper/ui/public/js/bootstrap/alert.js +94 -0
  135. data/lib/flipper/ui/public/js/bootstrap/button.js +116 -0
  136. data/lib/flipper/ui/public/js/bootstrap/carousel.js +237 -0
  137. data/lib/flipper/ui/public/js/bootstrap/collapse.js +211 -0
  138. data/lib/flipper/ui/public/js/bootstrap/dropdown.js +161 -0
  139. data/lib/flipper/ui/public/js/bootstrap/modal.js +339 -0
  140. data/lib/flipper/ui/public/js/bootstrap/popover.js +108 -0
  141. data/lib/flipper/ui/public/js/bootstrap/scrollspy.js +172 -0
  142. data/lib/flipper/ui/public/js/bootstrap/tab.js +153 -0
  143. data/lib/flipper/ui/public/js/bootstrap/tooltip.js +476 -0
  144. data/lib/flipper/ui/public/js/bootstrap/transition.js +59 -0
  145. data/lib/flipper/ui/public/octicons/LICENSE.txt +9 -0
  146. data/lib/flipper/ui/public/octicons/README.md +1 -0
  147. data/lib/flipper/ui/public/octicons/octicons-local.ttf +0 -0
  148. data/lib/flipper/ui/public/octicons/octicons.css +236 -0
  149. data/lib/flipper/ui/public/octicons/octicons.eot +0 -0
  150. data/lib/flipper/ui/public/octicons/octicons.less +235 -0
  151. data/lib/flipper/ui/public/octicons/octicons.svg +200 -0
  152. data/lib/flipper/ui/public/octicons/octicons.ttf +0 -0
  153. data/lib/flipper/ui/public/octicons/octicons.woff +0 -0
  154. data/lib/flipper/ui/public/octicons/sprockets-octicons.scss +232 -0
  155. data/lib/flipper/ui/util.rb +4 -0
  156. data/lib/flipper/ui/version.rb +1 -1
  157. data/lib/flipper/ui/views/add_actor.erb +22 -0
  158. data/lib/flipper/ui/views/add_feature.erb +18 -0
  159. data/lib/flipper/ui/views/add_group.erb +31 -0
  160. data/lib/flipper/ui/views/feature.erb +209 -0
  161. data/lib/flipper/ui/views/features.erb +46 -0
  162. data/lib/flipper/ui/views/layout.erb +31 -149
  163. data/script/release +15 -0
  164. data/spec/flipper/ui/actions/actors_gate_spec.rb +68 -0
  165. data/spec/flipper/ui/actions/add_feature_spec.rb +17 -0
  166. data/spec/flipper/ui/actions/boolean_gate_spec.rb +41 -0
  167. data/spec/flipper/ui/actions/feature_spec.rb +59 -0
  168. data/spec/flipper/ui/actions/features_spec.rb +37 -0
  169. data/spec/flipper/ui/actions/file_spec.rb +43 -0
  170. data/spec/flipper/ui/actions/gate_spec.rb +24 -0
  171. data/spec/flipper/ui/actions/groups_gate_spec.rb +80 -0
  172. data/spec/flipper/ui/actions/home_spec.rb +16 -0
  173. data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +40 -0
  174. data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +39 -0
  175. data/spec/flipper/ui/decorators/feature_spec.rb +38 -1
  176. data/spec/flipper/ui/decorators/gate_spec.rb +1 -1
  177. data/spec/flipper/ui_spec.rb +18 -441
  178. data/spec/helper.rb +22 -7
  179. metadata +199 -52
  180. data/examples/flipper.html +0 -14
  181. data/examples/flipper.png +0 -0
  182. data/lib/flipper/ui/assets/javascripts/spine/ajax.coffee +0 -223
  183. data/lib/flipper/ui/assets/javascripts/spine/list.coffee +0 -43
  184. data/lib/flipper/ui/assets/javascripts/spine/local.coffee +0 -16
  185. data/lib/flipper/ui/assets/javascripts/spine/manager.coffee +0 -83
  186. data/lib/flipper/ui/assets/javascripts/spine/relation.coffee +0 -148
  187. data/lib/flipper/ui/assets/javascripts/spine/route.coffee +0 -146
  188. data/lib/flipper/ui/assets/javascripts/spine/spine.coffee +0 -542
  189. data/lib/flipper/ui/assets/javascripts/spine/version +0 -1
  190. data/lib/flipper/ui/public/css/images/animated-overlay.gif +0 -0
  191. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  192. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  193. data/lib/flipper/ui/public/css/images/ui-bg_flat_10_000000_40x100.png +0 -0
  194. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  195. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  196. data/lib/flipper/ui/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  197. data/lib/flipper/ui/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  198. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  199. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  200. data/lib/flipper/ui/public/css/images/ui-icons_222222_256x240.png +0 -0
  201. data/lib/flipper/ui/public/css/images/ui-icons_228ef1_256x240.png +0 -0
  202. data/lib/flipper/ui/public/css/images/ui-icons_ef8c08_256x240.png +0 -0
  203. data/lib/flipper/ui/public/css/images/ui-icons_ffd27a_256x240.png +0 -0
  204. data/lib/flipper/ui/public/css/images/ui-icons_ffffff_256x240.png +0 -0
  205. data/lib/flipper/ui/public/css/jquery-ui-1.10.3.slider.min.css +0 -5
  206. data/lib/flipper/ui/public/js/handlebars.js +0 -1992
  207. data/lib/flipper/ui/public/js/jquery-ui-1.10.3.slider.min.js +0 -6
  208. data/lib/flipper/ui/public/js/jquery.js +0 -9555
  209. data/lib/flipper/ui/public/js/jquery.min.js +0 -4
  210. data/lib/flipper/ui/public/js/jquery.min.map +0 -1
  211. data/lib/flipper/ui/public/js/spine/ajax.js +0 -320
  212. data/lib/flipper/ui/public/js/spine/list.js +0 -72
  213. data/lib/flipper/ui/public/js/spine/local.js +0 -29
  214. data/lib/flipper/ui/public/js/spine/manager.js +0 -157
  215. data/lib/flipper/ui/public/js/spine/relation.js +0 -260
  216. data/lib/flipper/ui/public/js/spine/route.js +0 -223
  217. data/lib/flipper/ui/public/js/spine/spine.js +0 -927
  218. data/lib/flipper/ui/views/index.erb +0 -9
@@ -1,43 +0,0 @@
1
- Spine = @Spine or require('spine')
2
- $ = Spine.$
3
-
4
- class Spine.List extends Spine.Controller
5
- events:
6
- 'click .item': 'click'
7
-
8
- selectFirst: false
9
-
10
- constructor: ->
11
- super
12
- @bind 'change', @change
13
-
14
- template: ->
15
- throw 'Override template'
16
-
17
- change: (item) =>
18
- @current = item
19
-
20
- unless @current
21
- @children().removeClass('active')
22
- return
23
-
24
- @children().removeClass('active')
25
- $(@children().get(@items.indexOf(@current))).addClass('active')
26
-
27
- render: (items) ->
28
- @items = items if items
29
- @html @template(@items)
30
- @change @current
31
- if @selectFirst
32
- unless @children('.active').length
33
- @children(':first').click()
34
-
35
- children: (sel) ->
36
- @el.children(sel)
37
-
38
- click: (e) ->
39
- item = @items[$(e.currentTarget).index()]
40
- @trigger('change', item)
41
- true
42
-
43
- module?.exports = Spine.List
@@ -1,16 +0,0 @@
1
- Spine = @Spine or require('spine')
2
-
3
- Spine.Model.Local =
4
- extended: ->
5
- @change @saveLocal
6
- @fetch @loadLocal
7
-
8
- saveLocal: ->
9
- result = JSON.stringify(@)
10
- localStorage[@className] = result
11
-
12
- loadLocal: ->
13
- result = localStorage[@className]
14
- @refresh(result or [], clear: true)
15
-
16
- module?.exports = Spine.Model.Local
@@ -1,83 +0,0 @@
1
- Spine = @Spine or require('spine')
2
- $ = Spine.$
3
-
4
- class Spine.Manager extends Spine.Module
5
- @include Spine.Events
6
-
7
- constructor: ->
8
- @controllers = []
9
- @bind 'change', @change
10
- @add(arguments...)
11
-
12
- add: (controllers...) ->
13
- @addOne(cont) for cont in controllers
14
-
15
- addOne: (controller) ->
16
- controller.bind 'active', (args...) =>
17
- @trigger('change', controller, args...)
18
- controller.bind 'release', =>
19
- @controllers.splice(@controllers.indexOf(controller), 1)
20
-
21
- @controllers.push(controller)
22
-
23
- deactivate: ->
24
- @trigger('change', false, arguments...)
25
-
26
- # Private
27
-
28
- change: (current, args...) ->
29
- for cont in @controllers
30
- if cont is current
31
- cont.activate(args...)
32
- else
33
- cont.deactivate(args...)
34
-
35
- Spine.Controller.include
36
- active: (args...) ->
37
- if typeof args[0] is 'function'
38
- @bind('active', args[0])
39
- else
40
- args.unshift('active')
41
- @trigger(args...)
42
- @
43
-
44
- isActive: ->
45
- @el.hasClass('active')
46
-
47
- activate: ->
48
- @el.addClass('active')
49
- @
50
-
51
- deactivate: ->
52
- @el.removeClass('active')
53
- @
54
-
55
- class Spine.Stack extends Spine.Controller
56
- controllers: {}
57
- routes: {}
58
-
59
- className: 'spine stack'
60
-
61
- constructor: ->
62
- super
63
-
64
- @manager = new Spine.Manager
65
-
66
- for key, value of @controllers
67
- @[key] = new value(stack: @)
68
- @add(@[key])
69
-
70
- for key, value of @routes
71
- do (key, value) =>
72
- callback = value if typeof value is 'function'
73
- callback or= => @[value].active(arguments...)
74
- @route(key, callback)
75
-
76
- @[@default].active() if @default
77
-
78
- add: (controller) ->
79
- @manager.add(controller)
80
- @append(controller)
81
-
82
- module?.exports = Spine.Manager
83
- module?.exports.Stack = Spine.Stack
@@ -1,148 +0,0 @@
1
- Spine = @Spine or require('spine')
2
- isArray = Spine.isArray
3
- require = @require or ((value) -> eval(value))
4
-
5
- class Collection extends Spine.Module
6
- constructor: (options = {}) ->
7
- for key, value of options
8
- @[key] = value
9
-
10
- all: ->
11
- @model.select (rec) => @associated(rec)
12
-
13
- first: ->
14
- @all()[0]
15
-
16
- last: ->
17
- values = @all()
18
- values[values.length - 1]
19
-
20
- find: (id) ->
21
- records = @select (rec) =>
22
- "#{rec.id}" is "#{id}"
23
- throw new Error("\"#{@model.className}\" model could not find a record for the ID \"#{id}\"") unless records[0]
24
- records[0]
25
-
26
- findAllByAttribute: (name, value) ->
27
- @model.select (rec) =>
28
- @associated(rec) and rec[name] is value
29
-
30
- findByAttribute: (name, value) ->
31
- @findAllByAttribute(name, value)[0]
32
-
33
- select: (cb) ->
34
- @model.select (rec) =>
35
- @associated(rec) and cb(rec)
36
-
37
- refresh: (values) ->
38
- delete @model.records[record.id] for record in @all()
39
- records = @model.fromJSON(values)
40
-
41
- records = [records] unless isArray(records)
42
-
43
- for record in records
44
- record.newRecord = false
45
- record[@fkey] = @record.id
46
- @model.records[record.id] = record
47
-
48
- @model.trigger('refresh', @model.cloneArray(records))
49
-
50
- create: (record) ->
51
- record[@fkey] = @record.id
52
- @model.create(record)
53
-
54
- # Private
55
-
56
- associated: (record) ->
57
- record[@fkey] is @record.id
58
-
59
- class Instance extends Spine.Module
60
- constructor: (options = {}) ->
61
- for key, value of options
62
- @[key] = value
63
-
64
- exists: ->
65
- @record[@fkey] and @model.exists(@record[@fkey])
66
-
67
- update: (value) ->
68
- unless value instanceof @model
69
- value = new @model(value)
70
- value.save() if value.isNew()
71
- @record[@fkey] = value and value.id
72
-
73
- class Singleton extends Spine.Module
74
- constructor: (options = {}) ->
75
- for key, value of options
76
- @[key] = value
77
-
78
- find: ->
79
- @record.id and @model.findByAttribute(@fkey, @record.id)
80
-
81
- update: (value) ->
82
- unless value instanceof @model
83
- value = @model.fromJSON(value)
84
-
85
- value[@fkey] = @record.id
86
- value.save()
87
-
88
- singularize = (str) ->
89
- str.replace(/s$/, '')
90
-
91
- underscore = (str) ->
92
- str.replace(/::/g, '/')
93
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
94
- .replace(/([a-z\d])([A-Z])/g, '$1_$2')
95
- .replace(/-/g, '_')
96
- .toLowerCase()
97
-
98
- Spine.Model.extend
99
- hasMany: (name, model, fkey) ->
100
- fkey ?= "#{underscore(this.className)}_id"
101
-
102
- association = (record) ->
103
- model = require(model) if typeof model is 'string'
104
-
105
- new Collection(
106
- name: name, model: model,
107
- record: record, fkey: fkey
108
- )
109
-
110
- @::[name] = (value) ->
111
- association(@).refresh(value) if value?
112
- association(@)
113
-
114
- belongsTo: (name, model, fkey) ->
115
- fkey ?= "#{singularize(name)}_id"
116
-
117
- association = (record) ->
118
- model = require(model) if typeof model is 'string'
119
-
120
- new Instance(
121
- name: name, model: model,
122
- record: record, fkey: fkey
123
- )
124
-
125
- @::[name] = (value) ->
126
- association(@).update(value) if value?
127
- association(@).exists()
128
-
129
- @attributes.push(fkey)
130
-
131
- hasOne: (name, model, fkey) ->
132
- fkey ?= "#{underscore(@className)}_id"
133
-
134
- association = (record) ->
135
- model = require(model) if typeof model is 'string'
136
-
137
- new Singleton(
138
- name: name, model: model,
139
- record: record, fkey: fkey
140
- )
141
-
142
- @::[name] = (value) ->
143
- association(@).update(value) if value?
144
- association(@).find()
145
-
146
- Spine.Collection = Collection
147
- Spine.Singleton = Singleton
148
- Spine.Instance = Instance
@@ -1,146 +0,0 @@
1
- Spine = @Spine or require('spine')
2
- $ = Spine.$
3
-
4
- hashStrip = /^#*/
5
- namedParam = /:([\w\d]+)/g
6
- splatParam = /\*([\w\d]+)/g
7
- escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
8
-
9
- class Spine.Route extends Spine.Module
10
- @extend Spine.Events
11
-
12
- @historySupport: window.history?.pushState?
13
-
14
- @routes: []
15
-
16
- @options:
17
- trigger: true
18
- history: false
19
- shim: false
20
-
21
- @add: (path, callback) ->
22
- if (typeof path is 'object' and path not instanceof RegExp)
23
- @add(key, value) for key, value of path
24
- else
25
- @routes.push(new @(path, callback))
26
-
27
- @setup: (options = {}) ->
28
- @options = $.extend({}, @options, options)
29
-
30
- if (@options.history)
31
- @history = @historySupport and @options.history
32
-
33
- return if @options.shim
34
-
35
- if @history
36
- $(window).bind('popstate', @change)
37
- else
38
- $(window).bind('hashchange', @change)
39
- @change()
40
-
41
- @unbind: ->
42
- return if @options.shim
43
-
44
- if @history
45
- $(window).unbind('popstate', @change)
46
- else
47
- $(window).unbind('hashchange', @change)
48
-
49
- @navigate: (args...) ->
50
- options = {}
51
-
52
- lastArg = args[args.length - 1]
53
- if typeof lastArg is 'object'
54
- options = args.pop()
55
- else if typeof lastArg is 'boolean'
56
- options.trigger = args.pop()
57
-
58
- options = $.extend({}, @options, options)
59
-
60
- path = args.join('/')
61
- return if @path is path
62
- @path = path
63
-
64
- @trigger('navigate', @path)
65
-
66
- @matchRoute(@path, options) if options.trigger
67
-
68
- return if options.shim
69
-
70
- if @history
71
- history.pushState({}, document.title, @path)
72
- else
73
- window.location.hash = @path
74
-
75
- # Private
76
-
77
- @getPath: ->
78
- path = window.location.pathname
79
- if path.substr(0,1) isnt '/'
80
- path = '/' + path
81
- path
82
-
83
- @getHash: -> window.location.hash
84
-
85
- @getFragment: -> @getHash().replace(hashStrip, '')
86
-
87
- @getHost: ->
88
- (document.location + '').replace(@getPath() + @getHash(), '')
89
-
90
- @change: ->
91
- path = if @history then @getPath() else @getFragment()
92
- return if path is @path
93
- @path = path
94
- @matchRoute(@path)
95
-
96
- @matchRoute: (path, options) ->
97
- for route in @routes when route.match(path, options)
98
- @trigger('change', route, path)
99
- return route
100
-
101
- constructor: (@path, @callback) ->
102
- @names = []
103
-
104
- if typeof path is 'string'
105
- namedParam.lastIndex = 0
106
- while (match = namedParam.exec(path)) != null
107
- @names.push(match[1])
108
-
109
- splatParam.lastIndex = 0
110
- while (match = splatParam.exec(path)) != null
111
- @names.push(match[1])
112
-
113
- path = path.replace(escapeRegExp, '\\$&')
114
- .replace(namedParam, '([^\/]*)')
115
- .replace(splatParam, '(.*?)')
116
-
117
- @route = new RegExp("^#{path}$")
118
- else
119
- @route = path
120
-
121
- match: (path, options = {}) ->
122
- match = @route.exec(path)
123
- return false unless match
124
- options.match = match
125
- params = match.slice(1)
126
-
127
- if @names.length
128
- for param, i in params
129
- options[@names[i]] = param
130
-
131
- @callback.call(null, options) isnt false
132
-
133
- # Coffee-script bug
134
- Spine.Route.change = Spine.Route.proxy(Spine.Route.change)
135
-
136
- Spine.Controller.include
137
- route: (path, callback) ->
138
- Spine.Route.add(path, @proxy(callback))
139
-
140
- routes: (routes) ->
141
- @route(key, value) for key, value of routes
142
-
143
- navigate: ->
144
- Spine.Route.navigate.apply(Spine.Route, arguments)
145
-
146
- module?.exports = Spine.Route
@@ -1,542 +0,0 @@
1
- Events =
2
- bind: (ev, callback) ->
3
- evs = ev.split(' ')
4
- calls = @hasOwnProperty('_callbacks') and @_callbacks or= {}
5
-
6
- for name in evs
7
- calls[name] or= []
8
- calls[name].push(callback)
9
- this
10
-
11
- one: (ev, callback) ->
12
- @bind ev, ->
13
- @unbind(ev, arguments.callee)
14
- callback.apply(this, arguments)
15
-
16
- trigger: (args...) ->
17
- ev = args.shift()
18
-
19
- list = @hasOwnProperty('_callbacks') and @_callbacks?[ev]
20
- return unless list
21
-
22
- for callback in list
23
- if callback.apply(this, args) is false
24
- break
25
- true
26
-
27
- unbind: (ev, callback) ->
28
- unless ev
29
- @_callbacks = {}
30
- return this
31
-
32
- list = @_callbacks?[ev]
33
- return this unless list
34
-
35
- unless callback
36
- delete @_callbacks[ev]
37
- return this
38
-
39
- for cb, i in list when cb is callback
40
- list = list.slice()
41
- list.splice(i, 1)
42
- @_callbacks[ev] = list
43
- break
44
- this
45
-
46
- Log =
47
- trace: true
48
-
49
- logPrefix: '(App)'
50
-
51
- log: (args...) ->
52
- return unless @trace
53
- if @logPrefix then args.unshift(@logPrefix)
54
- console?.log?(args...)
55
- this
56
-
57
- moduleKeywords = ['included', 'extended']
58
-
59
- class Module
60
- @include: (obj) ->
61
- throw new Error('include(obj) requires obj') unless obj
62
- for key, value of obj when key not in moduleKeywords
63
- @::[key] = value
64
- obj.included?.apply(this)
65
- this
66
-
67
- @extend: (obj) ->
68
- throw new Error('extend(obj) requires obj') unless obj
69
- for key, value of obj when key not in moduleKeywords
70
- @[key] = value
71
- obj.extended?.apply(this)
72
- this
73
-
74
- @proxy: (func) ->
75
- => func.apply(this, arguments)
76
-
77
- proxy: (func) ->
78
- => func.apply(this, arguments)
79
-
80
- constructor: ->
81
- @init?(arguments...)
82
-
83
- class Model extends Module
84
- @extend Events
85
-
86
- @records: {}
87
- @crecords: {}
88
- @attributes: []
89
-
90
- @configure: (name, attributes...) ->
91
- @className = name
92
- @records = {}
93
- @crecords = {}
94
- @attributes = attributes if attributes.length
95
- @attributes and= makeArray(@attributes)
96
- @attributes or= []
97
- @unbind()
98
- this
99
-
100
- @toString: -> "#{@className}(#{@attributes.join(", ")})"
101
-
102
- @find: (id) ->
103
- record = @records[id]
104
- if !record and ("#{id}").match(/c-\d+/)
105
- return @findCID(id)
106
- throw new Error("\"#{@className}\" model could not find a record for the ID \"#{id}\"") unless record
107
- record.clone()
108
-
109
- @findCID: (cid) ->
110
- record = @crecords[cid]
111
- throw new Error("\"#{@className}\" model could not find a record for the ID \"#{id}\"") unless record
112
- record.clone()
113
-
114
- @exists: (id) ->
115
- try
116
- return @find(id)
117
- catch e
118
- return false
119
-
120
- @refresh: (values, options = {}) ->
121
- if options.clear
122
- @records = {}
123
- @crecords = {}
124
-
125
- records = @fromJSON(values)
126
- records = [records] unless isArray(records)
127
-
128
- for record in records
129
- record.id or= record.cid
130
- @records[record.id] = record
131
- @crecords[record.cid] = record
132
-
133
- @trigger('refresh', @cloneArray(records))
134
- this
135
-
136
- @select: (callback) ->
137
- result = (record for id, record of @records when callback(record))
138
- @cloneArray(result)
139
-
140
- @findByAttribute: (name, value) ->
141
- for id, record of @records
142
- if record[name] is value
143
- return record.clone()
144
- null
145
-
146
- @findAllByAttribute: (name, value) ->
147
- @select (item) ->
148
- item[name] is value
149
-
150
- @each: (callback) ->
151
- for key, value of @records
152
- callback(value.clone())
153
-
154
- @all: ->
155
- @cloneArray(@recordsValues())
156
-
157
- @first: ->
158
- record = @recordsValues()[0]
159
- record?.clone()
160
-
161
- @last: ->
162
- values = @recordsValues()
163
- record = values[values.length - 1]
164
- record?.clone()
165
-
166
- @count: ->
167
- @recordsValues().length
168
-
169
- @deleteAll: ->
170
- for key, value of @records
171
- delete @records[key]
172
-
173
- @destroyAll: ->
174
- for key, value of @records
175
- @records[key].destroy()
176
-
177
- @update: (id, atts, options) ->
178
- @find(id).updateAttributes(atts, options)
179
-
180
- @create: (atts, options) ->
181
- record = new @(atts)
182
- record.save(options)
183
-
184
- @destroy: (id, options) ->
185
- @find(id).destroy(options)
186
-
187
- @change: (callbackOrParams) ->
188
- if typeof callbackOrParams is 'function'
189
- @bind('change', callbackOrParams)
190
- else
191
- @trigger('change', callbackOrParams)
192
-
193
- @fetch: (callbackOrParams) ->
194
- if typeof callbackOrParams is 'function'
195
- @bind('fetch', callbackOrParams)
196
- else
197
- @trigger('fetch', callbackOrParams)
198
-
199
- @toJSON: ->
200
- @recordsValues()
201
-
202
- @fromJSON: (objects) ->
203
- return unless objects
204
- if typeof objects is 'string'
205
- objects = JSON.parse(objects)
206
- if isArray(objects)
207
- (new @(value) for value in objects)
208
- else
209
- new @(objects)
210
-
211
- @fromForm: ->
212
- (new this).fromForm(arguments...)
213
-
214
- # Private
215
-
216
- @recordsValues: ->
217
- result = []
218
- for key, value of @records
219
- result.push(value)
220
- result
221
-
222
- @cloneArray: (array) ->
223
- (value.clone() for value in array)
224
-
225
- @idCounter: 0
226
-
227
- @uid: (prefix = '') ->
228
- uid = prefix + @idCounter++
229
- uid = @uid(prefix) if @exists(uid)
230
- uid
231
-
232
- # Instance
233
-
234
- constructor: (atts) ->
235
- super
236
- @load atts if atts
237
- @cid = @constructor.uid('c-')
238
-
239
- isNew: ->
240
- not @exists()
241
-
242
- isValid: ->
243
- not @validate()
244
-
245
- validate: ->
246
-
247
- load: (atts) ->
248
- for key, value of atts
249
- if typeof @[key] is 'function'
250
- @[key](value)
251
- else
252
- @[key] = value
253
- this
254
-
255
- attributes: ->
256
- result = {}
257
- for key in @constructor.attributes when key of this
258
- if typeof @[key] is 'function'
259
- result[key] = @[key]()
260
- else
261
- result[key] = @[key]
262
- result.id = @id if @id
263
- result
264
-
265
- eql: (rec) ->
266
- !!(rec and rec.constructor is @constructor and
267
- (rec.cid is @cid) or (rec.id and rec.id is @id))
268
-
269
- save: (options = {}) ->
270
- unless options.validate is false
271
- error = @validate()
272
- if error
273
- @trigger('error', error)
274
- return false
275
-
276
- @trigger('beforeSave', options)
277
- record = if @isNew() then @create(options) else @update(options)
278
- @stripCloneAttrs()
279
- @trigger('save', options)
280
- record
281
-
282
- stripCloneAttrs: ->
283
- return if @hasOwnProperty 'cid' # Make sure it's not the raw object
284
- for own key, value of @
285
- delete @[key] if @constructor.attributes.indexOf(key) > -1
286
- this
287
-
288
- updateAttribute: (name, value, options) ->
289
- @[name] = value
290
- @save(options)
291
-
292
- updateAttributes: (atts, options) ->
293
- @load(atts)
294
- @save(options)
295
-
296
- changeID: (id) ->
297
- records = @constructor.records
298
- records[id] = records[@id]
299
- delete records[@id]
300
- @id = id
301
- @save()
302
-
303
- destroy: (options = {}) ->
304
- @trigger('beforeDestroy', options)
305
- delete @constructor.records[@id]
306
- delete @constructor.crecords[@cid]
307
- @destroyed = true
308
- @trigger('destroy', options)
309
- @trigger('change', 'destroy', options)
310
- @unbind()
311
- this
312
-
313
- dup: (newRecord) ->
314
- result = new @constructor(@attributes())
315
- if newRecord is false
316
- result.cid = @cid
317
- else
318
- delete result.id
319
- result
320
-
321
- clone: ->
322
- createObject(this)
323
-
324
- reload: ->
325
- return this if @isNew()
326
- original = @constructor.find(@id)
327
- @load(original.attributes())
328
- original
329
-
330
- toJSON: ->
331
- @attributes()
332
-
333
- toString: ->
334
- "<#{@constructor.className} (#{JSON.stringify(this)})>"
335
-
336
- fromForm: (form) ->
337
- result = {}
338
- for key in $(form).serializeArray()
339
- result[key.name] = key.value
340
- @load(result)
341
-
342
- exists: ->
343
- @id && @id of @constructor.records
344
-
345
- # Private
346
-
347
- update: (options) ->
348
- @trigger('beforeUpdate', options)
349
- records = @constructor.records
350
- records[@id].load @attributes()
351
- clone = records[@id].clone()
352
- clone.trigger('update', options)
353
- clone.trigger('change', 'update', options)
354
- clone
355
-
356
- create: (options) ->
357
- @trigger('beforeCreate', options)
358
- @id = @cid unless @id
359
-
360
- record = @dup(false)
361
- @constructor.records[@id] = record
362
- @constructor.crecords[@cid] = record
363
-
364
- clone = record.clone()
365
- clone.trigger('create', options)
366
- clone.trigger('change', 'create', options)
367
- clone
368
-
369
- bind: (events, callback) ->
370
- @constructor.bind events, binder = (record) =>
371
- if record && @eql(record)
372
- callback.apply(this, arguments)
373
- @constructor.bind 'unbind', unbinder = (record) =>
374
- if record && @eql(record)
375
- @constructor.unbind(events, binder)
376
- @constructor.unbind('unbind', unbinder)
377
- binder
378
-
379
- one: (events, callback) ->
380
- binder = @bind events, =>
381
- @constructor.unbind(events, binder)
382
- callback.apply(this, arguments)
383
-
384
- trigger: (args...) ->
385
- args.splice(1, 0, this)
386
- @constructor.trigger(args...)
387
-
388
- unbind: ->
389
- @trigger('unbind')
390
-
391
- class Controller extends Module
392
- @include Events
393
- @include Log
394
-
395
- eventSplitter: /^(\S+)\s*(.*)$/
396
- tag: 'div'
397
-
398
- constructor: (options) ->
399
- @options = options
400
-
401
- for key, value of @options
402
- @[key] = value
403
-
404
- @el = document.createElement(@tag) unless @el
405
- @el = $(@el)
406
- @$el = @el
407
-
408
- @el.addClass(@className) if @className
409
- @el.attr(@attributes) if @attributes
410
-
411
- @events = @constructor.events unless @events
412
- @elements = @constructor.elements unless @elements
413
-
414
- @delegateEvents(@events) if @events
415
- @refreshElements() if @elements
416
-
417
- super
418
-
419
- release: =>
420
- @trigger 'release'
421
- @el.remove()
422
- @unbind()
423
-
424
- $: (selector) -> $(selector, @el)
425
-
426
- delegateEvents: (events) ->
427
- for key, method of events
428
-
429
- if typeof(method) is 'function'
430
- # Always return true from event handlers
431
- method = do (method) => =>
432
- method.apply(this, arguments)
433
- true
434
- else
435
- unless @[method]
436
- throw new Error("#{method} doesn't exist")
437
-
438
- method = do (method) => =>
439
- @[method].apply(this, arguments)
440
- true
441
-
442
- match = key.match(@eventSplitter)
443
- eventName = match[1]
444
- selector = match[2]
445
-
446
- if selector is ''
447
- @el.bind(eventName, method)
448
- else
449
- @el.delegate(selector, eventName, method)
450
-
451
- refreshElements: ->
452
- for key, value of @elements
453
- @[value] = @$(key)
454
-
455
- delay: (func, timeout) ->
456
- setTimeout(@proxy(func), timeout || 0)
457
-
458
- html: (element) ->
459
- @el.html(element.el or element)
460
- @refreshElements()
461
- @el
462
-
463
- append: (elements...) ->
464
- elements = (e.el or e for e in elements)
465
- @el.append(elements...)
466
- @refreshElements()
467
- @el
468
-
469
- appendTo: (element) ->
470
- @el.appendTo(element.el or element)
471
- @refreshElements()
472
- @el
473
-
474
- prepend: (elements...) ->
475
- elements = (e.el or e for e in elements)
476
- @el.prepend(elements...)
477
- @refreshElements()
478
- @el
479
-
480
- replace: (element) ->
481
- [previous, @el] = [@el, $(element.el or element)]
482
- previous.replaceWith(@el)
483
- @delegateEvents(@events)
484
- @refreshElements()
485
- @el
486
-
487
- # Utilities & Shims
488
-
489
- $ = window?.jQuery or window?.Zepto or (element) -> element
490
-
491
- createObject = Object.create or (o) ->
492
- Func = ->
493
- Func.prototype = o
494
- new Func()
495
-
496
- isArray = (value) ->
497
- Object::toString.call(value) is '[object Array]'
498
-
499
- isBlank = (value) ->
500
- return true unless value
501
- return false for key of value
502
- true
503
-
504
- makeArray = (args) ->
505
- Array::slice.call(args, 0)
506
-
507
- # Globals
508
-
509
- Spine = @Spine = {}
510
- module?.exports = Spine
511
-
512
- Spine.version = '1.0.8'
513
- Spine.isArray = isArray
514
- Spine.isBlank = isBlank
515
- Spine.$ = $
516
- Spine.Events = Events
517
- Spine.Log = Log
518
- Spine.Module = Module
519
- Spine.Controller = Controller
520
- Spine.Model = Model
521
-
522
- # Global events
523
-
524
- Module.extend.call(Spine, Events)
525
-
526
- # JavaScript compatability
527
-
528
- Module.create = Module.sub =
529
- Controller.create = Controller.sub =
530
- Model.sub = (instances, statics) ->
531
- class Result extends this
532
- Result.include(instances) if instances
533
- Result.extend(statics) if statics
534
- Result.unbind?()
535
- Result
536
-
537
- Model.setup = (name, attributes = []) ->
538
- class Instance extends this
539
- Instance.configure(name, attributes...)
540
- Instance
541
-
542
- Spine.Class = Module