lanes 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -2
- data/client/lanes/Config.coffee +6 -5
- data/client/lanes/access/LoginDialog.cjsx +4 -2
- data/client/lanes/access/Roles.coffee +3 -0
- data/client/lanes/access/screens/user-management/Editor.cjsx +1 -1
- data/client/lanes/components/grid/Body.cjsx +4 -3
- data/client/lanes/components/grid/Grid.cjsx +4 -3
- data/client/lanes/components/grid/Header.cjsx +1 -1
- data/client/lanes/components/grid/PopOverMixin.cjsx +6 -6
- data/client/lanes/components/grid/Selections.cjsx +16 -6
- data/client/lanes/components/grid/Toolbar.cjsx +1 -1
- data/client/lanes/components/grid/styles.scss +21 -5
- data/client/lanes/components/modal/Modal.cjsx +18 -9
- data/client/lanes/components/record-finder/Clause.cjsx +1 -2
- data/client/lanes/components/record-finder/Dialog.cjsx +11 -3
- data/client/lanes/components/record-finder/RecordFinder.cjsx +14 -7
- data/client/lanes/components/record-finder/styles.scss +1 -0
- data/client/lanes/components/select-field/SelectField.cjsx +36 -10
- data/client/lanes/components/select-field/styles.scss +7 -0
- data/client/lanes/components/shared/Checkbox.cjsx +34 -0
- data/client/lanes/components/shared/DateTime.cjsx +3 -2
- data/client/lanes/components/shared/ErrorDisplay.cjsx +1 -1
- data/client/lanes/components/shared/FieldMixin.cjsx +26 -15
- data/client/lanes/components/shared/FieldSet.cjsx +1 -1
- data/client/lanes/components/shared/FieldWrapper.cjsx +2 -2
- data/client/lanes/components/shared/FormGroup.cjsx +2 -2
- data/client/lanes/components/shared/Icon.cjsx +31 -4
- data/client/lanes/components/shared/IconButton.cjsx +8 -0
- data/client/lanes/components/shared/ImageAsset.cjsx +8 -5
- data/client/lanes/components/shared/IndeterminateCheckbox.cjsx +19 -0
- data/client/lanes/components/shared/InputFieldMixin.cjsx +6 -0
- data/client/lanes/components/shared/JobProgress.cjsx +4 -3
- data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +1 -1
- data/client/lanes/components/shared/ScreenWrapper.cjsx +1 -1
- data/client/lanes/components/shared/ToggleField.cjsx +2 -1
- data/client/lanes/components/shared/Tooltip.cjsx +10 -2
- data/client/lanes/components/shared/fields.scss +7 -2
- data/client/lanes/components/toolbar/RemoteChangeSets.cjsx +10 -7
- data/client/lanes/components/toolbar/SaveButton.cjsx +5 -4
- data/client/lanes/components/toolbar/Toolbar.cjsx +18 -19
- data/client/lanes/components/toolbar/changes-notification.scss +3 -1
- data/client/lanes/components/toolbar/styles.scss +6 -3
- data/client/lanes/lib/HotReload.coffee +1 -1
- data/client/lanes/lib/all.js +1 -1
- data/client/lanes/lib/dom.coffee +14 -1
- data/client/lanes/lib/format.coffee +3 -0
- data/client/lanes/lib/loader.coffee +0 -2
- data/client/lanes/lib/{dom-polyfills.coffee → polyfills.coffee} +3 -0
- data/client/lanes/lib/utilFunctions.coffee +5 -7
- data/client/lanes/models/Asset.coffee +33 -20
- data/client/lanes/models/AssociationMap.coffee +5 -6
- data/client/lanes/models/Base.coffee +5 -2
- data/client/lanes/models/Collection.coffee +23 -2
- data/client/lanes/models/PubSub.coffee +51 -13
- data/client/lanes/models/Query.coffee +53 -42
- data/client/lanes/models/State.coffee +5 -4
- data/client/lanes/models/Sync.coffee +5 -3
- data/client/lanes/models/index.js +1 -1
- data/client/lanes/models/mixins/TrackCollectionRemovals.coffee +17 -0
- data/client/lanes/models/query/ArrayResult.coffee +16 -17
- data/client/lanes/models/query/CollectionResult.coffee +4 -4
- data/client/lanes/react/Component.coffee +14 -13
- data/client/lanes/react/Root.cjsx +1 -1
- data/client/lanes/react/Screen.coffee +1 -0
- data/client/lanes/react/Viewport.coffee +52 -16
- data/client/lanes/react/mixins/Access.coffee +5 -0
- data/client/lanes/react/mixins/Data.coffee +52 -144
- data/client/lanes/react/mixins/MonitorSize.coffee +1 -1
- data/client/lanes/react/mixins/ReadEditingState.coffee +3 -0
- data/client/lanes/screens/Commands.coffee +1 -1
- data/client/lanes/screens/CommonComponents.cjsx +2 -2
- data/client/lanes/screens/Definitions.coffee +3 -1
- data/client/lanes/screens/SystemSettings.cjsx +13 -18
- data/client/lanes/screens/UserPreferences.cjsx +1 -1
- data/client/lanes/styles/fonts.scss +2 -3
- data/client/lanes/styles/global/styles.scss +2 -1
- data/client/lanes/testing/TestObjects.coffee +6 -2
- data/client/lanes/vendor/action_cable.js +590 -0
- data/client/lanes/vendor/development/calendar.js +56 -56
- data/client/lanes/vendor/development/commons.js +7819 -6690
- data/client/lanes/vendor/development/data.js +1877 -1455
- data/client/lanes/vendor/development/helpers.js +39 -82
- data/client/lanes/vendor/development/toggle.js +20 -20
- data/client/lanes/vendor/development/ui.js +14629 -14261
- data/client/lanes/vendor/development/widgets.js +3146 -2173
- data/client/lanes/vendor/index.js +1 -2
- data/client/lanes/vendor/production/calendar.js +56 -56
- data/client/lanes/vendor/production/commons.js +6352 -6185
- data/client/lanes/vendor/production/data.js +1871 -1456
- data/client/lanes/vendor/production/toggle.js +20 -20
- data/client/lanes/vendor/production/ui.js +14694 -14286
- data/client/lanes/vendor/production/widgets.js +3139 -2166
- data/client/lanes/vendor/standalone/index.js +5666 -4586
- data/client/lanes/vendor/styles/widgets.scss +5 -5
- data/client/lanes/workspace/Layout.cjsx +1 -10
- data/client/lanes/workspace/Navbar.cjsx +8 -13
- data/client/lanes/workspace/ScreenView.cjsx +3 -4
- data/client/lanes/workspace/ScreensMenu.cjsx +8 -19
- data/client/lanes/workspace/Tabs.cjsx +6 -14
- data/db/migrate/02_create_assets.rb +1 -3
- data/lanes.gemspec +16 -11
- data/lib/lanes/access/test_fixture_extensions.rb +6 -10
- data/lib/lanes/access/track_modifications.rb +24 -0
- data/lib/lanes/api/cable.rb +49 -0
- data/lib/lanes/api/controller_base.rb +52 -9
- data/lib/lanes/api/default_routes.rb +9 -0
- data/lib/lanes/api/generic_controller.rb +2 -9
- data/lib/lanes/api/handlers/asset.rb +5 -6
- data/lib/lanes/api/helper_methods.rb +3 -2
- data/lib/lanes/api/javascript_processor.rb +6 -5
- data/lib/lanes/api/pub_sub.rb +14 -32
- data/lib/lanes/api/request_wrapper.rb +1 -1
- data/lib/lanes/api/root.rb +4 -7
- data/lib/lanes/api/routing.rb +3 -3
- data/lib/lanes/api/sprockets_extension.rb +1 -1
- data/lib/lanes/api.rb +4 -0
- data/lib/lanes/asset.rb +25 -25
- data/lib/lanes/concerns/asset_uploader.rb +25 -47
- data/lib/lanes/concerns/export_scope.rb +5 -7
- data/lib/lanes/concerns/queries.rb +7 -3
- data/lib/lanes/concerns/set_attribute_data.rb +12 -6
- data/lib/lanes/configuration.rb +4 -7
- data/lib/lanes/db.rb +3 -1
- data/lib/lanes/guard_tasks.rb +2 -1
- data/lib/lanes/hot_reload_plugin.rb +2 -1
- data/lib/lanes/job.rb +0 -1
- data/lib/lanes/logger.rb +3 -3
- data/lib/lanes/model.rb +0 -3
- data/lib/lanes/spec_helper.rb +1 -1
- data/lib/lanes/system_settings.rb +8 -14
- data/lib/lanes/version.rb +1 -1
- data/npm-build/data.js +1 -0
- data/npm-build/package.json +21 -19
- data/npm-build/ui.js +3 -0
- data/npm-build/update-dayz +2 -2
- data/npm-build/update-model-bindings.js +5 -0
- data/spec/command-reference-files/initial/Gemfile +1 -1
- data/spec/command-reference-files/initial/config/database.yml +1 -0
- data/spec/command-reference-files/initial/config/lanes.rb +3 -3
- data/spec/command-reference-files/screen/spec/appy-app/screens/ready-set-go/ReadySetGoSpec.coffee +1 -1
- data/spec/lanes/components/grid/GridSpec.coffee +2 -2
- data/spec/lanes/components/grid/PopoverEditorSpec.coffee +11 -12
- data/spec/lanes/components/select-field/SelectFieldSpec.coffee +6 -4
- data/spec/lanes/components/shared/NetworkActivityOverlaySpec.coffee +1 -1
- data/spec/lanes/models/PubSubSpec.coffee +6 -8
- data/spec/lanes/models/QuerySpec.coffee +19 -0
- data/spec/lanes/react/mixins/DataSpec.coffee +18 -16
- data/spec/server/api/coffeescript_processor_spec.rb +1 -1
- data/spec/server/api/controller_base_spec.rb +77 -0
- data/spec/server/api/pub_sub_spec.rb +9 -0
- data/spec/server/asset_spec.rb +23 -18
- data/spec/server/concerns/export_scope_spec.rb +2 -1
- data/spec/server/concerns/exported_limits_spec.rb +14 -9
- data/spec/server/concerns/set_attribute_data_spec.rb +17 -0
- data/spec/server/spec_helper.rb +29 -11
- data/templates/config/database.yml +1 -0
- data/templates/spec/client/Screen.coffee +1 -1
- data/views/specs.erb +4 -1
- metadata +138 -89
- data/client/lanes/vendor/message-bus-ajax.js +0 -44
- data/client/lanes/vendor/message-bus.js +0 -414
@@ -1,37 +1,43 @@
|
|
1
|
+
IMAGES = [
|
2
|
+
"image/png", "image/jpg", "image/gif"
|
3
|
+
]
|
4
|
+
|
5
|
+
IS_IMAGE = (content_type) ->
|
6
|
+
!!(content_type && IMAGES.indexOf(content_type) isnt -1)
|
7
|
+
|
1
8
|
class Lanes.Models.Asset extends Lanes.Models.Base
|
2
9
|
|
3
10
|
props:
|
4
11
|
id: 'integer'
|
5
12
|
order: 'integer'
|
6
|
-
|
7
|
-
medium: 'object'
|
8
|
-
original: 'object'
|
9
|
-
metadata: 'object'
|
13
|
+
file_data: 'object'
|
10
14
|
|
11
15
|
session:
|
12
|
-
data:
|
13
|
-
blob:
|
14
|
-
owner:
|
15
|
-
parent:
|
16
|
+
data: 'string'
|
17
|
+
blob: 'object'
|
18
|
+
owner: 'object'
|
19
|
+
parent: 'object'
|
20
|
+
metadata: 'object'
|
16
21
|
parent_association: 'string'
|
17
22
|
|
18
23
|
derived:
|
19
24
|
original_url:
|
20
|
-
deps: ['data', '
|
21
|
-
if @data then @data else @original
|
25
|
+
deps: ['data', 'file_data'], fn: ->
|
26
|
+
if @data then @data else @urlFor('original')
|
22
27
|
medium_url:
|
23
|
-
deps: ['data', '
|
24
|
-
if @data then @data else @medium
|
28
|
+
deps: ['data', 'file_data'], fn: ->
|
29
|
+
if @data then @data else @urlFor('medium')
|
25
30
|
thumbnail_url:
|
26
|
-
deps: ['data', '
|
27
|
-
if @data then @data else @thumbnail
|
31
|
+
deps: ['data', 'file_data'], fn: ->
|
32
|
+
if @data then @data else @urlFor('thumbnail')
|
28
33
|
|
29
34
|
hasImage:
|
30
|
-
deps: ['data', '
|
31
|
-
@metadata
|
35
|
+
deps: ['data', 'file_data'], fn: ->
|
36
|
+
(@data && IS_IMAGE(@metadata.content_type)) ||
|
37
|
+
(@file_data && IS_IMAGE(@file_data.original
|
38
|
+
.metadata.mime_type))
|
32
39
|
events:
|
33
40
|
'change:blob': 'onBlobChange'
|
34
|
-
'parent:save': 'onParentSave'
|
35
41
|
|
36
42
|
initialize: ->
|
37
43
|
@on('change:parent', =>
|
@@ -40,11 +46,18 @@ class Lanes.Models.Asset extends Lanes.Models.Base
|
|
40
46
|
@listenTo(@parent, 'save', @save)
|
41
47
|
)
|
42
48
|
|
49
|
+
baseUrl: ->
|
50
|
+
Lanes.config.api_path + '/asset'
|
51
|
+
|
52
|
+
urlFor: (type) ->
|
53
|
+
data = @file_data?[type]
|
54
|
+
if data then "#{@baseUrl()}/#{data.id}" else undefined
|
43
55
|
|
44
56
|
onBlobChange: ->
|
45
57
|
if @blob
|
46
|
-
@metadata
|
47
|
-
|
58
|
+
@metadata = {
|
59
|
+
content_type: @blob.type
|
60
|
+
}
|
48
61
|
reader = new FileReader()
|
49
62
|
reader.onloadend = (ev) =>
|
50
63
|
@data = reader.result if ev.type is 'loadend'
|
@@ -69,7 +82,6 @@ class Lanes.Models.Asset extends Lanes.Models.Base
|
|
69
82
|
url = Lanes.config.api_path + '/asset'
|
70
83
|
|
71
84
|
Lanes.Vendor.xhr.post(url, {body: form}, (err, resp, body) =>
|
72
|
-
@blob = null
|
73
85
|
if err
|
74
86
|
@errors = { http: err.message }
|
75
87
|
else
|
@@ -77,5 +89,6 @@ class Lanes.Models.Asset extends Lanes.Models.Base
|
|
77
89
|
if reply.errors or reply.success is false
|
78
90
|
@errors = reply.errors
|
79
91
|
else
|
92
|
+
@metadata = @blob = @data = null
|
80
93
|
@set( reply.data )
|
81
94
|
)
|
@@ -46,7 +46,7 @@ class Lanes.Models.AssociationMap
|
|
46
46
|
definition = @definitions[name]
|
47
47
|
options = { parent: model }
|
48
48
|
if definition.inverse
|
49
|
-
options[ definition.inverse
|
49
|
+
options[ definition.inverse ] = model
|
50
50
|
if definition.options
|
51
51
|
_.extend(options, Lanes.u.resultsFor(model, definition.options))
|
52
52
|
options
|
@@ -75,14 +75,12 @@ class Lanes.Models.AssociationMap
|
|
75
75
|
options = association.getOptions(name, this)
|
76
76
|
options.filter ||= {}
|
77
77
|
options.filter[fk] = this.get(pk)
|
78
|
-
options.inverse =
|
79
|
-
name: definition.inverse
|
80
|
-
without: name
|
81
78
|
|
82
79
|
if true == target_class::isCollection
|
83
80
|
new target_class(options.models || [], options)
|
84
81
|
else
|
85
82
|
options.model = target_class
|
83
|
+
options.association_name=name
|
86
84
|
new Lanes.Models.AssociationCollection(options.models || [], options)
|
87
85
|
|
88
86
|
# returns a collection for the given association.
|
@@ -181,9 +179,10 @@ class Lanes.Models.AssociationMap
|
|
181
179
|
continue if def_options.readOnly or
|
182
180
|
(options.onlyAssociations and not _.includes(options.onlyAssociations, name))
|
183
181
|
assoc = model[name]
|
182
|
+
force = _.includes(options.includeAssociations, name)
|
184
183
|
ret[name] = assoc.dataForSave(
|
185
|
-
_.extend({}, options, def_options)
|
186
|
-
) if _.result(assoc, 'isDirty')
|
184
|
+
_.extend({}, options, def_options, saveAll: force)
|
185
|
+
) if _.result(assoc, 'isDirty') or force
|
187
186
|
ret
|
188
187
|
|
189
188
|
serialize: (model, options = {depth: 1}) ->
|
@@ -18,7 +18,7 @@ class BaseModel
|
|
18
18
|
derived:
|
19
19
|
isSavable:
|
20
20
|
deps: ['invalidAttributes'], fn: ->
|
21
|
-
_.isEmpty @invalidAttributes
|
21
|
+
_.isEmpty @invalidAttributes
|
22
22
|
|
23
23
|
hasErrors:
|
24
24
|
deps: ['errors'], fn: -> not _.isEmpty(@errors)
|
@@ -105,7 +105,7 @@ class BaseModel
|
|
105
105
|
|
106
106
|
clonedAttributes: ->
|
107
107
|
attributes = @serialize()
|
108
|
-
_.extend(attributes, @getAttributes(session: true) )
|
108
|
+
_.extend(attributes, @getAttributes(session: true, derived: false) )
|
109
109
|
|
110
110
|
attributeType: (name) ->
|
111
111
|
@_definition[name].type
|
@@ -261,6 +261,9 @@ class BaseModel
|
|
261
261
|
|
262
262
|
klass::session['created_at'] ||= 'date'
|
263
263
|
klass::session['updated_at'] ||= 'date'
|
264
|
+
# these columns are included when loaded with the 'with_user_logins' scope
|
265
|
+
klass::session['created_by_user.login'] ||= 'string'
|
266
|
+
klass::session['updated_by_user.login'] ||= 'string'
|
264
267
|
|
265
268
|
if klass::associations
|
266
269
|
klass::associations = new Lanes.Models.AssociationMap(klass)
|
@@ -54,7 +54,6 @@ class ModelsCollection
|
|
54
54
|
|
55
55
|
constructor: ->
|
56
56
|
@_isLoaded = false
|
57
|
-
@errors = []
|
58
57
|
Lanes.Vendor.Ampersand.Collection.apply(this, arguments)
|
59
58
|
this.on('add remove reset', this._triggerLengthEvent )
|
60
59
|
|
@@ -103,6 +102,7 @@ class ModelsCollection
|
|
103
102
|
|
104
103
|
# Sets the attribute data from a server respose
|
105
104
|
setFromServer: (data, options, method) ->
|
105
|
+
options ||= {}
|
106
106
|
@_isLoaded = true
|
107
107
|
if 'delete' == method
|
108
108
|
models = _.map(_.map(options.originalData, 'id'), (id) =>
|
@@ -177,7 +177,11 @@ class SubCollection
|
|
177
177
|
setFromServer: (data, options, type) ->
|
178
178
|
@collection.setFromServer(data, options, type)
|
179
179
|
|
180
|
-
Lanes.Models.SubCollection = Lanes.lib.MakeBaseClass(
|
180
|
+
Lanes.Models.SubCollection = Lanes.lib.MakeBaseClass(
|
181
|
+
Lanes.Vendor.Ampersand.SubCollection.extend(Lanes.Vendor.Ampersand.LDCollection), SubCollection
|
182
|
+
)
|
183
|
+
|
184
|
+
|
181
185
|
|
182
186
|
Lanes.Models.BasicCollection = Lanes.lib.MakeBaseClass(
|
183
187
|
Lanes.Vendor.Ampersand.Collection.extend(Lanes.Vendor.Ampersand.LDCollection), BasicCollection
|
@@ -196,6 +200,10 @@ class Lanes.Models.AssociationCollection extends Lanes.Models.Collection
|
|
196
200
|
@associationFilter = @options.filter
|
197
201
|
super
|
198
202
|
|
203
|
+
mixins: [
|
204
|
+
Lanes.Models.Mixins.TrackRemovals
|
205
|
+
]
|
206
|
+
|
199
207
|
_prepareModel: (attrs, options = {}) ->
|
200
208
|
if @associationFilter
|
201
209
|
_.extend(attrs, @associationFilter)
|
@@ -209,3 +217,16 @@ class Lanes.Models.AssociationCollection extends Lanes.Models.Collection
|
|
209
217
|
_.extend(options.query, @associationFilter)
|
210
218
|
_.extend(options, @options)
|
211
219
|
super(options)
|
220
|
+
|
221
|
+
isDirty: ->
|
222
|
+
super || @hasRemovedModels()
|
223
|
+
|
224
|
+
dataForSave: ->
|
225
|
+
data = super
|
226
|
+
for id in @getRemovedModelIds()
|
227
|
+
data.push({"#{@model.prototype.idAttribute}": id, _delete: true})
|
228
|
+
data
|
229
|
+
|
230
|
+
setFromServer: (data, options, method) ->
|
231
|
+
@clearRemovedModelIds()
|
232
|
+
super
|
@@ -8,8 +8,7 @@ class ModelConfig
|
|
8
8
|
|
9
9
|
add: (model) ->
|
10
10
|
if @count is 0
|
11
|
-
Lanes.
|
12
|
-
Lanes.Models.PubSub.mb?.subscribe(@channel, @mbCallBack(@models))
|
11
|
+
Lanes.Models.PubSub.channel?.subscribe(@channel) #, @mbCallBack(@models))
|
13
12
|
@count += 1
|
14
13
|
config = @modelConfig(model)
|
15
14
|
config.count += 1
|
@@ -32,14 +31,11 @@ class ModelConfig
|
|
32
31
|
_.remove(@models, {model: model}) if config.count is 0
|
33
32
|
|
34
33
|
unsubscribe: ->
|
35
|
-
Lanes.
|
36
|
-
Lanes.Models.PubSub.mb?.unsubscribe( @channel )
|
34
|
+
Lanes.Models.PubSub.channel?.unsubscribe( @channel )
|
37
35
|
delete @type.records[@id]
|
38
36
|
|
39
|
-
|
40
|
-
(
|
41
|
-
config.model.addChangeSet(changes) for config in models
|
42
|
-
|
37
|
+
onChange: (data) ->
|
38
|
+
config.model.addChangeSet(data) for config in @models
|
43
39
|
|
44
40
|
class ModelType
|
45
41
|
|
@@ -54,6 +50,11 @@ class ModelType
|
|
54
50
|
remove: (model) ->
|
55
51
|
@records[model.id]?.remove(model)
|
56
52
|
|
53
|
+
onChange: (id, data) ->
|
54
|
+
@records[id].onChange(data)
|
55
|
+
|
56
|
+
unsubscribeAll: ->
|
57
|
+
record.unsubscribe() for id, record of @records
|
57
58
|
|
58
59
|
class ModelTypesCollection extends Lanes.Models.BasicCollection
|
59
60
|
|
@@ -65,6 +66,30 @@ class ModelTypesCollection extends Lanes.Models.BasicCollection
|
|
65
66
|
models = this.get(path) || this.add(id: path)
|
66
67
|
|
67
68
|
|
69
|
+
CableChannel = {
|
70
|
+
connected: ->
|
71
|
+
@subscribe 'file-change', ->
|
72
|
+
Lanes.lib.HotReload.initiate(changes)
|
73
|
+
|
74
|
+
subscribe: (channel) ->
|
75
|
+
Lanes.log.info "[pubsub] subscribe to: #{channel}"
|
76
|
+
|
77
|
+
@perform("on", {channel})
|
78
|
+
|
79
|
+
unsubscribe: (channel) ->
|
80
|
+
Lanes.log.info "[pubsub] unsubscribe from: #{channel}"
|
81
|
+
@perform("off", {channel})
|
82
|
+
|
83
|
+
received: (data) ->
|
84
|
+
[channel, model, id] = data.channel.match(/(.*)\/(\d+)/)
|
85
|
+
Lanes.log.info "[pubsub] change recvd for: #{channel}"
|
86
|
+
|
87
|
+
Lanes.Models.PubSub.onChange(
|
88
|
+
model, id, _.omit(data, 'channel')
|
89
|
+
)
|
90
|
+
|
91
|
+
}
|
92
|
+
|
68
93
|
Lanes.Models.PubSub = {
|
69
94
|
|
70
95
|
types: new ModelTypesCollection
|
@@ -85,9 +110,22 @@ Lanes.Models.PubSub = {
|
|
85
110
|
@types = new ModelTypesCollection
|
86
111
|
|
87
112
|
initialize: ->
|
88
|
-
|
89
|
-
@
|
90
|
-
|
91
|
-
|
92
|
-
)
|
113
|
+
Lanes.current_user.on 'change:isLoggedIn', _.bind(@onLoginChange, @)
|
114
|
+
@onLoginChange() if Lanes.current_user.isLoggedIn
|
115
|
+
|
116
|
+
onChange: (path, id , data) ->
|
117
|
+
@types.get(path).onChange(id, data)
|
118
|
+
|
119
|
+
onLoginChange: ->
|
120
|
+
if Lanes.current_user.isLoggedIn
|
121
|
+
url = window.location.protocol.replace('http', 'ws') +
|
122
|
+
"//#{Lanes.config.api_host}#{Lanes.config.api_path}/ws"
|
123
|
+
@cable = ActionCable.createConsumer(url)
|
124
|
+
@channel = @cable.subscriptions.create "Lanes::API::PubSub", CableChannel
|
125
|
+
else
|
126
|
+
Lanes.Models.PubSub.types.each (t) -> t.unsubscribeAll()
|
127
|
+
delete @channel
|
128
|
+
@cable.disconnect()
|
129
|
+
delete @cable
|
130
|
+
|
93
131
|
}
|
@@ -4,7 +4,8 @@
|
|
4
4
|
##= require ./query/CollectionResult
|
5
5
|
|
6
6
|
class Field extends Lanes.Models.Base
|
7
|
-
|
7
|
+
registerForPubSub: false
|
8
|
+
|
8
9
|
constructor: (attributes) ->
|
9
10
|
super( _.defaults( attributes, {
|
10
11
|
title: _.titleize(_.humanize(attributes.id))
|
@@ -25,6 +26,7 @@ class Field extends Lanes.Models.Base
|
|
25
26
|
sortBy: type: 'any'
|
26
27
|
component: type: 'function'
|
27
28
|
onColumnClick: type: 'function'
|
29
|
+
fetchIndex: type: 'integer'
|
28
30
|
|
29
31
|
derived:
|
30
32
|
default_value: deps: ['id'], fn: ->
|
@@ -38,12 +40,6 @@ class Field extends Lanes.Models.Base
|
|
38
40
|
deps: ['model_field'], fn: ->
|
39
41
|
type = @model_field?.type || 'string'
|
40
42
|
if type == "code" then "string" else type
|
41
|
-
fetchIndex:
|
42
|
-
fn: ->
|
43
|
-
fetchIndex = 0
|
44
|
-
for field, index in this.collection.models
|
45
|
-
return fetchIndex if this == field
|
46
|
-
fetchIndex += 1 if field.query
|
47
43
|
|
48
44
|
validValue: (value) ->
|
49
45
|
if this.type == 'n'
|
@@ -58,6 +54,7 @@ class AvailableFields extends Lanes.Models.Collection
|
|
58
54
|
|
59
55
|
constructor: (models, options) ->
|
60
56
|
@query = options.query
|
57
|
+
this.on('add remove reset', @calculateFetchIndexes)
|
61
58
|
this.on('change', (changing) ->
|
62
59
|
return unless changing.selected
|
63
60
|
for model in @models
|
@@ -65,10 +62,15 @@ class AvailableFields extends Lanes.Models.Collection
|
|
65
62
|
)
|
66
63
|
super
|
67
64
|
@visible = @subcollection(where: {visible: true})
|
65
|
+
@calculateFetchIndexes()
|
68
66
|
|
67
|
+
calculateFetchIndexes: ->
|
68
|
+
index = 0
|
69
|
+
for field in @models
|
70
|
+
field.fetchIndex = if field.query then index++ else undefined
|
69
71
|
|
70
72
|
class Operator extends Lanes.Models.Base
|
71
|
-
|
73
|
+
registerForPubSub: false
|
72
74
|
|
73
75
|
session:
|
74
76
|
id: 'string'
|
@@ -77,17 +79,14 @@ class Operator extends Lanes.Models.Base
|
|
77
79
|
field: 'object'
|
78
80
|
selected: 'boolean'
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
-
deps: ['types', 'field']
|
83
|
-
fn: ->
|
84
|
-
!this.types || ( this.field && _.includes(this.types, this.field.type) )
|
82
|
+
validForField: (field) ->
|
83
|
+
_.isEmpty(@types) or _.includes(this.types, field.type)
|
85
84
|
|
86
85
|
|
87
86
|
class Operators extends Lanes.Models.Collection
|
88
87
|
model: Operator
|
89
88
|
|
90
|
-
constructor: ->
|
89
|
+
constructor: (models, attrs) ->
|
91
90
|
super
|
92
91
|
this.add([
|
93
92
|
{ id: 'like', name: 'Starts With', types: Lanes.Models.Query.LIKE_QUERY_TYPES }
|
@@ -100,17 +99,18 @@ class Operators extends Lanes.Models.Collection
|
|
100
99
|
for model in @models
|
101
100
|
model.selected = false unless model == changing
|
102
101
|
)
|
102
|
+
@setField(attrs.parent.query.initialField)
|
103
|
+
|
103
104
|
setField: (field) ->
|
104
|
-
|
105
|
+
@field = field
|
106
|
+
@valid = @subcollection(filter: (op) => op.validForField(@field) )
|
105
107
|
selected = this.findWhere(selected: true)
|
106
|
-
|
107
|
-
|
108
|
-
this.findWhere(valid:true).selected = true
|
109
|
-
|
108
|
+
unless selected && selected.validForField(field)
|
109
|
+
@valid.at(0)?.selected = true
|
110
110
|
|
111
111
|
|
112
112
|
class Clause extends Lanes.Models.Base
|
113
|
-
|
113
|
+
registerForPubSub: false
|
114
114
|
|
115
115
|
session:
|
116
116
|
value : { type: 'string', default: '' }
|
@@ -119,9 +119,7 @@ class Clause extends Lanes.Models.Base
|
|
119
119
|
|
120
120
|
associations:
|
121
121
|
operators : { collection: Operators }
|
122
|
-
fields:
|
123
|
-
collection: AvailableFields, options: ->
|
124
|
-
query: this.collection.query
|
122
|
+
fields: { collection: AvailableFields }
|
125
123
|
|
126
124
|
derived:
|
127
125
|
description:
|
@@ -138,15 +136,12 @@ class Clause extends Lanes.Models.Base
|
|
138
136
|
constructor: (options) ->
|
139
137
|
super
|
140
138
|
this.fields.reset(options.available_fields.models)
|
141
|
-
@operators.field = @fields.first()
|
142
139
|
@fields.on('change:selected', this.setField, this)
|
143
140
|
@operators.on('change:selected', this.setOperator, this)
|
144
|
-
|
145
|
-
if field
|
141
|
+
@operator = @operators.findWhere(selected: true)
|
142
|
+
if (field = @fields.findWhere(visible: true))
|
146
143
|
field.selected = true
|
147
144
|
@setField(field)
|
148
|
-
operator = @operators.find (o) -> o.valid
|
149
|
-
operator?.selected = true
|
150
145
|
|
151
146
|
setFromView: (type, val) ->
|
152
147
|
if type == "fields" || type == "operators"
|
@@ -157,7 +152,7 @@ class Clause extends Lanes.Models.Base
|
|
157
152
|
setField: (field) ->
|
158
153
|
return unless field.selected
|
159
154
|
@operators.setField( field )
|
160
|
-
|
155
|
+
@field = field
|
161
156
|
|
162
157
|
setOperator: (operator) ->
|
163
158
|
return unless operator.selected
|
@@ -172,7 +167,7 @@ class Clause extends Lanes.Models.Base
|
|
172
167
|
value = this.get('value')
|
173
168
|
value += '%' if 'like' == op
|
174
169
|
value = parseFloat(value) if @field.type == "n"
|
175
|
-
param[
|
170
|
+
param[ @field.id ] = if 'eq' == op then value else { op: op, value: value }
|
176
171
|
param
|
177
172
|
|
178
173
|
|
@@ -185,11 +180,14 @@ class Clauses extends Lanes.Models.Collection
|
|
185
180
|
@query = options.query
|
186
181
|
@fields = options.query.fields
|
187
182
|
|
183
|
+
session:
|
184
|
+
field: 'state'
|
185
|
+
|
188
186
|
# needs to inherit from Base so network events will be listened to
|
189
187
|
class Lanes.Models.Query extends Lanes.Models.Base
|
190
|
-
|
188
|
+
registerForPubSub: false
|
191
189
|
|
192
|
-
@LIKE_QUERY_TYPES: ['string', 'code']
|
190
|
+
@LIKE_QUERY_TYPES: ['string', 'code', 'visible_id']
|
193
191
|
@LESS_THAN_QUERY_TYPES: ['integer', 'bigdec', 'number']
|
194
192
|
@GREATER_THAN_QUERY_TYPES: ['integer', 'bigdec', 'number']
|
195
193
|
|
@@ -199,8 +197,6 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
199
197
|
|
200
198
|
session:
|
201
199
|
src: 'any'
|
202
|
-
fields: 'collection'
|
203
|
-
clauses: 'collection'
|
204
200
|
initialField: 'state'
|
205
201
|
idIndex: 'number'
|
206
202
|
initialFieldIndex: 'number'
|
@@ -213,6 +209,10 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
213
209
|
changeCount: {type: 'integer', default: 0}
|
214
210
|
sortAscending: ['boolean', true, true]
|
215
211
|
|
212
|
+
associations:
|
213
|
+
fields: {collection: AvailableFields, inverse: 'query'}
|
214
|
+
clauses: {collection: Clauses, inverse: 'query'}
|
215
|
+
|
216
216
|
derived:
|
217
217
|
isCollection:
|
218
218
|
deps: ['src'], fn: -> Lanes.u.isCollection(@src)
|
@@ -238,7 +238,8 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
238
238
|
|
239
239
|
constructor: (options = {}) ->
|
240
240
|
super
|
241
|
-
|
241
|
+
|
242
|
+
@fields.reset()
|
242
243
|
for col, i in options.fields
|
243
244
|
rec = if _.isObject(col) then col else { id: col }
|
244
245
|
@fields.add rec
|
@@ -246,7 +247,7 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
246
247
|
unless @idIndex?
|
247
248
|
@idIndex = @fields.length
|
248
249
|
@fields.add(id: @idAttribute)
|
249
|
-
@clauses = new Clauses([], query: this )
|
250
|
+
# @clauses = new Clauses([], query: this )
|
250
251
|
this.listenTo(@clauses, 'change remove reset', =>
|
251
252
|
@results.reset()
|
252
253
|
@results.ensureLoaded() if this.autoRetrieve
|
@@ -259,14 +260,21 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
259
260
|
)
|
260
261
|
|
261
262
|
if @initialFieldIndex
|
262
|
-
@initialField = this.fields.at(@initialFieldIndex)
|
263
|
+
@initialField = this.fields.visible.at(@initialFieldIndex)
|
263
264
|
|
264
|
-
@initialField ||= this.fields.findWhere(id: "code") ||
|
265
|
-
this.fields.findWhere(id: "
|
266
|
-
this.fields.first()
|
265
|
+
@initialField ||= this.fields.visible.findWhere(id: "code") ||
|
266
|
+
this.fields.visible.findWhere(id: "visible_id") ||
|
267
|
+
this.fields.visible.first()
|
267
268
|
@reset(true)
|
268
269
|
this
|
269
270
|
|
271
|
+
clonedAttributes: ->
|
272
|
+
attrs = @getAttributes(session: true, derived: false)
|
273
|
+
attrs.fields = this.fields.serialize({session: true})
|
274
|
+
attrs
|
275
|
+
|
276
|
+
markModified: -> @changeCount += 1
|
277
|
+
|
270
278
|
reset: (silent = false) ->
|
271
279
|
unless @defaultSort is false
|
272
280
|
sort = @defaultSort or @fields.findWhere(visible: true).id
|
@@ -289,14 +297,17 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
289
297
|
! @clauses.findWhere( isValid: false )
|
290
298
|
|
291
299
|
loadModel: (model, options = {}) ->
|
292
|
-
_.extend(options, _.result(this, 'syncOptions'))
|
300
|
+
_.extend(options, _.result(this, 'syncOptions'), force: true)
|
293
301
|
model.withAssociations(options.include || [], options)
|
294
302
|
|
295
303
|
loadSingle: (code, options = {}) ->
|
296
304
|
options.query = {}
|
297
305
|
options.query[ @initialField.id ] = code
|
298
306
|
_.extend(options, _.result(this, 'syncOptions'))
|
299
|
-
@
|
307
|
+
@trigger('request')
|
308
|
+
@src.fetch(options).then (model) =>
|
309
|
+
@trigger('load')
|
310
|
+
model
|
300
311
|
|
301
312
|
defaultField: ->
|
302
313
|
@fields.findWhere( field: @initialField )
|
@@ -5,10 +5,11 @@ class State
|
|
5
5
|
@
|
6
6
|
|
7
7
|
_bindEvent: (eventSpec, fnName) ->
|
8
|
-
[kp,
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
[kp, events...] = eventSpec.split(' ')
|
9
|
+
if _.isEmpty(events)
|
10
|
+
@on(kp, @[fnName])
|
11
|
+
else
|
12
|
+
@listenTo(_.get(@, kp), events.join(' '), @[fnName])
|
12
13
|
|
13
14
|
|
14
15
|
isState: true
|
@@ -61,7 +61,7 @@ Lanes.Models.Sync = {
|
|
61
61
|
|
62
62
|
|
63
63
|
perform: (method, options = {}) ->
|
64
|
-
query = {}
|
64
|
+
query = options.queryParams || {}
|
65
65
|
for key, value of options
|
66
66
|
query[ this.paramsMap[key] ] = value if this.paramsMap[key]
|
67
67
|
|
@@ -74,12 +74,14 @@ Lanes.Models.Sync = {
|
|
74
74
|
|
75
75
|
# Ensure that we have a URL.
|
76
76
|
options.url or Lanes.Models.Sync.urlError()
|
77
|
-
|
77
|
+
|
78
|
+
options.url = '//' + Lanes.config.api_host + options.url + '.json'
|
79
|
+
|
78
80
|
unless _.isEmpty(query)
|
79
81
|
options.url += '?' + Lanes.lib.objToParam(query)
|
80
82
|
|
81
83
|
options.headers ||= {}
|
82
|
-
|
84
|
+
options.withCredentials = true
|
83
85
|
if Lanes.config.csrf_token
|
84
86
|
options.headers['X_CSRF_TOKEN'] = Lanes.config.csrf_token
|
85
87
|
options.contentType = "application/json"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Lanes.Models.Mixins.TrackRemovals = {
|
2
|
+
|
3
|
+
initialize: ->
|
4
|
+
@on('remove', (model) ->
|
5
|
+
@_removedModelIds ||= []
|
6
|
+
@_removedModelIds.push model.getId()
|
7
|
+
)
|
8
|
+
|
9
|
+
getRemovedModelIds: ->
|
10
|
+
@_removedModelIds || []
|
11
|
+
|
12
|
+
clearRemovedModelIds: ->
|
13
|
+
delete @_removedModelIds
|
14
|
+
|
15
|
+
hasRemovedModels: ->
|
16
|
+
!!@_removedModelIds
|
17
|
+
}
|