lanes 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -2
  3. data/client/lanes/Config.coffee +6 -5
  4. data/client/lanes/access/LoginDialog.cjsx +4 -2
  5. data/client/lanes/access/Roles.coffee +3 -0
  6. data/client/lanes/access/screens/user-management/Editor.cjsx +1 -1
  7. data/client/lanes/components/grid/Body.cjsx +4 -3
  8. data/client/lanes/components/grid/Grid.cjsx +4 -3
  9. data/client/lanes/components/grid/Header.cjsx +1 -1
  10. data/client/lanes/components/grid/PopOverMixin.cjsx +6 -6
  11. data/client/lanes/components/grid/Selections.cjsx +16 -6
  12. data/client/lanes/components/grid/Toolbar.cjsx +1 -1
  13. data/client/lanes/components/grid/styles.scss +21 -5
  14. data/client/lanes/components/modal/Modal.cjsx +18 -9
  15. data/client/lanes/components/record-finder/Clause.cjsx +1 -2
  16. data/client/lanes/components/record-finder/Dialog.cjsx +11 -3
  17. data/client/lanes/components/record-finder/RecordFinder.cjsx +14 -7
  18. data/client/lanes/components/record-finder/styles.scss +1 -0
  19. data/client/lanes/components/select-field/SelectField.cjsx +36 -10
  20. data/client/lanes/components/select-field/styles.scss +7 -0
  21. data/client/lanes/components/shared/Checkbox.cjsx +34 -0
  22. data/client/lanes/components/shared/DateTime.cjsx +3 -2
  23. data/client/lanes/components/shared/ErrorDisplay.cjsx +1 -1
  24. data/client/lanes/components/shared/FieldMixin.cjsx +26 -15
  25. data/client/lanes/components/shared/FieldSet.cjsx +1 -1
  26. data/client/lanes/components/shared/FieldWrapper.cjsx +2 -2
  27. data/client/lanes/components/shared/FormGroup.cjsx +2 -2
  28. data/client/lanes/components/shared/Icon.cjsx +31 -4
  29. data/client/lanes/components/shared/IconButton.cjsx +8 -0
  30. data/client/lanes/components/shared/ImageAsset.cjsx +8 -5
  31. data/client/lanes/components/shared/IndeterminateCheckbox.cjsx +19 -0
  32. data/client/lanes/components/shared/InputFieldMixin.cjsx +6 -0
  33. data/client/lanes/components/shared/JobProgress.cjsx +4 -3
  34. data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +1 -1
  35. data/client/lanes/components/shared/ScreenWrapper.cjsx +1 -1
  36. data/client/lanes/components/shared/ToggleField.cjsx +2 -1
  37. data/client/lanes/components/shared/Tooltip.cjsx +10 -2
  38. data/client/lanes/components/shared/fields.scss +7 -2
  39. data/client/lanes/components/toolbar/RemoteChangeSets.cjsx +10 -7
  40. data/client/lanes/components/toolbar/SaveButton.cjsx +5 -4
  41. data/client/lanes/components/toolbar/Toolbar.cjsx +18 -19
  42. data/client/lanes/components/toolbar/changes-notification.scss +3 -1
  43. data/client/lanes/components/toolbar/styles.scss +6 -3
  44. data/client/lanes/lib/HotReload.coffee +1 -1
  45. data/client/lanes/lib/all.js +1 -1
  46. data/client/lanes/lib/dom.coffee +14 -1
  47. data/client/lanes/lib/format.coffee +3 -0
  48. data/client/lanes/lib/loader.coffee +0 -2
  49. data/client/lanes/lib/{dom-polyfills.coffee → polyfills.coffee} +3 -0
  50. data/client/lanes/lib/utilFunctions.coffee +5 -7
  51. data/client/lanes/models/Asset.coffee +33 -20
  52. data/client/lanes/models/AssociationMap.coffee +5 -6
  53. data/client/lanes/models/Base.coffee +5 -2
  54. data/client/lanes/models/Collection.coffee +23 -2
  55. data/client/lanes/models/PubSub.coffee +51 -13
  56. data/client/lanes/models/Query.coffee +53 -42
  57. data/client/lanes/models/State.coffee +5 -4
  58. data/client/lanes/models/Sync.coffee +5 -3
  59. data/client/lanes/models/index.js +1 -1
  60. data/client/lanes/models/mixins/TrackCollectionRemovals.coffee +17 -0
  61. data/client/lanes/models/query/ArrayResult.coffee +16 -17
  62. data/client/lanes/models/query/CollectionResult.coffee +4 -4
  63. data/client/lanes/react/Component.coffee +14 -13
  64. data/client/lanes/react/Root.cjsx +1 -1
  65. data/client/lanes/react/Screen.coffee +1 -0
  66. data/client/lanes/react/Viewport.coffee +52 -16
  67. data/client/lanes/react/mixins/Access.coffee +5 -0
  68. data/client/lanes/react/mixins/Data.coffee +52 -144
  69. data/client/lanes/react/mixins/MonitorSize.coffee +1 -1
  70. data/client/lanes/react/mixins/ReadEditingState.coffee +3 -0
  71. data/client/lanes/screens/Commands.coffee +1 -1
  72. data/client/lanes/screens/CommonComponents.cjsx +2 -2
  73. data/client/lanes/screens/Definitions.coffee +3 -1
  74. data/client/lanes/screens/SystemSettings.cjsx +13 -18
  75. data/client/lanes/screens/UserPreferences.cjsx +1 -1
  76. data/client/lanes/styles/fonts.scss +2 -3
  77. data/client/lanes/styles/global/styles.scss +2 -1
  78. data/client/lanes/testing/TestObjects.coffee +6 -2
  79. data/client/lanes/vendor/action_cable.js +590 -0
  80. data/client/lanes/vendor/development/calendar.js +56 -56
  81. data/client/lanes/vendor/development/commons.js +7819 -6690
  82. data/client/lanes/vendor/development/data.js +1877 -1455
  83. data/client/lanes/vendor/development/helpers.js +39 -82
  84. data/client/lanes/vendor/development/toggle.js +20 -20
  85. data/client/lanes/vendor/development/ui.js +14629 -14261
  86. data/client/lanes/vendor/development/widgets.js +3146 -2173
  87. data/client/lanes/vendor/index.js +1 -2
  88. data/client/lanes/vendor/production/calendar.js +56 -56
  89. data/client/lanes/vendor/production/commons.js +6352 -6185
  90. data/client/lanes/vendor/production/data.js +1871 -1456
  91. data/client/lanes/vendor/production/toggle.js +20 -20
  92. data/client/lanes/vendor/production/ui.js +14694 -14286
  93. data/client/lanes/vendor/production/widgets.js +3139 -2166
  94. data/client/lanes/vendor/standalone/index.js +5666 -4586
  95. data/client/lanes/vendor/styles/widgets.scss +5 -5
  96. data/client/lanes/workspace/Layout.cjsx +1 -10
  97. data/client/lanes/workspace/Navbar.cjsx +8 -13
  98. data/client/lanes/workspace/ScreenView.cjsx +3 -4
  99. data/client/lanes/workspace/ScreensMenu.cjsx +8 -19
  100. data/client/lanes/workspace/Tabs.cjsx +6 -14
  101. data/db/migrate/02_create_assets.rb +1 -3
  102. data/lanes.gemspec +16 -11
  103. data/lib/lanes/access/test_fixture_extensions.rb +6 -10
  104. data/lib/lanes/access/track_modifications.rb +24 -0
  105. data/lib/lanes/api/cable.rb +49 -0
  106. data/lib/lanes/api/controller_base.rb +52 -9
  107. data/lib/lanes/api/default_routes.rb +9 -0
  108. data/lib/lanes/api/generic_controller.rb +2 -9
  109. data/lib/lanes/api/handlers/asset.rb +5 -6
  110. data/lib/lanes/api/helper_methods.rb +3 -2
  111. data/lib/lanes/api/javascript_processor.rb +6 -5
  112. data/lib/lanes/api/pub_sub.rb +14 -32
  113. data/lib/lanes/api/request_wrapper.rb +1 -1
  114. data/lib/lanes/api/root.rb +4 -7
  115. data/lib/lanes/api/routing.rb +3 -3
  116. data/lib/lanes/api/sprockets_extension.rb +1 -1
  117. data/lib/lanes/api.rb +4 -0
  118. data/lib/lanes/asset.rb +25 -25
  119. data/lib/lanes/concerns/asset_uploader.rb +25 -47
  120. data/lib/lanes/concerns/export_scope.rb +5 -7
  121. data/lib/lanes/concerns/queries.rb +7 -3
  122. data/lib/lanes/concerns/set_attribute_data.rb +12 -6
  123. data/lib/lanes/configuration.rb +4 -7
  124. data/lib/lanes/db.rb +3 -1
  125. data/lib/lanes/guard_tasks.rb +2 -1
  126. data/lib/lanes/hot_reload_plugin.rb +2 -1
  127. data/lib/lanes/job.rb +0 -1
  128. data/lib/lanes/logger.rb +3 -3
  129. data/lib/lanes/model.rb +0 -3
  130. data/lib/lanes/spec_helper.rb +1 -1
  131. data/lib/lanes/system_settings.rb +8 -14
  132. data/lib/lanes/version.rb +1 -1
  133. data/npm-build/data.js +1 -0
  134. data/npm-build/package.json +21 -19
  135. data/npm-build/ui.js +3 -0
  136. data/npm-build/update-dayz +2 -2
  137. data/npm-build/update-model-bindings.js +5 -0
  138. data/spec/command-reference-files/initial/Gemfile +1 -1
  139. data/spec/command-reference-files/initial/config/database.yml +1 -0
  140. data/spec/command-reference-files/initial/config/lanes.rb +3 -3
  141. data/spec/command-reference-files/screen/spec/appy-app/screens/ready-set-go/ReadySetGoSpec.coffee +1 -1
  142. data/spec/lanes/components/grid/GridSpec.coffee +2 -2
  143. data/spec/lanes/components/grid/PopoverEditorSpec.coffee +11 -12
  144. data/spec/lanes/components/select-field/SelectFieldSpec.coffee +6 -4
  145. data/spec/lanes/components/shared/NetworkActivityOverlaySpec.coffee +1 -1
  146. data/spec/lanes/models/PubSubSpec.coffee +6 -8
  147. data/spec/lanes/models/QuerySpec.coffee +19 -0
  148. data/spec/lanes/react/mixins/DataSpec.coffee +18 -16
  149. data/spec/server/api/coffeescript_processor_spec.rb +1 -1
  150. data/spec/server/api/controller_base_spec.rb +77 -0
  151. data/spec/server/api/pub_sub_spec.rb +9 -0
  152. data/spec/server/asset_spec.rb +23 -18
  153. data/spec/server/concerns/export_scope_spec.rb +2 -1
  154. data/spec/server/concerns/exported_limits_spec.rb +14 -9
  155. data/spec/server/concerns/set_attribute_data_spec.rb +17 -0
  156. data/spec/server/spec_helper.rb +29 -11
  157. data/templates/config/database.yml +1 -0
  158. data/templates/spec/client/Screen.coffee +1 -1
  159. data/views/specs.erb +4 -1
  160. metadata +138 -89
  161. data/client/lanes/vendor/message-bus-ajax.js +0 -44
  162. data/client/lanes/vendor/message-bus.js +0 -414
