statebus 7.0.2 → 7.0.5

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,15 +2,14 @@
2
2
  var websocket_prefix = (clientjs_option('websocket_path')
3
3
  || '_connect_to_statebus_')
4
4
 
5
- // Todo: remove this global
6
- window.dom = window.dom || new Proxy({}, {
7
- get: function (o, k) { return o[k] },
8
- set: function (o, k, v) {
9
- o[k] = v
10
- make_component(k, v)
11
- return true
12
- }
13
- })
5
+ var react_render = ReactDOM.render
6
+ // make_client_statebus_maker()
7
+ window.bus = window.statebus()
8
+ bus.label = 'bus'
9
+
10
+ bus.libs = {}
11
+ bus.libs.react12 = {}
12
+ bus.libs.react17 = {}
14
13
 
15
14
  // ****************
16
15
  // Connecting over the Network
@@ -43,37 +42,43 @@
43
42
  // return new SockJS(url + '/' + websocket_prefix)
44
43
  }
45
44
  function client_creds (server_url) {
45
+ // This function is only used for websocket connections.
46
+ // http connections set the cookie on the server.
46
47
  var me = bus.get('ls/me')
47
48
  bus.log('connect: me is', me)
48
49
  if (!me.client) {
49
50
  // Create a client id if we have none yet.
50
51
  // Either from a cookie set by server, or a new one from scratch.
51
- var c = get_cookie('client')
52
+ var c = get_cookie('peer')
52
53
  me.client = c || (Math.random().toString(36).substring(2)
53
54
  + Math.random().toString(36).substring(2)
54
55
  + Math.random().toString(36).substring(2))
55
56
  bus.set(me)
56
57
  }
57
58
 
58
- set_cookie('client', me.client)
59
+ set_cookie('peer', me.client)
59
60
  return {clientid: me.client}
60
61
  }
61
62
 
62
- function http_mount (prefix, url) {
63
+ bus.libs.http_out = (prefix, url) => {
63
64
  var preprefix = prefix.slice(0,-1)
64
65
  var has_prefix = new RegExp('^' + preprefix)
65
66
  var is_absolute = /^https?:\/\//
66
67
  var subscriptions = {}
67
68
  var put_counter = 0
68
69
 
69
- function add_prefix (key) {
70
- return is_absolute.test(key) ? key : preprefix + key }
71
- function rem_prefix (key) {
72
- return has_prefix.test(key) ? key.substr(preprefix.length) : key }
70
+ function add_prefix (url) {
71
+ return is_absolute.test(url) ? url : preprefix + url }
72
+ function rem_prefix (url) {
73
+ return has_prefix.test(url) ? url.substr(preprefix.length) : url }
73
74
  function add_prefixes (obj) {
74
- return bus.translate_keys(bus.clone(obj), add_prefix) }
75
+ var keyed = bus.translate_keys(bus.clone(obj), add_prefix)
76
+ return bus.translate_links(bus.clone(keyed), add_prefix)
77
+ }
75
78
  function rem_prefixes (obj) {
76
- return bus.translate_keys(bus.clone(obj), rem_prefix) }
79
+ var keyed = bus.translate_keys(bus.clone(obj), rem_prefix)
80
+ return bus.translate_links(bus.clone(keyed), rem_prefix)
81
+ }
77
82
 
78
83
  var puts = new Map()
79
84
  function enqueue_put (url, body) {
@@ -112,14 +117,15 @@
112
117
  setTimeout(function () {send_put(id)}, 1000)
113
118
  }
