riot_js-rails 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80b0ba35335567e7fd7bbcf8817e9065ae6a22b3
4
- data.tar.gz: 4a1c402cab48fbd2abde9560987698c3e79f9873
3
+ metadata.gz: df3508dc1799dfbf259c92264868490c0fcc613d
4
+ data.tar.gz: 0de2937b97cad066642072c78e270f34359bde33
5
5
  SHA512:
6
- metadata.gz: 8c92c478dc4c74f8a0e34301173c4f3fa4fa6e36b9b2cee0fe9e93a269debb60ba0a655079a9352be55bb42c2dc8a36c7cfa33bf7487b5deb1fdaccca0fa93c7
7
- data.tar.gz: e589230ca36f8124f435f7ec815a5850519ad0a2c02d5dc34b02286671f1e42c6ce630005bdfb0181ade0e56551cad4699ded2e7f407e63d189eb7538c103596
6
+ metadata.gz: 54910c7eb889db79234749034943f5acae183b7d3e2c9d6fd2f8dd0f4a5372193245cf2e02fa7cd2c6481c830e5364a557868f129b5c949889f5158187133f00
7
+ data.tar.gz: 8da5cb92adeb951f47ece46d435a9831dd1661a85c6295645b54ca60c8480ef666f17fe4d03c0197a497b0bfc5b75428ffef2fcf353b81e670c6abe87e9aec19
data/.gitignore CHANGED
File without changes
data/Gemfile CHANGED
File without changes
data/README.md CHANGED
File without changes
data/Rakefile CHANGED
File without changes
data/lib/riot_js/rails.rb CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,5 +1,5 @@
1
1
  module RiotJs
2
2
  module Rails
3
- VERSION = '0.4.0'
3
+ VERSION = '0.4.1'
4
4
  end
5
5
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ['lib']
21
21
 
22
22
 
23
- spec.add_dependency 'rails', '>= 3.0', '< 4.3'
23
+ spec.add_dependency 'rails', '>= 3.0', '<= 5.0'
24
24
  spec.add_dependency 'execjs', '~> 2'
25
25
 
26
26
  spec.add_development_dependency 'bundler', '~> 1.8'
File without changes
File without changes
File without changes
File without changes
@@ -1,8 +1,8 @@
1
- /* Riot v2.3.15, @license MIT, (c) 2015 Muut Inc. + contributors */
1
+ /* Riot v2.4.1, @license MIT */
2
2
 