@@ -59,22 +59,12 @@ class Page
59
59
 
60
60
  modelAt: (index, options) ->
61
61
  row = @_rowAt(index)
62
- @modelCache ||= {}
63
- id = @idForRow(row)
64
- if _.isBlank(id)
65
- @_rowToModel(row)
66
- else
67
- @modelCache[id] ||= @_rowToModel(row)
62
+ @_rowToModel(row)
68
63
 
69
64
  saveModelChanges: (model, index) ->
70
65
  row = @_rowAt(index)
71
- @modelCache[ @idForRow(row)] = model
72
- data = model.serialize()
73
66
  for field, i in @result.query.fields.models
74
- row[field.fetchIndex] = data[field.id]
75
-
76
- idForRow: (row) ->
77
- row[@result.query.idIndex]
67
+ row[field.fetchIndex] = model[field.id]
78
68
 
79
69
  addBlankRow: (index) ->
80
70
  model = new @result.query.model
@@ -127,7 +117,7 @@ class Lanes.Models.Query.ArrayResult extends Lanes.Models.Query.Result
127
117
  eachRow: (fn) ->
128
118
  for i in [0...@length]
129
119
  row = @rowAt(i)
130
- fn(row, row[@xDataColumn], i)
120
+ return i if 'break' is fn(row, row[@xDataColumn], i)
131
121
 
