statebus 6.1.22 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  var websocket_prefix = (clientjs_option('websocket_path')
3
3
  || '_connect_to_statebus_')
4
4
 
5
+ // Todo: remove this global
5
6
  window.dom = window.dom || new Proxy({}, {
6
7
  get: function (o, k) { return o[k] },
7
8
  set: function (o, k, v) {
@@ -42,7 +43,7 @@
42
43
  // return new SockJS(url + '/' + websocket_prefix)
43
44
  }
44
45
  function client_creds (server_url) {
45
- var me = bus.fetch('ls/me')
46
+ var me = bus.get('ls/me')
46
47
  bus.log('connect: me is', me)
47
48
  if (!me.client) {
48
49
  // Create a client id if we have none yet.
@@ -51,19 +52,20 @@
51
52
  me.client = c || (Math.random().toString(36).substring(2)
52
53
  + Math.random().toString(36).substring(2)
53
54
  + Math.random().toString(36).substring(2))
54
- bus.save(me)
55
+ bus.set(me)
55
56
  }
56
57
 
57
58
  set_cookie('client', me.client)
58
59
  return {clientid: me.client}
59
60
  }
60
61
 
61
- function braid_http_mount (prefix, url) {
62
+ function http_mount (prefix, url) {
62
63
  var preprefix = prefix.slice(0,-1)
63
64
  var has_prefix = new RegExp('^' + preprefix)
64
65
  var is_absolute = /^https?:\/\//
65
66
  var subscriptions = {}
66
- //if (url[url.length-1]=='/') url = url.substr(0,url.length-1)
67
+ var put_counter = 0
68
+
67
69
  function add_prefix (key) {
68
70
  return is_absolute.test(key) ? key : preprefix + key }
69
71
  function rem_prefix (key) {
@@ -73,24 +75,66 @@
73
75
  function rem_prefixes (obj) {
74
76
  return bus.translate_keys(bus.clone(obj), rem_prefix) }
75
77
 
76
- bus(prefix).to_save = function (obj, t) {
77
- bus.save.fire(obj)
78
+ var puts = new Map()
79
+ function enqueue_put (url, body) {
80
+ var id = put_counter++
81
+ puts.set(id, {url: url, body: body, id: id})
82
+ send_put(id)
83
+ }
84
+ function send_put (id) {
85
+ try {
86
+ puts.get(id).status = 'sending'
87
+ braid_fetch(
88
+ puts.get(id).url,
89
+ {
90
+ method: 'put',
91
+ headers: {
92
+ 'content-type': 'application/json',
93
+ 'put-order': id,
94
+ },
95
+ body: puts.get(id).body
96
+ }
97
+ ).then(function (res) {
98
+ if (res.status !== 200)
99
+ console.error('Server gave error on PUT:',
100
+ e, 'for', puts.get(id).body)
101
+ puts.delete(id)
102
+ }).catch(function (e) {
103
+ console.error('Error on PUT, waiting...', puts.get(id).url)
104
+ puts.get(id).status = 'waiting'
105
+ })
106
+ } catch (e) {
107
+ console.error('Error on PUT, waiting...', puts.get(id).url)
108
+ puts.get(id).status = 'waiting'
109
+ }
110
+ }
111
+ function retry_put (id) {
112
+ setTimeout(function () {send_put(id)}, 1000)
113
+ }
114
+ function send_all_puts () {
115
+ for (var id of puts.keys())
116
+ if (puts.get(id).status === 'waiting') {
117
+ console.log('Sending waiting put', id)
118
+ send_put(id)
119
+ }
120
+ }
78
121
 
79
- // var x = {save: obj}
80
- // if (t.version) x.version = t.version
81
- // if (t.parents) x.parents = t.parents
82
- // if (t.patch) x.patch = t.patch
83
- // if (t.patch) x.save = rem_prefix(x.save.key)
122
+ bus(prefix).to_set = function (obj, t) {
123
+ bus.set.fire(obj)
84
124
 
85
- braid_fetch(url + rem_prefix(obj.key),
86
- {
87
- method: 'put',
88
- 'content-type': 'application/json',
89
- body: JSON.stringify(obj.val)
90
- })
125
+ var put = {
126
+ url: url + rem_prefix(obj.key),
127
+ body: JSON.stringify(obj.val)
128
+ }
129
+ if (t.version) put.version = t.version
130
+ if (t.parents) put.parents = t.parents
131
+ if (t.patch) put.patch = t.patch
132
+ var put_id = put_counter++
133
+ puts.set(put_id, put)
134
+ send_put(put_id)
91
135
  }
92
136
 
93
- bus(prefix).to_fetch = function (key, t) {
137
+ bus(prefix).to_get = function (key, t) {
94
138
  // Subscription can be in states:
95
139
  // - connecting
96
140
  // - connected
@@ -98,13 +142,13 @@
98
142
  // - reconnecting
99
143
  // - aborted
100
144
 
101
- // If we have an outstanding fetch running, then let's tell it to
145
+ // If we have an outstanding get running, then let's tell it to
102
146
  // re-activate!
103
147
  if (subscriptions[key]) {
104
148
  // We should only be here if an existing subscription was
105
149
  // aborted, but hasn't cleared yet.
106
150
  console.assert(subscriptions[key].status === 'aborted',
107
- 'Refetching a subscription of status '
151
+ 'Regetting a subscription of status '
108
152
  + subscriptions[key].status)
109
153
 
110
154
  console.trace('foo')
@@ -140,6 +184,7 @@
140
184
  'color: blue')
141
185
  reconnect_attempts = 0
142
186
  subscriptions[key].status = 'connected'
187
+ send_all_puts()
143
188
  }
144
189
 
145
190
  // Return the update
@@ -149,7 +194,7 @@
149
194
  })
150
195
  }).catch( function (e) {
151
196
  if (subscriptions[key].status === 'aborted') {
152
- // Then this fetch is over and done with!
197
+ // Then this get is over and done with!
153
198
  delete subscriptions[key]
154
199
  return
155
200
  }
@@ -185,31 +230,31 @@
185
230
  var bus = this
186
231
  bus.log(this)
187
232
 
188
- // Fetch returns the value immediately in a save
189
- // Saves are queued up, to store values with a delay, in batch
190
- var saves_are_pending = false
191
- var pending_saves = {}
233
+ // Get returns the value immediately in a set
234
+ // Sets are queued up, to store values with a delay, in batch
235
+ var sets_are_pending = false
236
+ var pending_sets = {}
192
237
 
193
- function save_the_pending_saves() {
194
- bus.log('localstore: saving', pending_saves)
195
- for (var k in pending_saves)
196
- localStorage.setItem(k, JSON.stringify(pending_saves[k]))
197
- saves_are_pending = false
238
+ function set_the_pending_sets() {
239
+ bus.log('localstore: saving', pending_sets)
240
+ for (var k in pending_sets)
241
+ localStorage.setItem(k, JSON.stringify(pending_sets[k]))
242
+ sets_are_pending = false
198
243
  }
199
244
 
200
- bus(prefix).to_fetch = function (key) {
245
+ bus(prefix).to_get = function (key) {
201
246
  var result = localStorage.getItem(key)
202
247
  return result ? JSON.parse(result) : {key: key}
203
248
  }
204
- bus(prefix).to_save = function (obj) {
249
+ bus(prefix).to_set = function (obj) {
205
250
  // Do I need to make this recurse into the object?
206
- bus.log('localStore: on_save:', obj.key)
207
- pending_saves[obj.key] = obj
208
- if (!saves_are_pending) {
209
- setTimeout(save_the_pending_saves, 50)
210
- saves_are_pending = true
251
+ bus.log('localStore: on_set:', obj.key)
252
+ pending_sets[obj.key] = obj
253
+ if (!sets_are_pending) {
254
+ setTimeout(set_the_pending_sets, 50)
255
+ sets_are_pending = true
211
256
  }
212
- bus.save.fire(obj)
257
+ bus.set.fire(obj)
213
258
  return obj
214
259
  }
215
260
  bus(prefix).to_delete = function (key) { localStorage.removeItem(key) }
@@ -218,6 +263,7 @@
218
263
  // Hm... this update stuff doesn't seem to work on file:/// urls in chrome
219
264
  function update (event) {
220
265
  bus.log('Got a localstorage update', event)
266
+ bus.dirty(event.key)
221
267
  //this.get(event.key.substr('statebus '.length))
222
268
  }
223
269
  if (window.addEventListener) window.addEventListener("storage", update, false)
@@ -242,39 +288,16 @@
242
288
  data = (data && JSON.parse(data)) || {key : key}
243
289
  // Then I would need to:
244
290
  // - Change the key prefix
245
- // - Save this into the cache
291
+ // - Set this into the cache
246
292
 
247
- bus(prefix).to_save = function (obj) {
293
+ bus(prefix).to_set = function (obj) {
248
294
  window.history.replaceState(
249
295
  '',
250
296
  '',
251
297
  document.location.origin
252
298
  + document.location.pathname
253
299
  + escape('?'+key+'='+JSON.stringify(obj)))
254
- bus.save.fire(obj)
255
- }
256
- }
257
-
258
- function live_reload_from (prefix) {
259
- if (!window.live_reload_initialized) {
260
- var first_time = true
261
- this(function () {
262
- var re = new RegExp(".*/" + prefix + "/(.*)")
263
- var file = window.location.href.match(re)[1]
264
- var code = bus.fetch('/code/invisible.college/' + file).code
265
- if (!code) return
266
- if (first_time) {first_time = false; return}
267
- var old_scroll_position = window.pageYOffset
268
- document.body.innerHTML = code
269
- var i = 0
270
- var d = 100
271
- var interval = setInterval(function () {
272
- if (i > 500) clearInterval(interval)
273
- i += d
274
- window.scrollTo(0, old_scroll_position)
275
- }, d)
276
- })
277
- window.live_reload_initialized = true
300
+ bus.set.fire(obj)
278
301
  }
279
302
  }
280
303
 
@@ -282,30 +305,40 @@
282
305
  // ****************
283
306
  // Wrapper for React Components
284
307
 
308
+ function react_version () {
309
+ if (!window.React) return undefined;
310
+ return Number(window.React.version.split('.')[0])
311
+ }
312
+
313
+ // Newer React requires createReactClass as a separate libary
314
+ if (window.React && !React.createClass && window.createReactClass)
315
+ React.createClass = createReactClass
316
+
285
317
  // XXX Currently assumes there's a statebus named "bus" in global
286
318
  // XXX scope.
287
319
 
288
320
  var components = {} // Indexed by 'component/0', 'component/1', etc.
289
321
  var components_count = 0
290
322
  var dirty_components = {}
291
- function React_View(component) {
323
+ function create_react_class(component) {
292
324
  function wrap(name, new_func) {
293
325
  var old_func = component[name]
294
326
  component[name] = function wrapper () { return new_func.bind(this)(old_func) }
295
327
  }
296
328
 
297
329
  // Register the component's basic info
298
- wrap('componentWillMount', function new_cwm (orig_func) {
299
- if (component.displayName === undefined)
300
- throw 'Component needs a displayName'
301
- this.name = component.displayName.toLowerCase().replace(' ', '_')
330
+ wrap((react_version() >= 16 ? 'UNSAFE_' : '') + 'componentWillMount',
331
+ function new_cwm (orig_func) {
332
+ // if (component.displayName === undefined)
333
+ // throw 'Component needs a displayName'
334
+ //this.name = component.displayName.toLowerCase().replace(' ', '_')
302
335
  this.key = 'component/' + components_count++
303
336
  components[this.key] = this
304
337
 
305
338
  function add_shortcut (obj, shortcut_name, to_key) {
306
339
  delete obj[shortcut_name]
307
340
  Object.defineProperty(obj, shortcut_name, {
308
- get: function () { return bus.fetch(to_key) },
341
+ get: function () { return bus.get(to_key) },
309
342
  configurable: true })
310
343
  }
311
344
  add_shortcut(this, 'local', this.key)
@@ -326,7 +359,7 @@
326
359
  && typeof this.props[k] === 'object'
327
360
  && this.props[k].key)
328
361
 
329
- bus.fetch(this.props[k].key)
362
+ bus.get(this.props[k].key)
330
363
 
331
364
  // Call the renderer!
332
365
  return orig_render.apply(this, arguments)
@@ -397,17 +430,12 @@
397
430
  props['data-key'] = props.key
398
431
  props['data-widget'] = component.displayName
399
432
 
400
- return (React.version >= '0.12.'
401
- ? React.createElement(react_class, props, children)
402
- : react_class(props, children))
433
+ return React.createElement(react_class, props, children)
403
434
  }
404
- // Give it the same prototype as the original class so that it
405
- // passes React.isValidClass() inspection
406
- result.prototype = react_class.prototype
435
+ Object.defineProperty(result, 'name',
436
+ {value: component.displayName, writable: false})
407
437
  return result
408
438
  }
409
- window.React_View = React_View
410
- if (window.statebus) window.statebus.create_react_class = window.statebus.createReactClass = React_View
411
439
 
412
440
  // *****************
413
441
  // Re-rendering react components
@@ -442,7 +470,7 @@
442
470
 
443
471
  function make_client_statebus_maker () {
444
472
  var extra_stuff = ['localstorage_client make_websocket client_creds',
445
- 'url_store components live_reload_from'].join(' ').split(' ')
473
+ 'url_store components'].join(' ').split(' ')
446
474
  if (window.statebus) {
447
475
  var orig_statebus = statebus
448
476
  window.statebus = function make_client_bus () {
@@ -463,17 +491,17 @@
463
491
  if (statebus_dir) statebus_dir = statebus_dir[1] + '/'
464
492
  else statebus_dir = ''
465
493
 
466
- var js_urls = {
467
- react: statebus_dir + 'extras/react.js',
468
- sockjs: statebus_dir + 'extras/sockjs.js',
469
- coffee: statebus_dir + 'extras/coffee.js',
470
- statebus: statebus_dir + 'statebus.js'
471
- }
472
- if (statebus_dir == 'https://stateb.us/')
473
- js_urls.statebus = statebus_dir + 'statebus4.js'
494
+ // var js_urls = {
495
+ // react: statebus_dir + 'extras/react.js',
496
+ // sockjs: statebus_dir + 'extras/sockjs.js',
497
+ // coffee: statebus_dir + 'extras/coffee.js',
498
+ // statebus: statebus_dir + 'statebus.js'
499
+ // }
500
+ // if (statebus_dir == 'https://stateb.us/')
501
+ // js_urls.statebus = statebus_dir + 'statebus4.js'
474
502
 
475
- for (var name in js_urls)
476
- document.write('<script src="' + js_urls[name] + '" charset="utf-8"></script>')
503
+ // for (var name in js_urls)
504
+ // document.write('<script src="' + js_urls[name] + '" charset="utf-8"></script>')
477
505
 
478
506
  document.addEventListener('DOMContentLoaded', scripts_ready, false)
479
507
  }
@@ -484,56 +512,50 @@
484
512
  function clientjs_option (option_name) {
485
513
  // This function must be copy/paste synchronized with statebus.js. Be
486
514
  // sure to clone all edits there.
487
- var script_elem = (
488
- document.querySelector('script[src*="/client"][src$=".js"]') ||
489
- document.querySelector('script[src^="client"][src$=".js"]'))
515
+ var script_elem = document.querySelector('script[src$="statebus/client.js"]')
490
516
  return script_elem && script_elem.getAttribute(option_name)
491
517
  }
492
518
  var loaded_from_file_url = window.location.href.match(/^file:\/\//)
493
- window.statebus_server = window.statebus_server || clientjs_option('server') ||
494
- (loaded_from_file_url ? 'https://stateb.us:3006' : '/')
495
- window.statebus_backdoor = window.statebus_backdoor || clientjs_option('backdoor')
519
+
520
+ // Todo: remove this global
521
+ window.statebus_server = clientjs_option('server')
496
522
  var react_render
497
523
  function scripts_ready () {
498
- react_render = React.version >= '0.14.' ? ReactDOM.render : React.render
524
+ react_render = ReactDOM.render
499
525
  make_client_statebus_maker()
500
526
  window.bus = window.statebus()
501
527
  bus.label = 'bus'
502
- if (clientjs_option('braid_mode'))
503
- bus.state = bus.braid_proxy()
504
- else
505
- window.sb = bus.sb
506
- statebus.widget = React_View
507
- statebus.create_react_class = React_View
508
- statebus.createReactClass = React_View
509
-
510
- improve_react()
511
- window.ignore_flashbacks = false
512
- if (statebus_server !== 'none') {
513
- if (clientjs_option('braid_mode')) {
514
- console.log('Using Braid-HTTP!')
515
- braid_http_mount ('/*', statebus_server)
516
- } else {
517
- bus.net_mount ('/*', statebus_server)
518
- }
519
- }
520
528
 
521
- if (window.statebus_backdoor) {
522
- window.master = statebus()
523
- master.net_mount('*', statebus_backdoor)
524
- }
529
+ statebus.create_react_class = create_react_class
530
+ statebus.createReactClass = create_react_class
531
+
532
+ // improve_react()
533
+ statebus.ignore_flashbacks = false
534
+ bus.libs = {}
535
+ bus.libs.http_out = http_mount
536
+ bus.libs.react_class = create_react_class
537
+ bus.libs.input = make_better_input_17()
538
+ // if (statebus_server !== 'none') {
539
+ // if (clientjs_option('braid_mode')) {
540
+ // console.log('Using Braid-HTTP!')
541
+ // http_mount ('/*', statebus_server)
542
+ // } else {
543
+ // bus.ws_mount ('/*', statebus_server)
544
+ // }
545
+ // }
546
+
525
547
  bus.net_automount()
526
548
 
527
549
  // This /new/* code is deprecated
528
550
  if (!clientjs_option('braid_mode')) {
529
- bus('/new/*').to_save = function (o) {
551
+ bus('/new/*').to_set = function (o) {
530
552
  if (o.key.split('/').length > 3) return
531
553
 
532
554
  var old_key = o.key
533
555
  o.key = old_key + '/' + Math.random().toString(36).substring(2,12)
534
556
  statebus.cache[o.key] = o
535
557
  delete statebus.cache[old_key]
536
- bus.save(o)
558
+ bus.set(o)
537
559
  }
538
560
  }
539
561
 
@@ -543,6 +565,17 @@
543
565
  statebus.load_client_code = load_client_code
544
566
  statebus.load_widgets = load_widgets
545
567
 
568
+ if (clientjs_option('globals')) {
569
+ // Setup globals
570
+ var globals = ['get', 'set', 'state']
571
+
572
+ for (var i=0; i<globals.length; i++) {
573
+ console.log('globalizing', globals[i], 'as',
574
+ eval('bus.' + globals[i]))
575
+ window[globals[i]] = eval('bus.' + globals[i])
576
+ }
577
+ }
578
+
546
579
  document.addEventListener('DOMContentLoaded', function () {
547
580
  if (window.statebus_ready)
548
581
  for (var i=0; i<statebus_ready.length; i++)
@@ -662,7 +695,7 @@
662
695
  for (var i=0; i<arguments.length; i++) {
663
696
  args.push(arguments[i])
664
697
  if (arguments[i].state)
665
- args[i].src = 'data:;base64,' + fetch(args[i].state)._
698
+ args[i].src = 'data:;base64,' + bus.get(args[i].state)._
666
699
  }
667
700
  return og_img.apply(this, args)
668
701
  }
@@ -694,12 +727,38 @@
694
727
  }
695
728
 
696
729
 
730
+ // This one is for react v17+
731
+ function make_better_input_17 () {
732
+ return createReactClass({
733
+ getInitialState: function() {
734
+ return {value: this.props.value}
735
+ },
736
+ UNSAVE_componentWillReceiveProps: function(new_props) {
737
+ this.setState({value: new_props.value})
738
+ },
739
+ onChange: function(e) {
740
+ this.props.onChange && this.props.onChange(e)
741
+ if (this.props.value)
742
+ this.setState({value: e.target.value})
743
+ },
744
+ render: function() {
745
+ var new_props = {}
746
+ for (var k in this.props)
747
+ if (this.props.hasOwnProperty(k))
748
+ new_props[k] = this.props[k]
749
+ if (this.state.value) new_props.value = this.state.value
750
+ new_props.onChange = this.onChange
751
+ return React.createElement('input', new_props)
752
+ }
753
+ })
754
+ }
755
+
697
756
  // Load the components
698
757
  var users_widgets = {}
699
758
  function make_component(name, func) {
700
759
  // Define the component
701
760
 
702
- window[name] = users_widgets[name] = window.React_View({
761
+ window[name] = users_widgets[name] = statebus.create_react_class({
703
762
  displayName: name,
704
763
  render: function () {
705
764
  var args = []