flipper-ui 0.2.0.beta2 → 0.2.0.beta3

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