132
122
  map: (fn) ->
133
123
  rows = []
@@ -135,6 +125,12 @@ class Lanes.Models.Query.ArrayResult extends Lanes.Models.Query.Result
135
125
  rows.push fn(row, xd, i)
136
126
  rows
137
127
 
128
+ findById: (id) ->
129
+ @eachRow (row, xd, i) =>
130
+ return 'break' if id is @idForRow(row)
131
+
132
+ idForRow: (row) ->
133
+ row[@query.idIndex]
138
134
 
139
135
  filteredRows: (fn) ->
140
136
  found = []
@@ -146,7 +142,10 @@ class Lanes.Models.Query.ArrayResult extends Lanes.Models.Query.Result
146
142
  @pageForIndex(index).modelAt(index)
147
143
 
148
144
  saveModelChanges: (model, index) ->
145
+ if _.isUndefined(index)
146
+ index = @findById( model.getId() )
149
147
  @pageForIndex(index).saveModelChanges(model, index)
148
+ @query.markModified()
150
149
 
151
150
  removeRow: (index = 0) ->
152
151
  @total -= 1
@@ -184,8 +183,8 @@ class Lanes.Models.Query.ArrayResult extends Lanes.Models.Query.Result
184
183
  rowRepresentation: (rowNum) ->
185
184
  @pageForIndex(rowNum).rowAt(rowNum)
186
185
 
187
- valueForField: (rowNum, field) ->
188
- @rowAt(rowNum)[ field.fetchIndex ]
186
+ valueForField: (row, field) ->
187
+ if field.fetchIndex? then row[ field.fetchIndex ] else undefined
189
188
 
190
189
  _updateSort: ->
191
190
  fn = @sortingFunction()
