lanes 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/client/lanes/Config.coffee +26 -9
  3. data/client/lanes/access/screens/user-management/UserManagement.cjsx +1 -1
  4. data/client/lanes/components/grid/EditingMixin.cjsx +8 -4
  5. data/client/lanes/components/grid/PopOverMixin.cjsx +7 -1
  6. data/client/lanes/components/modal/Modal.cjsx +11 -1
  7. data/client/lanes/components/record-finder/RecordFinder.cjsx +8 -4
  8. data/client/lanes/components/shared/DateTime.cjsx +8 -6
  9. data/client/lanes/components/shared/FieldMixin.cjsx +3 -3
  10. data/client/lanes/components/shared/Icon.cjsx +1 -1
  11. data/client/lanes/components/shared/ImageAsset.cjsx +23 -11
  12. data/client/lanes/components/shared/NetworkActivityOverlay.cjsx +2 -0
  13. data/client/lanes/extension/Base.coffee +2 -0
  14. data/client/lanes/fonts/fontawesome-webfont.woff +0 -0
  15. data/client/lanes/fonts/fontawesome-webfont.woff2 +0 -0
  16. data/client/lanes/lib/RequestAssets.coffee +30 -0
  17. data/client/lanes/lib/all.js +1 -0
  18. data/client/lanes/lib/loader.js +93 -0
  19. data/client/lanes/lib/utilFunctions.coffee +12 -0
  20. data/client/lanes/models/Asset.coffee +3 -4
  21. data/client/lanes/models/AssociationMap.coffee +17 -6
  22. data/client/lanes/models/Base.coffee +20 -13
  23. data/client/lanes/models/Collection.coffee +4 -1
  24. data/client/lanes/models/PubSub.coffee +2 -3
  25. data/client/lanes/models/SmtpSettings.coffee +7 -0
  26. data/client/lanes/models/Sync.coffee +2 -2
  27. data/client/lanes/react/Viewport.coffee +14 -10
  28. data/client/lanes/react/mixins/FieldErrors.coffee +3 -4
  29. data/client/lanes/react/mixins/ReadEditingState.coffee +1 -0
  30. data/client/lanes/remote/Bootstrap.coffee +85 -0
  31. data/client/lanes/remote/api.coffee +2 -1
  32. data/client/lanes/remote/onDocumentReady.coffee +12 -0
  33. data/client/lanes/screens/Definitions.coffee +29 -12
  34. data/client/lanes/screens/SystemSettings.cjsx +12 -3
  35. data/client/lanes/styles/fonts/_bordered-pulled.scss +9 -0
  36. data/client/lanes/styles/fonts/_core.scss +1 -2
  37. data/client/lanes/styles/fonts/_icons.scss +56 -0
  38. data/client/lanes/styles/fonts/_mixins.scss +37 -4
  39. data/client/lanes/styles/fonts/_path.scss +2 -2
  40. data/client/lanes/styles/fonts/_screen-reader.scss +5 -0
  41. data/client/lanes/styles/fonts/_variables.scss +58 -2
  42. data/client/lanes/styles/fonts/font-awesome.scss +3 -1
  43. data/client/lanes/vendor/development/calendar.js +56 -57
  44. data/client/lanes/vendor/development/commons.js +31319 -29618
  45. data/client/lanes/vendor/development/data.js +8468 -7607
  46. data/client/lanes/vendor/development/helpers.js +265 -131
  47. data/client/lanes/vendor/development/toggle.js +288 -184
  48. data/client/lanes/vendor/development/ui.js +3387 -3492
  49. data/client/lanes/vendor/development/widgets.js +972 -1229
  50. data/client/lanes/vendor/production/calendar.js +60 -61
  51. data/client/lanes/vendor/production/commons.js +30695 -29032
  52. data/client/lanes/vendor/production/data.js +8457 -7598
  53. data/client/lanes/vendor/production/toggle.js +288 -184
  54. data/client/lanes/vendor/production/ui.js +3264 -3373
  55. data/client/lanes/vendor/production/widgets.js +972 -1229
  56. data/client/lanes/vendor/standalone/index.js +21106 -18761
  57. data/client/lanes/vendor/styles/toggle.scss +4 -3
  58. data/client/lanes/workspace/ScreenView.cjsx +2 -2
  59. data/client/lanes/workspace/styles/header.scss +4 -0
  60. data/config/routes.rb +0 -2
  61. data/db/migrate/01_create_system_settings.rb +1 -1
  62. data/db/migrate/02_create_assets.rb +1 -1
  63. data/lanes.gemspec +1 -0
  64. data/lib/lanes.rb +1 -0
  65. data/lib/lanes/access/track_modifications.rb +4 -2
  66. data/lib/lanes/api.rb +1 -0
  67. data/lib/lanes/api/cable.rb +11 -3
  68. data/lib/lanes/api/controller_base.rb +23 -19
  69. data/lib/lanes/api/default_routes.rb +9 -1
  70. data/lib/lanes/api/generic_controller.rb +1 -1
  71. data/lib/lanes/api/handlers/asset.rb +2 -3
  72. data/lib/lanes/api/helper_methods.rb +5 -11
  73. data/lib/lanes/api/pub_sub.rb +13 -7
  74. data/lib/lanes/api/request_wrapper.rb +1 -1
  75. data/lib/lanes/api/routing.rb +10 -7
  76. data/lib/lanes/api/to_json.rb +7 -0
  77. data/lib/lanes/asset.rb +4 -1
  78. data/lib/lanes/concerns/set_attribute_data.rb +2 -1
  79. data/lib/lanes/extension.rb +3 -1
  80. data/lib/lanes/mailer.rb +40 -0
  81. data/lib/lanes/rake_tasks.rb +4 -0
  82. data/lib/lanes/spec_helper.rb +11 -3
  83. data/lib/lanes/system_settings.rb +22 -9
  84. data/lib/lanes/version.rb +1 -1
  85. data/lib/lanes/workspace/extension.rb +5 -0
  86. data/npm-build/package.json +2 -2
  87. data/npm-build/react-toggle.js +1 -1
  88. data/npm-build/standalone.js +3 -0
  89. data/spec/command-reference-files/initial/Gemfile +1 -1
  90. data/spec/command-reference-files/initial/client/appy-app/Extension.coffee +2 -1
  91. data/spec/command-reference-files/model/db/migrate/20150218032025_create_test_tests.rb +1 -1
  92. data/spec/command-reference-files/screen/client/appy-app/Extension.coffee +2 -1
  93. data/spec/fixtures/system_settings.yml +8 -1
  94. data/spec/server/mailer_spec.rb +33 -0
  95. data/spec/server/system_settings_spec.rb +16 -0
  96. data/templates/client/Extension.coffee +2 -1
  97. data/templates/config/database.yml +1 -1
  98. data/templates/db/create_table_migration.rb +1 -1
  99. metadata +27 -6
  100. data/client/fonts/fontawesome-webfont.woff +0 -0
  101. data/client/fonts/fontawesome-webfont.woff2 +0 -0
  102. data/client/lanes/lib/loader.coffee +0 -100
  103. 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/jpg", "image/gif"
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 || !_.isEmpty(@file_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( @getClassFor(name),
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.autoCreate
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
- new Proxy( association, association.getOptions(name, @) )
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[name][fn_name]( value )
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
- if !@errors then ''
29
- else if @errors.exception then @errors.exception
30
- else _.toSentence( _.map(@errors, (value, key) ->
31
- _.titleize(_.humanize(key)) + ' ' + value
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" can be set to "value"
226
- # Returns an empty string if value, and an appropriate error message if not
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
- return '' unless @unmaskedInvalidFields and _.includes(@requiredAttributes, name) and
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
- @trigger("invalid-fields", this, @unmaskedInvalidFields)
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 @isBlank(name)
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( @invoke( 'clone' ), @options )
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 = window.location.protocol.replace('http', 'ws') +
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
@@ -0,0 +1,7 @@
1
+ class Lanes.Models.SmtpSettings extends Lanes.Models.Base
2
+
3
+ props:
4
+ server: 'string'
5
+ login: 'string'
6
+ password: 'string'
7
+ from: 'string'
@@ -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 = '//' + Lanes.config.api_host + options.url + '.json'
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['X_CSRF_TOKEN'] = Lanes.config.csrf_token
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
- domRoot: 'element'
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
- return unless @selector
31
- @domRoot = document.body.querySelector(@selector)
32
- _.dom(@domRoot).addClass('lanes-root')
33
- Lanes.fatal("Root selector #{@selector} not found") unless @domRoot
34
- _.dom(@domRoot).html = "<div class='lanes'/>"
35
- this.lanes = @domRoot.querySelector('.lanes')
36
-
37
- Lanes.lib.ResizeSensor(@domRoot, _.debounce( =>
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.domRoot.previousElementSibling)
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
- return '' unless @isEditingRecord()
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
  )
@@ -10,5 +10,6 @@ Lanes.React.Mixins.ReadEditingState = {
10
10
  editOnly: React.PropTypes.bool
11
11
 
12
12
  isEditingRecord: ->
13
+ # if updated, also change Lanes.React.Mixins.FieldErrors.fieldInvalidValueMessage
13
14
  @props.editOnly or @context.recordDisplay == 'edit'
14
15
  }
@@ -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)
@@ -1,8 +1,9 @@
1
1
  ##=require ../vendor/standalone
2
2
  ##=require ../lib/all
3
3
  ##=require ../models
4
+ ##=require ../Config
4
5
  ##=require ../extension
5
6
  ##=require ../react
6
7
  ##=require_self
7
8
 
8
- Lanes.config = {api_path: null}
9
+ # Lanes.config = {api_path: null}
@@ -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'