3
3
  ;(function(window, undefined) {
4
4
  'use strict';
5
- var riot = { version: 'v2.3.15', settings: {} },
5
+ var riot = { version: 'v2.4.1', settings: {} },
6
6
  // be aware, internal usage
7
7
  // ATTENTION: prefix the global dynamic variables with `__`
8
8
 
@@ -16,9 +16,12 @@ var riot = { version: 'v2.3.15', settings: {} },
16
16
  /**
17
17
  * Const
18
18
  */
19
+ GLOBAL_MIXIN = '__global_mixin',
20
+
19
21
  // riot specific prefixes
20
22
  RIOT_PREFIX = 'riot-',
21
23
  RIOT_TAG = RIOT_PREFIX + 'tag',
24
+ RIOT_TAG_IS = 'data-is',
22
25
 
23
26
  // for typeof == '' comparisons
24
27
  T_STRING = 'string',
@@ -27,10 +30,15 @@ var riot = { version: 'v2.3.15', settings: {} },
27
30
  T_FUNCTION = 'function',
28
31
  // special native tags that cannot be treated like the others
29
32
  SPECIAL_TAGS_REGEX = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/,
30
- RESERVED_WORDS_BLACKLIST = ['_item', '_id', '_parent', 'update', 'root', 'mount', 'unmount', 'mixin', 'isMounted', 'isLoop', 'tags', 'parent', 'opts', 'trigger', 'on', 'off', 'one'],
33
+ RESERVED_WORDS_BLACKLIST = /^(?:_(?:item|id|parent)|update|root|(?:un)?mount|mixin|is(?:Mounted|Loop)|tags|parent|opts|trigger|o(?:n|ff|ne))$/,
34
+ // SVG tags list https://www.w3.org/TR/SVG/attindex.html#PresentationAttributes
35
+ SVG_TAGS_LIST = ['altGlyph', 'animate', 'animateColor', 'circle', 'clipPath', 'defs', 'ellipse', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feFlood', 'feGaussianBlur', 'feImage', 'feMerge', 'feMorphology', 'feOffset', 'feSpecularLighting', 'feTile', 'feTurbulence', 'filter', 'font', 'foreignObject', 'g', 'glyph', 'glyphRef', 'image', 'line', 'linearGradient', 'marker', 'mask', 'missing-glyph', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'switch', 'symbol', 'text', 'textPath', 'tref', 'tspan', 'use'],
31
36
 
32
37
  // version# for IE 8-11, 0 for others
33
- IE_VERSION = (window && window.document || {}).documentMode | 0
38
+ IE_VERSION = (window && window.document || {}).documentMode | 0,
39
+
40
+ // detect firefox to fix #1374
41
+ FIREFOX = window && !!window.InstallTrigger
34
42
  /* istanbul ignore next */
35
43
  riot.observable = function(el) {
36
44
 
@@ -42,102 +50,146 @@ riot.observable = function(el) {
42
50
  el = el || {}
43
51
 
44
52
  /**
45
- * Private variables and methods
53
+ * Private variables
46
54
  */
47
55
  var callbacks = {},
48
- slice = Array.prototype.slice,
49
- onEachEvent = function(e, fn) { e.replace(/\S+/g, fn) },
50
- defineProperty = function (key, value) {
51
- Object.defineProperty(el, key, {
52
- value: value,
53
- enumerable: false,
54
- writable: false,
55
- configurable: false
56
- })
57
- }
56
+ slice = Array.prototype.slice
58
57
 
59
58
  /**
60
- * Listen to the given space separated list of `events` and execute the `callback` each time an event is triggered.
61
- * @param { String } events - events ids
62
- * @param { Function } fn - callback function
63
- * @returns { Object } el
59
+ * Private Methods
64
60
  */
65
- defineProperty('on', function(events, fn) {
66
- if (typeof fn != 'function') return el
67
-
68
- onEachEvent(events, function(name, pos) {
69
- (callbacks[name] = callbacks[name] || []).push(fn)
70
- fn.typed = pos > 0
71
- })
72
-
73
- return el
74
- })
75
61
 
76
62
  /**
77
- * Removes the given space separated list of `events` listeners
78
- * @param { String } events - events ids
79
- * @param { Function } fn - callback function
80
- * @returns { Object } el
63
+ * Helper function needed to get and loop all the events in a string
64
+ * @param { String } e - event string
65
+ * @param {Function} fn - callback
81
66
  */
82
- defineProperty('off', function(events, fn) {
83
- if (events == '*' && !fn) callbacks = {}
84
- else {
85
- onEachEvent(events, function(name) {
86
- if (fn) {
87
- var arr = callbacks[name]
88
- for (var i = 0, cb; cb = arr && arr[i]; ++i) {
89
- if (cb == fn) arr.splice(i--, 1)
90
- }
91
- } else delete callbacks[name]
92
- })
67
+ function onEachEvent(e, fn) {
68
+ var es = e.split(' '), l = es.length, i = 0, name, indx
69
+ for (; i < l; i++) {
70
+ name = es[i]
71
+ indx = name.indexOf('.')
72
+ if (name) fn( ~indx ? name.substring(0, indx) : name, i, ~indx ? name.slice(indx + 1) : null)
93
73
  }
94
- return el
95
- })
74
+ }
96
75
 
97
76
  /**
98
- * Listen to the given space separated list of `events` and execute the `callback` at most once
99
- * @param { String } events - events ids
100
- * @param { Function } fn - callback function
101
- * @returns { Object } el
77
+ * Public Api
102
78
  */
103
- defineProperty('one', function(events, fn) {
104
- function on() {
105
- el.off(events, on)
106
- fn.apply(el, arguments)
107
- }
108
- return el.on(events, on)
109
- })
110
79
 
111
- /**
112
- * Execute all callback functions that listen to the given space separated list of `events`
113
- * @param { String } events - events ids
114
- * @returns { Object } el
115
- */
116
- defineProperty('trigger', function(events) {
80
+ // extend the el object adding the observable methods
81
+ Object.defineProperties(el, {
82
+ /**
83
+ * Listen to the given space separated list of `events` and
84
+ * execute the `callback` each time an event is triggered.
85
+ * @param { String } events - events ids
86
+ * @param { Function } fn - callback function
87
+ * @returns { Object } el
88
+ */
89
+ on: {
90
+ value: function(events, fn) {
91
+ if (typeof fn != 'function') return el
92
+
93
+ onEachEvent(events, function(name, pos, ns) {
94
+ (callbacks[name] = callbacks[name] || []).push(fn)
95
+ fn.typed = pos > 0
96
+ fn.ns = ns
97
+ })
98
+
99
+ return el
100
+ },
101
+ enumerable: false,
102
+ writable: false,
103
+ configurable: false
104
+ },
117
105
 
118
- // getting the arguments
119
- // skipping the first one
120
- var args = slice.call(arguments, 1),
121
- fns
106
+ /**
107
+ * Removes the given space separated list of `events` listeners
108
+ * @param { String } events - events ids
109
+ * @param { Function } fn - callback function
110
+ * @returns { Object } el
111
+ */
112
+ off: {
113
+ value: function(events, fn) {
114
+ if (events == '*' && !fn) callbacks = {}
115
+ else {
116
+ onEachEvent(events, function(name, pos, ns) {
117
+ if (fn || ns) {
118
+ var arr = callbacks[name]
119
+ for (var i = 0, cb; cb = arr && arr[i]; ++i) {
120
+ if (cb == fn || ns && cb.ns == ns) arr.splice(i--, 1)
121
+ }
122
+ } else delete callbacks[name]
123
+ })
124
+ }
125
+ return el
126
+ },
127
+ enumerable: false,
128
+ writable: false,
129
+ configurable: false
130
+ },
131
+
132
+ /**
133
+ * Listen to the given space separated list of `events` and
134
+ * execute the `callback` at most once
135
+ * @param { String } events - events ids
136
+ * @param { Function } fn - callback function
137
+ * @returns { Object } el
138
+ */
139
+ one: {
140
+ value: function(events, fn) {
141
+ function on() {
142
+ el.off(events, on)
143
+ fn.apply(el, arguments)
144
+ }
145
+ return el.on(events, on)
146
+ },
147
+ enumerable: false,
148
+ writable: false,
149
+ configurable: false
150
+ },
122
151
 
123
- onEachEvent(events, function(name) {
152
+ /**
153
+ * Execute all callback functions that listen to
154
+ * the given space separated list of `events`
155
+ * @param { String } events - events ids
156
+ * @returns { Object } el
157
+ */
158
+ trigger: {
159
+ value: function(events) {
124
160
 
125
- fns = slice.call(callbacks[name] || [], 0)
161
+ // getting the arguments
162
+ var arglen = arguments.length - 1,
163
+ args = new Array(arglen),
164
+ fns
126
165
 
127
- for (var i = 0, fn; fn = fns[i]; ++i) {
128
- if (fn.busy) return
129
- fn.busy = 1
130
- fn.apply(el, fn.typed ? [name].concat(args) : args)
131
- if (fns[i] !== fn) { i-- }
132
- fn.busy = 0
133
- }
166
+ for (var i = 0; i < arglen; i++) {
167
+ args[i] = arguments[i + 1] // skip first argument
168
+ }
134
169
 
135
- if (callbacks['*'] && name != '*')
136
- el.trigger.apply(el, ['*', name].concat(args))
170
+ onEachEvent(events, function(name, pos, ns) {
137
171
 
138
- })
172
+ fns = slice.call(callbacks[name] || [], 0)
139
173
 
140
- return el
174
+ for (var i = 0, fn; fn = fns[i]; ++i) {
175
+ if (fn.busy) continue
176
+ fn.busy = 1
177
+ if (!ns || fn.ns == ns) fn.apply(el, fn.typed ? [name].concat(args) : args)
178
+ if (fns[i] !== fn) { i-- }
179
+ fn.busy = 0
180
+ }
181
+
182
+ if (callbacks['*'] && name != '*')
183
+ el.trigger.apply(el, ['*', name].concat(args))
184
+
185
+ })
186
+
187
+ return el
188
+ },
189
+ enumerable: false,
190
+ writable: false,
191
+ configurable: false
192
+ }
141
193
  })
142
194
 
143
195
  return el
@@ -152,7 +204,7 @@ riot.observable = function(el) {
152
204
  */
153
205
 
154
206
 
155
- var RE_ORIGIN = /^.+?\/+[^\/]+/,
207
+ var RE_ORIGIN = /^.+?\/\/+[^\/]+/,
156
208
  EVENT_LISTENER = 'EventListener',
157
209
  REMOVE_EVENT_LISTENER = 'remove' + EVENT_LISTENER,
158
210
  ADD_EVENT_LISTENER = 'add' + EVENT_LISTENER,
@@ -246,7 +298,7 @@ function isString(str) {
246
298
  * @returns {string} path from root
247
299
  */
248
300
  function getPathFromRoot(href) {
249
- return (href || loc.href || '')[REPLACE](RE_ORIGIN, '')
301
+ return (href || loc.href)[REPLACE](RE_ORIGIN, '')
250
302
  }
251
303
 
252
304
  /**
@@ -257,7 +309,7 @@ function getPathFromRoot(href) {
257
309
  function getPathFromBase(href) {
258
310
  return base[0] == '#'
259
311
  ? (href || loc.href || '').split(base)[1] || ''
260
- : getPathFromRoot(href)[REPLACE](base, '')
312
+ : (loc ? getPathFromRoot(href) : href || '')[REPLACE](base, '')
261
313
  }
262
314
 
263
315
  function emit(force) {
@@ -291,6 +343,7 @@ function click(e) {
291
343
 
292
344
  var el = e.target
293
345
  while (el && el.nodeName != 'A') el = el.parentNode
346
+
294
347
  if (
295
348
  !el || el.nodeName != 'A' // not A tag
296
349
  || el[HAS_ATTRIBUTE]('download') // has download attr
@@ -397,10 +450,11 @@ var route = mainRouter.m.bind(mainRouter)
397
450
  */
398
451
  route.create = function() {
399
452
  var newSubRouter = new Router()
453
+ // assign sub-router's main method
454
+ var router = newSubRouter.m.bind(newSubRouter)
400
455
  // stop only this sub-router
401
- newSubRouter.m.stop = newSubRouter.s.bind(newSubRouter)
402
- // return sub-router's main method
403
- return newSubRouter.m.bind(newSubRouter)
456
+ router.stop = newSubRouter.s.bind(newSubRouter)
457
+ return router
404
458
  }
405
459
 
406
460
  /**
@@ -484,9 +538,8 @@ riot.route = route
484
538
 
485
539
  /**
486
540
  * The riot template engine
487
- * @version v2.3.21
541
+ * @version v2.4.0
488
542
  */
489
-
490
543
  /**
491
544
  * riot.util.brackets
492
545
  *
@@ -549,7 +602,7 @@ var brackets = (function (UNDEF) {
549
602
 
550
603
  var arr = pair.split(' ')
551
604
 
552
- if (arr.length !== 2 || /[\x00-\x1F<>a-zA-Z0-9'",;\\]/.test(pair)) {
605
+ if (arr.length !== 2 || /[\x00-\x1F<>a-zA-Z0-9'",;\\]/.test(pair)) { // eslint-disable-line
553
606
  throw new Error('Unsupported brackets "' + pair + '"')
554
607
  }
555
608
  arr = arr.concat(pair.replace(/(?=[[\]()*+?.^$|])/g, '\\').split(' '))
@@ -580,7 +633,7 @@ var brackets = (function (UNDEF) {
580
633
 
581
634
  isexpr = start = re.lastIndex = 0
582
635
 
583
- while (match = re.exec(str)) {
636
+ while ((match = re.exec(str))) {
584
637
 
585
638
  pos = match.index
586
639
 
@@ -590,8 +643,9 @@ var brackets = (function (UNDEF) {
590
643
  re.lastIndex = skipBraces(str, match[2], re.lastIndex)
591
644
  continue
592
645
  }
593
- if (!match[3])
646
+ if (!match[3]) {
594
647
  continue
648
+ }
595
649
  }
596
650
 
597
651
  if (!match[1]) {
@@ -609,10 +663,11 @@ var brackets = (function (UNDEF) {
609
663
  return parts
610
664
 
611
665
  function unescapeStr (s) {
612
- if (tmpl || isexpr)
666
+ if (tmpl || isexpr) {
613
667
  parts.push(s && s.replace(_bp[5], '$1'))
614
- else
668
+ } else {
615
669
  parts.push(s)
670
+ }
616
671
  }
617
672
 
618
673
  function skipBraces (s, ch, ix) {
@@ -622,7 +677,7 @@ var brackets = (function (UNDEF) {
622
677
 
623
678
  recch.lastIndex = ix
624
679
  ix = 1
625
- while (match = recch.exec(s)) {
680
+ while ((match = recch.exec(s))) {
626
681
  if (match[1] &&
627
682
  !(match[1] === ch ? ++ix : --ix)) break
628
683
  }
@@ -636,15 +691,12 @@ var brackets = (function (UNDEF) {
636
691
 
637
692
  _brackets.loopKeys = function loopKeys (expr) {
638
693
  var m = expr.match(_cache[9])
694
+
639
695
  return m
640
696
  ? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
641
697
  : { val: expr.trim() }
642
698
  }
643
699
 
644
- _brackets.hasRaw = function (src) {
645
- return _cache[10].test(src)
646
- }
647
-
648
700
  _brackets.array = function array (pair) {
649
701
  return pair ? _create(pair) : _cache
650
702
  }
@@ -654,13 +706,13 @@ var brackets = (function (UNDEF) {
654
706
  _cache = _create(pair)
655
707
  _regex = pair === DEFAULT ? _loopback : _rewrite
656
708
  _cache[9] = _regex(_pairs[9])
657
- _cache[10] = _regex(_pairs[10])
658
709
  }
659
710
  cachedBrackets = pair
660
711
  }
661
712
 
662
713
  function _setSettings (o) {
663
714
  var b
715
+
664
716
  o = o || {}
665
717
  b = o.brackets
666
718
  Object.defineProperty(o, 'brackets', {
@@ -728,22 +780,28 @@ var tmpl = (function () {
728
780
  }
729
781
 
730
782
  function _create (str) {
731
-
732
783
  var expr = _getTmpl(str)
784
+
733
785
  if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
734
786
 
787
+ /* eslint-disable */
788
+
735
789
  return new Function('E', expr + ';')
790
+ /* eslint-enable */
736
791
  }
737
792
 
738
793
  var
794
+ CH_IDEXPR = '\u2057',
795
+ RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/,
739
796
  RE_QBLOCK = RegExp(brackets.S_QBLOCKS, 'g'),
740
- RE_QBMARK = /\x01(\d+)~/g
797
+ RE_DQUOTE = /\u2057/g,
798
+ RE_QBMARK = /\u2057(\d+)~/g
741
799
 
742
800
  function _getTmpl (str) {
743
801
  var
744
802
  qstr = [],
745
803
  expr,
746
- parts = brackets.split(str.replace(/\u2057/g, '"'), 1)
804
+ parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1)
747
805
 
748
806
  if (parts.length > 2 || parts[0]) {
749
807
  var i, j, list = []
@@ -752,11 +810,11 @@ var tmpl = (function () {
752
810
 
753
811
  expr = parts[i]
754
812
 
755
- if (expr && (expr = i & 1 ?
813
+ if (expr && (expr = i & 1
756
814
 
757
- _parseExpr(expr, 1, qstr) :
815
+ ? _parseExpr(expr, 1, qstr)
758
816
 
759
- '"' + expr
817
+ : '"' + expr
760
818
  .replace(/\\/g, '\\\\')
761
819
  .replace(/\r\n?|\n/g, '\\n')
762
820
  .replace(/"/g, '\\"') +
@@ -766,21 +824,21 @@ var tmpl = (function () {
766
824
 
767
825
  }
768
826
 
769
- expr = j < 2 ? list[0] :
770
- '[' + list.join(',') + '].join("")'
827
+ expr = j < 2 ? list[0]
828
+ : '[' + list.join(',') + '].join("")'
771
829
 
772
830
  } else {
773
831
 
774
832
  expr = _parseExpr(parts[1], 0, qstr)
775
833
  }
776
834
 
777
- if (qstr[0])
835
+ if (qstr[0]) {
778
836
  expr = expr.replace(RE_QBMARK, function (_, pos) {
779
837
  return qstr[pos]
780
838
  .replace(/\r/g, '\\r')
781
839
  .replace(/\n/g, '\\n')
782
840
  })
783
-
841
+ }
784
842
  return expr
785
843
  }
786
844
 
@@ -789,16 +847,13 @@ var tmpl = (function () {
789
847
  '(': /[()]/g,
790
848
  '[': /[[\]]/g,
791
849
  '{': /[{}]/g
792
- },
793
- CS_IDENT = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\x01(\d+)~):/
850
+ }
794
851
 
795
852
  function _parseExpr (expr, asText, qstr) {
796
853
 
797
- if (expr[0] === '=') expr = expr.slice(1)
798
-
799
854
  expr = expr
800
855
  .replace(RE_QBLOCK, function (s, div) {
801
- return s.length > 2 && !div ? '\x01' + (qstr.push(s) - 1) + '~' : s
856
+ return s.length > 2 && !div ? CH_IDEXPR + (qstr.push(s) - 1) + '~' : s
802
857
  })
803
858
  .replace(/\s+/g, ' ').trim()
804
859
  .replace(/\ ?([[\({},?\.:])\ ?/g, '$1')
@@ -810,7 +865,7 @@ var tmpl = (function () {
810
865
  match
811
866
 
812
867
  while (expr &&
813
- (match = expr.match(CS_IDENT)) &&
868
+ (match = expr.match(RE_CSNAME)) &&
814
869
  !match.index
815
870
  ) {
816
871
  var
@@ -829,8 +884,8 @@ var tmpl = (function () {
829
884
  list[cnt++] = _wrapExpr(jsb, 1, key)
830
885
  }
831
886
 
832
- expr = !cnt ? _wrapExpr(expr, asText) :
833
- cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
887
+ expr = !cnt ? _wrapExpr(expr, asText)
888
+ : cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
834
889
  }
835
890
  return expr
836
891
 
@@ -850,7 +905,7 @@ var tmpl = (function () {
850
905
  }
851
906
 
852
907
  // istanbul ignore next: not both
853
- var
908
+ var // eslint-disable-next-line max-len
854
909
  JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
855
910
  JS_VARNAME = /[,{][$\w]+:|(^ *|[^$\w\.])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,
856
911
  JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/
@@ -878,14 +933,14 @@ var tmpl = (function () {
878
933
 
879
934
  if (key) {
880
935
 
881
- expr = (tb ?
882
- 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
936
+ expr = (tb
937
+ ? 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
883
938
  ) + '?"' + key + '":""'
884
939
 
885
940
  } else if (asText) {
886
941
 
887
- expr = 'function(v){' + (tb ?
888
- expr.replace('return ', 'v=') : 'v=(' + expr + ')'
942
+ expr = 'function(v){' + (tb
943
+ ? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
889
944
  ) + ';return v||v===0?v:""}.call(this)'
890
945
  }
891
946
 
@@ -895,7 +950,7 @@ var tmpl = (function () {
895
950
  // istanbul ignore next: compatibility fix for beta versions
896
951
  _tmpl.parse = function (s) { return s }
897
952
 
898
- _tmpl.version = brackets.version = 'v2.3.21'
953
+ _tmpl.version = brackets.version = 'v2.4.0'
899
954
 
900
955
  return _tmpl
901
956
 
@@ -908,41 +963,50 @@ var tmpl = (function () {
908
963
  See: http://kangax.github.io/compat-table/es5/#ie8
909
964
  http://codeplanet.io/dropping-ie8/
910
965
  */
911
- var mkdom = (function (checkIE) {
912
-
966
+ var mkdom = (function _mkdom() {
967
+ var
968
+ reHasYield = /<yield\b/i,
969
+ reYieldAll = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>|>)/ig,
970
+ reYieldSrc = /<yield\s+to=['"]([^'">]*)['"]\s*>([\S\s]*?)<\/yield\s*>/ig,
971
+ reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig
913
972
  var
914
- reToSrc = /<yield\s+to=(['"])?@\1\s*>([\S\s]+?)<\/yield\s*>/.source,
915
973
  rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' },
916
- GENERIC = 'div'
917
-
918
- checkIE = checkIE && checkIE < 10
919
- var tblTags = checkIE
920
- ? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/
974
+ tblTags = IE_VERSION && IE_VERSION < 10
975
+ ? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/
921
976
 
922
- // creates any dom element in a div, table, or colgroup container
977
+ /**
978
+ * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
979
+ * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
980
+ *
981
+ * @param {string} templ - The template coming from the custom tag definition
982
+ * @param {string} [html] - HTML content that comes from the DOM element where you
983
+ * will mount the tag, mostly the original tag in the page
984
+ * @returns {HTMLElement} DOM element with _templ_ merged through `YIELD` with the _html_.
985
+ */
923
986
  function _mkdom(templ, html) {
924
-
925
- var match = templ && templ.match(/^\s*<([-\w]+)/),
987
+ var
988
+ match = templ && templ.match(/^\s*<([-\w]+)/),
926
989
  tagName = match && match[1].toLowerCase(),
927
- el = mkEl(GENERIC)
990
+ el = mkEl('div', isSVGTag(tagName))
928
991
 
929
992
  // replace all the yield tags with the tag inner html
930
- templ = replaceYield(templ, html || '')
993
+ templ = replaceYield(templ, html)
931
994
 
932
995
  /* istanbul ignore next */
933
- //if ((checkIE || !startsWith(tagName, 'opt')) && SPECIAL_TAGS_REGEX.test(tagName))
934
996
  if (tblTags.test(tagName))
935
997
  el = specialTags(el, templ, tagName)
936
998
  else
937
- el.innerHTML = templ
999
+ setInnerHTML(el, templ)
938
1000
 
939
1001
  el.stub = true
940
1002
 
941
1003
  return el
942
1004
  }
943
1005
 
944
- // creates the root element for table and select child elements
945
- // tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
1006
+ /*
1007
+ Creates the root element for table or select child elements:
1008
+ tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
1009
+ */
946
1010
  function specialTags(el, templ, tagName) {
947
1011
  var
948
1012
  select = tagName[0] === 'o',
@@ -958,39 +1022,41 @@ var mkdom = (function (checkIE) {
958
1022
  if (select) {
959
1023
  parent.selectedIndex = -1 // for IE9, compatible w/current riot behavior
960
1024
  } else {
1025
+ // avoids insertion of cointainer inside container (ex: tbody inside tbody)
961
1026
  var tname = rootEls[tagName]
962
- if (tname && parent.children.length === 1) parent = $(tname, parent)
1027
+ if (tname && parent.childElementCount === 1) parent = $(tname, parent)
963
1028
  }
964
1029
  return parent
965
1030
  }
966
1031
 
967
- /**
968
- * Replace the yield tag from any tag template with the innerHTML of the
969
- * original tag in the page
970
- * @param { String } templ - tag implementation template
971
- * @param { String } html - original content of the tag in the DOM
972
- * @returns { String } tag template updated without the yield tag
973
- */
1032
+ /*
1033
+ Replace the yield tag from any tag template with the innerHTML of the
1034
+ original tag in the page
1035
+ */
974
1036
  function replaceYield(templ, html) {
975
1037
  // do nothing if no yield
976
- if (!/<yield\b/i.test(templ)) return templ
1038
+ if (!reHasYield.test(templ)) return templ
977
1039
 
978
1040
  // be careful with #1343 - string on the source having `$1`
979
- var n = 0
980
- templ = templ.replace(/<yield\s+from=['"]([-\w]+)['"]\s*(?:\/>|>\s*<\/yield\s*>)/ig,
981
- function (str, ref) {
982
- var m = html.match(RegExp(reToSrc.replace('@', ref), 'i'))
983
- ++n
984
- return m && m[2] || ''
985
- })
1041
+ var src = {}
1042
+
1043
+ html = html && html.replace(reYieldSrc, function (_, ref, text) {
1044
+ src[ref] = src[ref] || text // preserve first definition
1045
+ return ''
1046
+ }).trim()
986
1047
 
987
- // yield without any "from", replace yield in templ with the innerHTML
988
- return n ? templ : templ.replace(/<yield\s*(?:\/>|>\s*<\/yield\s*>)/gi, html)
1048
+ return templ
1049
+ .replace(reYieldDest, function (_, ref, def) { // yield with from - to attrs
1050
+ return src[ref] || def || ''
1051
+ })
1052
+ .replace(reYieldAll, function (_, def) { // yield without any "from"
1053
+ return html || def || ''
1054
+ })
989
1055
  }
990
1056
 
991
1057
  return _mkdom
992
1058
 
993
- })(IE_VERSION)
1059
+ })()
994
1060
 
995
1061
  /**
996
1062
  * Convert the item looped into an object used to extend the child tag properties
@@ -1097,12 +1163,12 @@ function _each(dom, parent, expr) {
1097
1163
 
1098
1164
  var mustReorder = typeof getAttr(dom, 'no-reorder') !== T_STRING || remAttr(dom, 'no-reorder'),
1099
1165
  tagName = getTagName(dom),
1100
- impl = __tagImpl[tagName] || { tmpl: dom.outerHTML },
1166
+ impl = __tagImpl[tagName] || { tmpl: getOuterHTML(dom) },
1101
1167
  useRoot = SPECIAL_TAGS_REGEX.test(tagName),
1102
1168
  root = dom.parentNode,
1103
1169
  ref = document.createTextNode(''),
1104
1170
  child = getTag(dom),
1105
- isOption = /^option$/i.test(tagName), // the option tags must be treated differently
1171
+ isOption = tagName.toLowerCase() === 'option', // the option tags must be treated differently
1106
1172
  tags = [],
1107
1173
  oldItems = [],
1108
1174
  hasKeys,
@@ -1127,8 +1193,6 @@ function _each(dom, parent, expr) {
1127
1193
  // create a fragment to hold the new DOM nodes to inject in the parent tag
1128
1194
  frag = document.createDocumentFragment()
1129
1195
 
1130
-
1131
-
1132
1196
  // object loop. any changes cause full redraw
1133
1197
  if (!isArray(items)) {
1134
1198
  hasKeys = items || false
@@ -1139,9 +1203,14 @@ function _each(dom, parent, expr) {
1139
1203
  }
1140
1204
 
1141
1205
  // loop all the new items
1142
- items.forEach(function(item, i) {
1206
+ var i = 0,
1207
+ itemsLength = items.length
1208
+
1209
+ for (; i < itemsLength; i++) {
1143
1210
  // reorder only if the items are objects
1144
- var _mustReorder = mustReorder && item instanceof Object,
1211
+ var
1212
+ item = items[i],
1213
+ _mustReorder = mustReorder && typeof item == T_OBJECT && !hasKeys,
1145
1214
  oldPos = oldItems.indexOf(item),
1146
1215
  pos = ~oldPos && _mustReorder ? oldPos : i,
1147
1216
  // does a tag exist in this position?
@@ -1165,9 +1234,10 @@ function _each(dom, parent, expr) {
1165
1234
  }, dom.innerHTML)
1166
1235
 
1167
1236
  tag.mount()
1237
+
1168
1238
  if (isVirtual) tag._root = tag.root.firstChild // save reference for further moves or inserts
1169
1239
  // this tag must be appended
1170
- if (i == tags.length) {
1240
+ if (i == tags.length || !tags[i]) { // fix 1581
1171
1241
  if (isVirtual)
1172
1242
  addVirtual(tag, frag)
1173
1243
  else frag.appendChild(tag.root)
@@ -1176,16 +1246,19 @@ function _each(dom, parent, expr) {
1176
1246
  else {
1177
1247
  if (isVirtual)
1178
1248
  addVirtual(tag, root, tags[i])
1179
- else root.insertBefore(tag.root, tags[i].root)
1249
+ else root.insertBefore(tag.root, tags[i].root) // #1374 some browsers reset selected here
1180
1250
  oldItems.splice(i, 0, item)
1181
1251
  }
1182
1252
 
1183
1253
  tags.splice(i, 0, tag)
1184
1254
  pos = i // handled here so no move
1185
- } else tag.update(item)
1255
+ } else tag.update(item, true)
1186
1256
 
1187
1257
  // reorder the tag if it's not located in its previous position
1188
- if (pos !== i && _mustReorder) {
1258
+ if (
1259
+ pos !== i && _mustReorder &&
1260
+ tags[i] // fix 1581 unable to reproduce it in a test!
1261
+ ) {
1189
1262
  // update the DOM
1190
1263
  if (isVirtual)
1191
1264
  moveVirtual(tag, root, tags[i], dom.childNodes.length)
@@ -1207,14 +1280,26 @@ function _each(dom, parent, expr) {
1207
1280
  tag._item = item
1208
1281
  // cache the real parent tag internally
1209
1282
  defineProperty(tag, '_parent', parent)
1210
-
1211
- })
1283
+ }
1212
1284
 
1213
1285
  // remove the redundant tags
1214
1286
  unmountRedundant(items, tags)
1215
1287
 
1216
1288
  // insert the new nodes
1217
- if (isOption) root.appendChild(frag)
1289
+ if (isOption) {
1290
+ root.appendChild(frag)
1291
+
1292
+ // #1374 FireFox bug in <option selected={expression}>
1293
+ if (FIREFOX && !root.multiple) {
1294
+ for (var n = 0; n < root.length; n++) {
1295
+ if (root[n].__riot1374) {
1296
+ root.selectedIndex = n // clear other options
1297
+ delete root[n].__riot1374
1298
+ break
1299
+ }
1300
+ }
1301
+ }
1302
+ }
1218
1303
  else root.insertBefore(frag, ref)
1219
1304
 
1220
1305
  // set the 'tags' property of the parent tag
@@ -1367,13 +1452,13 @@ function Tag(impl, conf, innerHTML) {
1367
1452
  expressions = [],
1368
1453
  childTags = [],
1369
1454
  root = conf.root,
1370
- fn = impl.fn,
1371
1455
  tagName = root.tagName.toLowerCase(),
1372
1456
  attr = {},
1373
1457
  propsInSyncWithParent = [],
1374
1458
  dom
1375
1459
 
1376
- if (fn && root._tag) root._tag.unmount(true)
1460
+ // only call unmount if we have a valid __tagImpl (has name property)
1461
+ if (impl.name && root._tag) root._tag.unmount(true)
1377
1462
 
1378
1463
  // not yet mounted
1379
1464
  this.isMounted = false
@@ -1387,7 +1472,9 @@ function Tag(impl, conf, innerHTML) {
1387
1472
  // it could be handy to use it also to improve the virtual dom rendering speed
1388
1473
  defineProperty(this, '_riot_id', ++__uid) // base 1 allows test !t._riot_id
1389
1474
 
1390
- extend(this, { parent: parent, root: root, opts: opts, tags: {} }, item)
1475
+ extend(this, { parent: parent, root: root, opts: opts}, item)
1476
+ // protect the "tags" property from being overridden
1477
+ defineProperty(this, 'tags', {})
1391
1478
 
1392
1479
  // grab attributes
1393
1480
  each(root.attributes, function(el) {
@@ -1424,7 +1511,7 @@ function Tag(impl, conf, innerHTML) {
1424
1511
  if (!self.parent || !isLoop) return
1425
1512
  each(Object.keys(self.parent), function(k) {
1426
1513
  // some properties must be always in sync with the parent tag
1427
- var mustSync = !contains(RESERVED_WORDS_BLACKLIST, k) && contains(propsInSyncWithParent, k)
1514
+ var mustSync = !RESERVED_WORDS_BLACKLIST.test(k) && contains(propsInSyncWithParent, k)
1428
1515
  if (typeof self[k] === T_UNDEF || mustSync) {
1429
1516
  // track the property to keep in sync
1430
1517
  // so we can keep it updated
@@ -1434,7 +1521,13 @@ function Tag(impl, conf, innerHTML) {
1434
1521
  })
1435
1522
  }
1436
1523
 
1437
- defineProperty(this, 'update', function(data) {
1524
+ /**
1525
+ * Update the tag expressions and options
1526
+ * @param { * } data - data we want to use to extend the tag properties
1527
+ * @param { Boolean } isInherited - is this update coming from a parent tag?
1528
+ * @returns { self }
1529
+ */
1530
+ defineProperty(this, 'update', function(data, isInherited) {
1438
1531
 
1439
1532
  // make sure the data passed will not override
1440
1533
  // the component core methods
@@ -1442,7 +1535,7 @@ function Tag(impl, conf, innerHTML) {
1442
1535
  // inherit properties from the parent
1443
1536
  inheritFromParent()
1444
1537
  // normalize the tag properties in case an item object was initially passed
1445
- if (data && typeof item === T_OBJECT) {
1538
+ if (data && isObject(item)) {
1446
1539
  normalizeData(data)
1447
1540
  item = data
1448
1541
  }
@@ -1450,11 +1543,16 @@ function Tag(impl, conf, innerHTML) {
1450
1543
  updateOpts()
1451
1544
  self.trigger('update', data)
1452
1545
  update(expressions, self)
1546
+
1453
1547
  // the updated event will be triggered
1454
- // once the DOM will be ready and all the reflows are completed
1548
+ // once the DOM will be ready and all the re-flows are completed
1455
1549
  // this is useful if you want to get the "real" root properties
1456
1550
  // 4 ex: root.offsetWidth ...
1457
- rAF(function() { self.trigger('updated') })
1551
+ if (isInherited && self.parent)
1552
+ // closes #1599
1553
+ self.parent.one('updated', function() { self.trigger('updated') })
1554
+ else rAF(function() { self.trigger('updated') })
1555
+
1458
1556
  return this
1459
1557
  })
1460
1558
 
@@ -1491,8 +1589,15 @@ function Tag(impl, conf, innerHTML) {
1491
1589
 
1492
1590
  updateOpts()
1493
1591
 
1592
+ // add global mixins
1593
+ var globalMixin = riot.mixin(GLOBAL_MIXIN)
1594
+ if (globalMixin)
1595
+ for (var i in globalMixin)
1596
+ if (globalMixin.hasOwnProperty(i))
1597
+ self.mixin(globalMixin[i])
1598
+
1494
1599
  // initialiation
1495
- if (fn) fn.call(self, opts)
1600
+ if (impl.fn) impl.fn.call(self, opts)
1496
1601
 
1497
1602
  // parse layout after init. fn may calculate args for nested custom tags
1498
1603
  parseExpressions(dom, self, expressions)
@@ -1502,10 +1607,10 @@ function Tag(impl, conf, innerHTML) {
1502
1607
 
1503
1608
  // update the root adding custom attributes coming from the compiler
1504
1609
  // it fixes also #1087
1505
- if (impl.attrs || hasImpl) {
1610
+ if (impl.attrs)
1506
1611
  walkAttributes(impl.attrs, function (k, v) { setAttr(root, k, v) })
1612
+ if (impl.attrs || hasImpl)
1507
1613
  parseExpressions(self.root, self, expressions)
1508
- }
1509
1614
 
1510
1615
  if (!self.parent || isLoop) self.update(item)
1511
1616
 
@@ -1514,13 +1619,14 @@ function Tag(impl, conf, innerHTML) {
1514
1619
 
1515
1620
  if (isLoop && !hasImpl) {
1516
1621
  // update the root attribute for the looped elements
1517
- self.root = root = dom.firstChild
1518
-
1622
+ root = dom.firstChild
1519
1623
  } else {
1520
1624
  while (dom.firstChild) root.appendChild(dom.firstChild)
1521
- if (root.stub) self.root = root = parent.root
1625
+ if (root.stub) root = parent.root
1522
1626
  }
1523
1627
 
1628
+ defineProperty(self, 'root', root)
1629
+
1524
1630
  // parse the named dom nodes in the looped child
1525
1631
  // adding them to the parent as well
1526
1632
  if (isLoop)
@@ -1555,12 +1661,6 @@ function Tag(impl, conf, innerHTML) {
1555
1661
  if (~tagIndex)
1556
1662
  __virtualDom.splice(tagIndex, 1)
1557
1663
 
1558
- if (this._virts) {
1559
- each(this._virts, function(v) {
1560
- if (v.parentNode) v.parentNode.removeChild(v)
1561
- })
1562
- }
1563
-
1564
1664
  if (p) {
1565
1665
 
1566
1666
  if (parent) {
@@ -1583,11 +1683,19 @@ function Tag(impl, conf, innerHTML) {
1583
1683
 
1584
1684
  if (!keepRootTag)
1585
1685
  p.removeChild(el)
1586
- else
1587
- // the riot-tag attribute isn't needed anymore, remove it
1588
- remAttr(p, 'riot-tag')
1686
+ else {
1687
+ // the riot-tag and the data-is attributes aren't needed anymore, remove them
1688
+ remAttr(p, RIOT_TAG_IS)
1689
+ remAttr(p, RIOT_TAG) // this will be removed in riot 3.0.0
1690
+ }
1691
+
1589
1692
  }
1590
1693
 
1694
+ if (this._virts) {
1695
+ each(this._virts, function(v) {
1696
+ if (v.parentNode) v.parentNode.removeChild(v)
1697
+ })
1698
+ }
1591
1699
 
1592
1700
  self.trigger('unmount')
1593
1701
  toggle()
@@ -1597,6 +1705,10 @@ function Tag(impl, conf, innerHTML) {
1597
1705
 
1598
1706
  })
1599
1707
 
1708
+ // proxy function to bind updates
1709
+ // dispatched from a parent tag
1710
+ function onChildUpdate(data) { self.update(data, true) }
1711
+
1600
1712
  function toggle(isMount) {
1601
1713
 
1602
1714
  // mount/unmount children
@@ -1609,10 +1721,12 @@ function Tag(impl, conf, innerHTML) {
1609
1721
  // the loop tags will be always in sync with the parent automatically
1610
1722
  if (isLoop)
1611
1723
  parent[evt]('unmount', self.unmount)
1612
- else
1613
- parent[evt]('update', self.update)[evt]('unmount', self.unmount)
1724
+ else {
1725
+ parent[evt]('update', onChildUpdate)[evt]('unmount', self.unmount)
1726
+ }
1614
1727
  }
1615
1728
 
1729
+
1616
1730
  // named elements available for fn
1617
1731
  parseNamedElements(dom, this, childTags)
1618
1732
 
@@ -1690,31 +1804,44 @@ function update(expressions, tag) {
1690
1804
  value = tmpl(expr.expr, tag),
1691
1805
  parent = expr.dom.parentNode
1692
1806
 
1693
- if (expr.bool)
1694
- value = value ? attrName : false
1695
- else if (value == null)
1807
+ if (expr.bool) {
1808
+ value = !!value
1809
+ } else if (value == null) {
1696
1810
  value = ''
1697
-
1698
- // leave out riot- prefixes from strings inside textarea
1699
- // fix #815: any value -> string
1700
- if (parent && parent.tagName == 'TEXTAREA') {
1701
- value = ('' + value).replace(/riot-/g, '')
1702
- // change textarea's value
1703
- parent.value = value
1704
1811
  }
1705
1812
 
1706
- // no change
1707
- if (expr.value === value) return
1813
+ // #1638: regression of #1612, update the dom only if the value of the
1814
+ // expression was changed
1815
+ if (expr.value === value) {
1816
+ return
1817
+ }
1708
1818
  expr.value = value
1709
1819
 
1710
- // text node
1820
+ // textarea and text nodes has no attribute name
1711
1821
  if (!attrName) {
1712
- dom.nodeValue = '' + value // #815 related
1822
+ // about #815 w/o replace: the browser converts the value to a string,
1823
+ // the comparison by "==" does too, but not in the server
1824
+ value += ''
1825
+ // test for parent avoids error with invalid assignment to nodeValue
1826
+ if (parent) {
1827
+ if (parent.tagName === 'TEXTAREA') {
1828
+ parent.value = value // #1113
1829
+ if (!IE_VERSION) dom.nodeValue = value // #1625 IE throws here, nodeValue
1830
+ } // will be available on 'updated'
1831
+ else dom.nodeValue = value
1832
+ }
1833
+ return
1834
+ }
1835
+
1836
+ // ~~#1612: look for changes in dom.value when updating the value~~
1837
+ if (attrName === 'value') {
1838
+ dom.value = value
1713
1839
  return
1714
1840
  }
1715
1841
 
1716
1842
  // remove original attribute
1717
1843
  remAttr(dom, attrName)
1844
+
1718
1845
  // event handler
1719
1846
  if (isFunction(value)) {
1720
1847
  setEventHandler(attrName, value, dom, tag)
@@ -1751,28 +1878,25 @@ function update(expressions, tag) {
1751
1878
  dom.inStub = true
1752
1879
  }
1753
1880
  // show / hide
1754
- } else if (/^(show|hide)$/.test(attrName)) {
1755
- if (attrName == 'hide') value = !value
1881
+ } else if (attrName === 'show') {
1756
1882
  dom.style.display = value ? '' : 'none'
1757
1883
 
1758
- // field value
1759
- } else if (attrName == 'value') {
1760
- dom.value = value
1761
-
1762
- // <img src="{ expr }">
1763
- } else if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
1764
- if (value)
1765
- setAttr(dom, attrName.slice(RIOT_PREFIX.length), value)
1884
+ } else if (attrName === 'hide') {
1885
+ dom.style.display = value ? 'none' : ''
1766
1886
 
1767
- } else {
1768
- if (expr.bool) {
1769
- dom[attrName] = value
1770
- if (!value) return
1887
+ } else if (expr.bool) {
1888
+ dom[attrName] = value
1889
+ if (value) setAttr(dom, attrName, attrName)
1890
+ if (FIREFOX && attrName === 'selected' && dom.tagName === 'OPTION') {
1891
+ dom.__riot1374 = value // #1374
1771
1892
  }
1772
1893
 
1773
- if (value === 0 || value && typeof value !== T_OBJECT)
1774
- setAttr(dom, attrName, value)
1775
-
1894
+ } else if (value === 0 || value && typeof value !== T_OBJECT) {
1895
+ // <img src="{ expr }">
1896
+ if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
1897
+ attrName = attrName.slice(RIOT_PREFIX.length)
1898
+ }
1899
+ setAttr(dom, attrName, value)
1776
1900
  }
1777
1901
 
1778
1902
  })
@@ -1804,6 +1928,56 @@ function isFunction(v) {
1804
1928
  return typeof v === T_FUNCTION || false // avoid IE problems
1805
1929
  }
1806
1930
 
1931
+ /**
1932
+ * Get the outer html of any DOM node SVGs included
1933
+ * @param { Object } el - DOM node to parse
1934
+ * @returns { String } el.outerHTML
1935
+ */
1936
+ function getOuterHTML(el) {
1937
+ if (el.outerHTML) return el.outerHTML
1938
+ // some browsers do not support outerHTML on the SVGs tags
1939
+ else {
1940
+ var container = mkEl('div')
1941
+ container.appendChild(el.cloneNode(true))
1942
+ return container.innerHTML
1943
+ }
1944
+ }
1945
+
1946
+ /**
1947
+ * Set the inner html of any DOM node SVGs included
1948
+ * @param { Object } container - DOM node where we will inject the new html
1949
+ * @param { String } html - html to inject
1950
+ */
1951
+ function setInnerHTML(container, html) {
1952
+ if (typeof container.innerHTML != T_UNDEF) container.innerHTML = html
1953
+ // some browsers do not support innerHTML on the SVGs tags
1954
+ else {
1955
+ var doc = new DOMParser().parseFromString(html, 'application/xml')
1956
+ container.appendChild(
1957
+ container.ownerDocument.importNode(doc.documentElement, true)
1958
+ )
1959
+ }
1960
+ }
1961
+
1962
+ /**
1963
+ * Checks wether a DOM node must be considered part of an svg document
1964
+ * @param { String } name - tag name
1965
+ * @returns { Boolean } -
1966
+ */
1967
+ function isSVGTag(name) {
1968
+ return ~SVG_TAGS_LIST.indexOf(name)
1969
+ }
1970
+
1971
+ /**
1972
+ * Detect if the argument passed is an object, exclude null.
1973
+ * NOTE: Use isObject(x) && !isArray(x) to excludes arrays.
1974
+ * @param { * } v - whatever you want to pass to this function
1975
+ * @returns { Boolean } -
1976
+ */
1977
+ function isObject(v) {
1978
+ return v && typeof v === T_OBJECT // typeof null is 'object'
1979
+ }
1980
+
1807
1981
  /**
1808
1982
  * Remove any DOM attribute from a node
1809
1983
  * @param { Object } dom - DOM node we want to update
@@ -1850,7 +2024,8 @@ function setAttr(dom, name, val) {
1850
2024
  * @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
1851
2025
  */
1852
2026
  function getTag(dom) {
1853
- return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
2027
+ return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG_IS) ||
2028
+ getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
1854
2029
  }
1855
2030
  /**
1856
2031
  * Add a child tag to its parent into the `tags` object
@@ -1954,7 +2129,7 @@ function defineProperty(el, key, value, options) {
1954
2129
  value: value,
1955
2130
  enumerable: false,
1956
2131
  writable: false,
1957
- configurable: false
2132
+ configurable: true
1958
2133
  }, options))
1959
2134
  return el
1960
2135
  }
@@ -2038,8 +2213,7 @@ function cleanUpData(data) {
2038
2213
 
2039
2214
  var o = {}
2040
2215
  for (var key in data) {
2041
- if (!contains(RESERVED_WORDS_BLACKLIST, key))
2042
- o[key] = data[key]
2216
+ if (!RESERVED_WORDS_BLACKLIST.test(key)) o[key] = data[key]
2043
2217
  }
2044
2218
  return o
2045
2219
  }
@@ -2094,10 +2268,13 @@ function isInStub(dom) {
2094
2268
  /**
2095
2269
  * Create a generic DOM node
2096
2270
  * @param { String } name - name of the DOM node we want to create
2271
+ * @param { Boolean } isSvg - should we use a SVG as parent node?
2097
2272
  * @returns { Object } DOM node just created
2098
2273
  */
2099
- function mkEl(name) {
2100
- return document.createElement(name)
2274
+ function mkEl(name, isSvg) {
2275
+ return isSvg ?
2276
+ document.createElementNS('http://www.w3.org/2000/svg', 'svg') :
2277
+ document.createElement(name)
2101
2278
  }
2102
2279
 
2103
2280
  /**
@@ -2251,17 +2428,41 @@ riot.util = { brackets: brackets, tmpl: tmpl }
2251
2428
  * Create a mixin that could be globally shared across all the tags
2252
2429
  */
2253
2430
  riot.mixin = (function() {
2254
- var mixins = {}
2431
+ var mixins = {},
2432
+ globals = mixins[GLOBAL_MIXIN] = {},
2433
+ _id = 0
2255
2434
 
2256
2435
  /**
2257
2436
  * Create/Return a mixin by its name
2258
- * @param { String } name - mixin name
2259
- * @param { Object } mixin - mixin logic
2260
- * @returns { Object } the mixin logic
2437
+ * @param { String } name - mixin name (global mixin if object)
2438
+ * @param { Object } mixin - mixin logic
2439
+ * @param { Boolean } g - is global?
2440
+ * @returns { Object } the mixin logic
2261
2441
  */
2262
- return function(name, mixin) {
2263
- if (!mixin) return mixins[name]
2264
- mixins[name] = mixin
2442
+ return function(name, mixin, g) {
2443
+ // Unnamed global
2444
+ if (isObject(name)) {
2445
+ riot.mixin('__unnamed_'+_id++, name, true)
2446
+ return
2447
+ }
2448
+
2449
+ var store = g ? globals : mixins
2450
+
2451
+ // Getter
2452
+ if (!mixin) {
2453
+ if (typeof store[name] === T_UNDEF) {
2454
+ throw new Error('Unregistered mixin: ' + name)
2455
+ }
2456
+ return store[name]
2457
+ }
2458
+ // Setter
2459
+ if (isFunction(mixin)) {
2460
+ extend(mixin.prototype, store[name] || {})
2461
+ store[name] = mixin
2462
+ }
2463
+ else {
2464
+ store[name] = extend(store[name] || {}, mixin)
2465
+ }
2265
2466
  }
2266
2467
 
2267
2468
  })()
@@ -2287,6 +2488,7 @@ riot.tag = function(name, html, css, attrs, fn) {
2287
2488
  if (isFunction(css)) fn = css
2288
2489
  else styleManager.add(css)
2289
2490
  }
2491
+ name = name.toLowerCase()
2290
2492
  __tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
2291
2493
  return name
2292
2494
  }
@@ -2298,10 +2500,9 @@ riot.tag = function(name, html, css, attrs, fn) {
2298
2500
  * @param { String } css - custom tag css
2299
2501
  * @param { String } attrs - root tag attributes
2300
2502
  * @param { Function } fn - user function
2301
- * @param { string } [bpair] - brackets used in the compilation
2302
2503
  * @returns { String } name/id of the tag just created
2303
2504
  */
2304
- riot.tag2 = function(name, html, css, attrs, fn, bpair) {
2505
+ riot.tag2 = function(name, html, css, attrs, fn) {
2305
2506
  if (css) styleManager.add(css)
2306
2507
  //if (bpair) riot.settings.brackets = bpair
2307
2508
  __tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
@@ -2326,8 +2527,10 @@ riot.mount = function(selector, tagName, opts) {
2326
2527
  function addRiotTags(arr) {
2327
2528
  var list = ''
2328
2529
  each(arr, function (e) {
2329
- if (!/[^-\w]/.test(e))
2330
- list += ',*[' + RIOT_TAG + '=' + e.trim() + ']'
2530
+ if (!/[^-\w]/.test(e)) {
2531
+ e = e.trim().toLowerCase()
2532
+ list += ',[' + RIOT_TAG_IS + '="' + e + '"],[' + RIOT_TAG + '="' + e + '"]'
2533
+ }
2331
2534
  })
2332
2535
  return list
2333
2536
  }
@@ -2338,18 +2541,21 @@ riot.mount = function(selector, tagName, opts) {
2338
2541
  }
2339
2542
 
2340
2543
  function pushTags(root) {
2341
- var last
2342
-
2343
2544
  if (root.tagName) {
2344
- if (tagName && (!(last = getAttr(root, RIOT_TAG)) || last != tagName))
2345
- setAttr(root, RIOT_TAG, tagName)
2545
+ var riotTag = getAttr(root, RIOT_TAG_IS) || getAttr(root, RIOT_TAG)
2346
2546
 
2347
- var tag = mountTo(root, tagName || root.getAttribute(RIOT_TAG) || root.tagName.toLowerCase(), opts)
2547
+ // have tagName? force riot-tag to be the same
2548
+ if (tagName && riotTag !== tagName) {
2549
+ riotTag = tagName
2550
+ setAttr(root, RIOT_TAG_IS, tagName)
2551
+ setAttr(root, RIOT_TAG, tagName) // this will be removed in riot 3.0.0
2552
+ }
2553
+ var tag = mountTo(root, riotTag || root.tagName.toLowerCase(), opts)
2348
2554
 
2349
2555
  if (tag) tags.push(tag)
2350
- } else if (root.length)
2556
+ } else if (root.length) {
2351
2557
  each(root, pushTags) // assume nodeList
2352
-
2558
+ }
2353
2559
  }
2354
2560
 
2355
2561
  // ----- mount code -----
@@ -2357,7 +2563,7 @@ riot.mount = function(selector, tagName, opts) {
2357
2563
  // inject styles into DOM
2358
2564
  styleManager.inject()
2359
2565
 
2360
- if (typeof tagName === T_OBJECT) {
2566
+ if (isObject(tagName)) {
2361
2567
  opts = tagName
2362
2568
  tagName = 0
2363
2569
  }
@@ -2370,7 +2576,7 @@ riot.mount = function(selector, tagName, opts) {
2370
2576
  selector = allTags = selectAllTags()
2371
2577
  else
2372
2578
  // or just the ones named like the selector
2373
- selector += addRiotTags(selector.split(','))
2579
+ selector += addRiotTags(selector.split(/, */))
2374
2580
 
2375
2581
  // make sure to pass always a selector
2376
2582
  // to the querySelectorAll function
@@ -2399,10 +2605,7 @@ riot.mount = function(selector, tagName, opts) {
2399
2605
  tagName = 0
2400
2606
  }
2401
2607
 
2402
- if (els.tagName)
2403
- pushTags(els)
2404
- else
2405
- each(els, pushTags)
2608
+ pushTags(els)
2406
2609
 
2407
2610
  return tags
2408
2611
  }
@@ -2417,6 +2620,11 @@ riot.update = function() {
2417
2620
  })
2418
2621
  }
2419
2622
 
2623
+ /**
2624
+ * Export the Virtual DOM
2625
+ */
2626
+ riot.vdom = __virtualDom
2627
+
2420
2628
  /**
2421
2629
  * Export the Tag constructor
2422
2630
  */