@@ -194,11 +193,11 @@ class Lanes.Models.Query.ArrayResult extends Lanes.Models.Query.Result
194
193
  rows = if @query.sortAscending then rows.reverse() else rows
195
194
  page = new Page(0, @, rows: rows)
196
195
  @pages = [page]
197
- @query.changeCount += 1
196
+ @query.markModified()
198
197
  @
199
198
  else
200
199
  @reload().then =>
201
- @query.changeCount += 1
200
+ @query.markModified()
202
201
  @
203
202
 
204
203
  Object.defineProperty Lanes.Models.Query.ArrayResult.prototype, 'length',
@@ -5,7 +5,7 @@ class Lanes.Models.Query.CollectionResult extends Lanes.Models.Query.Result
5
5
  @query.trigger('change', @query)
6
6
  )
7
7
  @collection.on('sort', =>
8
- @query.changeCount += 1
8
+ @query.markModified()
9
9
  )
10
10
 
11
11
  this
@@ -28,7 +28,7 @@ class Lanes.Models.Query.CollectionResult extends Lanes.Models.Query.Result
28
28
 
29
29
  @collection.remove(old)
30
30
  @collection.add(model, at:index)
31
- @query.changeCount++
31
+ @query.markModified()
32
32
 
33
33
  addBlankRow: (index) ->
34
34
  @collection.add({}, at: index)
@@ -49,8 +49,8 @@ class Lanes.Models.Query.CollectionResult extends Lanes.Models.Query.Result
49
49
  rowRepresentation: (rowNum) ->
50
50
  @modelAt(rowNum)
51
51
 
52
- valueForField: (rowNum, field) ->
53
- @collection.at(rowNum)[field.id]
52
+ valueForField: (model, field) ->
53
+ model[field.id]
54
54
 
55
55
  fieldToSortValue:
56
56
  any: (v) -> v
@@ -1,20 +1,21 @@
1
- createHelper = (component, name) ->
2
- unless component.prototype[name]
3
- Object.defineProperty(component.prototype, name,
4
- configurable: true, enumerable: true
5
- get: -> @data?.states[name]
6
- set: (val) ->
7
- @data?.rebind("#{name}": val)
8
- )
1
+ # createHelper = (component, name) ->
2
+ # unless component.prototype[name]
3
+ # Object.defineProperty(component.prototype, name,
4
+ # configurable: true, enumerable: true
5
+ # get: -> @data?.states[name]
6
+ # set: (val) ->
7
+ # @data?.rebind("#{name}": val)
8
+ # )
9
9
 
10
- extendComponent = (component) ->
11
- names = _.keys(component.prototype.dataObjects).concat(['collection', 'model'])
12
- for name in _.uniq(names)
13
- createHelper(component, name)
10
+ # extendComponent = (component) ->
11
+ # names = _.keys(component.prototype.dataObjects).concat(['collection', 'model'])
12
+ # for name in _.uniq(names)
13
+ # createHelper(component, name)
14
14
 