114
119
  function send_all_puts () {
115
- for (var id of puts.keys())
116
- if (puts.get(id).status === 'waiting') {
120
+ puts.forEach(function (value, id) {
121
+ if (value.status === 'waiting') {
117
122
  console.log('Sending waiting put', id)
118
123
  send_put(id)
119
124
  }
125
+ })
120
126
  }
121
127
 
122
- bus(prefix).to_set = function (obj, t) {
128
+ bus(prefix).setter = function (obj, t) {
123
129
  bus.set.fire(obj)
124
130
 
125
131
  var put = {
@@ -134,7 +140,7 @@
134
140
  send_put(put_id)
135
141
  }
136
142
 
137
- bus(prefix).to_get = function (key, t) {
143
+ bus(prefix).getter = function (key, t) {
138
144
  // Subscription can be in states:
139
145
  // - connecting
140
146
  // - connected
@@ -212,16 +218,35 @@
212
218
  }
213
219
  }
214
220
  }
215
- bus(prefix).to_forget = function (key) {
221
+ bus(prefix).forgetter = function (key) {
216
222
  subscriptions[key].status = 'aborted'
217
223
  subscriptions[key].aborter.abort()
218
224
  }
219
225
  }
220
226
 
227
+ function http_automount () {
228
+ function get_domain (key) { // Returns e.g. "state://foo.com"
229
+ var m = key.match(/^https?\:\/\/(([^:\/?#]*)(?:\:([0-9]+))?)/)
230
+ return m && m[0]
231
+ }
232
+
233
+ var old_route = bus.route
234
+ var connections = {}
235
+ bus.route = function (key, method, arg, t) {
236
+ var d = get_domain(key)
237
+ if (d && !connections[d]) {
238
+ bus.libs.http_out(d + '/*', d + '/')
239
+ connections[d] = true
240
+ }
241
+
242
+ return old_route(key, method, arg, t)
243
+ }
244
+ }
245
+
221
246
 
222
247
  // ****************
223
248
  // Manipulate Localstorage
224
- function localstorage_client (prefix) {
249
+ bus.libs.localstorage = (prefix) => {
225
250
  try { localStorage } catch (e) { return }
226
251
 
227
252
  // Sets are queued up, to store values with a delay, in batch
@@ -235,11 +260,11 @@
235
260
  sets_are_pending = false
236
261
  }
237
262
 
238
- bus(prefix).to_get = function (key) {
263
+ bus(prefix).getter = function (key) {
239
264
  var result = localStorage.getItem(key)
240
265
  return result ? JSON.parse(result) : {key: key}
241
266
  }
242
- bus(prefix).to_set = function (obj) {
267
+ bus(prefix).setter = function (obj) {
243
268
  // Do I need to make this recurse into the object?
244
269
  bus.log('localStore: on_set:', obj.key)
245
270
  pending_sets[obj.key] = obj
@@ -250,7 +275,7 @@
250
275
  bus.set.fire(obj)
251
276
  return obj
252
277
  }
253
- bus(prefix).to_delete = function (key) { localStorage.removeItem(key) }
278
+ bus(prefix).deleter = function (key) { localStorage.removeItem(key) }
254
279
 
255
280
 
256
281
  // Hm... this update stuff doesn't seem to work on file:/// urls in chrome
@@ -283,7 +308,7 @@
283
308
  // - Change the key prefix
284
309
  // - Set this into the cache
285
310
 
286
- bus(prefix).to_set = function (obj) {
311
+ bus(prefix).setter = function (obj) {
287
312
  window.history.replaceState(
288
313
  '',
289
314
  '',
@@ -461,46 +486,19 @@
461
486
  // ### Full-featured single-file app methods
462
487
  // ###
463
488
 
464
- function make_client_statebus_maker () {
465
- var extra_stuff = ['localstorage_client make_websocket client_creds',
466
- 'url_store components'].join(' ').split(' ')
467
- if (window.statebus) {
468
- var orig_statebus = statebus
469
- window.statebus = function make_client_bus () {
470
- var bus = orig_statebus()
471
- for (var i=0; i<extra_stuff.length; i++)
472
- bus[extra_stuff[i]] = eval(extra_stuff[i])
473
- bus.localstorage_client('ls/*')
474
- return bus
475
- }
476
- }
477
- }
478
-
479
- function load_scripts() {
480
- // console.info('Loading scripts! if', !!!window.statebus)
481
- if (!window.statebus) {
482
- var statebus_dir = clientjs_option('src')
483
- if (statebus_dir) statebus_dir = statebus_dir.match(/(.*)[\/\\]/)
484
- if (statebus_dir) statebus_dir = statebus_dir[1] + '/'
485
- else statebus_dir = ''
486
-
487
- // var js_urls = {
488
- // react: statebus_dir + 'extras/react.js',
489
- // sockjs: statebus_dir + 'extras/sockjs.js',
490
- // coffee: statebus_dir + 'extras/coffee.js',
491
- // statebus: statebus_dir + 'statebus.js'
492
- // }
493
- // if (statebus_dir == 'https://stateb.us/')
494
- // js_urls.statebus = statebus_dir + 'statebus4.js'
495
-
496
- // for (var name in js_urls)
497
- // document.write('<script src="' + js_urls[name] + '" charset="utf-8"></script>')
498
-
499
- document.addEventListener('DOMContentLoaded', scripts_ready, false)
500
- }
501
- else
502
- scripts_ready()
503
- }
489
+ // function make_client_statebus_maker () {
490
+ // var extra_stuff = ['make_websocket client_creds',
491
+ // 'url_store components'].join(' ').split(' ')
492
+ // if (window.statebus) {
493
+ // var orig_statebus = statebus
494
+ // window.statebus = function make_client_bus () {
495
+ // var bus = orig_statebus()
496
+ // for (var i=0; i<extra_stuff.length; i++)
497
+ // bus[extra_stuff[i]] = eval(extra_stuff[i])
498
+ // return bus
499
+ // }
500
+ // }
501
+ // }
504
502
 
505
503
  function clientjs_option (option_name) {
506
504
  // This function must be copy/paste synchronized with statebus.js. Be
@@ -512,97 +510,35 @@
512
510
 
513
511
  // Todo: remove this global
514
512
  window.statebus_server = clientjs_option('server')
515
- var react_render
516
- function scripts_ready () {
517
- react_render = ReactDOM.render
518
- // make_client_statebus_maker()
519
- window.bus = window.statebus()
520
- bus.label = 'bus'
521
-
522
- statebus.create_react_class = create_react_class
523
- statebus.createReactClass = create_react_class
524
-
525
- // improve_react()
526
- statebus.ignore_flashbacks = false
527
- bus.libs = {}
528
- bus.libs.http_out = http_mount
529
- bus.libs.react_class = create_react_class
530
- bus.libs.input = make_better_input_17()
531
- bus.libs.localstorage = localstorage_client
532
-
533
- // if (statebus_server !== 'none') {
534
- // if (clientjs_option('braid_mode')) {
535
- // console.log('Using Braid-HTTP!')
536
- // http_mount ('/*', statebus_server)
537
- // } else {
538
- // bus.ws_mount ('/*', statebus_server)
539
- // }
540
- // }
541
-
542
- bus.net_automount()
543
513
 
544
- // This /new/* code is deprecated
545
- if (!clientjs_option('braid_mode')) {
546
- bus('/new/*').to_set = function (o) {
547
- if (o.key.split('/').length > 3) return
548
-
549
- var old_key = o.key
550
- o.key = old_key + '/' + Math.random().toString(36).substring(2,12)
551
- statebus.cache[o.key] = o
552
- delete statebus.cache[old_key]
553
- bus.set(o)
554
- }
555
- }
556
-
557
- load_coffee()
514
+ function is_css_prop (name) {
515
+ if (!is_css_prop.memoized) {
516
+ // Precompute all the css props
517
+ is_css_prop.memoized = {}
558
518
 
559
- statebus.compile_coffee = compile_coffee
560
- statebus.load_client_code = load_client_code
561
- statebus.load_widgets = load_widgets
519
+ // We used to get all_css_props like this:
520
+ //
521
+ // var all_css_props = Object.keys(document.body.style)
522
+ // if (all_css_props.length < 100) // Firefox
523
+ // all_css_props = Object.keys(document.body.style.__proto__)
524
+ //
525
+ // But now I've hard-coded them:
526
+ var all_css_props = ["alignContent","alignItems","alignSelf","alignmentBaseline","all","animation","animationDelay","animationDirection","animationDuration","animationFillMode","animationIterationCount","animationName","animationPlayState","animationTimingFunction","backfaceVisibility","background","backgroundAttachment","backgroundBlendMode","backgroundClip","backgroundColor","backgroundImage","backgroundOrigin","backgroundPosition","backgroundPositionX","backgroundPositionY","backgroundRepeat","backgroundRepeatX","backgroundRepeatY","backgroundSize","baselineShift","blockSize","border","borderBottom","borderBottomColor","borderBottomLeftRadius","borderBottomRightRadius","borderBottomStyle","borderBottomWidth","borderCollapse","borderColor","borderImage","borderImageOutset","borderImageRepeat","borderImageSlice","borderImageSource","borderImageWidth","borderLeft","borderLeftColor","borderLeftStyle","borderLeftWidth","borderRadius","borderRight","borderRightColor","borderRightStyle","borderRightWidth","borderSpacing","borderStyle","borderTop","borderTopColor","borderTopLeftRadius","borderTopRightRadius","borderTopStyle","borderTopWidth","borderWidth","bottom","boxShadow","boxSizing","breakAfter","breakBefore","breakInside","bufferedRendering","captionSide","caretColor","clear","clip","clipPath","clipRule","color","colorInterpolation","colorInterpolationFilters","colorRendering","columnCount","columnFill","columnGap","columnRule","columnRuleColor","columnRuleStyle","columnRuleWidth","columnSpan","columnWidth","columns","contain","content","counterIncrement","counterReset","cursor","cx","cy","d","direction","display","dominantBaseline","emptyCells","fill","fillOpacity","fillRule","filter","flex","flexBasis","flexDirection","flexFlow","flexGrow","flexShrink","flexWrap","float","floodColor","floodOpacity","font","fontDisplay","fontFamily","fontFeatureSettings","fontKerning","fontSize","fontStretch","fontStyle","fontVariant","fontVariantCaps","fontVariantEastAsian","fontVariantLigatures","fontVariantNumeric","fontVariationSettings","fontWeight","gap","grid","gridArea","gridAutoColumns","gridAutoFlow","gridAutoRows","gridColumn","gridColumnEnd","gridColumnGap","gridColumnStart","gridGap","gridRow","gridRowEnd","gridRowGap","gridRowStart","gridTemplate","gridTemplateAreas","gridTemplateColumns","gridTemplateRows","height","hyphens","imageRendering","inlineSize","isolation","justifyContent","justifyItems","justifySelf","left","letterSpacing","lightingColor","lineBreak","lineHeight","listStyle","listStyleImage","listStylePosition","listStyleType","margin","marginBottom","marginLeft","marginRight","marginTop","marker","markerEnd","markerMid","markerStart","mask","maskType","maxBlockSize","maxHeight","maxInlineSize","maxWidth","maxZoom","minBlockSize","minHeight","minInlineSize","minWidth","minZoom","mixBlendMode","objectFit","objectPosition","offset","offsetDistance","offsetPath","offsetRotate","opacity","order","orientation","orphans","outline","outlineColor","outlineOffset","outlineStyle","outlineWidth","overflow","overflowAnchor","overflowWrap","overflowX","overflowY","overscrollBehavior","overscrollBehaviorX","overscrollBehaviorY","padding","paddingBottom","paddingLeft","paddingRight","paddingTop","page","pageBreakAfter","pageBreakBefore","pageBreakInside","paintOrder","perspective","perspectiveOrigin","placeContent","placeItems","placeSelf","pointerEvents","position","quotes","r","resize","right","rowGap","rx","ry","scrollBehavior","shapeImageThreshold","shapeMargin","shapeOutside","shapeRendering","size","speak","src","stopColor","stopOpacity","stroke","strokeDasharray","strokeDashoffset","strokeLinecap","strokeLinejoin","strokeMiterlimit","strokeOpacity","strokeWidth","tabSize","tableLayout","textAlign","textAlignLast","textAnchor","textCombineUpright","textDecoration","textDecorationColor","textDecorationLine","textDecorationSkipInk","textDecorationStyle","textIndent","textOrientation","textOverflow","textRendering","textShadow","textSizeAdjust","textTransform","textUnderlinePosition","top","touchAction","transform","transformBox","transformOrigin","transformStyle","transition","transitionDelay","transitionDuration","transitionProperty","transitionTimingFunction","unicodeBidi","unicodeRange","userSelect","userZoom","vectorEffect","verticalAlign","visibility","webkitAlignContent","webkitAlignItems","webkitAlignSelf","webkitAnimation","webkitAnimationDelay","webkitAnimationDirection","webkitAnimationDuration","webkitAnimationFillMode","webkitAnimationIterationCount","webkitAnimationName","webkitAnimationPlayState","webkitAnimationTimingFunction","webkitAppRegion","webkitAppearance","webkitBackfaceVisibility","webkitBackgroundClip","webkitBackgroundOrigin","webkitBackgroundSize","webkitBorderAfter","webkitBorderAfterColor","webkitBorderAfterStyle","webkitBorderAfterWidth","webkitBorderBefore","webkitBorderBeforeColor","webkitBorderBeforeStyle","webkitBorderBeforeWidth","webkitBorderBottomLeftRadius","webkitBorderBottomRightRadius","webkitBorderEnd","webkitBorderEndColor","webkitBorderEndStyle","webkitBorderEndWidth","webkitBorderHorizontalSpacing","webkitBorderImage","webkitBorderRadius","webkitBorderStart","webkitBorderStartColor","webkitBorderStartStyle","webkitBorderStartWidth","webkitBorderTopLeftRadius","webkitBorderTopRightRadius","webkitBorderVerticalSpacing","webkitBoxAlign","webkitBoxDecorationBreak","webkitBoxDirection","webkitBoxFlex","webkitBoxOrdinalGroup","webkitBoxOrient","webkitBoxPack","webkitBoxReflect","webkitBoxShadow","webkitBoxSizing","webkitClipPath","webkitColumnBreakAfter","webkitColumnBreakBefore","webkitColumnBreakInside","webkitColumnCount","webkitColumnGap","webkitColumnRule","webkitColumnRuleColor","webkitColumnRuleStyle","webkitColumnRuleWidth","webkitColumnSpan","webkitColumnWidth","webkitColumns","webkitFilter","webkitFlex","webkitFlexBasis","webkitFlexDirection","webkitFlexFlow","webkitFlexGrow","webkitFlexShrink","webkitFlexWrap","webkitFontFeatureSettings","webkitFontSizeDelta","webkitFontSmoothing","webkitHighlight","webkitHyphenateCharacter","webkitJustifyContent","webkitLineBreak","webkitLineClamp","webkitLocale","webkitLogicalHeight","webkitLogicalWidth","webkitMarginAfter","webkitMarginAfterCollapse","webkitMarginBefore","webkitMarginBeforeCollapse","webkitMarginBottomCollapse","webkitMarginCollapse","webkitMarginEnd","webkitMarginStart","webkitMarginTopCollapse","webkitMask","webkitMaskBoxImage","webkitMaskBoxImageOutset","webkitMaskBoxImageRepeat","webkitMaskBoxImageSlice","webkitMaskBoxImageSource","webkitMaskBoxImageWidth","webkitMaskClip","webkitMaskComposite","webkitMaskImage","webkitMaskOrigin","webkitMaskPosition","webkitMaskPositionX","webkitMaskPositionY","webkitMaskRepeat","webkitMaskRepeatX","webkitMaskRepeatY","webkitMaskSize","webkitMaxLogicalHeight","webkitMaxLogicalWidth","webkitMinLogicalHeight","webkitMinLogicalWidth","webkitOpacity","webkitOrder","webkitPaddingAfter","webkitPaddingBefore","webkitPaddingEnd","webkitPaddingStart","webkitPerspective","webkitPerspectiveOrigin","webkitPerspectiveOriginX","webkitPerspectiveOriginY","webkitPrintColorAdjust","webkitRtlOrdering","webkitRubyPosition","webkitShapeImageThreshold","webkitShapeMargin","webkitShapeOutside","webkitTapHighlightColor","webkitTextCombine","webkitTextDecorationsInEffect","webkitTextEmphasis","webkitTextEmphasisColor","webkitTextEmphasisPosition","webkitTextEmphasisStyle","webkitTextFillColor","webkitTextOrientation","webkitTextSecurity","webkitTextSizeAdjust","webkitTextStroke","webkitTextStrokeColor","webkitTextStrokeWidth","webkitTransform","webkitTransformOrigin","webkitTransformOriginX","webkitTransformOriginY","webkitTransformOriginZ","webkitTransformStyle","webkitTransition","webkitTransitionDelay","webkitTransitionDuration","webkitTransitionProperty","webkitTransitionTimingFunction","webkitUserDrag","webkitUserModify","webkitUserSelect","webkitWritingMode","whiteSpace","widows","width","willChange","wordBreak","wordSpacing","wordWrap","writingMode","x","y","zIndex","zoom"]
562
527
 
563
- if (clientjs_option('globals')) {
564
- // Setup globals
565
- var globals = ['get', 'set', 'state']
528
+ var ignore = {d:1, cx:1, cy:1, rx:1, ry:1, x:1, y:1,
529
+ content:1, fill:1, stroke:1, src:1}
566
530
 
567
- for (var i=0; i<globals.length; i++) {
568
- console.log('globalizing', globals[i], 'as',
569
- eval('bus.' + globals[i]))
570
- window[globals[i]] = eval('bus.' + globals[i])
571
- }
531
+ for (var i=0; i<all_css_props.length; i++)
532
+ if (!ignore[all_css_props[i]])
533
+ is_css_prop.memoized[all_css_props[i]] = true
572
534
  }
573
-
574
- document.addEventListener('DOMContentLoaded', function () {
575
- if (window.statebus_ready)
576
- for (var i=0; i<statebus_ready.length; i++)
577
- statebus_ready[i]()
578
- }, false)
579
-
580
- document.addEventListener('DOMContentLoaded', load_widgets, false)
581
-
582
- // if (dom.Body || dom.body || dom.BODY)
583
- // react_render((window.Body || window.body || window.BODY)(), document.body)
535
+ return is_css_prop.memoized[name]
584
536
  }
585
537
 
586
- function improve_react() {
587
- function capitalize (s) {return s[0].toUpperCase() + s.slice(1)}
588
- function camelcase (s) { var a = s.split(/[_-]/)
589
- return a.slice(0,1).concat(a.slice(1).map(capitalize)).join('') }
590
-
591
- // We used to get all_css_props like this:
592
- //
593
- // var all_css_props = Object.keys(document.body.style)
594
- // if (all_css_props.length < 100) // Firefox
595
- // all_css_props = Object.keys(document.body.style.__proto__)
596
- //
597
- // But now I've hard-coded them:
598
- var all_css_props = ["alignContent","alignItems","alignSelf","alignmentBaseline","all","animation","animationDelay","animationDirection","animationDuration","animationFillMode","animationIterationCount","animationName","animationPlayState","animationTimingFunction","backfaceVisibility","background","backgroundAttachment","backgroundBlendMode","backgroundClip","backgroundColor","backgroundImage","backgroundOrigin","backgroundPosition","backgroundPositionX","backgroundPositionY","backgroundRepeat","backgroundRepeatX","backgroundRepeatY","backgroundSize","baselineShift","blockSize","border","borderBottom","borderBottomColor","borderBottomLeftRadius","borderBottomRightRadius","borderBottomStyle","borderBottomWidth","borderCollapse","borderColor","borderImage","borderImageOutset","borderImageRepeat","borderImageSlice","borderImageSource","borderImageWidth","borderLeft","borderLeftColor","borderLeftStyle","borderLeftWidth","borderRadius","borderRight","borderRightColor","borderRightStyle","borderRightWidth","borderSpacing","borderStyle","borderTop","borderTopColor","borderTopLeftRadius","borderTopRightRadius","borderTopStyle","borderTopWidth","borderWidth","bottom","boxShadow","boxSizing","breakAfter","breakBefore","breakInside","bufferedRendering","captionSide","caretColor","clear","clip","clipPath","clipRule","color","colorInterpolation","colorInterpolationFilters","colorRendering","columnCount","columnFill","columnGap","columnRule","columnRuleColor","columnRuleStyle","columnRuleWidth","columnSpan","columnWidth","columns","contain","content","counterIncrement","counterReset","cursor","cx","cy","d","direction","display","dominantBaseline","emptyCells","fill","fillOpacity","fillRule","filter","flex","flexBasis","flexDirection","flexFlow","flexGrow","flexShrink","flexWrap","float","floodColor","floodOpacity","font","fontDisplay","fontFamily","fontFeatureSettings","fontKerning","fontSize","fontStretch","fontStyle","fontVariant","fontVariantCaps","fontVariantEastAsian","fontVariantLigatures","fontVariantNumeric","fontVariationSettings","fontWeight","gap","grid","gridArea","gridAutoColumns","gridAutoFlow","gridAutoRows","gridColumn","gridColumnEnd","gridColumnGap","gridColumnStart","gridGap","gridRow","gridRowEnd","gridRowGap","gridRowStart","gridTemplate","gridTemplateAreas","gridTemplateColumns","gridTemplateRows","height","hyphens","imageRendering","inlineSize","isolation","justifyContent","justifyItems","justifySelf","left","letterSpacing","lightingColor","lineBreak","lineHeight","listStyle","listStyleImage","listStylePosition","listStyleType","margin","marginBottom","marginLeft","marginRight","marginTop","marker","markerEnd","markerMid","markerStart","mask","maskType","maxBlockSize","maxHeight","maxInlineSize","maxWidth","maxZoom","minBlockSize","minHeight","minInlineSize","minWidth","minZoom","mixBlendMode","objectFit","objectPosition","offset","offsetDistance","offsetPath","offsetRotate","opacity","order","orientation","orphans","outline","outlineColor","outlineOffset","outlineStyle","outlineWidth","overflow","overflowAnchor","overflowWrap","overflowX","overflowY","overscrollBehavior","overscrollBehaviorX","overscrollBehaviorY","padding","paddingBottom","paddingLeft","paddingRight","paddingTop","page","pageBreakAfter","pageBreakBefore","pageBreakInside","paintOrder","perspective","perspectiveOrigin","placeContent","placeItems","placeSelf","pointerEvents","position","quotes","r","resize","right","rowGap","rx","ry","scrollBehavior","shapeImageThreshold","shapeMargin","shapeOutside","shapeRendering","size","speak","src","stopColor","stopOpacity","stroke","strokeDasharray","strokeDashoffset","strokeLinecap","strokeLinejoin","strokeMiterlimit","strokeOpacity","strokeWidth","tabSize","tableLayout","textAlign","textAlignLast","textAnchor","textCombineUpright","textDecoration","textDecorationColor","textDecorationLine","textDecorationSkipInk","textDecorationStyle","textIndent","textOrientation","textOverflow","textRendering","textShadow","textSizeAdjust","textTransform","textUnderlinePosition","top","touchAction","transform","transformBox","transformOrigin","transformStyle","transition","transitionDelay","transitionDuration","transitionProperty","transitionTimingFunction","unicodeBidi","unicodeRange","userSelect","userZoom","vectorEffect","verticalAlign","visibility","webkitAlignContent","webkitAlignItems","webkitAlignSelf","webkitAnimation","webkitAnimationDelay","webkitAnimationDirection","webkitAnimationDuration","webkitAnimationFillMode","webkitAnimationIterationCount","webkitAnimationName","webkitAnimationPlayState","webkitAnimationTimingFunction","webkitAppRegion","webkitAppearance","webkitBackfaceVisibility","webkitBackgroundClip","webkitBackgroundOrigin","webkitBackgroundSize","webkitBorderAfter","webkitBorderAfterColor","webkitBorderAfterStyle","webkitBorderAfterWidth","webkitBorderBefore","webkitBorderBeforeColor","webkitBorderBeforeStyle","webkitBorderBeforeWidth","webkitBorderBottomLeftRadius","webkitBorderBottomRightRadius","webkitBorderEnd","webkitBorderEndColor","webkitBorderEndStyle","webkitBorderEndWidth","webkitBorderHorizontalSpacing","webkitBorderImage","webkitBorderRadius","webkitBorderStart","webkitBorderStartColor","webkitBorderStartStyle","webkitBorderStartWidth","webkitBorderTopLeftRadius","webkitBorderTopRightRadius","webkitBorderVerticalSpacing","webkitBoxAlign","webkitBoxDecorationBreak","webkitBoxDirection","webkitBoxFlex","webkitBoxOrdinalGroup","webkitBoxOrient","webkitBoxPack","webkitBoxReflect","webkitBoxShadow","webkitBoxSizing","webkitClipPath","webkitColumnBreakAfter","webkitColumnBreakBefore","webkitColumnBreakInside","webkitColumnCount","webkitColumnGap","webkitColumnRule","webkitColumnRuleColor","webkitColumnRuleStyle","webkitColumnRuleWidth","webkitColumnSpan","webkitColumnWidth","webkitColumns","webkitFilter","webkitFlex","webkitFlexBasis","webkitFlexDirection","webkitFlexFlow","webkitFlexGrow","webkitFlexShrink","webkitFlexWrap","webkitFontFeatureSettings","webkitFontSizeDelta","webkitFontSmoothing","webkitHighlight","webkitHyphenateCharacter","webkitJustifyContent","webkitLineBreak","webkitLineClamp","webkitLocale","webkitLogicalHeight","webkitLogicalWidth","webkitMarginAfter","webkitMarginAfterCollapse","webkitMarginBefore","webkitMarginBeforeCollapse","webkitMarginBottomCollapse","webkitMarginCollapse","webkitMarginEnd","webkitMarginStart","webkitMarginTopCollapse","webkitMask","webkitMaskBoxImage","webkitMaskBoxImageOutset","webkitMaskBoxImageRepeat","webkitMaskBoxImageSlice","webkitMaskBoxImageSource","webkitMaskBoxImageWidth","webkitMaskClip","webkitMaskComposite","webkitMaskImage","webkitMaskOrigin","webkitMaskPosition","webkitMaskPositionX","webkitMaskPositionY","webkitMaskRepeat","webkitMaskRepeatX","webkitMaskRepeatY","webkitMaskSize","webkitMaxLogicalHeight","webkitMaxLogicalWidth","webkitMinLogicalHeight","webkitMinLogicalWidth","webkitOpacity","webkitOrder","webkitPaddingAfter","webkitPaddingBefore","webkitPaddingEnd","webkitPaddingStart","webkitPerspective","webkitPerspectiveOrigin","webkitPerspectiveOriginX","webkitPerspectiveOriginY","webkitPrintColorAdjust","webkitRtlOrdering","webkitRubyPosition","webkitShapeImageThreshold","webkitShapeMargin","webkitShapeOutside","webkitTapHighlightColor","webkitTextCombine","webkitTextDecorationsInEffect","webkitTextEmphasis","webkitTextEmphasisColor","webkitTextEmphasisPosition","webkitTextEmphasisStyle","webkitTextFillColor","webkitTextOrientation","webkitTextSecurity","webkitTextSizeAdjust","webkitTextStroke","webkitTextStrokeColor","webkitTextStrokeWidth","webkitTransform","webkitTransformOrigin","webkitTransformOriginX","webkitTransformOriginY","webkitTransformOriginZ","webkitTransformStyle","webkitTransition","webkitTransitionDelay","webkitTransitionDuration","webkitTransitionProperty","webkitTransitionTimingFunction","webkitUserDrag","webkitUserModify","webkitUserSelect","webkitWritingMode","whiteSpace","widows","width","willChange","wordBreak","wordSpacing","wordWrap","writingMode","x","y","zIndex","zoom"]
599
-
600
- var ignore = {d:1, cx:1, cy:1, rx:1, ry:1, x:1, y:1,
601
- content:1, fill:1, stroke:1, src:1}
602
- var is_css_prop = {}
603
- for (var i=0; i<all_css_props.length; i++)
604
- if (!ignore[all_css_props[i]])
605
- is_css_prop[all_css_props[i]] = true
538
+ // ================================================================
539
+ // React v12 Support
540
+
541
+ bus.libs.react12.improve_react = () => {
606
542
 
607
543
  function better_element(el) {
608
544
  // To do:
@@ -631,7 +567,7 @@
631
567
  // Styles get redirected to the style field
632
568
  else if (arg instanceof Object)
633
569
  for (var k in arg)
634
- if (is_css_prop[k]
570
+ if (is_css_prop(k)
635
571
  && !(k in {width:1,height:1,size:1}
636
572
  && el in {canvas:1, input:1, embed:1, object:1}))
637
573
  attrs.style[k] = arg[k] // Merge styles
@@ -654,6 +590,13 @@
654
590
  for (var el in React.DOM)
655
591
  window[el.toUpperCase()] = better_element(el)
656
592
 
593
+ // Fixes React controlled textarea widgets so they can work with state
594
+ // updates triggered by forceUpdate, rather than just setState(),
595
+ // because statebus keeps its own state outside of React's setState(),
596
+ // but react doesn't know how to preserve the cursor (and selection)
597
+ // position for updates unless they go through setState(). So this
598
+ // function just wraps input widgets with a component that uses
599
+ // setState().
657
600
  function make_better_input (name, element) {
658
601
  window[name] = React.createFactory(React.createClass({
659
602
  getInitialState: function() {
@@ -711,24 +654,141 @@
711
654
  }
712
655
  }
713
656
 
714
- function autodetect_args (func) {
715
- if (func.args) return
657
+ bus.libs.react17.reactive_dom = () => {
658
+ // The window.dom object lets the user define new react components as
659
+ // functions
660
+ window.dom = window.dom || new Proxy({}, {
661
+ get: function (o, k) { return o[k] },
662
+ set: function (o, k, v) {
663
+ o[k] = v
664
+ make_component(k, v)
665
+ return true
666
+ }
667
+ })
716
668
 
717
- // Get an array of the func's params
718
- var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
719
- params = /([^\s,]+)/g,
720
- s = func.toString().replace(comments, '')
721
- func.args = s.slice(s.indexOf('(')+1, s.indexOf(')')).match(params) || []
722
- }
669
+ // We'll define functions for all HTML tags...
670
+ var function_for_tag = (tag) =>
671
+ (...arguments) => {
672
+ var children = []
673
+ var attrs = {style: {}}
674
+
675
+ for (var i=0; i<arguments.length; i++) {
676
+ var arg = arguments[i]
677
+
678
+ if (arg === undefined)
679
+ continue
680
+
681
+ // Strings, DOM nodes, and arrays become children
682
+ else if (typeof arg === 'string' // For "foo"
683
+ || arg instanceof String // For new String()
684
+ || arg && React.isValidElement(arg)
685
+ || arg instanceof Array)
686
+ children.push(arg)
687
+
688
+ // // Arrays append onto the children
689
+ // else if (arg instanceof Array)
690
+ // Array.prototype.push.apply(children, arg)
691
+
692
+ // Pure objects get merged into object city
693
+ // Styles get redirected to the style field
694
+ else if (arg instanceof Object)
695
+ for (var k in arg)
696
+ if (is_css_prop(k)
697
+ && !(k in {width:1,height:1,size:1}
698
+ && tag in {canvas:1, input:1, embed:1, object:1}))
699
+ attrs.style[k] = arg[k] // Merge styles
700
+ else if (k === 'style') // Merge insides of style tags
701
+ for (var k2 in arg[k])
702
+ attrs.style[k2] = arg[k][k2]
703
+ else
704
+ attrs[k] = arg[k] // Or be normal.
705
+ }
706
+
707
+ // Now call React.createElement(tag, attrs, children...)
708
+ return React.createElement.apply(
709
+ null,
710
+ [tag, attrs].concat(children)
711
+ )
712
+ }
713
+
714
+ // ... or at least most of them -- there are just a few missing from
715
+ // this list.
716
+ var all_tags = 'a,abbr,address,area,article,aside,audio,b,base,bdi,bdo,blockquote,body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,ins,kbd,label,legend,li,link,main,map,mark,menu,meta,meter,nav,noscript,object,ol,optgroup,option,output,p,param,picture,pre,progress,q,s,samp,script,section,select,slot,small,source,span,strong,style,sub,summary,sup,table,tbody,td,template,tfoot,th,thead,title,tr,u,ul,video,input'.split(',')
717
+ all_tags.forEach((tagname) => {
718
+ window[tagname.toUpperCase()] = function_for_tag(tagname)
719
+ })
720
+
721
+ // We create special functions for INPUT and TEXTAREA, because they
722
+ // have to do extra work to maintain the cursor when we use statebus
723
+ // instead of React's setState() for state updates.
724
+ window.INPUT = function_for_tag(make_fixed_textbox('input'))
725
+ window.TEXTAREA = function_for_tag(make_fixed_textbox('textarea'))
726
+
727
+
728
+ // Improve the functions ^^^ put this above
729
+ function better_element(el) {
730
+ // To do:
731
+ // - Don't put all args into a children array, cause react thinks
732
+ // that means they need a key.
733
+
734
+ return function () {
735
+ var children = []
736
+ var attrs = {style: {}}
737
+
738
+ for (var i=0; i<arguments.length; i++) {
739
+ var arg = arguments[i]
740
+
741
+ // Strings and DOM nodes and undefined become children
742
+ if (typeof arg === 'string' // For "foo"
743
+ || arg instanceof String // For new String()
744
+ || arg && React.isValidElement(arg)
745
+ || arg === undefined)
746
+ children.push(arg)
723
747
 
748
+ // Arrays append onto the children
749
+ else if (arg instanceof Array)
750
+ Array.prototype.push.apply(children, arg)
751
+
752
+ // Pure objects get merged into object city
753
+ // Styles get redirected to the style field
754
+ else if (arg instanceof Object)
755
+ for (var k in arg)
756
+ if (is_css_prop(k)
757
+ && !(k in {width:1,height:1,size:1}
758
+ && el in {canvas:1, input:1, embed:1, object:1}))
759
+ attrs.style[k] = arg[k] // Merge styles
760
+ else if (k === 'style') // Merge insides of style tags
761
+ for (var k2 in arg[k])
762
+ attrs.style[k2] = arg[k][k2]
763
+ else {
764
+ attrs[k] = arg[k] // Or be normal.
765
+
766
+ if (k === 'key')
767
+ attrs['data-key'] = arg[k]
768
+ }
769
+ }
770
+ if (children.length === 0) children = undefined
771
+ return React.DOM[el](attrs, children)
772
+ }
773
+ }
774
+ }
724
775
 
725
- // This one is for react v17+
726
- function make_better_input_17 () {
776
+ // Fixes React controlled textarea widgets so they can work with state
777
+ // updates triggered by forceUpdate, rather than just setState(),
778
+ // because statebus keeps its own state outside of React's setState(),
779
+ // but react doesn't know how to preserve the cursor (and selection)
780
+ // position for updates unless they go through setState(). So this
781
+ // function just wraps input widgets with a component that uses
782
+ // setState().
783
+ //
784
+ // This one is for react v17+.
785
+ function make_fixed_textbox (tagname) {
786
+ // `tagname` can be either "input" or "textarea"
727
787
  return createReactClass({
728
788
  getInitialState: function() {
729
789
  return {value: this.props.value}
730
790
  },
731
- UNSAVE_componentWillReceiveProps: function(new_props) {
791
+ UNSAFE_componentWillReceiveProps: function(new_props) {
732
792
  this.setState({value: new_props.value})
733
793
  },
734
794
  onChange: function(e) {
@@ -741,19 +801,30 @@
741
801
  for (var k in this.props)
742
802
  if (this.props.hasOwnProperty(k))
743
803
  new_props[k] = this.props[k]
744
- if (this.state.value) new_props.value = this.state.value
804
+ if (this.state.hasOwnProperty('value'))
805
+ new_props.value = this.state.value
745
806
  new_props.onChange = this.onChange
746
- return React.createElement('input', new_props)
807
+ return React.createElement(tagname, new_props)
747
808
  }
748
809
  })
749
810
  }
750
811
 
812
+ function autodetect_args (func) {
813
+ if (func.args) return
814
+
815
+ // Get an array of the func's params
816
+ var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
817
+ params = /([^\s,]+)/g,
818
+ s = func.toString().replace(comments, '')
819
+ func.args = s.slice(s.indexOf('(')+1, s.indexOf(')')).match(params) || []
820
+ }
821
+
751
822
  // Load the components
752
823
  var users_widgets = {}
753
824
  function make_component(name, func) {
754
825
  // Define the component
755
826
 
756
- window[name] = users_widgets[name] = statebus.create_react_class({
827
+ window[name] = users_widgets[name] = create_react_class({
757
828
  displayName: name,
758
829
  render: function () {
759
830
  var args = []
@@ -776,10 +847,10 @@
776
847
  }
777
848
 
778
849
  // Wrap plain JS values with SPAN, so react doesn't complain
779
- if (!React.isValidElement(vdom))
780
- // To do: should arrays be flattened into a SPAN's arguments?
781
- vdom = React.DOM.span(null, (typeof vdom === 'string')
782
- ? vdom : JSON.stringify(vdom))
850
+ // if (!React.isValidElement(vdom))
851
+ // // To do: should arrays be flattened into a SPAN's arguments?
852
+ // vdom = React.DOM.span(null, (typeof vdom === 'string')
853
+ // ? vdom : JSON.stringify(vdom))
783
854
  return vdom
784
855
  },
785
856
  componentDidMount: function () {
@@ -803,103 +874,6 @@
803
874
  })
804
875
  }
805
876
 
806
- function make_syncarea () {
807
- // a textarea that syncs with other textareas via diffsync
808
- // options:
809
- // textarea_style : hashmap of styles to add to the textarea
810
- // cursor_style : hashmap of styles to add to each peer's cursor
811
- // autosize : true --> resizes the textarea vertically to fit the text inside it
812
- // ws_url : websocket url for the diffsync server,
813
- // e.g. 'wss://invisible.college:' + diffsync.port
814
- // channel : 'diffsync_channel' --> diffsync channel to connect to
815
- window['SYNCAREA'] = users_widgets['SYNCAREA'] = React.createClass({
816
- getInitialState : function () {
817
- return { cursor_positions : {} }
818
- },
819
- on_text_changed : function () {
820
- if (this.props.autosize) {
821
- var t = this.textarea_ref
822
- t.style.height = null
823
- while (t.rows > 1 && t.scrollHeight < t.offsetHeight) t.rows--
824
- while (t.scrollHeight > t.offsetHeight) t.rows++
825
- }
826
- },
827
- componentDidMount : function () {
828
- var self = this
829
- self.on_ranges = function (ranges) {
830
- self.ranges = ranges
831
- var cursor_positions = {}
832
- Object.keys(ranges).forEach(function (k) {
833
- var r = ranges[k]
834
- var xy = getCaretCoordinates(self.textarea_ref, r[0])
835
- var x = self.textarea_ref.offsetLeft - self.textarea_ref.scrollLeft + xy.left + 'px'
836
- var y = self.textarea_ref.offsetTop - self.textarea_ref.scrollTop + xy.top + 'px'
837
- cursor_positions[k] = [x, y]
838
- })
839
- self.setState({ cursor_positions : cursor_positions })
840
- }
841
-
842
- this.ds = diffsync.create_client({
843
- ws_url : this.props.ws_url,
844
- channel : this.props.channel,
845
- get_text : function () {
846
- return self.textarea_ref.value
847
- },
848
- get_range : function () {
849
- var t = self.textarea_ref
850
- return [t.selectionStart, t.selectionEnd]
851
- },
852
- on_text : function (s, range) {
853
- self.textarea_ref.value = s
854
- self.textarea_ref.setSelectionRange(range[0], range[1])
855
- self.on_text_changed()
856
- },
857
- on_ranges : this.on_ranges
858
- })
859
- },
860
- render : function () {
861
- var self = this
862
- var cursors = []
863
- Object.keys(this.state.cursor_positions).forEach(function (k) {
864
- var p = self.state.cursor_positions[k]
865
- var style = {
866
- position : 'absolute',
867
- left : p[0],
868
- top : p[1]
869
- }
870
- Object.keys(self.props.cursor_style).forEach(function (k) {
871
- style[k] = self.props.cursor_style[k]
872
- })
873
- cursors.push(React.createElement('div', {
874
- key : k,
875
- style : style
876
- }))
877
- })
878
- return React.createElement('div', {
879
- style : {
880
- clipPath : 'inset(0px 0px 0px 0px)'
881
- },
882
- }, React.createElement('textarea', {
883
- ref : function (t) { self.textarea_ref = t },
884
- style : this.props.textarea_style,
885
- onChange : function (e) {
886
- self.ds.on_change()
887
- self.on_text_changed()
888
- },
889
- onMouseDown : function () {
890
- setTimeout(function () { self.ds.on_change() }, 0)
891
- },
892
- onKeyDown : function () {
893
- setTimeout(function () { self.ds.on_change() }, 0)
894
- },
895
- onScroll : function () {
896
- self.on_ranges(self.ranges)
897
- }
898
- }), cursors)
899
- }
900
- })
901
- }
902
-
903
877
  function compile_coffee (coffee, filename) {
904
878
  var compiled
905
879
  try {
@@ -930,10 +904,12 @@
930
904
  return compiled
931
905
  }
932
906
  function load_client_code (code) {
933
- var dom = {}, ui = {}
934
- if (code) eval(code)
935
- else { dom = window.dom; ui = window.ui }
936
- for (var k in ui) dom[k] = dom[k] || ui[k]
907
+ // What is this function for?
908
+ var dom = {}
909
+ if (code)
910
+ eval(code)
911
+ else
912
+ dom = window.dom
937
913
  for (var widget_name in dom)
938
914
  window.dom[widget_name] = dom[widget_name]
939
915
  }
@@ -945,6 +921,11 @@
945
921
  if (scripts[i].getAttribute('type')
946
922
  in {'statebus':1, 'coffeedom':1,'statebus-js':1,
947
923
  'coffee':1, 'coffeescript':1}) {
924
+
925
+ if (!window.CoffeeScript) {
926
+ console.error('Cannot load <script type="coffee"> because coffeescript library isn\'t present')
927
+ return
928
+ }
948
929
  // Compile coffeescript to javascript
949
930
  var compiled = scripts[i].text
950
931
  if (scripts[i].getAttribute('type') !== 'statebus-js')
@@ -976,15 +957,63 @@
976
957
  return widge(props, children)
977
958
  }
978
959
 
979
- window.users_widgets = users_widgets
980
- function load_widgets () {
981
- for (var w in users_widgets) {
982
- var nodes = document.getElementsByTagName(w)
983
- for (var i=0; i<nodes.length; i++)
984
- if (!nodes[i].seen)
985
- react_render(dom_to_widget(nodes[i]), nodes[i])
986
- }
960
+ // window.users_widgets = users_widgets
961
+ // function load_widgets () {
962
+ // for (var w in users_widgets) {
963
+ // var nodes = document.getElementsByTagName(w)
964
+ // for (var i=0; i<nodes.length; i++)
965
+ // if (!nodes[i].seen)
966
+ // react_render(dom_to_widget(nodes[i]), nodes[i])
967
+ // }
968
+ // }
969
+
970
+
971
+ bus.libs.react17.react_class = create_react_class
972
+ bus.libs.react17.coffreact = () => {
973
+ bus.libs.react17.reactive_dom()
974
+ load_coffee()
975
+ if (dom.BODY)
976
+ document.addEventListener(
977
+ 'DOMContentLoaded',
978
+ () => {
979
+ var root = document.createElement('root')
980
+ document.body.appendChild(root)
981
+ ReactDOM.render(BODY(), root)
982
+ },
983
+ false
984
+ )
987
985
  }
988
986
 
989
- load_scripts()
987
+ // if (statebus_server !== 'none') {
988
+ // if (clientjs_option('braid_mode')) {
989
+ // console.log('Using Braid-HTTP!')
990
+ // bus.libs.http_out ('/*', statebus_server)
991
+ // } else {
992
+ // bus.ws_mount ('/*', statebus_server)
993
+ // }
994
+ // }
995
+
996
+ http_automount()
997
+
998
+ statebus.compile_coffee = compile_coffee
999
+ statebus.load_client_code = load_client_code
1000
+
1001
+ // if (clientjs_option('globals')) {
1002
+ // // Setup globals
1003
+ // var globals = ['get', 'set', 'state']
1004
+
1005
+ // for (var i=0; i<globals.length; i++) {
1006
+ // console.log('globalizing', globals[i], 'as',
1007
+ // eval('bus.' + globals[i]))
1008
+ // window[globals[i]] = eval('bus.' + globals[i])
1009
+ // }
1010
+ // }
1011
+
1012
+ document.addEventListener('DOMContentLoaded', function () {
1013
+ if (window.statebus_ready)
1014
+ for (var i=0; i<statebus_ready.length; i++)
1015
+ statebus_ready[i]()
1016
+ }, false)
1017
+
1018
+ // document.addEventListener('DOMContentLoaded', load_widgets, false)
990
1019
  })()