riot_js-rails 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ /*
2
+ Less CSS plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ var
10
+ mixobj = require('./_utils').mixobj,
11
+ parser = require('less')
12
+
13
+ var defopts = {
14
+ sync: true,
15
+ syncImport: true,
16
+ compress: true
17
+ }
18
+
19
+ module.exports = function _less (tag, css, opts, url) {
20
+ var ret
21
+
22
+ opts = mixobj(defopts, { filename: url }, opts)
23
+
24
+ parser.render(css, opts, function (err, result) {
25
+ // istanbul ignore next
26
+ if (err) throw err
27
+ ret = result.css
28
+ })
29
+
30
+ return ret
31
+ }
@@ -0,0 +1,20 @@
1
+ /*
2
+ LiveScript JS plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ var
10
+ mixobj = require('./_utils').mixobj,
11
+ parser = require('livescript')
12
+
13
+ var defopts = {
14
+ bare: true,
15
+ header: false
16
+ }
17
+
18
+ module.exports = function _livescript (js, opts) {
19
+ return parser.compile(js, mixobj(defopts, opts))
20
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ Jade HTML plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ var
10
+ mixobj = require('./_utils').mixobj,
11
+ parser = require('pug')
12
+
13
+ var defopts = {
14
+ pretty: true,
15
+ doctype: 'html'
16
+ }
17
+
18
+ module.exports = function _pug (html, opts, url) {
19
+
20
+ opts = mixobj(defopts, { filename: url }, opts)
21
+
22
+ return parser.render(html, opts)
23
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ Sass CSS plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ 2016-08-30: Fixed issues with indentation
9
+ */
10
+ var
11
+ mixobj = require('./_utils').mixobj,
12
+ getdir = require('path').dirname,
13
+ parser = require('node-sass')
14
+
15
+ var defopts = {
16
+ indentedSyntax: true,
17
+ omitSourceMapUrl: true,
18
+ outputStyle: 'compact'
19
+ }
20
+
21
+ module.exports = function _sass (tag, css, opts, url) {
22
+ var spc = css.match(/^\s+/)
23
+
24
+ if (spc) {
25
+ css = css.replace(RegExp('^' + spc[0], 'gm'), '')
26
+ if (/^\t/gm.test(css)) {
27
+ opts.indentType = 'tab'
28
+ }
29
+ }
30
+
31
+ opts = mixobj(defopts, { data: css, includePaths: [getdir(url)] }, opts)
32
+
33
+ return parser.renderSync(opts).css + ''
34
+ }
@@ -0,0 +1,25 @@
1
+ /*
2
+ Scss CSS plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ var
10
+ mixobj = require('./_utils').mixobj,
11
+ getdir = require('path').dirname,
12
+ parser = require('node-sass')
13
+
14
+ var defopts = {
15
+ indentedSyntax: false,
16
+ omitSourceMapUrl: true,
17
+ outputStyle: 'compact'
18
+ }
19
+
20
+ module.exports = function _scss (tag, css, opts, url) {
21
+
22
+ opts = mixobj(defopts, { data: css, includePaths: [getdir(url)] }, opts)
23
+
24
+ return parser.renderSync(opts).css + ''
25
+ }
@@ -0,0 +1,26 @@
1
+ /*
2
+ Stylus CSS plugin, with optional nib support.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ var
10
+ mixobj = require('./_utils').mixobj,
11
+ tryreq = require('./_utils').tryreq,
12
+ parser = require('stylus')
13
+
14
+ // Optional nib support
15
+ var nib = tryreq('nib')
16
+
17
+ // istanbul ignore next: can't run both
18
+ module.exports = nib
19
+ ? function _stylus (tag, css, opts, url) {
20
+ opts = mixobj({ filename: url }, opts)
21
+ return parser(css, opts).use(nib()).import('nib').render()
22
+ }
23
+ : function _stylus (tag, css, opts, url) {
24
+ opts = mixobj({ filename: url }, opts)
25
+ return parser.render(css, opts)
26
+ }
@@ -0,0 +1,9 @@
1
+ /*
2
+ TypeScript JS plugin.
3
+ Part of the riot-compiler, license MIT
4
+
5
+ History
6
+ -------
7
+ 2016-03-09: Initital release
8
+ */
9
+ module.exports = require('typescript-simple')
@@ -0,0 +1,19 @@
1
+
2
+ 'use strict'
3
+
4
+ // istanbul ignore next
5
+ function safeRegex (re) {
6
+ var src = re.source
7
+ var opt = re.global ? 'g' : ''
8
+
9
+ if (re.ignoreCase) opt += 'i'
10
+ if (re.multiline) opt += 'm'
11
+
12
+ for (var i = 1; i < arguments.length; i++) {
13
+ src = src.replace('@', '\\' + arguments[i])
14
+ }
15
+
16
+ return new RegExp(src, opt)
17
+ }
18
+
19
+ module.exports = safeRegex
@@ -1,539 +1,377 @@
1
- /* Riot v2.6.1, @license MIT */
2
-
3
- ;(function(window, undefined) {
4
- 'use strict';
5
- var riot = { version: 'v2.6.1', settings: {} },
6
- // be aware, internal usage
7
- // ATTENTION: prefix the global dynamic variables with `__`
8
-
9
- // counter to give a unique id to all the Tag instances
10
- __uid = 0,
11
- // tags instances cache
12
- __virtualDom = [],
13
- // tags implementation cache
14
- __tagImpl = {},
15
-
16
- /**
17
- * Const
18
- */
19
- GLOBAL_MIXIN = '__global_mixin',
20
-
21
- // riot specific prefixes
22
- RIOT_PREFIX = 'riot-',
23
- RIOT_TAG = RIOT_PREFIX + 'tag',
24
- RIOT_TAG_IS = 'data-is',
25
-
26
- // for typeof == '' comparisons
27
- T_STRING = 'string',
28
- T_OBJECT = 'object',
29
- T_UNDEF = 'undefined',
30
- T_FUNCTION = 'function',
31
- XLINK_NS = 'http://www.w3.org/1999/xlink',
32
- XLINK_REGEX = /^xlink:(\w+)/,
33
- // special native tags that cannot be treated like the others
34
- SPECIAL_TAGS_REGEX = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/,
35
- RESERVED_WORDS_BLACKLIST = /^(?:_(?:item|id|parent)|update|root|(?:un)?mount|mixin|is(?:Mounted|Loop)|tags|parent|opts|trigger|o(?:n|ff|ne))$/,
36
- // SVG tags list https://www.w3.org/TR/SVG/attindex.html#PresentationAttributes
37
- 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'],
38
-
39
- // version# for IE 8-11, 0 for others
40
- IE_VERSION = (window && window.document || {}).documentMode | 0,
41
-
42
- // detect firefox to fix #1374
43
- FIREFOX = window && !!window.InstallTrigger
44
- /* istanbul ignore next */
45
- riot.observable = function(el) {
46
-
47
- /**
48
- * Extend the original object or create a new empty one
49
- * @type { Object }
50
- */
51
-
52
- el = el || {}
53
-
54
- /**
55
- * Private variables
56
- */
57
- var callbacks = {},
58
- slice = Array.prototype.slice
59
-
60
- /**
61
- * Private Methods
62
- */
63
-
64
- /**
65
- * Helper function needed to get and loop all the events in a string
66
- * @param { String } e - event string
67
- * @param {Function} fn - callback
68
- */
69
- function onEachEvent(e, fn) {
70
- var es = e.split(' '), l = es.length, i = 0
71
- for (; i < l; i++) {
72
- var name = es[i]
73
- if (name) fn(name, i)
74
- }
75
- }
76
-
77
- /**
78
- * Public Api
79
- */
80
-
81
- // extend the el object adding the observable methods
82
- Object.defineProperties(el, {
83
- /**
84
- * Listen to the given space separated list of `events` and
85
- * execute the `callback` each time an event is triggered.
86
- * @param { String } events - events ids
87
- * @param { Function } fn - callback function
88
- * @returns { Object } el
89
- */
90
- on: {
91
- value: function(events, fn) {
92
- if (typeof fn != 'function') return el
93
-
94
- onEachEvent(events, function(name, pos) {
95
- (callbacks[name] = callbacks[name] || []).push(fn)
96
- fn.typed = pos > 0
97
- })
98
-
99
- return el
100
- },
101
- enumerable: false,
102
- writable: false,
103
- configurable: false
104
- },
105
-
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) {
117
- if (fn) {
118
- var arr = callbacks[name]
119
- for (var i = 0, cb; cb = arr && arr[i]; ++i) {
120
- if (cb == fn) 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
- },
151
-
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) {
160
-
161
- // getting the arguments
162
- var arglen = arguments.length - 1,
163
- args = new Array(arglen),
164
- fns
165
-
166
- for (var i = 0; i < arglen; i++) {
167
- args[i] = arguments[i + 1] // skip first argument
168
- }
169
-
170
- onEachEvent(events, function(name, pos) {
171
-
172
- fns = slice.call(callbacks[name] || [], 0)
173
-
174
- for (var i = 0, fn; fn = fns[i]; ++i) {
175
- if (fn.busy) continue
176
- fn.busy = 1
177
- 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
- }
193
- })
194
-
195
- return el
196
-
1
+ /* Riot v3.0.7, @license MIT */
2
+ (function (global, factory) {
3
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
5
+ (factory((global.riot = global.riot || {})));
6
+ }(this, (function (exports) { 'use strict';
7
+
8
+ var __TAGS_CACHE = [];
9
+ var __TAG_IMPL = {};
10
+ var GLOBAL_MIXIN = '__global_mixin';
11
+ var ATTRS_PREFIX = 'riot-';
12
+ var REF_DIRECTIVES = ['data-ref', 'ref'];
13
+ var IS_DIRECTIVE = 'data-is';
14
+ var CONDITIONAL_DIRECTIVE = 'if';
15
+ var LOOP_DIRECTIVE = 'each';
16
+ var LOOP_NO_REORDER_DIRECTIVE = 'no-reorder';
17
+ var SHOW_DIRECTIVE = 'show';
18
+ var HIDE_DIRECTIVE = 'hide';
19
+ var T_STRING = 'string';
20
+ var T_OBJECT = 'object';
21
+ var T_UNDEF = 'undefined';
22
+ var T_FUNCTION = 'function';
23
+ var XLINK_NS = 'http://www.w3.org/1999/xlink';
24
+ var XLINK_REGEX = /^xlink:(\w+)/;
25
+ var WIN = typeof window === T_UNDEF ? undefined : window;
26
+ var RE_SPECIAL_TAGS = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/;
27
+ var RE_SPECIAL_TAGS_NO_OPTION = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/;
28
+ var RE_RESERVED_NAMES = /^(?:_(?:item|id|parent)|update|root|(?:un)?mount|mixin|is(?:Mounted|Loop)|tags|refs|parent|opts|trigger|o(?:n|ff|ne))$/;
29
+ var RE_SVG_TAGS = /^(altGlyph|animate(?:Color)?|circle|clipPath|defs|ellipse|fe(?:Blend|ColorMatrix|ComponentTransfer|Composite|ConvolveMatrix|DiffuseLighting|DisplacementMap|Flood|GaussianBlur|Image|Merge|Morphology|Offset|SpecularLighting|Tile|Turbulence)|filter|font|foreignObject|g(?:lyph)?(?:Ref)?|image|line(?:arGradient)?|ma(?:rker|sk)|missing-glyph|path|pattern|poly(?:gon|line)|radialGradient|rect|stop|svg|switch|symbol|text(?:Path)?|tref|tspan|use)$/;
30
+ var RE_HTML_ATTRS = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g;
31
+ var CASE_SENSITIVE_ATTRIBUTES = { 'viewbox': 'viewBox' };
32
+ var RE_BOOL_ATTRS = /^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|selected|sortable|truespeed|typemustmatch)$/;
33
+ var IE_VERSION = (WIN && WIN.document || {}).documentMode | 0;
34
+
35
+ /**
36
+ * Check whether a DOM node must be considered a part of an svg document
37
+ * @param { String } name -
38
+ * @returns { Boolean } -
39
+ */
40
+ function isSVGTag(name) {
41
+ return RE_SVG_TAGS.test(name)
197
42
  }
198
- /* istanbul ignore next */
199
- ;(function(riot) {
200
43
 
201
44
  /**
202
- * Simple client-side router
203
- * @module riot-route
45
+ * Check Check if the passed argument is undefined
46
+ * @param { String } value -
47
+ * @returns { Boolean } -
204
48
  */
205
-
206
-
207
- var RE_ORIGIN = /^.+?\/\/+[^\/]+/,
208
- EVENT_LISTENER = 'EventListener',
209
- REMOVE_EVENT_LISTENER = 'remove' + EVENT_LISTENER,
210
- ADD_EVENT_LISTENER = 'add' + EVENT_LISTENER,
211
- HAS_ATTRIBUTE = 'hasAttribute',
212
- REPLACE = 'replace',
213
- POPSTATE = 'popstate',
214
- HASHCHANGE = 'hashchange',
215
- TRIGGER = 'trigger',
216
- MAX_EMIT_STACK_LEVEL = 3,
217
- win = typeof window != 'undefined' && window,
218
- doc = typeof document != 'undefined' && document,
219
- hist = win && history,
220
- loc = win && (hist.location || win.location), // see html5-history-api
221
- prot = Router.prototype, // to minify more
222
- clickEvent = doc && doc.ontouchstart ? 'touchstart' : 'click',
223
- started = false,
224
- central = riot.observable(),
225
- routeFound = false,
226
- debouncedEmit,
227
- base, current, parser, secondParser, emitStack = [], emitStackLevel = 0
49
+ function isBoolAttr(value) {
50
+ return RE_BOOL_ATTRS.test(value)
51
+ }
228
52
 
229
53
  /**
230
- * Default parser. You can replace it via router.parser method.
231
- * @param {string} path - current path (normalized)
232
- * @returns {array} array
54
+ * Check if passed argument is a function
55
+ * @param { * } value -
56
+ * @returns { Boolean } -
233
57
  */
234
- function DEFAULT_PARSER(path) {
235
- return path.split(/[/?#]/)
58
+ function isFunction(value) {
59
+ return typeof value === T_FUNCTION
236
60
  }
237
61
 
238
62
  /**
239
- * Default parser (second). You can replace it via router.parser method.
240
- * @param {string} path - current path (normalized)
241
- * @param {string} filter - filter string (normalized)
242
- * @returns {array} array
63
+ * Check if passed argument is an object, exclude null
64
+ * NOTE: use isObject(x) && !isArray(x) to excludes arrays.
65
+ * @param { * } value -
66
+ * @returns { Boolean } -
243
67
  */
244
- function DEFAULT_SECOND_PARSER(path, filter) {
245
- var re = new RegExp('^' + filter[REPLACE](/\*/g, '([^/?#]+?)')[REPLACE](/\.\./, '.*') + '$'),
246
- args = path.match(re)
247
-
248
- if (args) return args.slice(1)
68
+ function isObject(value) {
69
+ return value && typeof value === T_OBJECT // typeof null is 'object'
249
70
  }
250
71
 
251
72
  /**
252
- * Simple/cheap debounce implementation
253
- * @param {function} fn - callback
254
- * @param {number} delay - delay in seconds
255
- * @returns {function} debounced function
73
+ * Check if passed argument is undefined
74
+ * @param { * } value -
75
+ * @returns { Boolean } -
256
76
  */
257
- function debounce(fn, delay) {
258
- var t
259
- return function () {
260
- clearTimeout(t)
261
- t = setTimeout(fn, delay)
262
- }
77
+ function isUndefined(value) {
78
+ return typeof value === T_UNDEF
263
79
  }
264
80
 
265
81
  /**
266
- * Set the window listeners to trigger the routes
267
- * @param {boolean} autoExec - see route.start
82
+ * Check if passed argument is a string
83
+ * @param { * } value -
84
+ * @returns { Boolean } -
268
85
  */
269
- function start(autoExec) {
270
- debouncedEmit = debounce(emit, 1)
271
- win[ADD_EVENT_LISTENER](POPSTATE, debouncedEmit)
272
- win[ADD_EVENT_LISTENER](HASHCHANGE, debouncedEmit)
273
- doc[ADD_EVENT_LISTENER](clickEvent, click)
274
- if (autoExec) emit(true)
86
+ function isString(value) {
87
+ return typeof value === T_STRING
275
88
  }
276
89
 
277
90
  /**
278
- * Router class
91
+ * Check if passed argument is empty. Different from falsy, because we dont consider 0 or false to be blank
92
+ * @param { * } value -
93
+ * @returns { Boolean } -
279
94
  */
280
- function Router() {
281
- this.$ = []
282
- riot.observable(this) // make it observable
283
- central.on('stop', this.s.bind(this))
284
- central.on('emit', this.e.bind(this))
285
- }
286
-
287
- function normalize(path) {
288
- return path[REPLACE](/^\/|\/$/, '')
289
- }
290
-
291
- function isString(str) {
292
- return typeof str == 'string'
95
+ function isBlank(value) {
96
+ return isUndefined(value) || value === null || value === ''
293
97
  }
294
98
 
295
99
  /**
296
- * Get the part after domain name
297
- * @param {string} href - fullpath
298
- * @returns {string} path from root
100
+ * Check if passed argument is a kind of array
101
+ * @param { * } value -
102
+ * @returns { Boolean } -
299
103
  */
300
- function getPathFromRoot(href) {
301
- return (href || loc.href)[REPLACE](RE_ORIGIN, '')
104
+ function isArray(value) {
105
+ return Array.isArray(value) || value instanceof Array
302
106
  }
303
107
 
304
108
  /**
305
- * Get the part after base
306
- * @param {string} href - fullpath
307
- * @returns {string} path from base
109
+ * Check whether object's property could be overridden
110
+ * @param { Object } obj - source object
111
+ * @param { String } key - object property
112
+ * @returns { Boolean } -
308
113
  */
309
- function getPathFromBase(href) {
310
- return base[0] == '#'
311
- ? (href || loc.href || '').split(base)[1] || ''
312
- : (loc ? getPathFromRoot(href) : href || '')[REPLACE](base, '')
114
+ function isWritable(obj, key) {
115
+ var descriptor = Object.getOwnPropertyDescriptor(obj, key);
116
+ return isUndefined(obj[key]) || descriptor && descriptor.writable
313
117
  }
314
118
 
315
- function emit(force) {
316
- // the stack is needed for redirections
317
- var isRoot = emitStackLevel == 0, first
318
- if (MAX_EMIT_STACK_LEVEL <= emitStackLevel) return
319
-
320
- emitStackLevel++
321
- emitStack.push(function() {
322
- var path = getPathFromBase()
323
- if (force || path != current) {
324
- central[TRIGGER]('emit', path)
325
- current = path
326
- }
327
- })
328
- if (isRoot) {
329
- while (first = emitStack.shift()) first() // stack increses within this call
330
- emitStackLevel = 0
331
- }
119
+ /**
120
+ * Check if passed argument is a reserved name
121
+ * @param { String } value -
122
+ * @returns { Boolean } -
123
+ */
124
+ function isReservedName(value) {
125
+ return RE_RESERVED_NAMES.test(value)
332
126
  }
333
127
 
334
- function click(e) {
335
- if (
336
- e.which != 1 // not left click
337
- || e.metaKey || e.ctrlKey || e.shiftKey // or meta keys
338
- || e.defaultPrevented // or default prevented
339
- ) return
340
-
341
- var el = e.target
342
- while (el && el.nodeName != 'A') el = el.parentNode
128
+ var check = Object.freeze({
129
+ isSVGTag: isSVGTag,
130
+ isBoolAttr: isBoolAttr,
131
+ isFunction: isFunction,
132
+ isObject: isObject,
133
+ isUndefined: isUndefined,
134
+ isString: isString,
135
+ isBlank: isBlank,
136
+ isArray: isArray,
137
+ isWritable: isWritable,
138
+ isReservedName: isReservedName
139
+ });
343
140
 
344
- if (
345
- !el || el.nodeName != 'A' // not A tag
346
- || el[HAS_ATTRIBUTE]('download') // has download attr
347
- || !el[HAS_ATTRIBUTE]('href') // has no href attr
348
- || el.target && el.target != '_self' // another window or frame
349
- || el.href.indexOf(loc.href.match(RE_ORIGIN)[0]) == -1 // cross origin
350
- ) return
351
-
352
- if (el.href != loc.href
353
- && (
354
- el.href.split('#')[0] == loc.href.split('#')[0] // internal jump
355
- || base[0] != '#' && getPathFromRoot(el.href).indexOf(base) !== 0 // outside of base
356
- || base[0] == '#' && el.href.split(base)[0] != loc.href.split(base)[0] // outside of #base
357
- || !go(getPathFromBase(el.href), el.title || doc.title) // route not found
358
- )) return
359
-
360
- e.preventDefault()
141
+ /**
142
+ * Shorter and fast way to select multiple nodes in the DOM
143
+ * @param { String } selector - DOM selector
144
+ * @param { Object } ctx - DOM node where the targets of our search will is located
145
+ * @returns { Object } dom nodes found
146
+ */
147
+ function $$(selector, ctx) {
148
+ return (ctx || document).querySelectorAll(selector)
361
149
  }
362
150
 
363
151
  /**
364
- * Go to the path
365
- * @param {string} path - destination path
366
- * @param {string} title - page title
367
- * @param {boolean} shouldReplace - use replaceState or pushState
368
- * @returns {boolean} - route not found flag
152
+ * Shorter and fast way to select a single node in the DOM
153
+ * @param { String } selector - unique dom selector
154
+ * @param { Object } ctx - DOM node where the target of our search will is located
155
+ * @returns { Object } dom node found
369
156
  */
370
- function go(path, title, shouldReplace) {
371
- // Server-side usage: directly execute handlers for the path
372
- if (!hist) return central[TRIGGER]('emit', getPathFromBase(path))
373
-
374
- path = base + normalize(path)
375
- title = title || doc.title
376
- // browsers ignores the second parameter `title`
377
- shouldReplace
378
- ? hist.replaceState(null, title, path)
379
- : hist.pushState(null, title, path)
380
- // so we need to set it manually
381
- doc.title = title
382
- routeFound = false
383
- emit()
384
- return routeFound
157
+ function $(selector, ctx) {
158
+ return (ctx || document).querySelector(selector)
385
159
  }
386
160
 
387
161
  /**
388
- * Go to path or set action
389
- * a single string: go there
390
- * two strings: go there with setting a title
391
- * two strings and boolean: replace history with setting a title
392
- * a single function: set an action on the default route
393
- * a string/RegExp and a function: set an action on the route
394
- * @param {(string|function)} first - path / action / filter
395
- * @param {(string|RegExp|function)} second - title / action
396
- * @param {boolean} third - replace flag
162
+ * Create a document fragment
163
+ * @returns { Object } document fragment
397
164
  */
398
- prot.m = function(first, second, third) {
399
- if (isString(first) && (!second || isString(second))) go(first, second, third || false)
400
- else if (second) this.r(first, second)
401
- else this.r('@', first)
165
+ function createFrag() {
166
+ return document.createDocumentFragment()
402
167
  }
403
168
 
404
169
  /**
405
- * Stop routing
170
+ * Create a document text node
171
+ * @returns { Object } create a text node to use as placeholder
406
172
  */
407
- prot.s = function() {
408
- this.off('*')
409
- this.$ = []
173
+ function createDOMPlaceholder() {
174
+ return document.createTextNode('')
410
175
  }
411
176
 
412
177
  /**
413
- * Emit
414
- * @param {string} path - path
178
+ * Create a generic DOM node
179
+ * @param { String } name - name of the DOM node we want to create
180
+ * @param { Boolean } isSvg - should we use a SVG as parent node?
181
+ * @returns { Object } DOM node just created
415
182
  */
416
- prot.e = function(path) {
417
- this.$.concat('@').some(function(filter) {
418
- var args = (filter == '@' ? parser : secondParser)(normalize(path), normalize(filter))
419
- if (typeof args != 'undefined') {
420
- this[TRIGGER].apply(null, [filter].concat(args))
421
- return routeFound = true // exit from loop
422
- }
423
- }, this)
183
+ function mkEl(name, isSvg) {
184
+ return isSvg ?
185
+ document.createElementNS('http://www.w3.org/2000/svg', 'svg') :
186
+ document.createElement(name)
424
187
  }
425
188
 
426
189
  /**
427
- * Register route
428
- * @param {string} filter - filter for matching to url
429
- * @param {function} action - action to register
190
+ * Get the outer html of any DOM node SVGs included
191
+ * @param { Object } el - DOM node to parse
192
+ * @returns { String } el.outerHTML
430
193
  */
431
- prot.r = function(filter, action) {
432
- if (filter != '@') {
433
- filter = '/' + normalize(filter)
434
- this.$.push(filter)
194
+ function getOuterHTML(el) {
195
+ if (el.outerHTML)
196
+ { return el.outerHTML }
197
+ // some browsers do not support outerHTML on the SVGs tags
198
+ else {
199
+ var container = mkEl('div');
200
+ container.appendChild(el.cloneNode(true));
201
+ return container.innerHTML
435
202
  }
436
- this.on(filter, action)
437
203
  }
438
204
 
439
- var mainRouter = new Router()
440
- var route = mainRouter.m.bind(mainRouter)
441
-
442
205
  /**
443
- * Create a sub router
444
- * @returns {function} the method of a new Router object
206
+ * Set the inner html of any DOM node SVGs included
207
+ * @param { Object } container - DOM node where we'll inject new html
208
+ * @param { String } html - html to inject
445
209
  */
446
- route.create = function() {
447
- var newSubRouter = new Router()
448
- // assign sub-router's main method
449
- var router = newSubRouter.m.bind(newSubRouter)
450
- // stop only this sub-router
451
- router.stop = newSubRouter.s.bind(newSubRouter)
452
- return router
210
+ function setInnerHTML(container, html) {
211
+ if (!isUndefined(container.innerHTML))
212
+ { container.innerHTML = html; }
213
+ // some browsers do not support innerHTML on the SVGs tags
214
+ else {
215
+ var doc = new DOMParser().parseFromString(html, 'application/xml');
216
+ var node = container.ownerDocument.importNode(doc.documentElement, true);
217
+ container.appendChild(node);
218
+ }
453
219
  }
454
220
 
455
221
  /**
456
- * Set the base of url
457
- * @param {(str|RegExp)} arg - a new base or '#' or '#!'
222
+ * Remove any DOM attribute from a node
223
+ * @param { Object } dom - DOM node we want to update
224
+ * @param { String } name - name of the property we want to remove
458
225
  */
459
- route.base = function(arg) {
460
- base = arg || '#'
461
- current = getPathFromBase() // recalculate current path
226
+ function remAttr(dom, name) {
227
+ dom.removeAttribute(name);
462
228
  }
463
229
 
464
- /** Exec routing right now **/
465
- route.exec = function() {
466
- emit(true)
230
+ /**
231
+ * Get the value of any DOM attribute on a node
232
+ * @param { Object } dom - DOM node we want to parse
233
+ * @param { String } name - name of the attribute we want to get
234
+ * @returns { String | undefined } name of the node attribute whether it exists
235
+ */
236
+ function getAttr(dom, name) {
237
+ return dom.getAttribute(name)
467
238
  }
468
239
 
469
240
  /**
470
- * Replace the default router to yours
471
- * @param {function} fn - your parser function
472
- * @param {function} fn2 - your secondParser function
241
+ * Set any DOM attribute
242
+ * @param { Object } dom - DOM node we want to update
243
+ * @param { String } name - name of the property we want to set
244
+ * @param { String } val - value of the property we want to set
473
245
  */
474
- route.parser = function(fn, fn2) {
475
- if (!fn && !fn2) {
476
- // reset parser for testing...
477
- parser = DEFAULT_PARSER
478
- secondParser = DEFAULT_SECOND_PARSER
479
- }
480
- if (fn) parser = fn
481
- if (fn2) secondParser = fn2
246
+ function setAttr(dom, name, val) {
247
+ var xlink = XLINK_REGEX.exec(name);
248
+ if (xlink && xlink[1])
249
+ { dom.setAttributeNS(XLINK_NS, xlink[1], val); }
250
+ else
251
+ { dom.setAttribute(name, val); }
482
252
  }
483
253
 
484
254
  /**
485
- * Helper function to get url query as an object
486
- * @returns {object} parsed query
255
+ * Insert safely a tag to fix #1962 #1649
256
+ * @param { HTMLElement } root - children container
257
+ * @param { HTMLElement } curr - node to insert
258
+ * @param { HTMLElement } next - node that should preceed the current node inserted
487
259
  */
488
- route.query = function() {
489
- var q = {}
490
- var href = loc.href || current
491
- href[REPLACE](/[?&](.+?)=([^&]*)/g, function(_, k, v) { q[k] = v })
492
- return q
260
+ function safeInsert(root, curr, next) {
261
+ root.insertBefore(curr, next.parentNode && next);
493
262
  }
494
263
 
495
- /** Stop routing **/
496
- route.stop = function () {
497
- if (started) {
498
- if (win) {
499
- win[REMOVE_EVENT_LISTENER](POPSTATE, debouncedEmit)
500
- win[REMOVE_EVENT_LISTENER](HASHCHANGE, debouncedEmit)
501
- doc[REMOVE_EVENT_LISTENER](clickEvent, click)
502
- }
503
- central[TRIGGER]('stop')
504
- started = false
505
- }
264
+ /**
265
+ * Minimize risk: only zero or one _space_ between attr & value
266
+ * @param { String } html - html string we want to parse
267
+ * @param { Function } fn - callback function to apply on any attribute found
268
+ */
269
+ function walkAttrs(html, fn) {
270
+ if (!html)
271
+ { return }
272
+ var m;
273
+ while (m = RE_HTML_ATTRS.exec(html))
274
+ { fn(m[1].toLowerCase(), m[2] || m[3] || m[4]); }
506
275
  }
507
276
 
508
277
  /**
509
- * Start routing
510
- * @param {boolean} autoExec - automatically exec after starting if true
278
+ * Walk down recursively all the children tags starting dom node
279
+ * @param { Object } dom - starting node where we will start the recursion
280
+ * @param { Function } fn - callback to transform the child node just found
281
+ * @param { Object } context - fn can optionally return an object, which is passed to children
511
282
  */
512
- route.start = function (autoExec) {
513
- if (!started) {
514
- if (win) {
515
- if (document.readyState == 'complete') start(autoExec)
516
- // the timeout is needed to solve
517
- // a weird safari bug https://github.com/riot/route/issues/33
518
- else win[ADD_EVENT_LISTENER]('load', function() {
519
- setTimeout(function() { start(autoExec) }, 1)
520
- })
283
+ function walkNodes(dom, fn, context) {
284
+ if (dom) {
285
+ var res = fn(dom, context);
286
+ var next;
287
+ // stop the recursion
288
+ if (res === false) { return }
289
+
290
+ dom = dom.firstChild;
291
+
292
+ while (dom) {
293
+ next = dom.nextSibling;
294
+ walkNodes(dom, fn, res);
295
+ dom = next;
521
296
  }
522
- started = true
523
297
  }
524
298
  }
525
299
 
526
- /** Prepare the router **/
527
- route.base()
528
- route.parser()
300
+ var dom = Object.freeze({
301
+ $$: $$,
302
+ $: $,
303
+ createFrag: createFrag,
304
+ createDOMPlaceholder: createDOMPlaceholder,
305
+ mkEl: mkEl,
306
+ getOuterHTML: getOuterHTML,
307
+ setInnerHTML: setInnerHTML,
308
+ remAttr: remAttr,
309
+ getAttr: getAttr,
310
+ setAttr: setAttr,
311
+ safeInsert: safeInsert,
312
+ walkAttrs: walkAttrs,
313
+ walkNodes: walkNodes
314
+ });
315
+
316
+ var styleNode;
317
+ var cssTextProp;
318
+ var byName = {};
319
+ var remainder = [];
320
+ var needsInject = false;
321
+
322
+ // skip the following code on the server
323
+ if (WIN) {
324
+ styleNode = (function () {
325
+ // create a new style element with the correct type
326
+ var newNode = mkEl('style');
327
+ setAttr(newNode, 'type', 'text/css');
328
+
329
+ // replace any user node or insert the new one into the head
330
+ var userNode = $('style[type=riot]');
331
+ if (userNode) {
332
+ if (userNode.id) { newNode.id = userNode.id; }
333
+ userNode.parentNode.replaceChild(newNode, userNode);
334
+ }
335
+ else { document.getElementsByTagName('head')[0].appendChild(newNode); }
336
+
337
+ return newNode
338
+ })();
339
+ cssTextProp = styleNode.styleSheet;
340
+ }
529
341
 
530
- riot.route = route
531
- })(riot)
532
- /* istanbul ignore next */
342
+ /**
343
+ * Object that will be used to inject and manage the css of every tag instance
344
+ */
345
+ var styleManager = {
346
+ styleNode: styleNode,
347
+ /**
348
+ * Save a tag style to be later injected into DOM
349
+ * @param { String } css - css string
350
+ * @param { String } name - if it's passed we will map the css to a tagname
351
+ */
352
+ add: function add(css, name) {
353
+ if (name) { byName[name] = css; }
354
+ else { remainder.push(css); }
355
+ needsInject = true;
356
+ },
357
+ /**
358
+ * Inject all previously saved tag styles into DOM
359
+ * innerHTML seems slow: http://jsperf.com/riot-insert-style
360
+ */
361
+ inject: function inject() {
362
+ if (!WIN || !needsInject) { return }
363
+ needsInject = false;
364
+ var style = Object.keys(byName)
365
+ .map(function(k) { return byName[k] })
366
+ .concat(remainder).join('\n');
367
+ if (cssTextProp) { cssTextProp.cssText = style; }
368
+ else { styleNode.innerHTML = style; }
369
+ }
370
+ };
533
371
 
534
372
  /**
535
373
  * The riot template engine
536
- * @version v2.4.1
374
+ * @version v3.0.1
537
375
  */
538
376
  /**
539
377
  * riot.util.brackets
@@ -544,6 +382,8 @@ riot.route = route
544
382
  * @module
545
383
  */
546
384
 
385
+ /* global riot */
386
+
547
387
  var brackets = (function (UNDEF) {
548
388
 
549
389
  var
@@ -567,7 +407,7 @@ var brackets = (function (UNDEF) {
567
407
  '{': RegExp('([{}])|' + S_QBLOCKS, REGLOB)
568
408
  },
569
409
 
570
- DEFAULT = '{ }'
410
+ DEFAULT = '{ }';
571
411
 
572
412
  var _pairs = [
573
413
  '{', '}',
@@ -579,38 +419,38 @@ var brackets = (function (UNDEF) {
579
419
  DEFAULT,
580
420
  /^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,
581
421
  /(^|[^\\]){=[\S\s]*?}/
582
- ]
422
+ ];
583
423
 
584
424
  var
585
425
  cachedBrackets = UNDEF,
586
426
  _regex,
587
427
  _cache = [],
588
- _settings
428
+ _settings;
589
429
 
590
430
  function _loopback (re) { return re }
591
431
 
592
432
  function _rewrite (re, bp) {
593
- if (!bp) bp = _cache
433
+ if (!bp) { bp = _cache; }
594
434
  return new RegExp(
595
435
  re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : ''
596
436
  )
597
437
  }
598
438
 
599
439
  function _create (pair) {
600
- if (pair === DEFAULT) return _pairs
440
+ if (pair === DEFAULT) { return _pairs }
601
441
 
602
- var arr = pair.split(' ')
442
+ var arr = pair.split(' ');
603
443
 
604
444
  if (arr.length !== 2 || UNSUPPORTED.test(pair)) {
605
445
  throw new Error('Unsupported brackets "' + pair + '"')
606
446
  }
607
- arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' '))
447
+ arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' '));
608
448
 
609
- arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr)
610
- arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr)
611
- arr[6] = _rewrite(_pairs[6], arr)
612
- arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCKS, REGLOB)
613
- arr[8] = pair
449
+ arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr);
450
+ arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr);
451
+ arr[6] = _rewrite(_pairs[6], arr);
452
+ arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCKS, REGLOB);
453
+ arr[8] = pair;
614
454
  return arr
615
455
  }
616
456
 
@@ -620,7 +460,7 @@ var brackets = (function (UNDEF) {
620
460
 
621
461
  _brackets.split = function split (str, tmpl, _bp) {
622
462
  // istanbul ignore next: _bp is for the compiler
623
- if (!_bp) _bp = _cache
463
+ if (!_bp) { _bp = _cache; }
624
464
 
625
465
  var
626
466
  parts = [],
@@ -628,18 +468,18 @@ var brackets = (function (UNDEF) {
628
468
  isexpr,
629
469
  start,
630
470
  pos,
631
- re = _bp[6]
471
+ re = _bp[6];
632
472
 
633
- isexpr = start = re.lastIndex = 0
473
+ isexpr = start = re.lastIndex = 0;
634
474
 
635
475
  while ((match = re.exec(str))) {
636
476
 
637
- pos = match.index
477
+ pos = match.index;
638
478
 
639
479
  if (isexpr) {
640
480
 
641
481
  if (match[2]) {
642
- re.lastIndex = skipBraces(str, match[2], re.lastIndex)
482
+ re.lastIndex = skipBraces(str, match[2], re.lastIndex);
643
483
  continue
644
484
  }
645
485
  if (!match[3]) {
@@ -648,97 +488,97 @@ var brackets = (function (UNDEF) {
648
488
  }
649
489
 
650
490
  if (!match[1]) {
651
- unescapeStr(str.slice(start, pos))
652
- start = re.lastIndex
653
- re = _bp[6 + (isexpr ^= 1)]
654
- re.lastIndex = start
491
+ unescapeStr(str.slice(start, pos));
492
+ start = re.lastIndex;
493
+ re = _bp[6 + (isexpr ^= 1)];
494
+ re.lastIndex = start;
655
495
  }
656
496
  }
657
497
 
658
498
  if (str && start < str.length) {
659
- unescapeStr(str.slice(start))
499
+ unescapeStr(str.slice(start));
660
500
  }
661
501
 
662
502
  return parts
663
503
 
664
504
  function unescapeStr (s) {
665
505
  if (tmpl || isexpr) {
666
- parts.push(s && s.replace(_bp[5], '$1'))
506
+ parts.push(s && s.replace(_bp[5], '$1'));
667
507
  } else {
668
- parts.push(s)
508
+ parts.push(s);
669
509
  }
670
510
  }
671
511
 
672
512
  function skipBraces (s, ch, ix) {
673
513
  var
674
514
  match,
675
- recch = FINDBRACES[ch]
515
+ recch = FINDBRACES[ch];
676
516
 
677
- recch.lastIndex = ix
678
- ix = 1
517
+ recch.lastIndex = ix;
518
+ ix = 1;
679
519
  while ((match = recch.exec(s))) {
680
520
  if (match[1] &&
681
- !(match[1] === ch ? ++ix : --ix)) break
521
+ !(match[1] === ch ? ++ix : --ix)) { break }
682
522
  }
683
523
  return ix ? s.length : recch.lastIndex
684
524
  }
685
- }
525
+ };
686
526
 
687
527
  _brackets.hasExpr = function hasExpr (str) {
688
528
  return _cache[4].test(str)
689
- }
529
+ };
690
530
 
691
531
  _brackets.loopKeys = function loopKeys (expr) {
692
- var m = expr.match(_cache[9])
532
+ var m = expr.match(_cache[9]);
693
533
 
694
534
  return m
695
535
  ? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
696
536
  : { val: expr.trim() }
697
- }
537
+ };
698
538
 
699
539
  _brackets.array = function array (pair) {
700
540
  return pair ? _create(pair) : _cache
701
- }
541
+ };
702
542
 
703
543
  function _reset (pair) {
704
544
  if ((pair || (pair = DEFAULT)) !== _cache[8]) {
705
- _cache = _create(pair)
706
- _regex = pair === DEFAULT ? _loopback : _rewrite
707
- _cache[9] = _regex(_pairs[9])
545
+ _cache = _create(pair);
546
+ _regex = pair === DEFAULT ? _loopback : _rewrite;
547
+ _cache[9] = _regex(_pairs[9]);
708
548
  }
709
- cachedBrackets = pair
549
+ cachedBrackets = pair;
710
550
  }
711
551
 
712
552
  function _setSettings (o) {
713
- var b
553
+ var b;
714
554
 
715
- o = o || {}
716
- b = o.brackets
555
+ o = o || {};
556
+ b = o.brackets;
717
557
  Object.defineProperty(o, 'brackets', {
718
558
  set: _reset,
719
559
  get: function () { return cachedBrackets },
720
560
  enumerable: true
721
- })
722
- _settings = o
723
- _reset(b)
561
+ });
562
+ _settings = o;
563
+ _reset(b);
724
564
  }
725
565
 
726
566
  Object.defineProperty(_brackets, 'settings', {
727
567
  set: _setSettings,
728
568
  get: function () { return _settings }
729
- })
569
+ });
730
570
 
731
571
  /* istanbul ignore next: in the browser riot is always in the scope */
732
- _brackets.settings = typeof riot !== 'undefined' && riot.settings || {}
733
- _brackets.set = _reset
572
+ _brackets.settings = typeof riot !== 'undefined' && riot.settings || {};
573
+ _brackets.set = _reset;
734
574
 
735
- _brackets.R_STRINGS = R_STRINGS
736
- _brackets.R_MLCOMMS = R_MLCOMMS
737
- _brackets.S_QBLOCKS = S_QBLOCKS
575
+ _brackets.R_STRINGS = R_STRINGS;
576
+ _brackets.R_MLCOMMS = R_MLCOMMS;
577
+ _brackets.S_QBLOCKS = S_QBLOCKS;
738
578
 
739
579
  return _brackets
740
580
 
741
- })()
581
+ })();
742
582
 
743
583
  /**
744
584
  * @module tmpl
@@ -750,64 +590,69 @@ var brackets = (function (UNDEF) {
750
590
 
751
591
  var tmpl = (function () {
752
592
 
753
- var _cache = {}
593
+ var _cache = {};
754
594
 
755
595
  function _tmpl (str, data) {
756
- if (!str) return str
596
+ if (!str) { return str }
757
597
 
758
598
  return (_cache[str] || (_cache[str] = _create(str))).call(data, _logErr)
759
599
  }
760
600
 
761
- _tmpl.haveRaw = brackets.hasRaw
601
+ _tmpl.hasExpr = brackets.hasExpr;
762
602
 
763
- _tmpl.hasExpr = brackets.hasExpr
764
-
765
- _tmpl.loopKeys = brackets.loopKeys
603
+ _tmpl.loopKeys = brackets.loopKeys;
766
604
 
767
605
  // istanbul ignore next
768
- _tmpl.clearCache = function () { _cache = {} }
606
+ _tmpl.clearCache = function () { _cache = {}; };
769
607
 
770
- _tmpl.errorHandler = null
608
+ _tmpl.errorHandler = null;
771
609
 
772
610
  function _logErr (err, ctx) {
773
611
 
774
- if (_tmpl.errorHandler) {
775
-
776
- err.riotData = {
777
- tagName: ctx && ctx.root && ctx.root.tagName,
778
- _riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase
612
+ err.riotData = {
613
+ tagName: ctx && ctx.root && ctx.root.tagName,
614
+ _riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase
615
+ };
616
+
617
+ if (_tmpl.errorHandler) { _tmpl.errorHandler(err); }
618
+ else if (
619
+ typeof console !== 'undefined' &&
620
+ typeof console.error === 'function'
621
+ ) {
622
+ if (err.riotData.tagName) {
623
+ console.error('Riot template error thrown in the <%s> tag', err.riotData.tagName.toLowerCase());
779
624
  }
780
- _tmpl.errorHandler(err)
625
+ console.error(err);
781
626
  }
782
627
  }
783
628
 
784
629
  function _create (str) {
785
- var expr = _getTmpl(str)
630
+ var expr = _getTmpl(str);
786
631
 
787
- if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
632
+ if (expr.slice(0, 11) !== 'try{return ') { expr = 'return ' + expr; }
788
633
 
789
634
  return new Function('E', expr + ';') // eslint-disable-line no-new-func
790
635
  }
791
636
 
792
637
  var
793
- CH_IDEXPR = '\u2057',
638
+ CH_IDEXPR = String.fromCharCode(0x2057),
794
639
  RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/,
795
640
  RE_QBLOCK = RegExp(brackets.S_QBLOCKS, 'g'),
796
641
  RE_DQUOTE = /\u2057/g,
797
- RE_QBMARK = /\u2057(\d+)~/g
642
+ RE_QBMARK = /\u2057(\d+)~/g;
798
643
 
799
644
  function _getTmpl (str) {
800
645
  var
801
646
  qstr = [],
802
647
  expr,
803
- parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1)
648
+ parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1);
804
649
 
805
650
  if (parts.length > 2 || parts[0]) {
806
- var i, j, list = []
651
+ var i, j, list = [];
807
652
 
808
653
  for (i = j = 0; i < parts.length; ++i) {
809
654
 
810
- expr = parts[i]
655
+ expr = parts[i];
811
656
 
812
657
  if (expr && (expr = i & 1
813
658
 
@@ -819,16 +664,16 @@ var tmpl = (function () {
819
664
  .replace(/"/g, '\\"') +
820
665
  '"'
821
666
 
822
- )) list[j++] = expr
667
+ )) { list[j++] = expr; }
823
668
 
824
669
  }
825
670
 
826
671
  expr = j < 2 ? list[0]
827
- : '[' + list.join(',') + '].join("")'
672
+ : '[' + list.join(',') + '].join("")';
828
673
 
829
674
  } else {
830
675
 
831
- expr = _parseExpr(parts[1], 0, qstr)
676
+ expr = _parseExpr(parts[1], 0, qstr);
832
677
  }
833
678
 
834
679
  if (qstr[0]) {
@@ -836,7 +681,7 @@ var tmpl = (function () {
836
681
  return qstr[pos]
837
682
  .replace(/\r/g, '\\r')
838
683
  .replace(/\n/g, '\\n')
839
- })
684
+ });
840
685
  }
841
686
  return expr
842
687
  }
@@ -846,7 +691,7 @@ var tmpl = (function () {
846
691
  '(': /[()]/g,
847
692
  '[': /[[\]]/g,
848
693
  '{': /[{}]/g
849
- }
694
+ };
850
695
 
851
696
  function _parseExpr (expr, asText, qstr) {
852
697
 
@@ -855,13 +700,13 @@ var tmpl = (function () {
855
700
  return s.length > 2 && !div ? CH_IDEXPR + (qstr.push(s) - 1) + '~' : s
856
701
  })
857
702
  .replace(/\s+/g, ' ').trim()
858
- .replace(/\ ?([[\({},?\.:])\ ?/g, '$1')
703
+ .replace(/\ ?([[\({},?\.:])\ ?/g, '$1');
859
704
 
860
705
  if (expr) {
861
706
  var
862
707
  list = [],
863
708
  cnt = 0,
864
- match
709
+ match;
865
710
 
866
711
  while (expr &&
867
712
  (match = expr.match(RE_CSNAME)) &&
@@ -870,21 +715,21 @@ var tmpl = (function () {
870
715
  var
871
716
  key,
872
717
  jsb,
873
- re = /,|([[{(])|$/g
718
+ re = /,|([[{(])|$/g;
874
719
 
875
- expr = RegExp.rightContext
876
- key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1]
720
+ expr = RegExp.rightContext;
721
+ key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1];
877
722
 
878
- while (jsb = (match = re.exec(expr))[1]) skipBraces(jsb, re)
723
+ while (jsb = (match = re.exec(expr))[1]) { skipBraces(jsb, re); }
879
724
 
880
- jsb = expr.slice(0, match.index)
881
- expr = RegExp.rightContext
725
+ jsb = expr.slice(0, match.index);
726
+ expr = RegExp.rightContext;
882
727
 
883
- list[cnt++] = _wrapExpr(jsb, 1, key)
728
+ list[cnt++] = _wrapExpr(jsb, 1, key);
884
729
  }
885
730
 
886
731
  expr = !cnt ? _wrapExpr(expr, asText)
887
- : cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
732
+ : cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0];
888
733
  }
889
734
  return expr
890
735
 
@@ -892,173 +737,607 @@ var tmpl = (function () {
892
737
  var
893
738
  mm,
894
739
  lv = 1,
895
- ir = RE_BREND[ch]
740
+ ir = RE_BREND[ch];
896
741
 
897
- ir.lastIndex = re.lastIndex
742
+ ir.lastIndex = re.lastIndex;
898
743
  while (mm = ir.exec(expr)) {
899
- if (mm[0] === ch) ++lv
900
- else if (!--lv) break
744
+ if (mm[0] === ch) { ++lv; }
745
+ else if (!--lv) { break }
901
746
  }
902
- re.lastIndex = lv ? expr.length : ir.lastIndex
747
+ re.lastIndex = lv ? expr.length : ir.lastIndex;
903
748
  }
904
749
  }
905
750
 
906
751
  // istanbul ignore next: not both
907
752
  var // eslint-disable-next-line max-len
908
753
  JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
909
- 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,
910
- JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/
754
+ 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,
755
+ JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/;
911
756
 
912
757
  function _wrapExpr (expr, asText, key) {
913
- var tb
758
+ var tb;
914
759
 
915
760
  expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) {
916
761
  if (mvar) {
917
- pos = tb ? 0 : pos + match.length
762
+ pos = tb ? 0 : pos + match.length;
918
763
 
919
764
  if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') {
920
- match = p + '("' + mvar + JS_CONTEXT + mvar
921
- if (pos) tb = (s = s[pos]) === '.' || s === '(' || s === '['
765
+ match = p + '("' + mvar + JS_CONTEXT + mvar;
766
+ if (pos) { tb = (s = s[pos]) === '.' || s === '(' || s === '['; }
922
767
  } else if (pos) {
923
- tb = !JS_NOPROPS.test(s.slice(pos))
768
+ tb = !JS_NOPROPS.test(s.slice(pos));
924
769
  }
925
770
  }
926
771
  return match
927
- })
772
+ });
928
773
 
929
774
  if (tb) {
930
- expr = 'try{return ' + expr + '}catch(e){E(e,this)}'
775
+ expr = 'try{return ' + expr + '}catch(e){E(e,this)}';
931
776
  }
932
777
 
933
778
  if (key) {
934
779
 
935
780
  expr = (tb
936
781
  ? 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
937
- ) + '?"' + key + '":""'
782
+ ) + '?"' + key + '":""';
938
783
 
939
784
  } else if (asText) {
940
785
 
941
786
  expr = 'function(v){' + (tb
942
787
  ? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
943
- ) + ';return v||v===0?v:""}.call(this)'
788
+ ) + ';return v||v===0?v:""}.call(this)';
944
789
  }
945
790
 
946
791
  return expr
947
792
  }
948
793
 
949
- _tmpl.version = brackets.version = 'v2.4.1'
794
+ _tmpl.version = brackets.version = 'v3.0.1';
950
795
 
951
796
  return _tmpl
952
797
 
953
- })()
954
-
955
- /*
956
- lib/browser/tag/mkdom.js
798
+ })();
957
799
 
958
- Includes hacks needed for the Internet Explorer version 9 and below
959
- See: http://kangax.github.io/compat-table/es5/#ie8
960
- http://codeplanet.io/dropping-ie8/
961
- */
962
- var mkdom = (function _mkdom() {
963
- var
964
- reHasYield = /<yield\b/i,
965
- reYieldAll = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>|>)/ig,
966
- reYieldSrc = /<yield\s+to=['"]([^'">]*)['"]\s*>([\S\s]*?)<\/yield\s*>/ig,
967
- reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig
968
- var
969
- rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' },
970
- tblTags = IE_VERSION && IE_VERSION < 10
971
- ? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/
800
+ var observable$1 = function(el) {
972
801
 
973
802
  /**
974
- * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
975
- * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
976
- *
977
- * @param {string} templ - The template coming from the custom tag definition
978
- * @param {string} [html] - HTML content that comes from the DOM element where you
979
- * will mount the tag, mostly the original tag in the page
980
- * @returns {HTMLElement} DOM element with _templ_ merged through `YIELD` with the _html_.
803
+ * Extend the original object or create a new empty one
804
+ * @type { Object }
981
805
  */
982
- function _mkdom(templ, html) {
983
- var
984
- match = templ && templ.match(/^\s*<([-\w]+)/),
985
- tagName = match && match[1].toLowerCase(),
986
- el = mkEl('div', isSVGTag(tagName))
987
-
988
- // replace all the yield tags with the tag inner html
989
- templ = replaceYield(templ, html)
990
-
991
- /* istanbul ignore next */
992
- if (tblTags.test(tagName))
993
- el = specialTags(el, templ, tagName)
994
- else
995
- setInnerHTML(el, templ)
996
806
 
997
- el.stub = true
807
+ el = el || {};
998
808
 
999
- return el
1000
- }
809
+ /**
810
+ * Private variables
811
+ */
812
+ var callbacks = {},
813
+ slice = Array.prototype.slice;
1001
814
 
1002
- /*
1003
- Creates the root element for table or select child elements:
1004
- tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
1005
- */
1006
- function specialTags(el, templ, tagName) {
1007
- var
1008
- select = tagName[0] === 'o',
1009
- parent = select ? 'select>' : 'table>'
1010
-
1011
- // trim() is important here, this ensures we don't have artifacts,
1012
- // so we can check if we have only one element inside the parent
1013
- el.innerHTML = '<' + parent + templ.trim() + '</' + parent
1014
- parent = el.firstChild
1015
-
1016
- // returns the immediate parent if tr/th/td/col is the only element, if not
1017
- // returns the whole tree, as this can include additional elements
1018
- if (select) {
1019
- parent.selectedIndex = -1 // for IE9, compatible w/current riot behavior
1020
- } else {
1021
- // avoids insertion of cointainer inside container (ex: tbody inside tbody)
1022
- var tname = rootEls[tagName]
1023
- if (tname && parent.childElementCount === 1) parent = $(tname, parent)
1024
- }
1025
- return parent
1026
- }
815
+ /**
816
+ * Public Api
817
+ */
1027
818
 
1028
- /*
1029
- Replace the yield tag from any tag template with the innerHTML of the
1030
- original tag in the page
1031
- */
1032
- function replaceYield(templ, html) {
1033
- // do nothing if no yield
1034
- if (!reHasYield.test(templ)) return templ
1035
-
1036
- // be careful with #1343 - string on the source having `$1`
1037
- var src = {}
1038
-
1039
- html = html && html.replace(reYieldSrc, function (_, ref, text) {
1040
- src[ref] = src[ref] || text // preserve first definition
1041
- return ''
1042
- }).trim()
1043
-
1044
- return templ
1045
- .replace(reYieldDest, function (_, ref, def) { // yield with from - to attrs
1046
- return src[ref] || def || ''
1047
- })
1048
- .replace(reYieldAll, function (_, def) { // yield without any "from"
1049
- return html || def || ''
1050
- })
819
+ // extend the el object adding the observable methods
820
+ Object.defineProperties(el, {
821
+ /**
822
+ * Listen to the given `event` ands
823
+ * execute the `callback` each time an event is triggered.
824
+ * @param { String } event - event id
825
+ * @param { Function } fn - callback function
826
+ * @returns { Object } el
827
+ */
828
+ on: {
829
+ value: function(event, fn) {
830
+ if (typeof fn == 'function')
831
+ { (callbacks[event] = callbacks[event] || []).push(fn); }
832
+ return el
833
+ },
834
+ enumerable: false,
835
+ writable: false,
836
+ configurable: false
837
+ },
838
+
839
+ /**
840
+ * Removes the given `event` listeners
841
+ * @param { String } event - event id
842
+ * @param { Function } fn - callback function
843
+ * @returns { Object } el
844
+ */
845
+ off: {
846
+ value: function(event, fn) {
847
+ if (event == '*' && !fn) { callbacks = {}; }
848
+ else {
849
+ if (fn) {
850
+ var arr = callbacks[event];
851
+ for (var i = 0, cb; cb = arr && arr[i]; ++i) {
852
+ if (cb == fn) { arr.splice(i--, 1); }
853
+ }
854
+ } else { delete callbacks[event]; }
855
+ }
856
+ return el
857
+ },
858
+ enumerable: false,
859
+ writable: false,
860
+ configurable: false
861
+ },
862
+
863
+ /**
864
+ * Listen to the given `event` and
865
+ * execute the `callback` at most once
866
+ * @param { String } event - event id
867
+ * @param { Function } fn - callback function
868
+ * @returns { Object } el
869
+ */
870
+ one: {
871
+ value: function(event, fn) {
872
+ function on() {
873
+ el.off(event, on);
874
+ fn.apply(el, arguments);
875
+ }
876
+ return el.on(event, on)
877
+ },
878
+ enumerable: false,
879
+ writable: false,
880
+ configurable: false
881
+ },
882
+
883
+ /**
884
+ * Execute all callback functions that listen to
885
+ * the given `event`
886
+ * @param { String } event - event id
887
+ * @returns { Object } el
888
+ */
889
+ trigger: {
890
+ value: function(event) {
891
+ var arguments$1 = arguments;
892
+
893
+
894
+ // getting the arguments
895
+ var arglen = arguments.length - 1,
896
+ args = new Array(arglen),
897
+ fns,
898
+ fn,
899
+ i;
900
+
901
+ for (i = 0; i < arglen; i++) {
902
+ args[i] = arguments$1[i + 1]; // skip first argument
903
+ }
904
+
905
+ fns = slice.call(callbacks[event] || [], 0);
906
+
907
+ for (i = 0; fn = fns[i]; ++i) {
908
+ fn.apply(el, args);
909
+ }
910
+
911
+ if (callbacks['*'] && event != '*')
912
+ { el.trigger.apply(el, ['*', event].concat(args)); }
913
+
914
+ return el
915
+ },
916
+ enumerable: false,
917
+ writable: false,
918
+ configurable: false
919
+ }
920
+ });
921
+
922
+ return el
923
+
924
+ };
925
+
926
+ /**
927
+ * Specialized function for looping an array-like collection with `each={}`
928
+ * @param { Array } list - collection of items
929
+ * @param {Function} fn - callback function
930
+ * @returns { Array } the array looped
931
+ */
932
+ function each(list, fn) {
933
+ var len = list ? list.length : 0;
934
+
935
+ for (var i = 0, el; i < len; ++i) {
936
+ el = list[i];
937
+ // return false -> current item was removed by fn during the loop
938
+ if (fn(el, i) === false)
939
+ { i--; }
940
+ }
941
+ return list
942
+ }
943
+
944
+ /**
945
+ * Check whether an array contains an item
946
+ * @param { Array } array - target array
947
+ * @param { * } item - item to test
948
+ * @returns { Boolean } -
949
+ */
950
+ function contains(array, item) {
951
+ return ~array.indexOf(item)
952
+ }
953
+
954
+ /**
955
+ * Convert a string containing dashes to camel case
956
+ * @param { String } str - input string
957
+ * @returns { String } my-string -> myString
958
+ */
959
+ function toCamel(str) {
960
+ return str.replace(/-(\w)/g, function (_, c) { return c.toUpperCase(); })
961
+ }
962
+
963
+ /**
964
+ * Faster String startsWith alternative
965
+ * @param { String } str - source string
966
+ * @param { String } value - test string
967
+ * @returns { Boolean } -
968
+ */
969
+ function startsWith(str, value) {
970
+ return str.slice(0, value.length) === value
971
+ }
972
+
973
+ /**
974
+ * Helper function to set an immutable property
975
+ * @param { Object } el - object where the new property will be set
976
+ * @param { String } key - object key where the new property will be stored
977
+ * @param { * } value - value of the new property
978
+ * @param { Object } options - set the propery overriding the default options
979
+ * @returns { Object } - the initial object
980
+ */
981
+ function defineProperty(el, key, value, options) {
982
+ Object.defineProperty(el, key, extend({
983
+ value: value,
984
+ enumerable: false,
985
+ writable: false,
986
+ configurable: true
987
+ }, options));
988
+ return el
989
+ }
990
+
991
+ /**
992
+ * Extend any object with other properties
993
+ * @param { Object } src - source object
994
+ * @returns { Object } the resulting extended object
995
+ *
996
+ * var obj = { foo: 'baz' }
997
+ * extend(obj, {bar: 'bar', foo: 'bar'})
998
+ * console.log(obj) => {bar: 'bar', foo: 'bar'}
999
+ *
1000
+ */
1001
+ function extend(src) {
1002
+ var obj, args = arguments;
1003
+ for (var i = 1; i < args.length; ++i) {
1004
+ if (obj = args[i]) {
1005
+ for (var key in obj) {
1006
+ // check if this property of the source object could be overridden
1007
+ if (isWritable(src, key))
1008
+ { src[key] = obj[key]; }
1009
+ }
1010
+ }
1011
+ }
1012
+ return src
1013
+ }
1014
+
1015
+ var misc = Object.freeze({
1016
+ each: each,
1017
+ contains: contains,
1018
+ toCamel: toCamel,
1019
+ startsWith: startsWith,
1020
+ defineProperty: defineProperty,
1021
+ extend: extend
1022
+ });
1023
+
1024
+ var EVENTS_PREFIX_REGEX = /^on/;
1025
+
1026
+ /**
1027
+ * Trigger DOM events
1028
+ * @param { HTMLElement } dom - dom element target of the event
1029
+ * @param { Function } handler - user function
1030
+ * @param { Object } e - event object
1031
+ */
1032
+ function handleEvent(dom, handler, e) {
1033
+ var ptag = this._parent,
1034
+ item = this._item;
1035
+
1036
+ if (!item)
1037
+ { while (ptag && !item) {
1038
+ item = ptag._item;
1039
+ ptag = ptag._parent;
1040
+ } }
1041
+
1042
+ // override the event properties
1043
+ if (isWritable(e, 'currentTarget')) { e.currentTarget = dom; }
1044
+ if (isWritable(e, 'target')) { e.target = e.srcElement; }
1045
+ if (isWritable(e, 'which')) { e.which = e.charCode || e.keyCode; }
1046
+
1047
+ e.item = item;
1048
+
1049
+ handler.call(this, e);
1050
+
1051
+ if (!e.preventUpdate) {
1052
+ var p = getImmediateCustomParentTag(this);
1053
+ // fixes #2083
1054
+ if (p.isMounted) { p.update(); }
1055
+ }
1056
+ }
1057
+
1058
+ /**
1059
+ * Attach an event to a DOM node
1060
+ * @param { String } name - event name
1061
+ * @param { Function } handler - event callback
1062
+ * @param { Object } dom - dom node
1063
+ * @param { Tag } tag - tag instance
1064
+ */
1065
+ function setEventHandler(name, handler, dom, tag) {
1066
+ var eventName,
1067
+ cb = handleEvent.bind(tag, dom, handler);
1068
+
1069
+ if (!dom.addEventListener) {
1070
+ dom[name] = cb;
1071
+ return
1051
1072
  }
1052
1073
 
1053
- return _mkdom
1074
+ // avoid to bind twice the same event
1075
+ dom[name] = null;
1054
1076
 
1055
- })()
1077
+ // normalize event name
1078
+ eventName = name.replace(EVENTS_PREFIX_REGEX, '');
1079
+
1080
+ // cache the callback directly on the DOM node
1081
+ if (!dom._riotEvents) { dom._riotEvents = {}; }
1082
+
1083
+ if (dom._riotEvents[name])
1084
+ { dom.removeEventListener(eventName, dom._riotEvents[name]); }
1085
+
1086
+ dom._riotEvents[name] = cb;
1087
+ dom.addEventListener(eventName, cb, false);
1088
+ }
1089
+
1090
+ /**
1091
+ * Update dynamically created data-is tags with changing expressions
1092
+ * @param { Object } expr - expression tag and expression info
1093
+ * @param { Tag } parent - parent for tag creation
1094
+ */
1095
+ function updateDataIs(expr, parent) {
1096
+ var tagName = tmpl(expr.value, parent),
1097
+ conf;
1098
+
1099
+ if (expr.tag && expr.tagName === tagName) {
1100
+ expr.tag.update();
1101
+ return
1102
+ }
1103
+
1104
+ // sync _parent to accommodate changing tagnames
1105
+ if (expr.tag) {
1106
+ each(expr.attrs, function (a) { return setAttr(expr.tag.root, a.name, a.value); });
1107
+ expr.tag.unmount(true);
1108
+ }
1109
+
1110
+ expr.impl = __TAG_IMPL[tagName];
1111
+ conf = {root: expr.dom, parent: parent, hasImpl: true, tagName: tagName};
1112
+ expr.tag = initChildTag(expr.impl, conf, expr.dom.innerHTML, parent);
1113
+ expr.tagName = tagName;
1114
+ expr.tag.mount();
1115
+
1116
+ // parent is the placeholder tag, not the dynamic tag so clean up
1117
+ parent.on('unmount', function () {
1118
+ var delName = expr.tag.opts.dataIs,
1119
+ tags = expr.tag.parent.tags,
1120
+ _tags = expr.tag._parent.tags;
1121
+ arrayishRemove(tags, delName, expr.tag);
1122
+ arrayishRemove(_tags, delName, expr.tag);
1123
+ expr.tag.unmount();
1124
+ });
1125
+ }
1126
+
1127
+ /**
1128
+ * Update on single tag expression
1129
+ * @this Tag
1130
+ * @param { Object } expr - expression logic
1131
+ * @returns { undefined }
1132
+ */
1133
+ function updateExpression(expr) {
1134
+ var dom = expr.dom,
1135
+ attrName = expr.attr,
1136
+ isToggle = contains([SHOW_DIRECTIVE, HIDE_DIRECTIVE], attrName),
1137
+ value = tmpl(expr.expr, this),
1138
+ isValueAttr = attrName === 'riot-value',
1139
+ isVirtual = expr.root && expr.root.tagName === 'VIRTUAL',
1140
+ parent = dom && (expr.parent || dom.parentNode),
1141
+ old;
1142
+
1143
+ if (expr.bool)
1144
+ { value = value ? attrName : false; }
1145
+ else if (isUndefined(value) || value === null)
1146
+ { value = ''; }
1147
+
1148
+ if (expr._riot_id) { // if it's a tag
1149
+ if (expr.isMounted) {
1150
+ expr.update();
1151
+
1152
+ // if it hasn't been mounted yet, do that now.
1153
+ } else {
1154
+ expr.mount();
1155
+
1156
+ if (isVirtual) {
1157
+ var frag = document.createDocumentFragment();
1158
+ makeVirtual.call(expr, frag);
1159
+ expr.root.parentElement.replaceChild(frag, expr.root);
1160
+ }
1161
+ }
1162
+ return
1163
+ }
1164
+
1165
+ old = expr.value;
1166
+ expr.value = value;
1167
+
1168
+ if (expr.update) {
1169
+ expr.update();
1170
+ return
1171
+ }
1172
+
1173
+ if (expr.isRtag && value) { return updateDataIs(expr, this) }
1174
+ if (old === value) { return }
1175
+ // no change, so nothing more to do
1176
+ if (isValueAttr && dom.value === value) { return }
1177
+
1178
+ // textarea and text nodes have no attribute name
1179
+ if (!attrName) {
1180
+ // about #815 w/o replace: the browser converts the value to a string,
1181
+ // the comparison by "==" does too, but not in the server
1182
+ value += '';
1183
+ // test for parent avoids error with invalid assignment to nodeValue
1184
+ if (parent) {
1185
+ // cache the parent node because somehow it will become null on IE
1186
+ // on the next iteration
1187
+ expr.parent = parent;
1188
+ if (parent.tagName === 'TEXTAREA') {
1189
+ parent.value = value; // #1113
1190
+ if (!IE_VERSION) { dom.nodeValue = value; } // #1625 IE throws here, nodeValue
1191
+ } // will be available on 'updated'
1192
+ else { dom.nodeValue = value; }
1193
+ }
1194
+ return
1195
+ }
1196
+
1197
+ // remove original attribute
1198
+ if (!expr.isAttrRemoved || !value) {
1199
+ remAttr(dom, attrName);
1200
+ expr.isAttrRemoved = true;
1201
+ }
1202
+
1203
+ // event handler
1204
+ if (isFunction(value)) {
1205
+ setEventHandler(attrName, value, dom, this);
1206
+ // show / hide
1207
+ } else if (isToggle) {
1208
+ if (attrName === HIDE_DIRECTIVE) { value = !value; }
1209
+ dom.style.display = value ? '' : 'none';
1210
+ // field value
1211
+ } else if (isValueAttr) {
1212
+ dom.value = value;
1213
+ // <img src="{ expr }">
1214
+ } else if (startsWith(attrName, ATTRS_PREFIX) && attrName !== IS_DIRECTIVE) {
1215
+ attrName = attrName.slice(ATTRS_PREFIX.length);
1216
+ if (CASE_SENSITIVE_ATTRIBUTES[attrName])
1217
+ { attrName = CASE_SENSITIVE_ATTRIBUTES[attrName]; }
1218
+ if (value != null)
1219
+ { setAttr(dom, attrName, value); }
1220
+ } else {
1221
+ // <select> <option selected={true}> </select>
1222
+ if (attrName === 'selected' && parent && /^(SELECT|OPTGROUP)$/.test(parent.tagName) && value) {
1223
+ parent.value = dom.value;
1224
+ } if (expr.bool) {
1225
+ dom[attrName] = value;
1226
+ if (!value) { return }
1227
+ } if (value === 0 || value && typeof value !== T_OBJECT) {
1228
+ setAttr(dom, attrName, value);
1229
+ }
1230
+ }
1231
+ }
1232
+
1233
+ /**
1234
+ * Update all the expressions in a Tag instance
1235
+ * @this Tag
1236
+ * @param { Array } expressions - expression that must be re evaluated
1237
+ */
1238
+ function updateAllExpressions(expressions) {
1239
+ each(expressions, updateExpression.bind(this));
1240
+ }
1241
+
1242
+ var IfExpr = {
1243
+ init: function init(dom, tag, expr) {
1244
+ remAttr(dom, CONDITIONAL_DIRECTIVE);
1245
+ this.tag = tag;
1246
+ this.expr = expr;
1247
+ this.stub = document.createTextNode('');
1248
+ this.pristine = dom;
1249
+
1250
+ var p = dom.parentNode;
1251
+ p.insertBefore(this.stub, dom);
1252
+ p.removeChild(dom);
1253
+
1254
+ return this
1255
+ },
1256
+ update: function update() {
1257
+ var newValue = tmpl(this.expr, this.tag);
1258
+
1259
+ if (newValue && !this.current) { // insert
1260
+ this.current = this.pristine.cloneNode(true);
1261
+ this.stub.parentNode.insertBefore(this.current, this.stub);
1262
+
1263
+ this.expressions = [];
1264
+ parseExpressions.apply(this.tag, [this.current, this.expressions, true]);
1265
+ } else if (!newValue && this.current) { // remove
1266
+ unmountAll(this.expressions);
1267
+ if (this.current._tag) {
1268
+ this.current._tag.unmount();
1269
+ } else if (this.current.parentNode)
1270
+ { this.current.parentNode.removeChild(this.current); }
1271
+ this.current = null;
1272
+ this.expressions = [];
1273
+ }
1274
+
1275
+ if (newValue) { updateAllExpressions.call(this.tag, this.expressions); }
1276
+ },
1277
+ unmount: function unmount() {
1278
+ unmountAll(this.expressions || []);
1279
+ delete this.pristine;
1280
+ delete this.parentNode;
1281
+ delete this.stub;
1282
+ }
1283
+ };
1284
+
1285
+ var RefExpr = {
1286
+ init: function init(dom, parent, attrName, attrValue) {
1287
+ this.dom = dom;
1288
+ this.attr = attrName;
1289
+ this.rawValue = attrValue;
1290
+ this.parent = parent;
1291
+ this.hasExp = tmpl.hasExpr(attrValue);
1292
+ this.firstRun = true;
1293
+
1294
+ return this
1295
+ },
1296
+ update: function update() {
1297
+ var value = this.rawValue;
1298
+ if (this.hasExp)
1299
+ { value = tmpl(this.rawValue, this.parent); }
1300
+
1301
+ // if nothing changed, we're done
1302
+ if (!this.firstRun && value === this.value) { return }
1303
+
1304
+ var customParent = this.parent && getImmediateCustomParentTag(this.parent);
1305
+
1306
+ // if the referenced element is a custom tag, then we set the tag itself, rather than DOM
1307
+ var tagOrDom = this.tag || this.dom;
1308
+
1309
+ // the name changed, so we need to remove it from the old key (if present)
1310
+ if (!isBlank(this.value) && customParent)
1311
+ { arrayishRemove(customParent.refs, this.value, tagOrDom); }
1312
+
1313
+ if (isBlank(value)) {
1314
+ // if the value is blank, we remove it
1315
+ remAttr(this.dom, this.attr);
1316
+ } else {
1317
+ // add it to the refs of parent tag (this behavior was changed >=3.0)
1318
+ if (customParent) { arrayishAdd(customParent.refs, value, tagOrDom); }
1319
+ // set the actual DOM attr
1320
+ setAttr(this.dom, this.attr, value);
1321
+ }
1322
+ this.value = value;
1323
+ this.firstRun = false;
1324
+ },
1325
+ unmount: function unmount() {
1326
+ var tagOrDom = this.tag || this.dom;
1327
+ var customParent = this.parent && getImmediateCustomParentTag(this.parent);
1328
+ if (!isBlank(this.value) && customParent)
1329
+ { arrayishRemove(customParent.refs, this.value, tagOrDom); }
1330
+ delete this.dom;
1331
+ delete this.parent;
1332
+ }
1333
+ };
1056
1334
 
1057
1335
  /**
1058
1336
  * Convert the item looped into an object used to extend the child tag properties
1059
1337
  * @param { Object } expr - object containing the keys used to extend the children tags
1060
1338
  * @param { * } key - value to assign to the new object returned
1061
1339
  * @param { * } val - value containing the position of the item in the array
1340
+ * @param { Object } base - prototype object for the new item
1062
1341
  * @returns { Object } - new object containing the values of the original item
1063
1342
  *
1064
1343
  * The variables 'key' and 'val' are arbitrary.
@@ -1066,10 +1345,10 @@ var mkdom = (function _mkdom() {
1066
1345
  * and on the expression used on the each tag
1067
1346
  *
1068
1347
  */
1069
- function mkitem(expr, key, val) {
1070
- var item = {}
1071
- item[expr.key] = key
1072
- if (expr.pos) item[expr.pos] = val
1348
+ function mkitem(expr, key, val, base) {
1349
+ var item = base ? Object.create(base) : {};
1350
+ item[expr.key] = key;
1351
+ if (expr.pos) { item[expr.pos] = val; }
1073
1352
  return item
1074
1353
  }
1075
1354
 
@@ -1077,973 +1356,927 @@ function mkitem(expr, key, val) {
1077
1356
  * Unmount the redundant tags
1078
1357
  * @param { Array } items - array containing the current items to loop
1079
1358
  * @param { Array } tags - array containing all the children tags
1359
+ * @param { String } tagName - key used to identify the type of tag
1080
1360
  */
1081
- function unmountRedundant(items, tags) {
1082
-
1361
+ function unmountRedundant(items, tags, tagName) {
1083
1362
  var i = tags.length,
1084
1363
  j = items.length,
1085
- t
1364
+ t;
1086
1365
 
1087
1366
  while (i > j) {
1088
- t = tags[--i]
1089
- tags.splice(i, 1)
1090
- t.unmount()
1367
+ t = tags[--i];
1368
+ tags.splice(i, 1);
1369
+ t.unmount();
1370
+ arrayishRemove(t.parent, tagName, t, true);
1091
1371
  }
1092
1372
  }
1093
1373
 
1094
1374
  /**
1095
1375
  * Move the nested custom tags in non custom loop tags
1096
- * @param { Object } child - non custom loop tag
1376
+ * @this Tag
1097
1377
  * @param { Number } i - current position of the loop tag
1098
1378
  */
1099
- function moveNestedTags(child, i) {
1100
- Object.keys(child.tags).forEach(function(tagName) {
1101
- var tag = child.tags[tagName]
1379
+ function moveNestedTags(i) {
1380
+ var this$1 = this;
1381
+
1382
+ each(Object.keys(this.tags), function (tagName) {
1383
+ var tag = this$1.tags[tagName];
1102
1384
  if (isArray(tag))
1103
- each(tag, function (t) {
1104
- moveChildTag(t, tagName, i)
1105
- })
1385
+ { each(tag, function (t) {
1386
+ moveChildTag.apply(t, [tagName, i]);
1387
+ }); }
1106
1388
  else
1107
- moveChildTag(tag, tagName, i)
1108
- })
1389
+ { moveChildTag.apply(tag, [tagName, i]); }
1390
+ });
1109
1391
  }
1110
1392
 
1111
1393
  /**
1112
- * Adds the elements for a virtual tag
1113
- * @param { Tag } tag - the tag whose root's children will be inserted or appended
1114
- * @param { Node } src - the node that will do the inserting or appending
1115
- * @param { Tag } target - only if inserting, insert before this tag's first child
1394
+ * Move a child tag
1395
+ * @this Tag
1396
+ * @param { HTMLElement } root - dom node containing all the loop children
1397
+ * @param { Tag } nextTag - instance of the next tag preceding the one we want to move
1398
+ * @param { Boolean } isVirtual - is it a virtual tag?
1116
1399
  */
1117
- function addVirtual(tag, src, target) {
1118
- var el = tag._root, sib
1119
- tag._virts = []
1120
- while (el) {
1121
- sib = el.nextSibling
1122
- if (target)
1123
- src.insertBefore(el, target._root)
1124
- else
1125
- src.appendChild(el)
1126
-
1127
- tag._virts.push(el) // hold for unmounting
1128
- el = sib
1129
- }
1400
+ function move(root, nextTag, isVirtual) {
1401
+ if (isVirtual)
1402
+ { moveVirtual.apply(this, [root, nextTag]); }
1403
+ else
1404
+ { safeInsert(root, this.root, nextTag.root); }
1130
1405
  }
1131
1406
 
1132
1407
  /**
1133
- * Move virtual tag and all child nodes
1134
- * @param { Tag } tag - first child reference used to start move
1135
- * @param { Node } src - the node that will do the inserting
1136
- * @param { Tag } target - insert before this tag's first child
1137
- * @param { Number } len - how many child nodes to move
1138
- */
1139
- function moveVirtual(tag, src, target, len) {
1140
- var el = tag._root, sib, i = 0
1141
- for (; i < len; i++) {
1142
- sib = el.nextSibling
1143
- src.insertBefore(el, target._root)
1144
- el = sib
1145
- }
1408
+ * Insert and mount a child tag
1409
+ * @this Tag
1410
+ * @param { HTMLElement } root - dom node containing all the loop children
1411
+ * @param { Tag } nextTag - instance of the next tag preceding the one we want to insert
1412
+ * @param { Boolean } isVirtual - is it a virtual tag?
1413
+ */
1414
+ function insert(root, nextTag, isVirtual) {
1415
+ if (isVirtual)
1416
+ { makeVirtual.apply(this, [root, nextTag]); }
1417
+ else
1418
+ { safeInsert(root, this.root, nextTag.root); }
1146
1419
  }
1147
1420
 
1421
+ /**
1422
+ * Append a new tag into the DOM
1423
+ * @this Tag
1424
+ * @param { HTMLElement } root - dom node containing all the loop children
1425
+ * @param { Boolean } isVirtual - is it a virtual tag?
1426
+ */
1427
+ function append(root, isVirtual) {
1428
+ if (isVirtual)
1429
+ { makeVirtual.call(this, root); }
1430
+ else
1431
+ { root.appendChild(this.root); }
1432
+ }
1148
1433
 
1149
1434
  /**
1150
1435
  * Manage tags having the 'each'
1151
- * @param { Object } dom - DOM node we need to loop
1436
+ * @param { HTMLElement } dom - DOM node we need to loop
1152
1437
  * @param { Tag } parent - parent tag instance where the dom node is contained
1153
1438
  * @param { String } expr - string contained in the 'each' attribute
1439
+ * @returns { Object } expression object for this each loop
1154
1440
  */
1155
1441
  function _each(dom, parent, expr) {
1156
1442
 
1157
1443
  // remove the each property from the original tag
1158
- remAttr(dom, 'each')
1444
+ remAttr(dom, LOOP_DIRECTIVE);
1159
1445
 
1160
- var mustReorder = typeof getAttr(dom, 'no-reorder') !== T_STRING || remAttr(dom, 'no-reorder'),
1446
+ var mustReorder = typeof getAttr(dom, LOOP_NO_REORDER_DIRECTIVE) !== T_STRING || remAttr(dom, LOOP_NO_REORDER_DIRECTIVE),
1161
1447
  tagName = getTagName(dom),
1162
- impl = __tagImpl[tagName] || { tmpl: getOuterHTML(dom) },
1163
- useRoot = SPECIAL_TAGS_REGEX.test(tagName),
1164
- root = dom.parentNode,
1165
- ref = document.createTextNode(''),
1448
+ impl = __TAG_IMPL[tagName] || { tmpl: getOuterHTML(dom) },
1449
+ useRoot = RE_SPECIAL_TAGS.test(tagName),
1450
+ parentNode = dom.parentNode,
1451
+ ref = createDOMPlaceholder(),
1166
1452
  child = getTag(dom),
1167
- isOption = tagName.toLowerCase() === 'option', // the option tags must be treated differently
1453
+ ifExpr = getAttr(dom, CONDITIONAL_DIRECTIVE),
1168
1454
  tags = [],
1169
1455
  oldItems = [],
1170
1456
  hasKeys,
1171
- isVirtual = dom.tagName == 'VIRTUAL'
1457
+ isLoop = true,
1458
+ isAnonymous = !__TAG_IMPL[tagName],
1459
+ isVirtual = dom.tagName === 'VIRTUAL';
1172
1460
 
1173
1461
  // parse the each expression
1174
- expr = tmpl.loopKeys(expr)
1462
+ expr = tmpl.loopKeys(expr);
1463
+ expr.isLoop = true;
1175
1464
 
1176
- // insert a marked where the loop tags will be injected
1177
- root.insertBefore(ref, dom)
1465
+ if (ifExpr) { remAttr(dom, CONDITIONAL_DIRECTIVE); }
1178
1466
 
1179
- // clean template code
1180
- parent.one('before-mount', function () {
1467
+ // insert a marked where the loop tags will be injected
1468
+ parentNode.insertBefore(ref, dom);
1469
+ parentNode.removeChild(dom);
1181
1470
 
1182
- // remove the original DOM node
1183
- dom.parentNode.removeChild(dom)
1184
- if (root.stub) root = parent.root
1471
+ expr.update = function updateEach() {
1185
1472
 
1186
- }).on('update', function () {
1187
1473
  // get the new items collection
1188
1474
  var items = tmpl(expr.val, parent),
1189
- // create a fragment to hold the new DOM nodes to inject in the parent tag
1190
- frag = document.createDocumentFragment()
1475
+ frag = createFrag(),
1476
+ isObject$$1 = !isArray(items),
1477
+ root = ref.parentNode;
1191
1478
 
1192
1479
  // object loop. any changes cause full redraw
1193
- if (!isArray(items)) {
1194
- hasKeys = items || false
1480
+ if (isObject$$1) {
1481
+ hasKeys = items || false;
1195
1482
  items = hasKeys ?
1196
1483
  Object.keys(items).map(function (key) {
1197
- return mkitem(expr, key, items[key])
1198
- }) : []
1484
+ return mkitem(expr, items[key], key)
1485
+ }) : [];
1486
+ } else {
1487
+ hasKeys = false;
1199
1488
  }
1200
1489
 
1201
- // loop all the new items
1202
- var i = 0,
1203
- itemsLength = items.length
1490
+ if (ifExpr) {
1491
+ items = items.filter(function(item, i) {
1492
+ if (expr.key && !isObject$$1)
1493
+ { return !!tmpl(ifExpr, mkitem(expr, item, i, parent)) }
1204
1494
 
1205
- for (; i < itemsLength; i++) {
1495
+ return !!tmpl(ifExpr, extend(Object.create(parent), item))
1496
+ });
1497
+ }
1498
+
1499
+ // loop all the new items
1500
+ each(items, function(item, i) {
1206
1501
  // reorder only if the items are objects
1207
1502
  var
1208
- item = items[i],
1209
- _mustReorder = mustReorder && typeof item == T_OBJECT && !hasKeys,
1503
+ doReorder = mustReorder && typeof item === T_OBJECT && !hasKeys,
1210
1504
  oldPos = oldItems.indexOf(item),
1211
- pos = ~oldPos && _mustReorder ? oldPos : i,
1505
+ isNew = !~oldPos,
1506
+ mustAppend = i <= tags.length,
1507
+ pos = !isNew && doReorder ? oldPos : i,
1212
1508
  // does a tag exist in this position?
1213
- tag = tags[pos]
1509
+ tag = tags[pos];
1214
1510
 
1215
- item = !hasKeys && expr.key ? mkitem(expr, item, i) : item
1511
+ item = !hasKeys && expr.key ? mkitem(expr, item, i) : item;
1216
1512
 
1217
1513
  // new tag
1218
1514
  if (
1219
- !_mustReorder && !tag // with no-reorder we just update the old tags
1515
+ doReorder && isNew // by default we always try to reorder the DOM elements
1220
1516
  ||
1221
- _mustReorder && !~oldPos || !tag // by default we always try to reorder the DOM elements
1517
+ !doReorder && !tag // with no-reorder we just update the old tags
1222
1518
  ) {
1223
-
1224
- tag = new Tag(impl, {
1519
+ tag = new Tag$1(impl, {
1225
1520
  parent: parent,
1226
- isLoop: true,
1227
- hasImpl: !!__tagImpl[tagName],
1521
+ isLoop: isLoop,
1522
+ isAnonymous: isAnonymous,
1228
1523
  root: useRoot ? root : dom.cloneNode(),
1229
1524
  item: item
1230
- }, dom.innerHTML)
1525
+ }, dom.innerHTML);
1231
1526
 
1232
- tag.mount()
1527
+ // mount the tag
1528
+ tag.mount();
1233
1529
 
1234
- if (isVirtual) tag._root = tag.root.firstChild // save reference for further moves or inserts
1235
- // this tag must be appended
1236
- if (i == tags.length || !tags[i]) { // fix 1581
1237
- if (isVirtual)
1238
- addVirtual(tag, frag)
1239
- else frag.appendChild(tag.root)
1240
- }
1241
- // this tag must be insert
1242
- else {
1243
- if (isVirtual)
1244
- addVirtual(tag, root, tags[i])
1245
- else root.insertBefore(tag.root, tags[i].root) // #1374 some browsers reset selected here
1246
- oldItems.splice(i, 0, item)
1247
- }
1530
+ if (mustAppend)
1531
+ { append.apply(tag, [frag || root, isVirtual]); }
1532
+ else
1533
+ { insert.apply(tag, [root, tags[i], isVirtual]); }
1248
1534
 
1249
- tags.splice(i, 0, tag)
1250
- pos = i // handled here so no move
1251
- } else tag.update(item, true)
1535
+ if (!mustAppend) { oldItems.splice(i, 0, item); }
1536
+ tags.splice(i, 0, tag);
1537
+ if (child) { arrayishAdd(parent.tags, tagName, tag, true); }
1538
+ pos = i; // handled here so no move
1539
+ } else { tag.update(item); }
1252
1540
 
1253
1541
  // reorder the tag if it's not located in its previous position
1254
- if (
1255
- pos !== i && _mustReorder &&
1256
- tags[i] // fix 1581 unable to reproduce it in a test!
1257
- ) {
1258
- // update the DOM
1259
- if (isVirtual)
1260
- moveVirtual(tag, root, tags[i], dom.childNodes.length)
1261
- else if (tags[i].root.parentNode) root.insertBefore(tag.root, tags[i].root)
1542
+ if (pos !== i && doReorder) {
1543
+ // #closes 2040
1544
+ if (contains(items, oldItems[i])) {
1545
+ move.apply(tag, [root, tags[i], isVirtual]);
1546
+ }
1262
1547
  // update the position attribute if it exists
1263
- if (expr.pos)
1264
- tag[expr.pos] = i
1548
+ if (expr.pos) { tag[expr.pos] = i; }
1265
1549
  // move the old tag instance
1266
- tags.splice(i, 0, tags.splice(pos, 1)[0])
1550
+ tags.splice(i, 0, tags.splice(pos, 1)[0]);
1267
1551
  // move the old item
1268
- oldItems.splice(i, 0, oldItems.splice(pos, 1)[0])
1552
+ oldItems.splice(i, 0, oldItems.splice(pos, 1)[0]);
1269
1553
  // if the loop tags are not custom
1270
1554
  // we need to move all their custom tags into the right position
1271
- if (!child && tag.tags) moveNestedTags(tag, i)
1555
+ if (!child && tag.tags) { moveNestedTags.call(tag, i); }
1272
1556
  }
1273
1557
 
1274
1558
  // cache the original item to use it in the events bound to this node
1275
1559
  // and its children
1276
- tag._item = item
1560
+ tag._item = item;
1277
1561
  // cache the real parent tag internally
1278
- defineProperty(tag, '_parent', parent)
1279
- }
1562
+ defineProperty(tag, '_parent', parent);
1563
+ });
1280
1564
 
1281
1565
  // remove the redundant tags
1282
- unmountRedundant(items, tags)
1283
-
1284
- // insert the new nodes
1285
- root.insertBefore(frag, ref)
1286
- if (isOption) {
1287
-
1288
- // #1374 FireFox bug in <option selected={expression}>
1289
- if (FIREFOX && !root.multiple) {
1290
- for (var n = 0; n < root.length; n++) {
1291
- if (root[n].__riot1374) {
1292
- root.selectedIndex = n // clear other options
1293
- delete root[n].__riot1374
1294
- break
1295
- }
1296
- }
1566
+ unmountRedundant(items, tags, tagName);
1567
+
1568
+ // clone the items array
1569
+ oldItems = items.slice();
1570
+
1571
+ root.insertBefore(frag, ref);
1572
+ };
1573
+
1574
+ expr.unmount = function() {
1575
+ each(tags, function(t) { t.unmount(); });
1576
+ };
1577
+
1578
+ return expr
1579
+ }
1580
+
1581
+ /**
1582
+ * Walk the tag DOM to detect the expressions to evaluate
1583
+ * @this Tag
1584
+ * @param { HTMLElement } root - root tag where we will start digging the expressions
1585
+ * @param { Array } expressions - empty array where the expressions will be added
1586
+ * @param { Boolean } mustIncludeRoot - flag to decide whether the root must be parsed as well
1587
+ * @returns { Object } an object containing the root noode and the dom tree
1588
+ */
1589
+ function parseExpressions(root, expressions, mustIncludeRoot) {
1590
+ var this$1 = this;
1591
+
1592
+ var tree = {parent: {children: expressions}};
1593
+
1594
+ walkNodes(root, function (dom, ctx) {
1595
+ var type = dom.nodeType, parent = ctx.parent, attr, expr, tagImpl;
1596
+ if (!mustIncludeRoot && dom === root) { return {parent: parent} }
1597
+
1598
+ // text node
1599
+ if (type === 3 && dom.parentNode.tagName !== 'STYLE' && tmpl.hasExpr(dom.nodeValue))
1600
+ { parent.children.push({dom: dom, expr: dom.nodeValue}); }
1601
+
1602
+ if (type !== 1) { return ctx } // not an element
1603
+
1604
+ // loop. each does it's own thing (for now)
1605
+ if (attr = getAttr(dom, LOOP_DIRECTIVE)) {
1606
+ parent.children.push(_each(dom, this$1, attr));
1607
+ return false
1608
+ }
1609
+
1610
+ // if-attrs become the new parent. Any following expressions (either on the current
1611
+ // element, or below it) become children of this expression.
1612
+ if (attr = getAttr(dom, CONDITIONAL_DIRECTIVE)) {
1613
+ parent.children.push(Object.create(IfExpr).init(dom, this$1, attr));
1614
+ return false
1615
+ }
1616
+
1617
+ if (expr = getAttr(dom, IS_DIRECTIVE)) {
1618
+ if (tmpl.hasExpr(expr)) {
1619
+ parent.children.push({isRtag: true, expr: expr, dom: dom, attrs: [].slice.call(dom.attributes)});
1620
+ return false
1297
1621
  }
1298
1622
  }
1299
1623
 
1300
- // set the 'tags' property of the parent tag
1301
- // if child is 'undefined' it means that we don't need to set this property
1302
- // for example:
1303
- // we don't need store the `myTag.tags['div']` property if we are looping a div tag
1304
- // but we need to track the `myTag.tags['child']` property looping a custom child node named `child`
1305
- if (child) parent.tags[tagName] = tags
1624
+ // if this is a tag, stop traversing here.
1625
+ // we ignore the root, since parseExpressions is called while we're mounting that root
1626
+ tagImpl = getTag(dom);
1627
+ if (tagImpl && (dom !== root || mustIncludeRoot)) {
1628
+ var conf = {root: dom, parent: this$1, hasImpl: true};
1629
+ parent.children.push(initChildTag(tagImpl, conf, dom.innerHTML, this$1));
1630
+ return false
1631
+ }
1306
1632
 
1307
- // clone the items array
1308
- oldItems = items.slice()
1633
+ // attribute expressions
1634
+ parseAttributes.apply(this$1, [dom, dom.attributes, function(attr, expr) {
1635
+ if (!expr) { return }
1636
+ parent.children.push(expr);
1637
+ }]);
1309
1638
 
1310
- })
1639
+ // whatever the parent is, all child elements get the same parent.
1640
+ // If this element had an if-attr, that's the parent for all child elements
1641
+ return {parent: parent}
1642
+ }, tree);
1311
1643
 
1644
+ return { tree: tree, root: root }
1312
1645
  }
1646
+
1313
1647
  /**
1314
- * Object that will be used to inject and manage the css of every tag instance
1648
+ * Calls `fn` for every attribute on an element. If that attr has an expression,
1649
+ * it is also passed to fn.
1650
+ * @this Tag
1651
+ * @param { HTMLElement } dom - dom node to parse
1652
+ * @param { Array } attrs - array of attributes
1653
+ * @param { Function } fn - callback to exec on any iteration
1654
+ */
1655
+ function parseAttributes(dom, attrs, fn) {
1656
+ var this$1 = this;
1657
+
1658
+ each(attrs, function (attr) {
1659
+ var name = attr.name, bool = isBoolAttr(name), expr;
1660
+
1661
+ if (contains(REF_DIRECTIVES, name)) {
1662
+ expr = Object.create(RefExpr).init(dom, this$1, name, attr.value);
1663
+ } else if (tmpl.hasExpr(attr.value)) {
1664
+ expr = {dom: dom, expr: attr.value, attr: attr.name, bool: bool};
1665
+ }
1666
+
1667
+ fn(attr, expr);
1668
+ });
1669
+ }
1670
+
1671
+ /*
1672
+ Includes hacks needed for the Internet Explorer version 9 and below
1673
+ See: http://kangax.github.io/compat-table/es5/#ie8
1674
+ http://codeplanet.io/dropping-ie8/
1675
+ */
1676
+
1677
+ var reHasYield = /<yield\b/i;
1678
+ var reYieldAll = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>|>)/ig;
1679
+ var reYieldSrc = /<yield\s+to=['"]([^'">]*)['"]\s*>([\S\s]*?)<\/yield\s*>/ig;
1680
+ var reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig;
1681
+ var rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' };
1682
+ var tblTags = IE_VERSION && IE_VERSION < 10 ? RE_SPECIAL_TAGS : RE_SPECIAL_TAGS_NO_OPTION;
1683
+ var GENERIC = 'div';
1684
+
1685
+
1686
+ /*
1687
+ Creates the root element for table or select child elements:
1688
+ tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
1689
+ */
1690
+ function specialTags(el, tmpl, tagName) {
1691
+
1692
+ var
1693
+ select = tagName[0] === 'o',
1694
+ parent = select ? 'select>' : 'table>';
1695
+
1696
+ // trim() is important here, this ensures we don't have artifacts,
1697
+ // so we can check if we have only one element inside the parent
1698
+ el.innerHTML = '<' + parent + tmpl.trim() + '</' + parent;
1699
+ parent = el.firstChild;
1700
+
1701
+ // returns the immediate parent if tr/th/td/col is the only element, if not
1702
+ // returns the whole tree, as this can include additional elements
1703
+ if (select) {
1704
+ parent.selectedIndex = -1; // for IE9, compatible w/current riot behavior
1705
+ } else {
1706
+ // avoids insertion of cointainer inside container (ex: tbody inside tbody)
1707
+ var tname = rootEls[tagName];
1708
+ if (tname && parent.childElementCount === 1) { parent = $(tname, parent); }
1709
+ }
1710
+ return parent
1711
+ }
1712
+
1713
+ /*
1714
+ Replace the yield tag from any tag template with the innerHTML of the
1715
+ original tag in the page
1716
+ */
1717
+ function replaceYield(tmpl, html) {
1718
+ // do nothing if no yield
1719
+ if (!reHasYield.test(tmpl)) { return tmpl }
1720
+
1721
+ // be careful with #1343 - string on the source having `$1`
1722
+ var src = {};
1723
+
1724
+ html = html && html.replace(reYieldSrc, function (_, ref, text) {
1725
+ src[ref] = src[ref] || text; // preserve first definition
1726
+ return ''
1727
+ }).trim();
1728
+
1729
+ return tmpl
1730
+ .replace(reYieldDest, function (_, ref, def) { // yield with from - to attrs
1731
+ return src[ref] || def || ''
1732
+ })
1733
+ .replace(reYieldAll, function (_, def) { // yield without any "from"
1734
+ return html || def || ''
1735
+ })
1736
+ }
1737
+
1738
+ /**
1739
+ * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
1740
+ * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
1741
+ *
1742
+ * @param { String } tmpl - The template coming from the custom tag definition
1743
+ * @param { String } html - HTML content that comes from the DOM element where you
1744
+ * will mount the tag, mostly the original tag in the page
1745
+ * @param { Boolean } checkSvg - flag needed to know if we need to force the svg rendering in case of loop nodes
1746
+ * @returns { HTMLElement } DOM element with _tmpl_ merged through `YIELD` with the _html_.
1747
+ */
1748
+ function mkdom(tmpl, html, checkSvg) {
1749
+ var match = tmpl && tmpl.match(/^\s*<([-\w]+)/),
1750
+ tagName = match && match[1].toLowerCase(),
1751
+ el = mkEl(GENERIC, checkSvg && isSVGTag(tagName));
1752
+
1753
+ // replace all the yield tags with the tag inner html
1754
+ tmpl = replaceYield(tmpl, html);
1755
+
1756
+ /* istanbul ignore next */
1757
+ if (tblTags.test(tagName))
1758
+ { el = specialTags(el, tmpl, tagName); }
1759
+ else
1760
+ { setInnerHTML(el, tmpl); }
1761
+
1762
+ el.stub = true;
1763
+
1764
+ return el
1765
+ }
1766
+
1767
+ /**
1768
+ * Another way to create a riot tag a bit more es6 friendly
1769
+ * @param { HTMLElement } el - tag DOM selector or DOM node/s
1770
+ * @param { Object } opts - tag logic
1771
+ * @returns { Tag } new riot tag instance
1772
+ */
1773
+ function Tag$2(el, opts) {
1774
+ // get the tag properties from the class constructor
1775
+ var ref = this;
1776
+ var name = ref.name;
1777
+ var tmpl = ref.tmpl;
1778
+ var css = ref.css;
1779
+ var attrs = ref.attrs;
1780
+ var onCreate = ref.onCreate;
1781
+ // register a new tag and cache the class prototype
1782
+ if (!__TAG_IMPL[name]) {
1783
+ tag$1(name, tmpl, css, attrs, onCreate);
1784
+ // cache the class constructor
1785
+ __TAG_IMPL[name].class = this.constructor;
1786
+ }
1787
+
1788
+ // mount the tag using the class instance
1789
+ mountTo(el, name, opts, this);
1790
+ // inject the component css
1791
+ if (css) { styleManager.inject(); }
1792
+
1793
+ return this
1794
+ }
1795
+
1796
+ /**
1797
+ * Create a new riot tag implementation
1798
+ * @param { String } name - name/id of the new riot tag
1799
+ * @param { String } tmpl - tag template
1800
+ * @param { String } css - custom tag css
1801
+ * @param { String } attrs - root tag attributes
1802
+ * @param { Function } fn - user function
1803
+ * @returns { String } name/id of the tag just created
1804
+ */
1805
+ function tag$1(name, tmpl, css, attrs, fn) {
1806
+ if (isFunction(attrs)) {
1807
+ fn = attrs;
1808
+
1809
+ if (/^[\w\-]+\s?=/.test(css)) {
1810
+ attrs = css;
1811
+ css = '';
1812
+ } else
1813
+ { attrs = ''; }
1814
+ }
1815
+
1816
+ if (css) {
1817
+ if (isFunction(css))
1818
+ { fn = css; }
1819
+ else
1820
+ { styleManager.add(css); }
1821
+ }
1822
+
1823
+ name = name.toLowerCase();
1824
+ __TAG_IMPL[name] = { name: name, tmpl: tmpl, attrs: attrs, fn: fn };
1825
+
1826
+ return name
1827
+ }
1828
+
1829
+ /**
1830
+ * Create a new riot tag implementation (for use by the compiler)
1831
+ * @param { String } name - name/id of the new riot tag
1832
+ * @param { String } tmpl - tag template
1833
+ * @param { String } css - custom tag css
1834
+ * @param { String } attrs - root tag attributes
1835
+ * @param { Function } fn - user function
1836
+ * @returns { String } name/id of the tag just created
1837
+ */
1838
+ function tag2$1(name, tmpl, css, attrs, fn) {
1839
+ if (css)
1840
+ { styleManager.add(css, name); }
1841
+
1842
+ var exists = !!__TAG_IMPL[name];
1843
+ __TAG_IMPL[name] = { name: name, tmpl: tmpl, attrs: attrs, fn: fn };
1844
+
1845
+ if (exists && util.hotReloader)
1846
+ { util.hotReloader(name); }
1847
+
1848
+ return name
1849
+ }
1850
+
1851
+ /**
1852
+ * Mount a tag using a specific tag implementation
1853
+ * @param { * } selector - tag DOM selector or DOM node/s
1854
+ * @param { String } tagName - tag implementation name
1855
+ * @param { Object } opts - tag logic
1856
+ * @returns { Array } new tags instances
1315
1857
  */
1316
- var styleManager = (function(_riot) {
1317
-
1318
- if (!window) return { // skip injection on the server
1319
- add: function () {},
1320
- inject: function () {}
1321
- }
1858
+ function mount$1(selector, tagName, opts) {
1859
+ var tags = [];
1322
1860
 
1323
- var styleNode = (function () {
1324
- // create a new style element with the correct type
1325
- var newNode = mkEl('style')
1326
- setAttr(newNode, 'type', 'text/css')
1861
+ function pushTagsTo(root) {
1862
+ if (root.tagName) {
1863
+ var riotTag = getAttr(root, IS_DIRECTIVE);
1327
1864
 
1328
- // replace any user node or insert the new one into the head
1329
- var userNode = $('style[type=riot]')
1330
- if (userNode) {
1331
- if (userNode.id) newNode.id = userNode.id
1332
- userNode.parentNode.replaceChild(newNode, userNode)
1333
- }
1334
- else document.getElementsByTagName('head')[0].appendChild(newNode)
1865
+ // have tagName? force riot-tag to be the same
1866
+ if (tagName && riotTag !== tagName) {
1867
+ riotTag = tagName;
1868
+ setAttr(root, IS_DIRECTIVE, tagName);
1869
+ }
1335
1870
 
1336
- return newNode
1337
- })()
1871
+ var tag$$1 = mountTo(root, riotTag || root.tagName.toLowerCase(), opts);
1338
1872
 
1339
- // Create cache and shortcut to the correct property
1340
- var cssTextProp = styleNode.styleSheet,
1341
- stylesToInject = ''
1873
+ if (tag$$1)
1874
+ { tags.push(tag$$1); }
1875
+ } else if (root.length)
1876
+ { each(root, pushTagsTo); } // assume nodeList
1877
+ }
1342
1878
 
1343
- // Expose the style node in a non-modificable property
1344
- Object.defineProperty(_riot, 'styleNode', {
1345
- value: styleNode,
1346
- writable: true
1347
- })
1879
+ // inject styles into DOM
1880
+ styleManager.inject();
1348
1881
 
1349
- /**
1350
- * Public api
1351
- */
1352
- return {
1353
- /**
1354
- * Save a tag style to be later injected into DOM
1355
- * @param { String } css [description]
1356
- */
1357
- add: function(css) {
1358
- stylesToInject += css
1359
- },
1360
- /**
1361
- * Inject all previously saved tag styles into DOM
1362
- * innerHTML seems slow: http://jsperf.com/riot-insert-style
1363
- */
1364
- inject: function() {
1365
- if (stylesToInject) {
1366
- if (cssTextProp) cssTextProp.cssText += stylesToInject
1367
- else styleNode.innerHTML += stylesToInject
1368
- stylesToInject = ''
1369
- }
1370
- }
1882
+ if (isObject(tagName)) {
1883
+ opts = tagName;
1884
+ tagName = 0;
1371
1885
  }
1372
1886
 
1373
- })(riot)
1887
+ var elem;
1888
+ var allTags;
1374
1889
 
1890
+ // crawl the DOM to find the tag
1891
+ if (isString(selector)) {
1892
+ selector = selector === '*' ?
1893
+ // select all registered tags
1894
+ // & tags found with the riot-tag attribute set
1895
+ allTags = selectTags() :
1896
+ // or just the ones named like the selector
1897
+ selector + selectTags(selector.split(/, */));
1375
1898
 
1376
- function parseNamedElements(root, tag, childTags, forceParsingNamed) {
1377
-
1378
- walk(root, function(dom) {
1379
- if (dom.nodeType == 1) {
1380
- dom.isLoop = dom.isLoop ||
1381
- (dom.parentNode && dom.parentNode.isLoop || getAttr(dom, 'each'))
1382
- ? 1 : 0
1899
+ // make sure to pass always a selector
1900
+ // to the querySelectorAll function
1901
+ elem = selector ? $$(selector) : [];
1902
+ }
1903
+ else
1904
+ // probably you have passed already a tag or a NodeList
1905
+ { elem = selector; }
1383
1906
 
1384
- // custom child tag
1385
- if (childTags) {
1386
- var child = getTag(dom)
1907
+ // select all the registered and mount them inside their root elements
1908
+ if (tagName === '*') {
1909
+ // get all custom tags
1910
+ tagName = allTags || selectTags();
1911
+ // if the root els it's just a single tag
1912
+ if (elem.tagName)
1913
+ { elem = $$(tagName, elem); }
1914
+ else {
1915
+ // select all the children for all the different root elements
1916
+ var nodeList = [];
1387
1917
 
1388
- if (child && !dom.isLoop)
1389
- childTags.push(initChildTag(child, {root: dom, parent: tag}, dom.innerHTML, tag))
1390
- }
1918
+ each(elem, function (_el) { return nodeList.push($$(tagName, _el)); });
1391
1919
 
1392
- if (!dom.isLoop || forceParsingNamed)
1393
- setNamed(dom, tag, [])
1920
+ elem = nodeList;
1394
1921
  }
1922
+ // get rid of the tagName
1923
+ tagName = 0;
1924
+ }
1395
1925
 
1396
- })
1926
+ pushTagsTo(elem);
1397
1927
 
1928
+ return tags
1398
1929
  }
1399
1930
 
1400
- function parseExpressions(root, tag, expressions) {
1931
+ // Create a mixin that could be globally shared across all the tags
1932
+ var mixins = {};
1933
+ var globals = mixins[GLOBAL_MIXIN] = {};
1934
+ var _id = 0;
1401
1935
 
1402
- function addExpr(dom, val, extra) {
1403
- if (tmpl.hasExpr(val)) {
1404
- expressions.push(extend({ dom: dom, expr: val }, extra))
1405
- }
1936
+ /**
1937
+ * Create/Return a mixin by its name
1938
+ * @param { String } name - mixin name (global mixin if object)
1939
+ * @param { Object } mix - mixin logic
1940
+ * @param { Boolean } g - is global?
1941
+ * @returns { Object } the mixin logic
1942
+ */
1943
+ function mixin$1(name, mix, g) {
1944
+ // Unnamed global
1945
+ if (isObject(name)) {
1946
+ mixin$1(("__unnamed_" + (_id++)), name, true);
1947
+ return
1406
1948
  }
1407
1949
 
1408
- walk(root, function(dom) {
1409
- var type = dom.nodeType,
1410
- attr
1950
+ var store = g ? globals : mixins;
1411
1951
 
1412
- // text node
1413
- if (type == 3 && dom.parentNode.tagName != 'STYLE') addExpr(dom, dom.nodeValue)
1414
- if (type != 1) return
1952
+ // Getter
1953
+ if (!mix) {
1954
+ if (isUndefined(store[name]))
1955
+ { throw new Error('Unregistered mixin: ' + name) }
1415
1956
 
1416
- /* element */
1957
+ return store[name]
1958
+ }
1417
1959
 
1418
- // loop
1419
- attr = getAttr(dom, 'each')
1960
+ // Setter
1961
+ store[name] = isFunction(mix) ?
1962
+ extend(mix.prototype, store[name] || {}) && mix :
1963
+ extend(store[name] || {}, mix);
1964
+ }
1420
1965
 
1421
- if (attr) { _each(dom, tag, attr); return false }
1966
+ /**
1967
+ * Update all the tags instances created
1968
+ * @returns { Array } all the tags instances
1969
+ */
1970
+ function update$1() {
1971
+ return each(__TAGS_CACHE, function (tag$$1) { return tag$$1.update(); })
1972
+ }
1422
1973
 
1423
- // attribute expressions
1424
- each(dom.attributes, function(attr) {
1425
- var name = attr.name,
1426
- bool = name.split('__')[1]
1974
+ function unregister$1(name) {
1975
+ delete __TAG_IMPL[name];
1976
+ }
1427
1977
 
1428
- addExpr(dom, attr.value, { attr: bool || name, bool: bool })
1429
- if (bool) { remAttr(dom, name); return false }
1978
+ // counter to give a unique id to all the Tag instances
1979
+ var __uid = 0;
1430
1980
 
1431
- })
1981
+ /**
1982
+ * We need to update opts for this tag. That requires updating the expressions
1983
+ * in any attributes on the tag, and then copying the result onto opts.
1984
+ * @this Tag
1985
+ * @param {Boolean} isLoop - is it a loop tag?
1986
+ * @param { Tag } parent - parent tag node
1987
+ * @param { Boolean } isAnonymous - is it a tag without any impl? (a tag not registered)
1988
+ * @param { Object } opts - tag options
1989
+ * @param { Array } instAttrs - tag attributes array
1990
+ */
1991
+ function updateOpts(isLoop, parent, isAnonymous, opts, instAttrs) {
1992
+ // isAnonymous `each` tags treat `dom` and `root` differently. In this case
1993
+ // (and only this case) we don't need to do updateOpts, because the regular parse
1994
+ // will update those attrs. Plus, isAnonymous tags don't need opts anyway
1995
+ if (isLoop && isAnonymous) { return }
1432
1996
 
1433
- // skip custom tags
1434
- if (getTag(dom)) return false
1997
+ var ctx = !isAnonymous && isLoop ? this : parent || this;
1998
+ each(instAttrs, function (attr) {
1999
+ if (attr.expr) { updateAllExpressions.call(ctx, [attr.expr]); }
2000
+ opts[toCamel(attr.name)] = attr.expr ? attr.expr.value : attr.value;
2001
+ });
2002
+ }
1435
2003
 
1436
- })
1437
2004
 
1438
- }
1439
- function Tag(impl, conf, innerHTML) {
2005
+ /**
2006
+ * Tag class
2007
+ * @constructor
2008
+ * @param { Object } impl - it contains the tag template, and logic
2009
+ * @param { Object } conf - tag options
2010
+ * @param { String } innerHTML - html that eventually we need to inject in the tag
2011
+ */
2012
+ function Tag$1(impl, conf, innerHTML) {
1440
2013
 
1441
- var self = riot.observable(this),
1442
- opts = inherit(conf.opts) || {},
2014
+ var opts = extend({}, conf.opts),
1443
2015
  parent = conf.parent,
1444
2016
  isLoop = conf.isLoop,
1445
- hasImpl = conf.hasImpl,
2017
+ isAnonymous = conf.isAnonymous,
1446
2018
  item = cleanUpData(conf.item),
2019
+ instAttrs = [], // All attributes on the Tag when it's first parsed
2020
+ implAttrs = [], // expressions on this type of Tag
1447
2021
  expressions = [],
1448
- childTags = [],
1449
2022
  root = conf.root,
1450
- tagName = root.tagName.toLowerCase(),
1451
- attr = {},
2023
+ tagName = conf.tagName || getTagName(root),
2024
+ isVirtual = tagName === 'virtual',
1452
2025
  propsInSyncWithParent = [],
1453
- dom
2026
+ dom;
1454
2027
 
1455
- // only call unmount if we have a valid __tagImpl (has name property)
1456
- if (impl.name && root._tag) root._tag.unmount(true)
2028
+ // make this tag observable
2029
+ observable$1(this);
2030
+ // only call unmount if we have a valid __TAG_IMPL (has name property)
2031
+ if (impl.name && root._tag) { root._tag.unmount(true); }
1457
2032
 
1458
2033
  // not yet mounted
1459
- this.isMounted = false
1460
- root.isLoop = isLoop
1461
-
1462
- // keep a reference to the tag just created
1463
- // so we will be able to mount this tag multiple times
1464
- root._tag = this
2034
+ this.isMounted = false;
2035
+ root.isLoop = isLoop;
2036
+
2037
+ defineProperty(this, '_internal', {
2038
+ isAnonymous: isAnonymous,
2039
+ instAttrs: instAttrs,
2040
+ innerHTML: innerHTML,
2041
+ // these vars will be needed only for the virtual tags
2042
+ virts: [],
2043
+ tail: null,
2044
+ head: null
2045
+ });
1465
2046
 
1466
2047
  // create a unique id to this tag
1467
2048
  // it could be handy to use it also to improve the virtual dom rendering speed
1468
- defineProperty(this, '_riot_id', ++__uid) // base 1 allows test !t._riot_id
1469
-
1470
- extend(this, { parent: parent, root: root, opts: opts}, item)
1471
- // protect the "tags" property from being overridden
1472
- defineProperty(this, 'tags', {})
1473
-
1474
- // grab attributes
1475
- each(root.attributes, function(el) {
1476
- var val = el.value
1477
- // remember attributes with expressions only
1478
- if (tmpl.hasExpr(val)) attr[el.name] = val
1479
- })
1480
-
1481
- dom = mkdom(impl.tmpl, innerHTML)
1482
-
1483
- // options
1484
- function updateOpts() {
1485
- var ctx = hasImpl && isLoop ? self : parent || self
1486
-
1487
- // update opts from current DOM attributes
1488
- each(root.attributes, function(el) {
1489
- var val = el.value
1490
- opts[toCamel(el.name)] = tmpl.hasExpr(val) ? tmpl(val, ctx) : val
1491
- })
1492
- // recover those with expressions
1493
- each(Object.keys(attr), function(name) {
1494
- opts[toCamel(name)] = tmpl(attr[name], ctx)
1495
- })
1496
- }
1497
-
1498
- function normalizeData(data) {
1499
- for (var key in item) {
1500
- if (typeof self[key] !== T_UNDEF && isWritable(self, key))
1501
- self[key] = data[key]
1502
- }
1503
- }
2049
+ defineProperty(this, '_riot_id', ++__uid); // base 1 allows test !t._riot_id
1504
2050
 
1505
- function inheritFrom(target) {
1506
- each(Object.keys(target), function(k) {
1507
- // some properties must be always in sync with the parent tag
1508
- var mustSync = !RESERVED_WORDS_BLACKLIST.test(k) && contains(propsInSyncWithParent, k)
2051
+ extend(this, { root: root, opts: opts }, item);
2052
+ // protect the "tags" and "refs" property from being overridden
2053
+ defineProperty(this, 'parent', parent || null);
2054
+ defineProperty(this, 'tags', {});
2055
+ defineProperty(this, 'refs', {});
1509
2056
 
1510
- if (typeof self[k] === T_UNDEF || mustSync) {
1511
- // track the property to keep in sync
1512
- // so we can keep it updated
1513
- if (!mustSync) propsInSyncWithParent.push(k)
1514
- self[k] = target[k]
1515
- }
1516
- })
1517
- }
2057
+ dom = mkdom(impl.tmpl, innerHTML, isLoop);
1518
2058
 
1519
2059
  /**
1520
2060
  * Update the tag expressions and options
1521
2061
  * @param { * } data - data we want to use to extend the tag properties
1522
- * @param { Boolean } isInherited - is this update coming from a parent tag?
1523
- * @returns { self }
2062
+ * @returns { Tag } the current tag instance
1524
2063
  */
1525
- defineProperty(this, 'update', function(data, isInherited) {
2064
+ defineProperty(this, 'update', function tagUpdate(data) {
2065
+ if (isFunction(this.shouldUpdate) && !this.shouldUpdate(data)) { return this }
1526
2066
 
1527
2067
  // make sure the data passed will not override
1528
2068
  // the component core methods
1529
- data = cleanUpData(data)
1530
- // inherit properties from the parent in loop
1531
- if (isLoop) {
1532
- inheritFrom(self.parent)
1533
- }
1534
- // normalize the tag properties in case an item object was initially passed
1535
- if (data && isObject(item)) {
1536
- normalizeData(data)
1537
- item = data
1538
- }
1539
- extend(self, data)
1540
- updateOpts()
1541
- self.trigger('update', data)
1542
- update(expressions, self)
1543
-
1544
- // the updated event will be triggered
1545
- // once the DOM will be ready and all the re-flows are completed
1546
- // this is useful if you want to get the "real" root properties
1547
- // 4 ex: root.offsetWidth ...
1548
- if (isInherited && self.parent)
1549
- // closes #1599
1550
- self.parent.one('updated', function() { self.trigger('updated') })
1551
- else rAF(function() { self.trigger('updated') })
2069
+ data = cleanUpData(data);
2070
+
2071
+ // inherit properties from the parent, but only for isAnonymous tags
2072
+ if (isLoop && isAnonymous) { inheritFrom.apply(this, [this.parent, propsInSyncWithParent]); }
2073
+ extend(this, data);
2074
+ updateOpts.apply(this, [isLoop, parent, isAnonymous, opts, instAttrs]);
2075
+ if (this.isMounted) { this.trigger('update', data); }
2076
+ updateAllExpressions.call(this, expressions);
2077
+ if (this.isMounted) { this.trigger('updated'); }
1552
2078
 
1553
2079
  return this
1554
- })
1555
2080
 
1556
- defineProperty(this, 'mixin', function() {
1557
- each(arguments, function(mix) {
2081
+ }.bind(this));
2082
+
2083
+ /**
2084
+ * Add a mixin to this tag
2085
+ * @returns { Tag } the current tag instance
2086
+ */
2087
+ defineProperty(this, 'mixin', function tagMixin() {
2088
+ var this$1 = this;
2089
+
2090
+ each(arguments, function (mix) {
1558
2091
  var instance,
1559
2092
  props = [],
1560
- obj
2093
+ obj;
1561
2094
 
1562
- mix = typeof mix === T_STRING ? riot.mixin(mix) : mix
2095
+ mix = isString(mix) ? mixin$1(mix) : mix;
1563
2096
 
1564
2097
  // check if the mixin is a function
1565
2098
  if (isFunction(mix)) {
1566
2099
  // create the new mixin instance
1567
- instance = new mix()
1568
- } else instance = mix
2100
+ instance = new mix();
2101
+ } else { instance = mix; }
2102
+
2103
+ var proto = Object.getPrototypeOf(instance);
1569
2104
 
1570
2105
  // build multilevel prototype inheritance chain property list
1571
- do props = props.concat(Object.getOwnPropertyNames(obj || instance))
2106
+ do { props = props.concat(Object.getOwnPropertyNames(obj || instance)); }
1572
2107
  while (obj = Object.getPrototypeOf(obj || instance))
1573
2108
 
1574
2109
  // loop the keys in the function prototype or the all object keys
1575
- each(props, function(key) {
1576
- // bind methods to self
2110
+ each(props, function (key) {
2111
+ // bind methods to this
1577
2112
  // allow mixins to override other properties/parent mixins
1578
- if (key != 'init') {
2113
+ if (key !== 'init') {
1579
2114
  // check for getters/setters
1580
- var descriptor = Object.getOwnPropertyDescriptor(instance, key)
1581
- var hasGetterSetter = descriptor && (descriptor.get || descriptor.set)
2115
+ var descriptor = Object.getOwnPropertyDescriptor(instance, key) || Object.getOwnPropertyDescriptor(proto, key);
2116
+ var hasGetterSetter = descriptor && (descriptor.get || descriptor.set);
1582
2117
 
1583
2118
  // apply method only if it does not already exist on the instance
1584
- if (!self.hasOwnProperty(key) && hasGetterSetter) {
1585
- Object.defineProperty(self, key, descriptor)
2119
+ if (!this$1.hasOwnProperty(key) && hasGetterSetter) {
2120
+ Object.defineProperty(this$1, key, descriptor);
1586
2121
  } else {
1587
- self[key] = isFunction(instance[key]) ?
1588
- instance[key].bind(self) :
1589
- instance[key]
2122
+ this$1[key] = isFunction(instance[key]) ?
2123
+ instance[key].bind(this$1) :
2124
+ instance[key];
1590
2125
  }
1591
2126
  }
1592
- })
2127
+ });
1593
2128
 
1594
2129
  // init method will be called automatically
1595
- if (instance.init) instance.init.bind(self)()
1596
- })
2130
+ if (instance.init)
2131
+ { instance.init.bind(this$1)(); }
2132
+ });
1597
2133
  return this
1598
- })
2134
+ }.bind(this));
1599
2135
 
1600
- defineProperty(this, 'mount', function() {
2136
+ /**
2137
+ * Mount the current tag instance
2138
+ * @returns { Tag } the current tag instance
2139
+ */
2140
+ defineProperty(this, 'mount', function tagMount() {
2141
+ var this$1 = this;
1601
2142
 
1602
- updateOpts()
2143
+ root._tag = this; // keep a reference to the tag just created
1603
2144
 
1604
- // add global mixins
1605
- var globalMixin = riot.mixin(GLOBAL_MIXIN)
2145
+ // Read all the attrs on this instance. This give us the info we need for updateOpts
2146
+ parseAttributes.apply(parent, [root, root.attributes, function (attr, expr) {
2147
+ if (!isAnonymous && RefExpr.isPrototypeOf(expr)) { expr.tag = this$1; }
2148
+ attr.expr = expr;
2149
+ instAttrs.push(attr);
2150
+ }]);
1606
2151
 
1607
- if (globalMixin)
1608
- for (var i in globalMixin)
1609
- if (globalMixin.hasOwnProperty(i))
1610
- self.mixin(globalMixin[i])
2152
+ // update the root adding custom attributes coming from the compiler
2153
+ implAttrs = [];
2154
+ walkAttrs(impl.attrs, function (k, v) { implAttrs.push({name: k, value: v}); });
2155
+ parseAttributes.apply(this, [root, implAttrs, function (attr, expr) {
2156
+ if (expr) { expressions.push(expr); }
2157
+ else { setAttr(root, attr.name, attr.value); }
2158
+ }]);
1611
2159
 
1612
2160
  // children in loop should inherit from true parent
1613
- if (self._parent) {
1614
- inheritFrom(self._parent)
1615
- }
2161
+ if (this._parent && isAnonymous) { inheritFrom.apply(this, [this._parent, propsInSyncWithParent]); }
1616
2162
 
1617
2163
  // initialiation
1618
- if (impl.fn) impl.fn.call(self, opts)
2164
+ updateOpts.apply(this, [isLoop, parent, isAnonymous, opts, instAttrs]);
1619
2165
 
1620
- // parse layout after init. fn may calculate args for nested custom tags
1621
- parseExpressions(dom, self, expressions)
2166
+ // add global mixins
2167
+ var globalMixin = mixin$1(GLOBAL_MIXIN);
2168
+
2169
+ if (globalMixin) {
2170
+ for (var i in globalMixin) {
2171
+ if (globalMixin.hasOwnProperty(i)) {
2172
+ this$1.mixin(globalMixin[i]);
2173
+ }
2174
+ }
2175
+ }
1622
2176
 
1623
- // mount the child tags
1624
- toggle(true)
2177
+ if (impl.fn) { impl.fn.call(this, opts); }
1625
2178
 
1626
- // update the root adding custom attributes coming from the compiler
1627
- // it fixes also #1087
1628
- if (impl.attrs)
1629
- walkAttributes(impl.attrs, function (k, v) { setAttr(root, k, v) })
1630
- if (impl.attrs || hasImpl)
1631
- parseExpressions(self.root, self, expressions)
2179
+ this.trigger('before-mount');
1632
2180
 
1633
- if (!self.parent || isLoop) self.update(item)
2181
+ // parse layout after init. fn may calculate args for nested custom tags
2182
+ parseExpressions.apply(this, [dom, expressions, false]);
1634
2183
 
1635
- // internal use only, fixes #403
1636
- self.trigger('before-mount')
2184
+ this.update(item);
1637
2185
 
1638
- if (isLoop && !hasImpl) {
2186
+ if (isLoop && isAnonymous) {
1639
2187
  // update the root attribute for the looped elements
1640
- root = dom.firstChild
2188
+ this.root = root = dom.firstChild;
1641
2189
  } else {
1642
- while (dom.firstChild) root.appendChild(dom.firstChild)
1643
- if (root.stub) root = parent.root
2190
+ while (dom.firstChild) { root.appendChild(dom.firstChild); }
2191
+ if (root.stub) { root = parent.root; }
1644
2192
  }
1645
2193
 
1646
- defineProperty(self, 'root', root)
1647
-
1648
- // parse the named dom nodes in the looped child
1649
- // adding them to the parent as well
1650
- if (isLoop)
1651
- parseNamedElements(self.root, self.parent, null, true)
2194
+ defineProperty(this, 'root', root);
2195
+ this.isMounted = true;
1652
2196
 
1653
2197
  // if it's not a child tag we can trigger its mount event
1654
- if (!self.parent || self.parent.isMounted) {
1655
- self.isMounted = true
1656
- self.trigger('mount')
2198
+ if (!this.parent || this.parent.isMounted) {
2199
+ this.trigger('mount');
1657
2200
  }
1658
2201
  // otherwise we need to wait that the parent event gets triggered
1659
- else self.parent.one('mount', function() {
1660
- // avoid to trigger the `mount` event for the tags
1661
- // not visible included in an if statement
1662
- if (!isInStub(self.root)) {
1663
- self.parent.isMounted = self.isMounted = true
1664
- self.trigger('mount')
1665
- }
1666
- })
1667
- })
2202
+ else { this.parent.one('mount', function () {
2203
+ this$1.trigger('mount');
2204
+ }); }
1668
2205
 
2206
+ return this
2207
+
2208
+ }.bind(this));
2209
+
2210
+ /**
2211
+ * Unmount the tag instance
2212
+ * @param { Boolean } mustKeepRoot - if it's true the root node will not be removed
2213
+ * @returns { Tag } the current tag instance
2214
+ */
2215
+ defineProperty(this, 'unmount', function tagUnmount(mustKeepRoot) {
2216
+ var this$1 = this;
1669
2217
 
1670
- defineProperty(this, 'unmount', function(keepRootTag) {
1671
- var el = root,
2218
+ var el = this.root,
1672
2219
  p = el.parentNode,
1673
2220
  ptag,
1674
- tagIndex = __virtualDom.indexOf(self)
2221
+ tagIndex = __TAGS_CACHE.indexOf(this);
2222
+
2223
+ this.trigger('before-unmount');
1675
2224
 
1676
- self.trigger('before-unmount')
2225
+ // clear all attributes coming from the mounted tag
2226
+ walkAttrs(impl.attrs, function (name) {
2227
+ if (startsWith(name, ATTRS_PREFIX))
2228
+ { name = name.slice(ATTRS_PREFIX.length); }
2229
+ remAttr(root, name);
2230
+ });
1677
2231
 
1678
2232
  // remove this tag instance from the global virtualDom variable
1679
2233
  if (~tagIndex)
1680
- __virtualDom.splice(tagIndex, 1)
2234
+ { __TAGS_CACHE.splice(tagIndex, 1); }
1681
2235
 
1682
2236
  if (p) {
1683
-
1684
- if (parent) {
1685
- ptag = getImmediateCustomParentTag(parent)
1686
- // remove this tag from the parent tags object
1687
- // if there are multiple nested tags with same name..
1688
- // remove this element form the array
1689
- if (isArray(ptag.tags[tagName]))
1690
- each(ptag.tags[tagName], function(tag, i) {
1691
- if (tag._riot_id == self._riot_id)
1692
- ptag.tags[tagName].splice(i, 1)
1693
- })
1694
- else
1695
- // otherwise just delete the tag instance
1696
- ptag.tags[tagName] = undefined
1697
- }
1698
-
1699
- else
1700
- while (el.firstChild) el.removeChild(el.firstChild)
1701
-
1702
- if (!keepRootTag)
1703
- p.removeChild(el)
1704
- else {
1705
- // the riot-tag and the data-is attributes aren't needed anymore, remove them
1706
- remAttr(p, RIOT_TAG_IS)
1707
- remAttr(p, RIOT_TAG) // this will be removed in riot 3.0.0
1708
- }
1709
-
1710
- }
1711
-
1712
- if (this._virts) {
1713
- each(this._virts, function(v) {
1714
- if (v.parentNode) v.parentNode.removeChild(v)
1715
- })
1716
- }
1717
-
1718
- self.trigger('unmount')
1719
- toggle()
1720
- self.off('*')
1721
- self.isMounted = false
1722
- delete root._tag
1723
-
1724
- })
1725
-
1726
- // proxy function to bind updates
1727
- // dispatched from a parent tag
1728
- function onChildUpdate(data) { self.update(data, true) }
1729
-
1730
- function toggle(isMount) {
1731
-
1732
- // mount/unmount children
1733
- each(childTags, function(child) { child[isMount ? 'mount' : 'unmount']() })
1734
-
1735
- // listen/unlisten parent (events flow one way from parent to children)
1736
- if (!parent) return
1737
- var evt = isMount ? 'on' : 'off'
1738
-
1739
- // the loop tags will be always in sync with the parent automatically
1740
- if (isLoop)
1741
- parent[evt]('unmount', self.unmount)
1742
- else {
1743
- parent[evt]('update', onChildUpdate)[evt]('unmount', self.unmount)
1744
- }
1745
- }
1746
-
1747
-
1748
- // named elements available for fn
1749
- parseNamedElements(dom, this, childTags)
1750
-
1751
- }
1752
- /**
1753
- * Attach an event to a DOM node
1754
- * @param { String } name - event name
1755
- * @param { Function } handler - event callback
1756
- * @param { Object } dom - dom node
1757
- * @param { Tag } tag - tag instance
1758
- */
1759
- function setEventHandler(name, handler, dom, tag) {
1760
-
1761
- dom[name] = function(e) {
1762
-
1763
- var ptag = tag._parent,
1764
- item = tag._item,
1765
- el
1766
-
1767
- if (!item)
1768
- while (ptag && !item) {
1769
- item = ptag._item
1770
- ptag = ptag._parent
1771
- }
1772
-
1773
- // cross browser event fix
1774
- e = e || window.event
1775
-
1776
- // override the event properties
1777
- if (isWritable(e, 'currentTarget')) e.currentTarget = dom
1778
- if (isWritable(e, 'target')) e.target = e.srcElement
1779
- if (isWritable(e, 'which')) e.which = e.charCode || e.keyCode
1780
-
1781
- e.item = item
1782
-
1783
- // prevent default behaviour (by default)
1784
- if (handler.call(tag, e) !== true && !/radio|check/.test(dom.type)) {
1785
- if (e.preventDefault) e.preventDefault()
1786
- e.returnValue = false
1787
- }
1788
-
1789
- if (!e.preventUpdate) {
1790
- el = item ? getImmediateCustomParentTag(ptag) : tag
1791
- el.update()
1792
- }
1793
-
1794
- }
1795
-
1796
- }
1797
-
1798
-
1799
- /**
1800
- * Insert a DOM node replacing another one (used by if- attribute)
1801
- * @param { Object } root - parent node
1802
- * @param { Object } node - node replaced
1803
- * @param { Object } before - node added
1804
- */
1805
- function insertTo(root, node, before) {
1806
- if (!root) return
1807
- root.insertBefore(before, node)
1808
- root.removeChild(node)
1809
- }
1810
-
1811
- /**
1812
- * Update the expressions in a Tag instance
1813
- * @param { Array } expressions - expression that must be re evaluated
1814
- * @param { Tag } tag - tag instance
1815
- */
1816
- function update(expressions, tag) {
1817
-
1818
- each(expressions, function(expr, i) {
1819
-
1820
- var dom = expr.dom,
1821
- attrName = expr.attr,
1822
- value = tmpl(expr.expr, tag),
1823
- parent = expr.parent || expr.dom.parentNode
1824
-
1825
- if (expr.bool) {
1826
- value = !!value
1827
- } else if (value == null) {
1828
- value = ''
1829
- }
1830
-
1831
- // #1638: regression of #1612, update the dom only if the value of the
1832
- // expression was changed
1833
- if (expr.value === value) {
1834
- return
1835
- }
1836
- expr.value = value
1837
-
1838
- // textarea and text nodes has no attribute name
1839
- if (!attrName) {
1840
- // about #815 w/o replace: the browser converts the value to a string,
1841
- // the comparison by "==" does too, but not in the server
1842
- value += ''
1843
- // test for parent avoids error with invalid assignment to nodeValue
1844
2237
  if (parent) {
1845
- // cache the parent node because somehow it will become null on IE
1846
- // on the next iteration
1847
- expr.parent = parent
1848
- if (parent.tagName === 'TEXTAREA') {
1849
- parent.value = value // #1113
1850
- if (!IE_VERSION) dom.nodeValue = value // #1625 IE throws here, nodeValue
1851
- } // will be available on 'updated'
1852
- else dom.nodeValue = value
1853
- }
1854
- return
1855
- }
1856
-
1857
- // ~~#1612: look for changes in dom.value when updating the value~~
1858
- if (attrName === 'value') {
1859
- if (dom.value !== value) {
1860
- dom.value = value
1861
- setAttr(dom, attrName, value)
1862
- }
1863
- return
1864
- } else {
1865
- // remove original attribute
1866
- remAttr(dom, attrName)
1867
- }
1868
-
1869
- // event handler
1870
- if (isFunction(value)) {
1871
- setEventHandler(attrName, value, dom, tag)
1872
-
1873
- // if- conditional
1874
- } else if (attrName == 'if') {
1875
- var stub = expr.stub,
1876
- add = function() { insertTo(stub.parentNode, stub, dom) },
1877
- remove = function() { insertTo(dom.parentNode, dom, stub) }
1878
-
1879
- // add to DOM
1880
- if (value) {
1881
- if (stub) {
1882
- add()
1883
- dom.inStub = false
1884
- // avoid to trigger the mount event if the tags is not visible yet
1885
- // maybe we can optimize this avoiding to mount the tag at all
1886
- if (!isInStub(dom)) {
1887
- walk(dom, function(el) {
1888
- if (el._tag && !el._tag.isMounted)
1889
- el._tag.isMounted = !!el._tag.trigger('mount')
1890
- })
1891
- }
2238
+ ptag = getImmediateCustomParentTag(parent);
2239
+
2240
+ if (isVirtual) {
2241
+ Object.keys(this.tags).forEach(function (tagName) {
2242
+ arrayishRemove(ptag.tags, tagName, this$1.tags[tagName]);
2243
+ });
2244
+ } else {
2245
+ arrayishRemove(ptag.tags, tagName, this);
2246
+ if(parent !== ptag) // remove from _parent too
2247
+ { arrayishRemove(parent.tags, tagName, this); }
1892
2248
  }
1893
- // remove from DOM
1894
2249
  } else {
1895
- stub = expr.stub = stub || document.createTextNode('')
1896
- // if the parentNode is defined we can easily replace the tag
1897
- if (dom.parentNode)
1898
- remove()
1899
- // otherwise we need to wait the updated event
1900
- else (tag.parent || tag).one('updated', remove)
1901
-
1902
- dom.inStub = true
1903
- }
1904
- // show / hide
1905
- } else if (attrName === 'show') {
1906
- dom.style.display = value ? '' : 'none'
1907
-
1908
- } else if (attrName === 'hide') {
1909
- dom.style.display = value ? 'none' : ''
1910
-
1911
- } else if (expr.bool) {
1912
- dom[attrName] = value
1913
- if (value) setAttr(dom, attrName, attrName)
1914
- if (FIREFOX && attrName === 'selected' && dom.tagName === 'OPTION') {
1915
- dom.__riot1374 = value // #1374
2250
+ while (el.firstChild) { el.removeChild(el.firstChild); }
1916
2251
  }
1917
2252
 
1918
- } else if (value === 0 || value && typeof value !== T_OBJECT) {
1919
- // <img src="{ expr }">
1920
- if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
1921
- attrName = attrName.slice(RIOT_PREFIX.length)
2253
+ if (!mustKeepRoot) {
2254
+ p.removeChild(el);
2255
+ } else {
2256
+ // the riot-tag and the data-is attributes aren't needed anymore, remove them
2257
+ remAttr(p, IS_DIRECTIVE);
1922
2258
  }
1923
- setAttr(dom, attrName, value)
1924
2259
  }
1925
2260
 
1926
- })
1927
-
1928
- }
1929
- /**
1930
- * Specialized function for looping an array-like collection with `each={}`
1931
- * @param { Array } els - collection of items
1932
- * @param {Function} fn - callback function
1933
- * @returns { Array } the array looped
1934
- */
1935
- function each(els, fn) {
1936
- var len = els ? els.length : 0
1937
-
1938
- for (var i = 0, el; i < len; i++) {
1939
- el = els[i]
1940
- // return false -> current item was removed by fn during the loop
1941
- if (el != null && fn(el, i) === false) i--
1942
- }
1943
- return els
1944
- }
1945
-
1946
- /**
1947
- * Detect if the argument passed is a function
1948
- * @param { * } v - whatever you want to pass to this function
1949
- * @returns { Boolean } -
1950
- */
1951
- function isFunction(v) {
1952
- return typeof v === T_FUNCTION || false // avoid IE problems
1953
- }
1954
-
1955
- /**
1956
- * Get the outer html of any DOM node SVGs included
1957
- * @param { Object } el - DOM node to parse
1958
- * @returns { String } el.outerHTML
1959
- */
1960
- function getOuterHTML(el) {
1961
- if (el.outerHTML) return el.outerHTML
1962
- // some browsers do not support outerHTML on the SVGs tags
1963
- else {
1964
- var container = mkEl('div')
1965
- container.appendChild(el.cloneNode(true))
1966
- return container.innerHTML
1967
- }
1968
- }
1969
-
1970
- /**
1971
- * Set the inner html of any DOM node SVGs included
1972
- * @param { Object } container - DOM node where we will inject the new html
1973
- * @param { String } html - html to inject
1974
- */
1975
- function setInnerHTML(container, html) {
1976
- if (typeof container.innerHTML != T_UNDEF) container.innerHTML = html
1977
- // some browsers do not support innerHTML on the SVGs tags
1978
- else {
1979
- var doc = new DOMParser().parseFromString(html, 'application/xml')
1980
- container.appendChild(
1981
- container.ownerDocument.importNode(doc.documentElement, true)
1982
- )
1983
- }
1984
- }
1985
-
1986
- /**
1987
- * Checks wether a DOM node must be considered part of an svg document
1988
- * @param { String } name - tag name
1989
- * @returns { Boolean } -
1990
- */
1991
- function isSVGTag(name) {
1992
- return ~SVG_TAGS_LIST.indexOf(name)
1993
- }
2261
+ if (this._internal.virts) {
2262
+ each(this._internal.virts, function (v) {
2263
+ if (v.parentNode) { v.parentNode.removeChild(v); }
2264
+ });
2265
+ }
1994
2266
 
1995
- /**
1996
- * Detect if the argument passed is an object, exclude null.
1997
- * NOTE: Use isObject(x) && !isArray(x) to excludes arrays.
1998
- * @param { * } v - whatever you want to pass to this function
1999
- * @returns { Boolean } -
2000
- */
2001
- function isObject(v) {
2002
- return v && typeof v === T_OBJECT // typeof null is 'object'
2003
- }
2267
+ // allow expressions to unmount themselves
2268
+ unmountAll(expressions);
2269
+ each(instAttrs, function (a) { return a.expr && a.expr.unmount && a.expr.unmount(); });
2004
2270
 
2005
- /**
2006
- * Remove any DOM attribute from a node
2007
- * @param { Object } dom - DOM node we want to update
2008
- * @param { String } name - name of the property we want to remove
2009
- */
2010
- function remAttr(dom, name) {
2011
- dom.removeAttribute(name)
2012
- }
2271
+ this.trigger('unmount');
2272
+ this.off('*');
2273
+ this.isMounted = false;
2013
2274
 
2014
- /**
2015
- * Convert a string containing dashes to camel case
2016
- * @param { String } string - input string
2017
- * @returns { String } my-string -> myString
2018
- */
2019
- function toCamel(string) {
2020
- return string.replace(/-(\w)/g, function(_, c) {
2021
- return c.toUpperCase()
2022
- })
2023
- }
2275
+ delete this.root._tag;
2024
2276
 
2025
- /**
2026
- * Get the value of any DOM attribute on a node
2027
- * @param { Object } dom - DOM node we want to parse
2028
- * @param { String } name - name of the attribute we want to get
2029
- * @returns { String | undefined } name of the node attribute whether it exists
2030
- */
2031
- function getAttr(dom, name) {
2032
- return dom.getAttribute(name)
2033
- }
2277
+ return this
2034
2278
 
2035
- /**
2036
- * Set any DOM/SVG attribute
2037
- * @param { Object } dom - DOM node we want to update
2038
- * @param { String } name - name of the property we want to set
2039
- * @param { String } val - value of the property we want to set
2040
- */
2041
- function setAttr(dom, name, val) {
2042
- var xlink = XLINK_REGEX.exec(name)
2043
- if (xlink && xlink[1])
2044
- dom.setAttributeNS(XLINK_NS, xlink[1], val)
2045
- else
2046
- dom.setAttribute(name, val)
2279
+ }.bind(this));
2047
2280
  }
2048
2281
 
2049
2282
  /**
@@ -2052,51 +2285,49 @@ function setAttr(dom, name, val) {
2052
2285
  * @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
2053
2286
  */
2054
2287
  function getTag(dom) {
2055
- return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG_IS) ||
2056
- getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
2057
- }
2058
- /**
2059
- * Add a child tag to its parent into the `tags` object
2060
- * @param { Object } tag - child tag instance
2061
- * @param { String } tagName - key where the new tag will be stored
2062
- * @param { Object } parent - tag instance where the new child tag will be included
2063
- */
2064
- function addChildTag(tag, tagName, parent) {
2065
- var cachedTag = parent.tags[tagName]
2066
-
2067
- // if there are multiple children tags having the same name
2068
- if (cachedTag) {
2069
- // if the parent tags property is not yet an array
2070
- // create it adding the first cached tag
2071
- if (!isArray(cachedTag))
2072
- // don't add the same tag twice
2073
- if (cachedTag !== tag)
2074
- parent.tags[tagName] = [cachedTag]
2075
- // add the new nested tag to the array
2076
- if (!contains(parent.tags[tagName], tag))
2077
- parent.tags[tagName].push(tag)
2078
- } else {
2079
- parent.tags[tagName] = tag
2080
- }
2288
+ return dom.tagName && __TAG_IMPL[getAttr(dom, IS_DIRECTIVE) ||
2289
+ getAttr(dom, IS_DIRECTIVE) || dom.tagName.toLowerCase()]
2290
+ }
2291
+
2292
+ /**
2293
+ * Inherit properties from a target tag instance
2294
+ * @this Tag
2295
+ * @param { Tag } target - tag where we will inherit properties
2296
+ * @param { Array } propsInSyncWithParent - array of properties to sync with the target
2297
+ */
2298
+ function inheritFrom(target, propsInSyncWithParent) {
2299
+ var this$1 = this;
2300
+
2301
+ each(Object.keys(target), function (k) {
2302
+ // some properties must be always in sync with the parent tag
2303
+ var mustSync = !isReservedName(k) && contains(propsInSyncWithParent, k);
2304
+
2305
+ if (isUndefined(this$1[k]) || mustSync) {
2306
+ // track the property to keep in sync
2307
+ // so we can keep it updated
2308
+ if (!mustSync) { propsInSyncWithParent.push(k); }
2309
+ this$1[k] = target[k];
2310
+ }
2311
+ });
2081
2312
  }
2082
2313
 
2083
2314
  /**
2084
2315
  * Move the position of a custom tag in its parent tag
2085
- * @param { Object } tag - child tag instance
2316
+ * @this Tag
2086
2317
  * @param { String } tagName - key where the tag was stored
2087
2318
  * @param { Number } newPos - index where the new tag will be stored
2088
2319
  */
2089
- function moveChildTag(tag, tagName, newPos) {
2090
- var parent = tag.parent,
2091
- tags
2320
+ function moveChildTag(tagName, newPos) {
2321
+ var parent = this.parent,
2322
+ tags;
2092
2323
  // no parent no move
2093
- if (!parent) return
2324
+ if (!parent) { return }
2094
2325
 
2095
- tags = parent.tags[tagName]
2326
+ tags = parent.tags[tagName];
2096
2327
 
2097
2328
  if (isArray(tags))
2098
- tags.splice(newPos, 0, tags.splice(tags.indexOf(tag), 1)[0])
2099
- else addChildTag(tag, tagName, parent)
2329
+ { tags.splice(newPos, 0, tags.splice(tags.indexOf(this), 1)[0]); }
2330
+ else { arrayishAdd(parent.tags, tagName, this); }
2100
2331
  }
2101
2332
 
2102
2333
  /**
@@ -2108,24 +2339,26 @@ function moveChildTag(tag, tagName, newPos) {
2108
2339
  * @returns { Object } instance of the new child tag just created
2109
2340
  */
2110
2341
  function initChildTag(child, opts, innerHTML, parent) {
2111
- var tag = new Tag(child, opts, innerHTML),
2112
- tagName = getTagName(opts.root),
2113
- ptag = getImmediateCustomParentTag(parent)
2342
+ var tag = new Tag$1(child, opts, innerHTML),
2343
+ tagName = opts.tagName || getTagName(opts.root, true),
2344
+ ptag = getImmediateCustomParentTag(parent);
2114
2345
  // fix for the parent attribute in the looped elements
2115
- tag.parent = ptag
2346
+ defineProperty(tag, 'parent', ptag);
2116
2347
  // store the real parent tag
2117
2348
  // in some cases this could be different from the custom parent tag
2118
2349
  // for example in nested loops
2119
- tag._parent = parent
2350
+ tag._parent = parent;
2120
2351
 
2121
2352
  // add this tag to the custom parent tag
2122
- addChildTag(tag, tagName, ptag)
2353
+ arrayishAdd(ptag.tags, tagName, tag);
2354
+
2123
2355
  // and also to the real parent tag
2124
2356
  if (ptag !== parent)
2125
- addChildTag(tag, tagName, parent)
2357
+ { arrayishAdd(parent.tags, tagName, tag); }
2358
+
2126
2359
  // empty the child node once we got its template
2127
2360
  // to avoid that its children get compiled multiple times
2128
- opts.root.innerHTML = ''
2361
+ opts.root.innerHTML = '';
2129
2362
 
2130
2363
  return tag
2131
2364
  }
@@ -2136,148 +2369,96 @@ function initChildTag(child, opts, innerHTML, parent) {
2136
2369
  * @returns { Object } the instance of the first custom parent tag found
2137
2370
  */
2138
2371
  function getImmediateCustomParentTag(tag) {
2139
- var ptag = tag
2140
- while (!getTag(ptag.root)) {
2141
- if (!ptag.parent) break
2142
- ptag = ptag.parent
2372
+ var ptag = tag;
2373
+ while (ptag._internal.isAnonymous) {
2374
+ if (!ptag.parent) { break }
2375
+ ptag = ptag.parent;
2143
2376
  }
2144
2377
  return ptag
2145
2378
  }
2146
2379
 
2147
2380
  /**
2148
- * Helper function to set an immutable property
2149
- * @param { Object } el - object where the new property will be set
2150
- * @param { String } key - object key where the new property will be stored
2151
- * @param { * } value - value of the new property
2152
- * @param { Object } options - set the propery overriding the default options
2153
- * @returns { Object } - the initial object
2381
+ * Trigger the unmount method on all the expressions
2382
+ * @param { Array } expressions - DOM expressions
2154
2383
  */
2155
- function defineProperty(el, key, value, options) {
2156
- Object.defineProperty(el, key, extend({
2157
- value: value,
2158
- enumerable: false,
2159
- writable: false,
2160
- configurable: true
2161
- }, options))
2162
- return el
2384
+ function unmountAll(expressions) {
2385
+ each(expressions, function(expr) {
2386
+ if (expr instanceof Tag$1) { expr.unmount(true); }
2387
+ else if (expr.unmount) { expr.unmount(); }
2388
+ });
2163
2389
  }
2164
2390
 
2165
2391
  /**
2166
2392
  * Get the tag name of any DOM node
2167
2393
  * @param { Object } dom - DOM node we want to parse
2394
+ * @param { Boolean } skipDataIs - hack to ignore the data-is attribute when attaching to parent
2168
2395
  * @returns { String } name to identify this dom node in riot
2169
2396
  */
2170
- function getTagName(dom) {
2397
+ function getTagName(dom, skipDataIs) {
2171
2398
  var child = getTag(dom),
2172
- namedTag = getAttr(dom, 'name'),
2173
- tagName = namedTag && !tmpl.hasExpr(namedTag) ?
2399
+ namedTag = !skipDataIs && getAttr(dom, IS_DIRECTIVE);
2400
+ return namedTag && !tmpl.hasExpr(namedTag) ?
2174
2401
  namedTag :
2175
2402
  child ? child.name : dom.tagName.toLowerCase()
2176
-
2177
- return tagName
2178
- }
2179
-
2180
- /**
2181
- * Extend any object with other properties
2182
- * @param { Object } src - source object
2183
- * @returns { Object } the resulting extended object
2184
- *
2185
- * var obj = { foo: 'baz' }
2186
- * extend(obj, {bar: 'bar', foo: 'bar'})
2187
- * console.log(obj) => {bar: 'bar', foo: 'bar'}
2188
- *
2189
- */
2190
- function extend(src) {
2191
- var obj, args = arguments
2192
- for (var i = 1; i < args.length; ++i) {
2193
- if (obj = args[i]) {
2194
- for (var key in obj) {
2195
- // check if this property of the source object could be overridden
2196
- if (isWritable(src, key))
2197
- src[key] = obj[key]
2198
- }
2199
- }
2200
- }
2201
- return src
2202
- }
2203
-
2204
- /**
2205
- * Check whether an array contains an item
2206
- * @param { Array } arr - target array
2207
- * @param { * } item - item to test
2208
- * @returns { Boolean } Does 'arr' contain 'item'?
2209
- */
2210
- function contains(arr, item) {
2211
- return ~arr.indexOf(item)
2212
- }
2213
-
2214
- /**
2215
- * Check whether an object is a kind of array
2216
- * @param { * } a - anything
2217
- * @returns {Boolean} is 'a' an array?
2218
- */
2219
- function isArray(a) { return Array.isArray(a) || a instanceof Array }
2220
-
2221
- /**
2222
- * Detect whether a property of an object could be overridden
2223
- * @param { Object } obj - source object
2224
- * @param { String } key - object property
2225
- * @returns { Boolean } is this property writable?
2226
- */
2227
- function isWritable(obj, key) {
2228
- var props = Object.getOwnPropertyDescriptor(obj, key)
2229
- return typeof obj[key] === T_UNDEF || props && props.writable
2230
2403
  }
2231
2404
 
2232
-
2233
2405
  /**
2234
2406
  * With this function we avoid that the internal Tag methods get overridden
2235
2407
  * @param { Object } data - options we want to use to extend the tag instance
2236
2408
  * @returns { Object } clean object without containing the riot internal reserved words
2237
2409
  */
2238
2410
  function cleanUpData(data) {
2239
- if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION))
2240
- return data
2411
+ if (!(data instanceof Tag$1) && !(data && isFunction(data.trigger)))
2412
+ { return data }
2241
2413
 
2242
- var o = {}
2414
+ var o = {};
2243
2415
  for (var key in data) {
2244
- if (!RESERVED_WORDS_BLACKLIST.test(key)) o[key] = data[key]
2416
+ if (!RE_RESERVED_NAMES.test(key)) { o[key] = data[key]; }
2245
2417
  }
2246
2418
  return o
2247
2419
  }
2248
2420
 
2249
2421
  /**
2250
- * Walk down recursively all the children tags starting dom node
2251
- * @param { Object } dom - starting node where we will start the recursion
2252
- * @param { Function } fn - callback to transform the child node just found
2422
+ * Set the property of an object for a given key. If something already
2423
+ * exists there, then it becomes an array containing both the old and new value.
2424
+ * @param { Object } obj - object on which to set the property
2425
+ * @param { String } key - property name
2426
+ * @param { Object } value - the value of the property to be set
2427
+ * @param { Boolean } ensureArray - ensure that the property remains an array
2253
2428
  */
2254
- function walk(dom, fn) {
2255
- if (dom) {
2256
- // stop the recursion
2257
- if (fn(dom) === false) return
2258
- else {
2259
- dom = dom.firstChild
2429
+ function arrayishAdd(obj, key, value, ensureArray) {
2430
+ var dest = obj[key];
2431
+ var isArr = isArray(dest);
2260
2432
 
2261
- while (dom) {
2262
- walk(dom, fn)
2263
- dom = dom.nextSibling
2264
- }
2265
- }
2433
+ if (dest && dest === value) { return }
2434
+
2435
+ // if the key was never set, set it once
2436
+ if (!dest && ensureArray) { obj[key] = [value]; }
2437
+ else if (!dest) { obj[key] = value; }
2438
+ // if it was an array and not yet set
2439
+ else if (!isArr || isArr && !contains(dest, value)) {
2440
+ if (isArr) { dest.push(value); }
2441
+ else { obj[key] = [dest, value]; }
2266
2442
  }
2267
2443
  }
2268
2444
 
2269
2445
  /**
2270
- * Minimize risk: only zero or one _space_ between attr & value
2271
- * @param { String } html - html string we want to parse
2272
- * @param { Function } fn - callback function to apply on any attribute found
2273
- */
2274
- function walkAttributes(html, fn) {
2275
- var m,
2276
- re = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g
2277
-
2278
- while (m = re.exec(html)) {
2279
- fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
2280
- }
2446
+ * Removes an item from an object at a given key. If the key points to an array,
2447
+ * then the item is just removed from the array.
2448
+ * @param { Object } obj - object on which to remove the property
2449
+ * @param { String } key - property name
2450
+ * @param { Object } value - the value of the property to be removed
2451
+ * @param { Boolean } ensureArray - ensure that the property remains an array
2452
+ */
2453
+ function arrayishRemove(obj, key, value, ensureArray) {
2454
+ if (isArray(obj[key])) {
2455
+ each(obj[key], function(item, i) {
2456
+ if (item === value) { obj[key].splice(i, 1); }
2457
+ });
2458
+ if (!obj[key].length) { delete obj[key]; }
2459
+ else if (obj[key].length === 1 && !ensureArray) { obj[key] = obj[key][0]; }
2460
+ } else
2461
+ { delete obj[key]; } // otherwise just delete the key
2281
2462
  }
2282
2463
 
2283
2464
  /**
@@ -2287,383 +2468,196 @@ function walkAttributes(html, fn) {
2287
2468
  */
2288
2469
  function isInStub(dom) {
2289
2470
  while (dom) {
2290
- if (dom.inStub) return true
2291
- dom = dom.parentNode
2471
+ if (dom.inStub)
2472
+ { return true }
2473
+ dom = dom.parentNode;
2292
2474
  }
2293
2475
  return false
2294
2476
  }
2295
2477
 
2296
- /**
2297
- * Create a generic DOM node
2298
- * @param { String } name - name of the DOM node we want to create
2299
- * @param { Boolean } isSvg - should we use a SVG as parent node?
2300
- * @returns { Object } DOM node just created
2301
- */
2302
- function mkEl(name, isSvg) {
2303
- return isSvg ?
2304
- document.createElementNS('http://www.w3.org/2000/svg', 'svg') :
2305
- document.createElement(name)
2306
- }
2307
-
2308
- /**
2309
- * Shorter and fast way to select multiple nodes in the DOM
2310
- * @param { String } selector - DOM selector
2311
- * @param { Object } ctx - DOM node where the targets of our search will is located
2312
- * @returns { Object } dom nodes found
2313
- */
2314
- function $$(selector, ctx) {
2315
- return (ctx || document).querySelectorAll(selector)
2316
- }
2317
-
2318
- /**
2319
- * Shorter and fast way to select a single node in the DOM
2320
- * @param { String } selector - unique dom selector
2321
- * @param { Object } ctx - DOM node where the target of our search will is located
2322
- * @returns { Object } dom node found
2323
- */
2324
- function $(selector, ctx) {
2325
- return (ctx || document).querySelector(selector)
2326
- }
2327
-
2328
- /**
2329
- * Simple object prototypal inheritance
2330
- * @param { Object } parent - parent object
2331
- * @returns { Object } child instance
2332
- */
2333
- function inherit(parent) {
2334
- function Child() {}
2335
- Child.prototype = parent
2336
- return new Child()
2337
- }
2338
-
2339
- /**
2340
- * Get the name property needed to identify a DOM node in riot
2341
- * @param { Object } dom - DOM node we need to parse
2342
- * @returns { String | undefined } give us back a string to identify this dom node
2343
- */
2344
- function getNamedKey(dom) {
2345
- return getAttr(dom, 'id') || getAttr(dom, 'name')
2346
- }
2347
-
2348
- /**
2349
- * Set the named properties of a tag element
2350
- * @param { Object } dom - DOM node we need to parse
2351
- * @param { Object } parent - tag instance where the named dom element will be eventually added
2352
- * @param { Array } keys - list of all the tag instance properties
2353
- */
2354
- function setNamed(dom, parent, keys) {
2355
- // get the key value we want to add to the tag instance
2356
- var key = getNamedKey(dom),
2357
- isArr,
2358
- // add the node detected to a tag instance using the named property
2359
- add = function(value) {
2360
- // avoid to override the tag properties already set
2361
- if (contains(keys, key)) return
2362
- // check whether this value is an array
2363
- isArr = isArray(value)
2364
- // if the key was never set
2365
- if (!value)
2366
- // set it once on the tag instance
2367
- parent[key] = dom
2368
- // if it was an array and not yet set
2369
- else if (!isArr || isArr && !contains(value, dom)) {
2370
- // add the dom node into the array
2371
- if (isArr)
2372
- value.push(dom)
2373
- else
2374
- parent[key] = [value, dom]
2375
- }
2376
- }
2377
-
2378
- // skip the elements with no named properties
2379
- if (!key) return
2380
-
2381
- // check whether this key has been already evaluated
2382
- if (tmpl.hasExpr(key))
2383
- // wait the first updated event only once
2384
- parent.one('mount', function() {
2385
- key = getNamedKey(dom)
2386
- add(parent[key])
2387
- })
2388
- else
2389
- add(parent[key])
2390
-
2391
- }
2392
-
2393
- /**
2394
- * Faster String startsWith alternative
2395
- * @param { String } src - source string
2396
- * @param { String } str - test string
2397
- * @returns { Boolean } -
2398
- */
2399
- function startsWith(src, str) {
2400
- return src.slice(0, str.length) === str
2401
- }
2402
-
2403
- /**
2404
- * requestAnimationFrame function
2405
- * Adapted from https://gist.github.com/paulirish/1579671, license MIT
2406
- */
2407
- var rAF = (function (w) {
2408
- var raf = w.requestAnimationFrame ||
2409
- w.mozRequestAnimationFrame || w.webkitRequestAnimationFrame
2410
-
2411
- if (!raf || /iP(ad|hone|od).*OS 6/.test(w.navigator.userAgent)) { // buggy iOS6
2412
- var lastTime = 0
2413
-
2414
- raf = function (cb) {
2415
- var nowtime = Date.now(), timeout = Math.max(16 - (nowtime - lastTime), 0)
2416
- setTimeout(function () { cb(lastTime = nowtime + timeout) }, timeout)
2417
- }
2418
- }
2419
- return raf
2420
-
2421
- })(window || {})
2422
-
2423
2478
  /**
2424
2479
  * Mount a tag creating new Tag instance
2425
2480
  * @param { Object } root - dom node where the tag will be mounted
2426
2481
  * @param { String } tagName - name of the riot tag we want to mount
2427
2482
  * @param { Object } opts - options to pass to the Tag instance
2483
+ * @param { Object } ctx - optional context that will be used to extend an existing class ( used in riot.Tag )
2428
2484
  * @returns { Tag } a new Tag instance
2429
2485
  */
2430
- function mountTo(root, tagName, opts) {
2431
- var tag = __tagImpl[tagName],
2486
+ function mountTo(root, tagName, opts, ctx) {
2487
+ var impl = __TAG_IMPL[tagName],
2488
+ implClass = __TAG_IMPL[tagName].class,
2489
+ tag = ctx || (implClass ? Object.create(implClass.prototype) : {}),
2432
2490
  // cache the inner HTML to fix #855
2433
- innerHTML = root._innerHTML = root._innerHTML || root.innerHTML
2491
+ innerHTML = root._innerHTML = root._innerHTML || root.innerHTML;
2434
2492
 
2435
2493
  // clear the inner html
2436
- root.innerHTML = ''
2494
+ root.innerHTML = '';
2495
+
2496
+ var conf = { root: root, opts: opts };
2497
+ if (opts && opts.parent) { conf.parent = opts.parent; }
2437
2498
 
2438
- if (tag && root) tag = new Tag(tag, { root: root, opts: opts }, innerHTML)
2499
+ if (impl && root) { Tag$1.apply(tag, [impl, conf, innerHTML]); }
2439
2500
 
2440
2501
  if (tag && tag.mount) {
2441
- tag.mount()
2502
+ tag.mount(true);
2442
2503
  // add this tag to the virtualDom variable
2443
- if (!contains(__virtualDom, tag)) __virtualDom.push(tag)
2504
+ if (!contains(__TAGS_CACHE, tag)) { __TAGS_CACHE.push(tag); }
2444
2505
  }
2445
2506
 
2446
2507
  return tag
2447
2508
  }
2448
- /**
2449
- * Riot public api
2450
- */
2451
2509
 
2452
- // share methods for other riot parts, e.g. compiler
2453
- riot.util = { brackets: brackets, tmpl: tmpl }
2454
2510
 
2455
2511
  /**
2456
- * Create a mixin that could be globally shared across all the tags
2512
+ * Adds the elements for a virtual tag
2513
+ * @this Tag
2514
+ * @param { Node } src - the node that will do the inserting or appending
2515
+ * @param { Tag } target - only if inserting, insert before this tag's first child
2457
2516
  */
2458
- riot.mixin = (function() {
2459
- var mixins = {},
2460
- globals = mixins[GLOBAL_MIXIN] = {},
2461
- _id = 0
2517
+ function makeVirtual(src, target) {
2518
+ var this$1 = this;
2462
2519
 
2463
- /**
2464
- * Create/Return a mixin by its name
2465
- * @param { String } name - mixin name (global mixin if object)
2466
- * @param { Object } mixin - mixin logic
2467
- * @param { Boolean } g - is global?
2468
- * @returns { Object } the mixin logic
2469
- */
2470
- return function(name, mixin, g) {
2471
- // Unnamed global
2472
- if (isObject(name)) {
2473
- riot.mixin('__unnamed_'+_id++, name, true)
2474
- return
2475
- }
2476
-
2477
- var store = g ? globals : mixins
2520
+ var head = createDOMPlaceholder(),
2521
+ tail = createDOMPlaceholder(),
2522
+ frag = createFrag(),
2523
+ sib, el;
2478
2524
 
2479
- // Getter
2480
- if (!mixin) {
2481
- if (typeof store[name] === T_UNDEF) {
2482
- throw new Error('Unregistered mixin: ' + name)
2483
- }
2484
- return store[name]
2485
- }
2486
- // Setter
2487
- if (isFunction(mixin)) {
2488
- extend(mixin.prototype, store[name] || {})
2489
- store[name] = mixin
2490
- }
2491
- else {
2492
- store[name] = extend(store[name] || {}, mixin)
2493
- }
2494
- }
2525
+ this._internal.head = this.root.insertBefore(head, this.root.firstChild);
2526
+ this._internal.tail = this.root.appendChild(tail);
2495
2527
 
2496
- })()
2528
+ el = this._internal.head;
2497
2529
 
2498
- /**
2499
- * Create a new riot tag implementation
2500
- * @param { String } name - name/id of the new riot tag
2501
- * @param { String } html - tag template
2502
- * @param { String } css - custom tag css
2503
- * @param { String } attrs - root tag attributes
2504
- * @param { Function } fn - user function
2505
- * @returns { String } name/id of the tag just created
2506
- */
2507
- riot.tag = function(name, html, css, attrs, fn) {
2508
- if (isFunction(attrs)) {
2509
- fn = attrs
2510
- if (/^[\w\-]+\s?=/.test(css)) {
2511
- attrs = css
2512
- css = ''
2513
- } else attrs = ''
2514
- }
2515
- if (css) {
2516
- if (isFunction(css)) fn = css
2517
- else styleManager.add(css)
2530
+ while (el) {
2531
+ sib = el.nextSibling;
2532
+ frag.appendChild(el);
2533
+ this$1._internal.virts.push(el); // hold for unmounting
2534
+ el = sib;
2518
2535
  }
2519
- name = name.toLowerCase()
2520
- __tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
2521
- return name
2522
- }
2523
2536
 
2524
- /**
2525
- * Create a new riot tag implementation (for use by the compiler)
2526
- * @param { String } name - name/id of the new riot tag
2527
- * @param { String } html - tag template
2528
- * @param { String } css - custom tag css
2529
- * @param { String } attrs - root tag attributes
2530
- * @param { Function } fn - user function
2531
- * @returns { String } name/id of the tag just created
2532
- */
2533
- riot.tag2 = function(name, html, css, attrs, fn) {
2534
- if (css) styleManager.add(css)
2535
- //if (bpair) riot.settings.brackets = bpair
2536
- __tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
2537
- return name
2537
+ if (target)
2538
+ { src.insertBefore(frag, target._internal.head); }
2539
+ else
2540
+ { src.appendChild(frag); }
2538
2541
  }
2539
2542
 
2540
2543
  /**
2541
- * Mount a tag using a specific tag implementation
2542
- * @param { String } selector - tag DOM selector
2543
- * @param { String } tagName - tag implementation name
2544
- * @param { Object } opts - tag logic
2545
- * @returns { Array } new tags instances
2544
+ * Move virtual tag and all child nodes
2545
+ * @this Tag
2546
+ * @param { Node } src - the node that will do the inserting
2547
+ * @param { Tag } target - insert before this tag's first child
2546
2548
  */
2547
- riot.mount = function(selector, tagName, opts) {
2548
-
2549
- var els,
2550
- allTags,
2551
- tags = []
2552
-
2553
- // helper functions
2554
-
2555
- function addRiotTags(arr) {
2556
- var list = ''
2557
- each(arr, function (e) {
2558
- if (!/[^-\w]/.test(e)) {
2559
- e = e.trim().toLowerCase()
2560
- list += ',[' + RIOT_TAG_IS + '="' + e + '"],[' + RIOT_TAG + '="' + e + '"]'
2561
- }
2562
- })
2563
- return list
2564
- }
2565
-
2566
- function selectAllTags() {
2567
- var keys = Object.keys(__tagImpl)
2568
- return keys + addRiotTags(keys)
2569
- }
2570
-
2571
- function pushTags(root) {
2572
- if (root.tagName) {
2573
- var riotTag = getAttr(root, RIOT_TAG_IS) || getAttr(root, RIOT_TAG)
2574
-
2575
- // have tagName? force riot-tag to be the same
2576
- if (tagName && riotTag !== tagName) {
2577
- riotTag = tagName
2578
- setAttr(root, RIOT_TAG_IS, tagName)
2579
- setAttr(root, RIOT_TAG, tagName) // this will be removed in riot 3.0.0
2580
- }
2581
- var tag = mountTo(root, riotTag || root.tagName.toLowerCase(), opts)
2582
-
2583
- if (tag) tags.push(tag)
2584
- } else if (root.length) {
2585
- each(root, pushTags) // assume nodeList
2586
- }
2587
- }
2588
-
2589
- // ----- mount code -----
2590
-
2591
- // inject styles into DOM
2592
- styleManager.inject()
2593
-
2594
- if (isObject(tagName)) {
2595
- opts = tagName
2596
- tagName = 0
2597
- }
2598
-
2599
- // crawl the DOM to find the tag
2600
- if (typeof selector === T_STRING) {
2601
- if (selector === '*')
2602
- // select all the tags registered
2603
- // and also the tags found with the riot-tag attribute set
2604
- selector = allTags = selectAllTags()
2605
- else
2606
- // or just the ones named like the selector
2607
- selector += addRiotTags(selector.split(/, */))
2549
+ function moveVirtual(src, target) {
2550
+ var this$1 = this;
2608
2551
 
2609
- // make sure to pass always a selector
2610
- // to the querySelectorAll function
2611
- els = selector ? $$(selector) : []
2612
- }
2613
- else
2614
- // probably you have passed already a tag or a NodeList
2615
- els = selector
2552
+ var el = this._internal.head,
2553
+ frag = createFrag(),
2554
+ sib;
2616
2555
 
2617
- // select all the registered and mount them inside their root elements
2618
- if (tagName === '*') {
2619
- // get all custom tags
2620
- tagName = allTags || selectAllTags()
2621
- // if the root els it's just a single tag
2622
- if (els.tagName)
2623
- els = $$(tagName, els)
2624
- else {
2625
- // select all the children for all the different root elements
2626
- var nodeList = []
2627
- each(els, function (_el) {
2628
- nodeList.push($$(tagName, _el))
2629
- })
2630
- els = nodeList
2556
+ while (el) {
2557
+ sib = el.nextSibling;
2558
+ frag.appendChild(el);
2559
+ el = sib;
2560
+ if (el === this$1._internal.tail) {
2561
+ frag.appendChild(el);
2562
+ src.insertBefore(frag, target._internal.head);
2563
+ break
2631
2564
  }
2632
- // get rid of the tagName
2633
- tagName = 0
2634
2565
  }
2635
-
2636
- pushTags(els)
2637
-
2638
- return tags
2639
2566
  }
2640
2567
 
2641
2568
  /**
2642
- * Update all the tags instances created
2643
- * @returns { Array } all the tags instances
2569
+ * Get selectors for tags
2570
+ * @param { Array } tags - tag names to select
2571
+ * @returns { String } selector
2644
2572
  */
2645
- riot.update = function() {
2646
- return each(__virtualDom, function(tag) {
2647
- tag.update()
2648
- })
2649
- }
2573
+ function selectTags(tags) {
2574
+ // select all tags
2575
+ if (!tags) {
2576
+ var keys = Object.keys(__TAG_IMPL);
2577
+ return keys + selectTags(keys)
2578
+ }
2650
2579
 
2651
- /**
2652
- * Export the Virtual DOM
2653
- */
2654
- riot.vdom = __virtualDom
2580
+ return tags
2581
+ .filter(function (t) { return !/[^-\w]/.test(t); })
2582
+ .reduce(function (list, t) {
2583
+ var name = t.trim().toLowerCase();
2584
+ return list + ",[" + IS_DIRECTIVE + "=\"" + name + "\"]"
2585
+ }, '')
2586
+ }
2587
+
2588
+
2589
+ var tags = Object.freeze({
2590
+ getTag: getTag,
2591
+ inheritFrom: inheritFrom,
2592
+ moveChildTag: moveChildTag,
2593
+ initChildTag: initChildTag,
2594
+ getImmediateCustomParentTag: getImmediateCustomParentTag,
2595
+ unmountAll: unmountAll,
2596
+ getTagName: getTagName,
2597
+ cleanUpData: cleanUpData,
2598
+ arrayishAdd: arrayishAdd,
2599
+ arrayishRemove: arrayishRemove,
2600
+ isInStub: isInStub,
2601
+ mountTo: mountTo,
2602
+ makeVirtual: makeVirtual,
2603
+ moveVirtual: moveVirtual,
2604
+ selectTags: selectTags
2605
+ });
2655
2606
 
2656
2607
  /**
2657
- * Export the Tag constructor
2608
+ * Riot public api
2658
2609
  */
2659
- riot.Tag = Tag
2660
- // support CommonJS, AMD & browser
2661
- /* istanbul ignore next */
2662
- if (typeof exports === T_OBJECT)
2663
- module.exports = riot
2664
- else if (typeof define === T_FUNCTION && typeof define.amd !== T_UNDEF)
2665
- define(function() { return riot })
2666
- else
2667
- window.riot = riot
2668
-
2669
- })(typeof window != 'undefined' ? window : void 0);
2610
+ var settings = Object.create(brackets.settings);
2611
+
2612
+ var util = {
2613
+ tmpl: tmpl,
2614
+ brackets: brackets,
2615
+ styleManager: styleManager,
2616
+ vdom: __TAGS_CACHE,
2617
+ styleNode: styleManager.styleNode,
2618
+ // export the riot internal utils as well
2619
+ dom: dom,
2620
+ check: check,
2621
+ misc: misc,
2622
+ tags: tags
2623
+ };
2624
+
2625
+ // export the core props/methods
2626
+ var Tag$$1 = Tag$2;
2627
+ var tag$$1 = tag$1;
2628
+ var tag2$$1 = tag2$1;
2629
+ var mount$$1 = mount$1;
2630
+ var mixin$$1 = mixin$1;
2631
+ var update$$1 = update$1;
2632
+ var unregister$$1 = unregister$1;
2633
+ var observable = observable$1;
2634
+
2635
+ var riot$1 = {
2636
+ settings: settings,
2637
+ util: util,
2638
+ // core
2639
+ Tag: Tag$$1,
2640
+ tag: tag$$1,
2641
+ tag2: tag2$$1,
2642
+ mount: mount$$1,
2643
+ mixin: mixin$$1,
2644
+ update: update$$1,
2645
+ unregister: unregister$$1,
2646
+ observable: observable
2647
+ };
2648
+
2649
+ exports.settings = settings;
2650
+ exports.util = util;
2651
+ exports.Tag = Tag$$1;
2652
+ exports.tag = tag$$1;
2653
+ exports.tag2 = tag2$$1;
2654
+ exports.mount = mount$$1;
2655
+ exports.mixin = mixin$$1;
2656
+ exports.update = update$$1;
2657
+ exports.unregister = unregister$$1;
2658
+ exports.observable = observable;
2659
+ exports['default'] = riot$1;
2660
+
2661
+ Object.defineProperty(exports, '__esModule', { value: true });
2662
+
2663
+ })));