riot_js-rails 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ })));