lanes 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/client/lanes/Config.coffee +26 -9
- data/client/lanes/access/screens/user-management/UserManagement.cjsx +1 -1
- data/client/lanes/components/grid/EditingMixin.cjsx +8 -4
- data/client/lanes/components/grid/PopOverMixin.cjsx +7 -1
- data/client/lanes/components/modal/Modal.cjsx +11 -1
- data/client/lanes/components/record-finder/RecordFinder.cjsx +8 -4
- data/client/lanes/components/shared/DateTime.cjsx +8 -6
- data/client/lanes/components/shared/FieldMixin.cjsx +3 -3
- data/client/lanes/components/shared/Icon.cjsx +1 -1
- data/client/lanes/components/shared/ImageAsset.cjsx +23 -11
- data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +2 -0
- data/client/lanes/extension/Base.coffee +2 -0
- data/client/lanes/fonts/fontawesome-webfont.woff +0 -0
- data/client/lanes/fonts/fontawesome-webfont.woff2 +0 -0
- data/client/lanes/lib/RequestAssets.coffee +30 -0
- data/client/lanes/lib/all.js +1 -0
- data/client/lanes/lib/loader.js +93 -0
- data/client/lanes/lib/utilFunctions.coffee +12 -0
- data/client/lanes/models/Asset.coffee +3 -4
- data/client/lanes/models/AssociationMap.coffee +17 -6
- data/client/lanes/models/Base.coffee +20 -13
- data/client/lanes/models/Collection.coffee +4 -1
- data/client/lanes/models/PubSub.coffee +2 -3
- data/client/lanes/models/SmtpSettings.coffee +7 -0
- data/client/lanes/models/Sync.coffee +2 -2
- data/client/lanes/react/Viewport.coffee +14 -10
- data/client/lanes/react/mixins/FieldErrors.coffee +3 -4
- data/client/lanes/react/mixins/ReadEditingState.coffee +1 -0
- data/client/lanes/remote/Bootstrap.coffee +85 -0
- data/client/lanes/remote/api.coffee +2 -1
- data/client/lanes/remote/onDocumentReady.coffee +12 -0
- data/client/lanes/screens/Definitions.coffee +29 -12
- data/client/lanes/screens/SystemSettings.cjsx +12 -3
- data/client/lanes/styles/fonts/_bordered-pulled.scss +9 -0
- data/client/lanes/styles/fonts/_core.scss +1 -2
- data/client/lanes/styles/fonts/_icons.scss +56 -0
- data/client/lanes/styles/fonts/_mixins.scss +37 -4
- data/client/lanes/styles/fonts/_path.scss +2 -2
- data/client/lanes/styles/fonts/_screen-reader.scss +5 -0
- data/client/lanes/styles/fonts/_variables.scss +58 -2
- data/client/lanes/styles/fonts/font-awesome.scss +3 -1
- data/client/lanes/vendor/development/calendar.js +56 -57
- data/client/lanes/vendor/development/commons.js +31319 -29618
- data/client/lanes/vendor/development/data.js +8468 -7607
- data/client/lanes/vendor/development/helpers.js +265 -131
- data/client/lanes/vendor/development/toggle.js +288 -184
- data/client/lanes/vendor/development/ui.js +3387 -3492
- data/client/lanes/vendor/development/widgets.js +972 -1229
- data/client/lanes/vendor/production/calendar.js +60 -61
- data/client/lanes/vendor/production/commons.js +30695 -29032
- data/client/lanes/vendor/production/data.js +8457 -7598
- data/client/lanes/vendor/production/toggle.js +288 -184
- data/client/lanes/vendor/production/ui.js +3264 -3373
- data/client/lanes/vendor/production/widgets.js +972 -1229
- data/client/lanes/vendor/standalone/index.js +21106 -18761
- data/client/lanes/vendor/styles/toggle.scss +4 -3
- data/client/lanes/workspace/ScreenView.cjsx +2 -2
- data/client/lanes/workspace/styles/header.scss +4 -0
- data/config/routes.rb +0 -2
- data/db/migrate/01_create_system_settings.rb +1 -1
- data/db/migrate/02_create_assets.rb +1 -1
- data/lanes.gemspec +1 -0
- data/lib/lanes.rb +1 -0
- data/lib/lanes/access/track_modifications.rb +4 -2
- data/lib/lanes/api.rb +1 -0
- data/lib/lanes/api/cable.rb +11 -3
- data/lib/lanes/api/controller_base.rb +23 -19
- data/lib/lanes/api/default_routes.rb +9 -1
- data/lib/lanes/api/generic_controller.rb +1 -1
- data/lib/lanes/api/handlers/asset.rb +2 -3
- data/lib/lanes/api/helper_methods.rb +5 -11
- data/lib/lanes/api/pub_sub.rb +13 -7
- data/lib/lanes/api/request_wrapper.rb +1 -1
- data/lib/lanes/api/routing.rb +10 -7
- data/lib/lanes/api/to_json.rb +7 -0
- data/lib/lanes/asset.rb +4 -1
- data/lib/lanes/concerns/set_attribute_data.rb +2 -1
- data/lib/lanes/extension.rb +3 -1
- data/lib/lanes/mailer.rb +40 -0
- data/lib/lanes/rake_tasks.rb +4 -0
- data/lib/lanes/spec_helper.rb +11 -3
- data/lib/lanes/system_settings.rb +22 -9
- data/lib/lanes/version.rb +1 -1
- data/lib/lanes/workspace/extension.rb +5 -0
- data/npm-build/package.json +2 -2
- data/npm-build/react-toggle.js +1 -1
- data/npm-build/standalone.js +3 -0
- data/spec/command-reference-files/initial/Gemfile +1 -1
- data/spec/command-reference-files/initial/client/appy-app/Extension.coffee +2 -1
- data/spec/command-reference-files/model/db/migrate/20150218032025_create_test_tests.rb +1 -1
- data/spec/command-reference-files/screen/client/appy-app/Extension.coffee +2 -1
- data/spec/fixtures/system_settings.yml +8 -1
- data/spec/server/mailer_spec.rb +33 -0
- data/spec/server/system_settings_spec.rb +16 -0
- data/templates/client/Extension.coffee +2 -1
- data/templates/config/database.yml +1 -1
- data/templates/db/create_table_migration.rb +1 -1
- metadata +27 -6
- data/client/fonts/fontawesome-webfont.woff +0 -0
- data/client/fonts/fontawesome-webfont.woff2 +0 -0
- data/client/lanes/lib/loader.coffee +0 -100
- data/client/lanes/workspace/Modal.cjsx +0 -47
@@ -15,6 +15,16 @@ distillTypes = (type, ns) ->
|
|
15
15
|
_.reduce( type.split( '.' ), ( ( memo, val ) -> return if memo then memo[ val ] else null ), ns )
|
16
16
|
|
17
17
|
Lanes.u = {
|
18
|
+
|
19
|
+
utcToLocalDate: (val) ->
|
20
|
+
new Date(val.getTime() - (val.getTimezoneOffset() * 60000))
|
21
|
+
|
22
|
+
dateToUTC: (val) ->
|
23
|
+
new Date(
|
24
|
+
val.getUTCFullYear(), val.getUTCMonth(), val.getUTCDate(),
|
25
|
+
val.getUTCHours(), val.getUTCMinutes(), val.getUTCSeconds()
|
26
|
+
)
|
27
|
+
|
18
28
|
cleanBsSizes: (props) ->
|
19
29
|
_.omit(props, @getBsSizeProps() )
|
20
30
|
|
@@ -168,6 +178,8 @@ _.mixin({
|
|
168
178
|
false
|
169
179
|
when _.isNumber(value)
|
170
180
|
!value
|
181
|
+
when _.isObject(value)
|
182
|
+
_.keys(value).length is 0
|
171
183
|
else
|
172
184
|
_.isEmpty(value)
|
173
185
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
IMAGES = [
|
2
|
-
"image/png", "image/
|
2
|
+
"image/png", "image/jpeg", "image/gif"
|
3
3
|
]
|
4
4
|
|
5
5
|
IS_IMAGE = (content_type) ->
|
@@ -33,13 +33,12 @@ class Lanes.Models.Asset extends Lanes.Models.Base
|
|
33
33
|
|
34
34
|
isPresent:
|
35
35
|
deps: ['data', 'file_data'], fn: ->
|
36
|
-
@data || !_.
|
36
|
+
@data || !_.isBlank(@file_data)
|
37
37
|
|
38
38
|
hasImage:
|
39
39
|
deps: ['data', 'file_data'], fn: ->
|
40
40
|
(@data && IS_IMAGE(@metadata.content_type)) ||
|
41
|
-
(@file_data && IS_IMAGE(@file_data.original
|
42
|
-
.metadata.mime_type))
|
41
|
+
(@file_data && IS_IMAGE(@file_data.original?.metadata.mime_type))
|
43
42
|
events:
|
44
43
|
'change:blob': 'onBlobChange'
|
45
44
|
|
@@ -22,8 +22,10 @@ class Lanes.Models.AssociationMap
|
|
22
22
|
Lanes.u.findObject(object, 'Models', @klass::FILE)
|
23
23
|
|
24
24
|
getProxyFor: (name) ->
|
25
|
+
klass = @getClassFor(name)
|
26
|
+
return null unless klass
|
25
27
|
@proxy[name] ||= (
|
26
|
-
Lanes.Models.AssocationProxy.construct(
|
28
|
+
Lanes.Models.AssocationProxy.construct( klass,
|
27
29
|
association_pk: @pk(name)
|
28
30
|
association_name: name
|
29
31
|
)
|
@@ -35,17 +37,20 @@ class Lanes.Models.AssociationMap
|
|
35
37
|
model._cache[name] = new Proxy( this, this.getOptions(name, model) )
|
36
38
|
|
37
39
|
replace: (parent, name, model) ->
|
40
|
+
parent._changing = true
|
41
|
+
parent._previousAttributes[name] = parent[name]
|
38
42
|
parent._cache[name] = model
|
39
43
|
model.parent = parent if model.hasAttribute?('parent') or model.parent?
|
40
44
|
model.parent_association = name if model.hasAttribute?('parent_association') or model.parent_association?
|
41
|
-
|
42
45
|
parent.trigger("change", parent, {})
|
43
46
|
parent.trigger("change:#{name}", model, {})
|
47
|
+
parent._changing = false
|
44
48
|
|
45
49
|
getOptions: (name, model) ->
|
46
50
|
definition = @definitions[name]
|
47
51
|
options = { parent: model }
|
48
52
|
if definition.inverse
|
53
|
+
options.inverse = { name: definition.inverse, value: model }
|
49
54
|
options[ definition.inverse ] = model
|
50
55
|
if definition.options
|
51
56
|
_.extend(options, Lanes.u.resultsFor(model, definition.options))
|
@@ -53,7 +58,7 @@ class Lanes.Models.AssociationMap
|
|
53
58
|
|
54
59
|
# will be called in the scope of the parent model
|
55
60
|
createModel: (association, name, definition, fk, pk, target_class) ->
|
56
|
-
if definition.
|
61
|
+
if definition.required
|
57
62
|
target_class ||= association.getClassFor(name)
|
58
63
|
options = association.getOptions(name, this)
|
59
64
|
model_id = this.get(pk)
|
@@ -67,11 +72,15 @@ class Lanes.Models.AssociationMap
|
|
67
72
|
existing
|
68
73
|
else
|
69
74
|
Proxy = association.getProxyFor(name)
|
70
|
-
|
75
|
+
if Proxy
|
76
|
+
new Proxy( association, association.getOptions(name, @) )
|
77
|
+
else
|
78
|
+
null
|
71
79
|
|
72
80
|
# will be called in the scope of the parent model
|
73
81
|
createCollection: (association, name, definition, fk, pk, target_class) ->
|
74
82
|
target_class ||= association.getClassFor(name)
|
83
|
+
return null unless target_class
|
75
84
|
options = association.getOptions(name, this)
|
76
85
|
options.filter ||= {}
|
77
86
|
options.filter[fk] = this.get(pk)
|
@@ -82,6 +91,8 @@ class Lanes.Models.AssociationMap
|
|
82
91
|
options.model = target_class
|
83
92
|
options.association_name = name
|
84
93
|
klass = options.collectionClass or Lanes.Models.AssociationCollection
|
94
|
+
if _.isString(klass)
|
95
|
+
klass = Lanes.u.findObject(klass, 'Models', association.klass::FILE)
|
85
96
|
new klass(options.models || [], options)
|
86
97
|
|
87
98
|
# returns a collection for the given association.
|
@@ -152,12 +163,12 @@ class Lanes.Models.AssociationMap
|
|
152
163
|
@_setModel(model, name, value, options, fn_name)
|
153
164
|
|
154
165
|
_setModel: (model, name, value, options, fn_name) ->
|
155
|
-
model.set(this.pk(name), value.id, options) if value.id
|
156
166
|
|
157
167
|
if Lanes.u.isModel(value)
|
158
168
|
@replace(model, name, value)
|
159
169
|
else
|
160
|
-
model
|
170
|
+
model.set(this.pk(name), value.id, options) if value.id
|
171
|
+
model[name]?[fn_name]( value )
|
161
172
|
if options?.silent isnt true
|
162
173
|
model.trigger("change:#{name}", value, {})
|
163
174
|
|
@@ -25,11 +25,13 @@ class BaseModel
|
|
25
25
|
|
26
26
|
errorMessage:
|
27
27
|
deps:['errors'], fn: ->
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
return '' unless @hasErrors
|
29
|
+
@errors.exception or (
|
30
|
+
_.toSentence( _.map(@errors, (value, key) ->
|
31
|
+
_.titleize(_.humanize(key.replace('.', ' '))) + ' ' + value
|
32
|
+
))
|
33
|
+
)
|
34
|
+
|
33
35
|
constructor: (attrs, options = {}) ->
|
34
36
|
super
|
35
37
|
@on('change', @_calculateInvalidAttributes)
|
@@ -222,16 +224,18 @@ class BaseModel
|
|
222
224
|
sync: (options...) ->
|
223
225
|
Lanes.Models.Sync.state(options...)
|
224
226
|
|
225
|
-
# Check if an attribute named "name"
|
226
|
-
# Returns an empty string if
|
227
|
+
# Check if an attribute named "name" is invalid
|
228
|
+
# Returns an empty string if valid, and an appropriate error message if not
|
227
229
|
invalidMessageFor: (name) ->
|
228
|
-
|
229
|
-
_.includes(@unmaskedInvalidFields, name)
|
230
|
-
|
231
|
-
if @isBlank(name)
|
230
|
+
if @shouldCheckFieldValidity(name) and @isBlank(name)
|
232
231
|
"Cannot be empty"
|
233
232
|
else
|
234
233
|
''
|
234
|
+
shouldCheckFieldValidity: (name) ->
|
235
|
+
@unmaskedInvalidFields and
|
236
|
+
_.includes(@requiredAttributes, name) and
|
237
|
+
(@unmaskedInvalidFields is 'all' or
|
238
|
+
_.includes(@unmaskedInvalidFields, name))
|
235
239
|
|
236
240
|
maskInvalidFields: ->
|
237
241
|
delete @unmaskedInvalidFields
|
@@ -239,17 +243,20 @@ class BaseModel
|
|
239
243
|
unmaskInvalidField: (attr) ->
|
240
244
|
if attr is 'all'
|
241
245
|
@unmaskedInvalidFields = 'all'
|
242
|
-
@
|
246
|
+
@_calculateInvalidAttributes()
|
247
|
+
unless _.isEmpty(@invalidAttributes)
|
248
|
+
@trigger("invalid-fields", this, @invalidAttributes)
|
243
249
|
else if @unmaskedInvalidFields isnt 'all'
|
244
250
|
@unmaskedInvalidFields ||= []
|
245
251
|
if _.includes(@requiredAttributes, attr) and !_.includes(@unmaskedInvalidFields, attr)
|
246
252
|
@unmaskedInvalidFields.push(attr)
|
253
|
+
|
247
254
|
@trigger("invalid-field:#{attr}", this)
|
248
255
|
|
249
256
|
_calculateInvalidAttributes: ->
|
250
257
|
invalid = []
|
251
258
|
for name in @requiredAttributes
|
252
|
-
invalid.push(name) if @
|
259
|
+
invalid.push(name) if @invalidMessageFor(name)
|
253
260
|
@invalidAttributes = invalid
|
254
261
|
|
255
262
|
# When the model is extended it auto-creates the created_at and updated_at
|
@@ -13,6 +13,9 @@ CommonMethods = {
|
|
13
13
|
pick: (keys...) ->
|
14
14
|
_.map(@models, _.partialRight(_.pick, keys...))
|
15
15
|
|
16
|
+
unSaved: ->
|
17
|
+
@filter (pymnt) -> pymnt.isNew()
|
18
|
+
|
16
19
|
sum: ->
|
17
20
|
args = _.toArray(arguments)
|
18
21
|
args.unshift(this.models)
|
@@ -35,7 +38,7 @@ CommonMethods = {
|
|
35
38
|
)
|
36
39
|
|
37
40
|
clone: ->
|
38
|
-
new @constructor( @
|
41
|
+
new @constructor( @each( 'clone' ), @options )
|
39
42
|
|
40
43
|
serialize: (options = {depth: 1}) ->
|
41
44
|
depth = options.depth
|
@@ -81,7 +81,7 @@ CableChannel = {
|
|
81
81
|
@perform("off", {channel})
|
82
82
|
|
83
83
|
received: (data) ->
|
84
|
-
[channel, model, id] = data.channel.match(
|
84
|
+
[channel, prefix, model, id] = data.channel.match(/^(.*)\:(.*)\/([^\/]+)$/)
|
85
85
|
Lanes.log.info "[pubsub] change recvd for: #{channel}"
|
86
86
|
Lanes.Models.PubSub.onChange(
|
87
87
|
model, id, _.omit(data, 'channel')
|
@@ -117,8 +117,7 @@ Lanes.Models.PubSub = {
|
|
117
117
|
|
118
118
|
onLoginChange: ->
|
119
119
|
if Lanes.current_user.isLoggedIn
|
120
|
-
url =
|
121
|
-
"//#{Lanes.config.api_host}#{Lanes.config.api_path}/ws"
|
120
|
+
url = "#{Lanes.config.api_host}#{Lanes.config.api_path}/ws"
|
122
121
|
@cable = ActionCable.createConsumer(url)
|
123
122
|
@channel = @cable.subscriptions.create "Lanes::API::PubSub", CableChannel
|
124
123
|
else
|
@@ -75,7 +75,7 @@ Lanes.Models.Sync = {
|
|
75
75
|
# Ensure that we have a URL.
|
76
76
|
options.url or Lanes.Models.Sync.urlError()
|
77
77
|
|
78
|
-
options.url =
|
78
|
+
options.url = Lanes.config.api_host + options.url + '.json'
|
79
79
|
|
80
80
|
unless _.isEmpty(query)
|
81
81
|
options.url += '?' + Lanes.lib.objToParam(query)
|
@@ -83,7 +83,7 @@ Lanes.Models.Sync = {
|
|
83
83
|
options.headers ||= {}
|
84
84
|
options.withCredentials = true
|
85
85
|
if Lanes.config.csrf_token
|
86
|
-
options.headers['
|
86
|
+
options.headers['X-CSRF-TOKEN'] = Lanes.config.csrf_token
|
87
87
|
options.contentType = "application/json"
|
88
88
|
|
89
89
|
new _.Promise( (resolve, reject) ->
|
@@ -13,7 +13,7 @@ class Lanes.React.Viewport extends Lanes.Models.State
|
|
13
13
|
height: 'number'
|
14
14
|
el: 'element'
|
15
15
|
selector: 'string'
|
16
|
-
|
16
|
+
container: 'element'
|
17
17
|
reactRoot: 'object'
|
18
18
|
lanes: 'element'
|
19
19
|
modalProps: 'object'
|
@@ -27,14 +27,17 @@ class Lanes.React.Viewport extends Lanes.Models.State
|
|
27
27
|
constructor: ->
|
28
28
|
super
|
29
29
|
ALL_INSTANCES.push(this)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
unless (@container or (@selector and
|
31
|
+
(@container = document.body.querySelector(@selector))))
|
32
|
+
console.warn("Unable to render without container or valid selector")
|
33
|
+
return
|
34
|
+
|
35
|
+
_.dom(@container).addClass('lanes-root')
|
36
|
+
Lanes.fatal("Root selector #{@selector} not found") unless @container
|
37
|
+
_.dom(@container).html = "<div class='lanes'/>"
|
38
|
+
this.lanes = @container.querySelector('.lanes')
|
39
|
+
|
40
|
+
Lanes.lib.ResizeSensor(@container, _.debounce( =>
|
38
41
|
@_updateDimensions()
|
39
42
|
, 250))
|
40
43
|
this._updateDimensions()
|
@@ -47,7 +50,7 @@ class Lanes.React.Viewport extends Lanes.Models.State
|
|
47
50
|
|
48
51
|
|
49
52
|
onBoot: ->
|
50
|
-
prev = _.dom(this.
|
53
|
+
prev = _.dom(this.container.previousElementSibling)
|
51
54
|
if @useHistory
|
52
55
|
@initializeHistory()
|
53
56
|
else
|
@@ -81,6 +84,7 @@ class Lanes.React.Viewport extends Lanes.Models.State
|
|
81
84
|
close: ->
|
82
85
|
@hideModal()
|
83
86
|
@_historyStopListening?()
|
87
|
+
@container.remove()
|
84
88
|
|
85
89
|
_updateDimensions: ->
|
86
90
|
this.set
|
@@ -1,8 +1,5 @@
|
|
1
1
|
Lanes.React.Mixins.FieldErrors = {
|
2
2
|
|
3
|
-
mixins: [
|
4
|
-
Lanes.React.Mixins.ReadEditingState
|
5
|
-
]
|
6
3
|
|
7
4
|
componentWillMount: ->
|
8
5
|
@getInvalidModel()?.maskInvalidFields?()
|
@@ -17,7 +14,9 @@ Lanes.React.Mixins.FieldErrors = {
|
|
17
14
|
!!@fieldInvalidValueMessage()
|
18
15
|
|
19
16
|
fieldInvalidValueMessage: ->
|
20
|
-
|
17
|
+
# check needs to stay in sync with Lanes.React.Mixins.ReadEditingState.isEditingRecord
|
18
|
+
return '' unless @props.editOnly or @context.recordDisplay == 'edit'
|
19
|
+
|
21
20
|
@getInvalidModel()?.invalidMessageFor?(
|
22
21
|
@getInvalidFieldName()
|
23
22
|
)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
##=require lanes/remote/onDocumentReady
|
2
|
+
##=require lanes/lib/loader
|
3
|
+
##=require_self
|
4
|
+
|
5
|
+
LOADING = 1
|
6
|
+
ERROR = -1
|
7
|
+
COMPLETE = 99
|
8
|
+
|
9
|
+
ASSETS_PREFIX = '/assets/'
|
10
|
+
|
11
|
+
class Bootstrapper
|
12
|
+
|
13
|
+
constructor:(@options) ->
|
14
|
+
@srcTagSubstrIndx = @options.srcTag.length * -1
|
15
|
+
@callbacks = { onComplete: [] }
|
16
|
+
@onComplete(@options.onComplete) if @options.onComplete
|
17
|
+
|
18
|
+
Lanes.Remote.onDocumentReady =>
|
19
|
+
@readLoadUrl()
|
20
|
+
|
21
|
+
tagMatchesSuffix: (tag) ->
|
22
|
+
tag.src.substr(@srcTagSubstrIndx) == @options.srcTag
|
23
|
+
|
24
|
+
readLoadUrl: ->
|
25
|
+
for tag in document.querySelectorAll('script') when @tagMatchesSuffix(tag)
|
26
|
+
@api_host = tag.src.replace(/\/assets\/.*/, '')
|
27
|
+
break
|
28
|
+
if @api_host
|
29
|
+
@requestFullAssets()
|
30
|
+
else
|
31
|
+
@state = ERROR
|
32
|
+
console.error("Unable to find script tag that Stockor was loaded from")
|
33
|
+
|
34
|
+
srcFor: (type) ->
|
35
|
+
@api_host + ASSETS_PREFIX + @options.scripts[type]
|
36
|
+
|
37
|
+
requestFullAssets: ->
|
38
|
+
@pending = {}
|
39
|
+
if @options.scripts.js
|
40
|
+
@pending.js = Lanes.lib.loader.js(@srcFor('js'), (ev) =>
|
41
|
+
@onLoadComplete(ev)
|
42
|
+
)
|
43
|
+
if @options.scripts.css
|
44
|
+
@pending.css = Lanes.lib.loader.css(@srcFor('css'), (ev) =>
|
45
|
+
@onLoadComplete(ev)
|
46
|
+
)
|
47
|
+
@setLoadingStatus()
|
48
|
+
|
49
|
+
setLoadingStatus: ->
|
50
|
+
if Object.keys(@pending).length
|
51
|
+
@state = LOADING
|
52
|
+
else
|
53
|
+
@state = COMPLETE
|
54
|
+
Lanes.config.api_host = @api_host
|
55
|
+
for cb in @callbacks.onComplete
|
56
|
+
cb(@)
|
57
|
+
|
58
|
+
onComplete: (cb) ->
|
59
|
+
@callbacks.onComplete.push(cb)
|
60
|
+
|
61
|
+
onLoadComplete: (tag) ->
|
62
|
+
switch tag.tagName
|
63
|
+
when 'LINK' then delete @pending.css
|
64
|
+
when 'SCRIPT' then delete @pending.js
|
65
|
+
else
|
66
|
+
console.warn "Complete called for unknown tag type #{tag.tagName}"
|
67
|
+
@setLoadingStatus()
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
Object.defineProperties(Bootstrapper.prototype, {
|
72
|
+
|
73
|
+
isComplete:
|
74
|
+
get: -> @state == COMPLETE
|
75
|
+
isPending:
|
76
|
+
get: -> @state isnt COMPLETE and @state isnt ERROR
|
77
|
+
hasFailed:
|
78
|
+
get: -> @state isnt ERROR
|
79
|
+
|
80
|
+
})
|
81
|
+
|
82
|
+
Lanes.namespace('Bootstrap')
|
83
|
+
|
84
|
+
Lanes.Remote.Bootstrap = (srcTag, options) ->
|
85
|
+
new Bootstrapper(srcTag, options)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
##=require lanes/lib/namespace
|
2
|
+
|
3
|
+
Lanes.namespace('Remote')
|
4
|
+
|
5
|
+
Lanes.Remote.onDocumentReady = (fn) ->
|
6
|
+
if document.readyState isnt 'loading'
|
7
|
+
fn()
|
8
|
+
else if document.addEventListener
|
9
|
+
document.addEventListener 'DOMContentLoaded', fn
|
10
|
+
else
|
11
|
+
document.attachEvent 'onreadystatechange', ->
|
12
|
+
fn() if document.readyState != 'loading'
|