15
15
  Lanes.React.Component = {
16
16
 
17
17
  defaultMixins: [
18
+ Lanes.Vendor.ReactModelMixin
18
19
  Lanes.React.Mixins.Data
19
20
  Lanes.React.Mixins.Viewport
20
21
  Lanes.React.Mixins.Component
@@ -24,7 +25,7 @@ Lanes.React.Component = {
24
25
  klass::mixins ||= []
25
26
  klass::mixins = _.uniq(klass::mixins.concat(mixins))
26
27
  comp = React.createClass(klass.prototype)
27
- extendComponent(comp)
28
+ # extendComponent(comp)
28
29
  return Lanes.lib.HotReload?.remember(comp) or comp
29
30
  }
30
31
 
@@ -6,6 +6,6 @@ Lanes.React.Root = React.createClass
6
6
  viewport: @props.viewport
7
7
 
8
8
  render: ->
9
- <div classNames="root">
9
+ <div className="root">
10
10
  {this.props.children}
11
11
  </div>
@@ -1,6 +1,7 @@
1
1
  Lanes.React.Screen = {
2
2
 
3
3
  defaultMixins: [
4
+ Lanes.Vendor.ReactModelMixin
4
5
  Lanes.React.Mixins.Data
5
6
  Lanes.React.Mixins.Viewport
6
7
  Lanes.React.Mixins.Screen
@@ -1,4 +1,7 @@
1
1
  ALL_INSTANCES = []
2
+ class FakeHistory
3
+ push: Lanes.emptyFn
4
+ replace: Lanes.emptyFn
2
5
 
3
6
  class Lanes.React.Viewport extends Lanes.Models.State
4
7
 
@@ -6,17 +9,20 @@ class Lanes.React.Viewport extends Lanes.Models.State
6
9
  @displayError: (msg) -> _.first(ALL_INSTANCES)?.displayError(msg)
7
10
 
8
11
  session:
9
- width: 'number'
10
- height: 'number'
11
- el: 'element'
12
- selector: 'string'
13
- domRoot: 'element'
14
- reactRoot: 'object'
15
- lanes: 'element'
16
- modalProps: 'object'
12
+ width: 'number'
13
+ height: 'number'
14
+ el: 'element'
15
+ selector: 'string'
16
+ domRoot: 'element'
17
+ reactRoot: 'object'
18
+ lanes: 'element'
19
+ modalProps: 'object'
17
20
  pubSubDisabled: 'boolean'
18
- rootComponent: 'any'
19
- rootProps: 'object'
21
+ rootComponent: 'any'
22
+ rootProps: 'object'
23
+ rootElement: 'object'
24
+ history: 'object'
25
+ useHistory: {type: 'boolean', default: true}
20
26
 
21
27
  constructor: ->
22
28
  super
@@ -27,6 +33,7 @@ class Lanes.React.Viewport extends Lanes.Models.State
27
33
  Lanes.fatal("Root selector #{@selector} not found") unless @domRoot
28
34
  _.dom(@domRoot).html = "<div class='lanes'/>"
29
35
  this.lanes = @domRoot.querySelector('.lanes')
36
+
30
37
  Lanes.lib.ResizeSensor(@domRoot, _.debounce( =>
31
38
  @_updateDimensions()
32
39
  , 250))
@@ -41,10 +48,39 @@ class Lanes.React.Viewport extends Lanes.Models.State
41
48
 
42
49
  onBoot: ->
43
50
  prev = _.dom(this.domRoot.previousElementSibling)
44
- prev.addClass('complete') if prev.hasClass('loading')
45
- _.delay(->
46
- prev.remove()
47
- , 100)
51
+ if @useHistory
52
+ @initializeHistory()
53
+ else
54
+ @history = new FakeHistory
55
+ if prev.hasClass('loading')
56
+ prev.addClass('complete')
57
+ _.delay(->
58
+ prev.remove()
59
+ , 100)
60
+
61
+ initializeHistory: ->
62
+ useBasename = Lanes.Vendor.BrowserHistory.useBasename
63
+
64
+ @history = useBasename(Lanes.Vendor.BrowserHistory.createHistory)({
65
+ basename: Lanes.config.root_path
66
+ })
67
+ @_historyStopListening = @history.listen (location) ->
68
+ Lanes.Screens.Definitions.setBrowserLocation(location)
69
+
70
+ Lanes.Screens.Definitions.setBrowserLocation(
71
+ @history.getCurrentLocation()
72
+ )
73
+
74
+ Lanes.Screens.Definitions.displaying.on('change:active', (screen) =>
75
+ if screen.active
76
+ @history.replace(screen.historyUrl())
77
+ if Lanes.Screens.Definitions.displaying.length is 0
78
+ @history.push('/')
79
+ )
80
+
81
+ close: ->
82
+ @hideModal()
83
+ @_historyStopListening?()
48
84
 
49
85
  _updateDimensions: ->
50
86
  this.set
@@ -70,7 +106,7 @@ class Lanes.React.Viewport extends Lanes.Models.State
70
106
  component = cntrl?.rootComponent?(this) ||
71
107
  Lanes.React.Root.DefaultComponentNotFound
72
108
  )
73
- root = React.createElement(Lanes.React.Root, {viewport: @},
109
+ @rootElement = React.createElement(Lanes.React.Root, {viewport: @},
74
110
  React.createElement(component, _.extend(@rootProps, extension: cntrl))
75
111
  )
76
- @reactRoot = Lanes.Vendor.ReactDOM.render(root, @lanes)
112
+ @reactRoot = Lanes.Vendor.ReactDOM.render(@rootElement, @lanes)
@@ -17,6 +17,11 @@ Lanes.React.Mixins.Access = {
17
17
  componentDidMount: ->
18
18
  calculateAccess(this, @props)
19
19
 
20
+ propTypes:
21
+ writable: React.PropTypes.bool
22
+ readable: React.PropTypes.bool
23
+ readonly: React.PropTypes.bool
24
+
20
25
  componentWillReceiveProps: (newProps) ->
21
26
  calculateAccess(this, newProps)
22
27
 
@@ -1,157 +1,65 @@
1
- # Patterned loosely after Backbone React
2
- # https://github.com/magalhas/backbone-react-component/blob/master/lib/component.js
3
-
4
- # Mixing `Backbone.Events` into `Wrapper.prototype`
5
- # Binds models and collections to a `React.Component`. It mixes `Backbone.Events`.
6
-
7
- class DataWrapper
8
-
9
- constructor: (@component, initialState = {}) ->
10
- # Object to store data state (not the component state)
11
- @states = {}
12
- @rebind(_.clone(initialState), silent: true)
13
-
14
- rebind: (objects, options = {}) ->
15
- customEvents = _.result(@component, 'bindDataEvents', {})
16
- @_rebindAttr(name, state, customEvents[name], options) for name, state of objects when state isnt false
17
-
18
- _rebindAttr: (name, state, ev, options) ->
19
- unless state
20
- Lanes.warn "#{name} is not set on #{@componentName()}"
21
- return
22
-
23
- prevState = @states[name]
24
-
25
- # onto next if the object is the same
26
- return if prevState == state
27
-
28
- @states[name] = state
29
-
30
- this.bindEvents(name, state, ev)
31
-
32
- if Lanes.u.isModel(state)
33
- @listenToNetworkEvents(state) if @component.listenNetworkEvents
34
- if @isStateUsingPubsub(name, state)
35
- if !prevState? or prevState.getId() != state.getId()
36
- Lanes.Models.PubSub.remove(prevState) if prevState
37
- unless false == state.pubsub
38
- if state.isNew()
39
- state.once "change:#{state.idAttribute}", -> Lanes.Models.PubSub.add(state)
40
- else
41
- Lanes.Models.PubSub.add(state)
42
- @listenTo(state, 'remote-update', @onPubSubChangeSet)
43
- this.setComponentState({}) unless options.silent
44
-
45
- listenToNetworkEvents: (state) ->
46
- @listenTo(state, 'error', @onError)
47
- .listenTo(state, 'request', @onRequest)
48
- .listenTo(state, 'load sync', @onSync)
49
-
50
- onPubSubChangeSet: (model, cs) ->
51
- screen = @component.context?.screen || @component.getChildContext?()?.screen
52
- screen?.onPubSubChangeSet?(model, cs)
53
-
54
- componentName: ->
55
- @component.constructor.displayName
56
-
57
- onProxyReplace: (name, model) ->
58
- @states[name] = model
59
- @setComponentState(name, model)
60
-
61
- bindEvents: (name, state, events) ->
62
- if state.isProxy
63
- @listenTo(state, 'proxyreplace', _.partial(@onProxyReplace, name))
64
- if !events
65
- if state.isState or state.isProxy
66
- events ||= 'change'
67
- else if Lanes.u.isCollection(state)
68
- events ||= 'add remove change sort reset'
69
- else
70
- Lanes.warn "Unable to listen to unknown type #{name}"
71
-
72
- @listenTo(state, events, _.partial(@setComponentState, name, state))
1
+ class NetworkEventListener
73
2
 
74
- onError: (modelOrCollection, options) ->
75
- # Set state only if there's no silent option
76
- unless options.silent
77
- @setComponentState
78
- isRequesting: false
79
- hasError: modelOrCollection?.errorMessage or true
80
- error: modelOrCollection.errors
3
+ constructor: (component) -> @component = component
4
+
5
+ bindEvents: (events, model) ->
6
+ model.on('error', @onError, @)
7
+ .on('request', @onRequest, @)
8
+ .on('load sync', @onSync, @)
9
+
10
+ unBindEvents: (events, model) ->
11
+ model.off(null, null, @)
81
12
 
82
- onInvalid: (model, res, options) ->
83
- @setComponentState(isInvalid: true) unless options.silent
13
+ setState: (state, options) ->
14
+ return if options?.silent
15
+ fn = @component.setNetworkActivity or @component.setState
16
+ fn.call(@component, state)
17
+
18
+ onError: (modelOrCollection, options) ->
19
+ @setState(
20
+ isRequesting: false
21
+ hasError: modelOrCollection?.errorMessage or true
22
+ errors: modelOrCollection.errors
23
+ )
84
24
 
85
25
  onRequest: (modelOrCollection, type, options) ->
86
- # Set `state` only if there's no silent option
87
- unless options.silent
88
- @setComponentState
89
- isRequesting: type
90
- hasError: false
91
- isInvalid: false
26
+ @setState(
27
+ isRequesting: true
28
+ hasError: false
29
+ errors: []
30
+ )
92
31
 
93
32
  onSync: (modelOrCollection, res, options = {}) ->
94
- # Calls `setReactState` only if there's no silent option
95
- this.setComponentState(isRequesting: false) unless options.silent
96
-
97
- setComponentState: (key, value, args...) ->
98
- return unless @component.isMounted()
99
- state = if _.isObject(key) then key else {"#{key}": value}
100
- if _.isFunction(@component.setDataState)
101
- @component.setDataState(state, args...)
102
- else
103
- @component.setState(state)
104
- true
105
-
106
- isStateUsingPubsub: (name, state) ->
107
- _.result(state, 'registerforPubSub') isnt false and
108
- not (false == @component.pubsub or false == @component.pubsub?[name])
109
-
110
- destroy: (state, events, fn) ->
111
- for name, state of @states when @isStateUsingPubsub(name, state)
112
- Lanes.Models.PubSub.remove(state) if Lanes.u.isModel(state)
113
- this.stopListening()
114
- delete @component.data
115
-
116
-
117
- Lanes.Vendor.Events.createEmitter(DataWrapper.prototype)
118
- #_.extend(DataWrapper.prototype, Lanes.Vendor.BBEvents)
119
-
120
- readDataObjects = (comp, newProps) ->
121
- bound = _.clone _.result(comp, 'dataObjects') || {}
122
- bound.model ||= 'props' if comp.props.model
123
- bound.collection ||= 'props' if comp.props.collection
124
- _.mapValues(bound, (value, name) ->
125
- if _.isFunction(value)
126
- if _.isEmpty(comp.data?.states[name]) then value.call(comp) else false
127
- else if 'props' == value then newProps?[name] or comp.props[name]
128
- else value
129
- )
33
+ @setState(isRequesting: false)
34
+
35
+ getNetworkListener = (component) ->
36
+ return unless _.result(component, 'listenNetworkEvents') is true
37
+ component._networkEventsListener ||= new NetworkEventListener(component)
130
38
 
131
39
  Lanes.React.Mixins.Data = {
132
- # When the component gets the initial state, instance a `DataWrapper` to take
133
- # care of models and collections binding
134
- getInitialState: ->
135
- defaults = readDataObjects(this)
136
- @data = new DataWrapper(this, defaults) unless _.isEmpty(defaults)
137
- {}
138
-
139
- # When the component unmounts, dispose listeners and delete @data reference.
140
- componentWillUnmount: ->
141
- @data.destroy() if @data
142
-
143
- componentWillReceiveProps: (newProps) ->
144
- newState = readDataObjects(this, newProps)
145
- return if _.isEmpty(newState)
146
- if @data
147
- @data.rebind(newState)
40
+
41
+ onModelUnbind: (model, name) ->
42
+ Lanes.Models.PubSub.remove(model)
43
+ getNetworkListener(@)?.unBindEvents(@modelBindings, model)
44
+
45
+ onModelBind: (model, name) ->
46
+ model = this[name]
47
+ return unless Lanes.u.isModel(model)
48
+
49
+ getNetworkListener(@)?.bindEvents(@modelBindings, @[name])
50
+
51
+ return if _.result(model, 'registerForPubSub') is false or
52
+ _.result(@, 'registerForPubSub') is false
53
+
54
+ if model.isNew()
55
+ model.once "change:#{model.idAttribute}", -> Lanes.Models.PubSub.add(model)
148
56
  else
149
- @data = new DataWrapper(this, newState)
57
+ Lanes.Models.PubSub.add(model)
150
58
 
151
- listenTo: (state, events, fn) ->
152
- @data.listenTo(state, events, fn)
59
+ this.modelBindings.listenTo(model, 'remote-update', (model, cs) =>
60
+ screen = @context?.screen || @getChildContext?()?.screen
61
+ screen?.onPubSubChangeSet?(model, cs)
62
+ )
153
63
 
154
- stopListening: (state, events, fn) ->
155
- @data.stopListening(state, events, fn)
156
64
 
157
65
  }
@@ -7,5 +7,5 @@ Lanes.React.Mixins.MonitorSize = {
7
7
 
8
8
  componentDidMount: ->
9
9
  if @context.viewport
10
- @data.listenTo(@context.viewport, 'change:height change:width', @_updateViewportSize)
10
+ @modelBindings.listenTo(@context.viewport, 'change:height change:width', @_updateViewportSize)
11
11
  }
@@ -6,6 +6,9 @@ Lanes.React.Mixins.ReadEditingState = {
6
6
  writable: React.PropTypes.bool
7
7
  readonly: React.PropTypes.bool
8
8
 
9
+ propTypes:
10
+ editOnly: React.PropTypes.bool
11
+
9
12
  isEditingRecord: ->
10
13
  @props.editOnly or @context.recordDisplay == 'edit'
11
14
  }
@@ -13,7 +13,7 @@ class Lanes.Screens.Commands extends Lanes.Models.State
13
13
 
14
14
  setModel: (model) ->
15
15
  @options.modelWillRebind?(model)
16
- @screen.data.rebind("#{@options.modelName}": model)
16
+ @screen.modelBindings.reset({"#{@options.modelName}": model})
17
17
  @screen.setModelUrl?(model)
18
18
  @options.modelDidRebind?(model)
19
19
 
@@ -1,7 +1,7 @@
1
1
  class Lanes.Screens.CommonComponents extends Lanes.React.Component
2
2
 
3
3
  propTypes:
4
- commands: React.PropTypes.instanceOf(Lanes.Screens.Commands).isRequired
4
+ commands: React.PropTypes.object.isRequired
5
5
  errors: React.PropTypes.bool
6
6
  networkActivity: React.PropTypes.bool
7
7
  toolbarProps: React.PropTypes.object
@@ -13,7 +13,7 @@ class Lanes.Screens.CommonComponents extends Lanes.React.Component
13
13
  <div>
14
14
  {unless @networkActivity is false
15
15
  <LC.NetworkActivityOverlay model={model} {...@props} /> }
16
- <LC.Toolbar {...@props} {...@props.toolbarProps}>
16
+ <LC.Toolbar {...@props} {...@props.toolbarProps} model={model}>
17
17
  {@props.children}
18
18
  </LC.Toolbar>
19
19
  {<LC.ErrorDisplay model={model} {...@props} /> unless @errors is false}
@@ -64,7 +64,9 @@ class ScreenDefinition extends Lanes.Models.BasicModel
64
64
  @url_prefix
65
65
  else
66
66
  "#{@extension.toLowerCase()}/screens"
67
- @assets.map (asset) -> "#{prefix}/#{asset}"
67
+ @assets.map (asset) ->
68
+ "//#{Lanes.config.api_host}" +
69
+ "#{Lanes.config.assets_path_prefix}/#{prefix}/#{asset}"
68
70
 
69
71
  extension_path:
70
72
  deps: ['extension', 'model'], fn: ->
@@ -1,6 +1,6 @@
1
1
  class Lanes.Screens.SystemSettings extends Lanes.React.Screen
2
2
 
3
- dataObjects:
3
+ modelBindings:
4
4
  config: -> Lanes.config.system_settings
5
5
 
6
6
  getInitialState: ->
@@ -15,7 +15,7 @@ class Lanes.Screens.SystemSettings extends Lanes.React.Screen
15
15
  dir = @config.settings.lanes.storage_dir
16
16
  <LC.FieldWrapper
17
17
  model={@config} displayComponent={<span />}
18
- label='Store Directory' sm=9 {...@props} value={dir}>
18
+ label='Store Directory' sm=9 value={dir}>
19
19
  <input type="text" className='value form-control'
20
20
  placeholder="Directory to store files" value={dir}
21
21
  onChange={_.partial(@onChange, 'storage_dir')} />
@@ -45,24 +45,19 @@ class Lanes.Screens.SystemSettings extends Lanes.React.Screen
45
45
 
46
46
  <LC.ScreenWrapper identifier="system-settings">
47
47
  <BS.Nav bsStyle="pills" className="lanes-toolbar">
48
- <BS.Button navItem componentClass="button"
49
- onClick={@saveConfig} className="save navbar-btn control">
50
- <LC.Icon type="cloud-upload" />Save
51
- </BS.Button>
48
+ <BS.NavItem>
49
+ <BS.Button componentClass="button"
50
+ onClick={@saveConfig}
51
+ className="save control"
52
+ >
53
+ <LC.Icon type="cloud-upload" />Save
54
+ </BS.Button>
55
+ </BS.NavItem>
52
56
  </BS.Nav>
53
-
54
- <BS.Row>
55
- <LC.FieldWrapper
56
- model={@config} displayComponent={<span />}
57
- label='File Storage' sm=3 {...@props} value={storage}>
58
- <Lanes.Vendor.ReactWidgets.DropdownList
59
- data={choices} value={storage} onChange={_.partial(@onChange, 'storage')} />
60
- </LC.FieldWrapper>
61
- {if storage is 'file' then @renderFileOptions() else @renderFogOptions()}
62
- </BS.Row>
63
57
  <BS.Row>
64
- <LC.ImageAsset sm=4 model={@config} name='logo' label='Logo' size='thumb' />
58
+ <LC.ImageAsset sm=4 model={@config} name='logo'
59
+ label='Logo' size='thumb' />
65
60
  </BS.Row>
66
61
  {for id, Ext of Lanes.Extensions.instances when Ext.getSettingsElement
67
- Ext.getSettingsElement(ref: id, key: id, settings: @config.settings[id])}
62
+ Ext.getSettingsElement(ref: id, key: id, settings: @config.settings[id] || {})}
68
63
  </LC.ScreenWrapper>
@@ -1,6 +1,6 @@
1
1
  class Lanes.Screens.UserPreferences extends Lanes.React.Screen
2
2
 
3
- dataObjects:
3
+ modelBindings:
4
4
  user: -> Lanes.current_user
5
5
 
6
6
  getInitialState: ->
@@ -4,10 +4,9 @@ $lanes-icon-font: 'FontAwesome';
4
4
 
5
5
  @import "./fonts/font-awesome";
6
6
 
7
- button, a.btn {
8
- .icon {
7
+ button, a.btn, [role=button] {
8
+ .icon:not(.flush) {
9
9
  margin-right: 0.5em;
10
- &.flush { margin-right: 0; }
11
10
  }
12
11
  &.icon:before{
13
12
  font-family: $lanes-icon-font;
@@ -1,5 +1,6 @@
1
1
  a { cursor: pointer; }
2
- .cursor-pointer { cursor: pointer; }
2
+ .cursor-pointer,
3
+ .clickable { cursor: pointer; }
3
4
  .non-printable { @include hidden-print; }
4
5
  .align-right { text-align: right; }
5
6
  .align-center { text-align: center; }