riot_js-rails 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/README.md +27 -0
- data/Rakefile +10 -0
- data/lib/riot_js/rails.rb +8 -0
- data/lib/riot_js/rails/engine.rb +8 -0
- data/lib/riot_js/rails/helper.rb +51 -0
- data/lib/riot_js/rails/processors/compiler.rb +38 -0
- data/lib/riot_js/rails/processors/processor.rb +34 -0
- data/lib/riot_js/rails/railtie.rb +25 -0
- data/lib/riot_js/rails/version.rb +5 -0
- data/riot_js-rails.gemspec +30 -0
- data/vendor/assets/javascripts/compiler/compiler.js +16 -0
- data/vendor/assets/javascripts/compiler/compiler_core.js +234 -0
- data/vendor/assets/javascripts/compiler/node_runner.js +21 -0
- data/vendor/assets/javascripts/compiler/parsers.js +32 -0
- data/vendor/assets/javascripts/riot.js +1370 -0
- data/vendor/assets/javascripts/riot_rails.js +62 -0
- metadata +146 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
(function(program, execJS) { execJS(program) })(function(module, exports, console) {
|
2
|
+
#{source}
|
3
|
+
}, function(program) {
|
4
|
+
var output, print = function(string) {
|
5
|
+
process.stdout.write('' + string);
|
6
|
+
};
|
7
|
+
try {
|
8
|
+
result = program();
|
9
|
+
if (typeof result == 'undefined' && result !== null) {
|
10
|
+
print('["ok"]');
|
11
|
+
} else {
|
12
|
+
try {
|
13
|
+
print(JSON.stringify(['ok', result]));
|
14
|
+
} catch (err) {
|
15
|
+
print('["err"]');
|
16
|
+
}
|
17
|
+
}
|
18
|
+
} catch (err) {
|
19
|
+
print(JSON.stringify(['err', '' + err]));
|
20
|
+
}
|
21
|
+
});
|
@@ -0,0 +1,32 @@
|
|
1
|
+
var parsers = {
|
2
|
+
html: {
|
3
|
+
jade: function(html) {
|
4
|
+
return require('jade').render(html, {pretty: true, doctype: 'html'})
|
5
|
+
}
|
6
|
+
},
|
7
|
+
css: {},
|
8
|
+
js: {
|
9
|
+
none: function(js) {
|
10
|
+
return js
|
11
|
+
},
|
12
|
+
livescript: function(js) {
|
13
|
+
return require('livescript').compile(js, { bare: true, header: false })
|
14
|
+
},
|
15
|
+
typescript: function(js) {
|
16
|
+
return require('typescript-simple')(js)
|
17
|
+
},
|
18
|
+
es6: function(js) {
|
19
|
+
return require('babel').transform(js, { blacklist: ['useStrict'] }).code
|
20
|
+
},
|
21
|
+
coffee: function(js) {
|
22
|
+
return require('coffee-script').compile(js, { bare: true })
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
// fix 913
|
28
|
+
parsers.js.javascript = parsers.js.none
|
29
|
+
// 4 the nostalgics
|
30
|
+
parsers.js.coffeescript = parsers.js.coffee
|
31
|
+
|
32
|
+
module.exports = parsers
|
@@ -0,0 +1,1370 @@
|
|
1
|
+
/* Riot v2.2.4, @license MIT, (c) 2015 Muut Inc. + contributors */
|
2
|
+
|
3
|
+
;(function(window, undefined) {
|
4
|
+
'use strict';
|
5
|
+
var riot = { version: 'v2.2.4', settings: {} },
|
6
|
+
//// be aware, internal usage
|
7
|
+
|
8
|
+
// counter to give a unique id to all the Tag instances
|
9
|
+
__uid = 0,
|
10
|
+
|
11
|
+
// riot specific prefixes
|
12
|
+
RIOT_PREFIX = 'riot-',
|
13
|
+
RIOT_TAG = RIOT_PREFIX + 'tag',
|
14
|
+
|
15
|
+
// for typeof == '' comparisons
|
16
|
+
T_STRING = 'string',
|
17
|
+
T_OBJECT = 'object',
|
18
|
+
T_UNDEF = 'undefined',
|
19
|
+
T_FUNCTION = 'function',
|
20
|
+
// special native tags that cannot be treated like the others
|
21
|
+
SPECIAL_TAGS_REGEX = /^(?:opt(ion|group)|tbody|col|t[rhd])$/,
|
22
|
+
RESERVED_WORDS_BLACKLIST = ['_item', '_id', 'update', 'root', 'mount', 'unmount', 'mixin', 'isMounted', 'isLoop', 'tags', 'parent', 'opts', 'trigger', 'on', 'off', 'one'],
|
23
|
+
|
24
|
+
// version# for IE 8-11, 0 for others
|
25
|
+
IE_VERSION = (window && window.document || {}).documentMode | 0,
|
26
|
+
|
27
|
+
// Array.isArray for IE8 is in the polyfills
|
28
|
+
isArray = Array.isArray
|
29
|
+
|
30
|
+
riot.observable = function(el) {
|
31
|
+
|
32
|
+
el = el || {}
|
33
|
+
|
34
|
+
var callbacks = {},
|
35
|
+
_id = 0
|
36
|
+
|
37
|
+
el.on = function(events, fn) {
|
38
|
+
if (isFunction(fn)) {
|
39
|
+
if (typeof fn.id === T_UNDEF) fn._id = _id++
|
40
|
+
|
41
|
+
events.replace(/\S+/g, function(name, pos) {
|
42
|
+
(callbacks[name] = callbacks[name] || []).push(fn)
|
43
|
+
fn.typed = pos > 0
|
44
|
+
})
|
45
|
+
}
|
46
|
+
return el
|
47
|
+
}
|
48
|
+
|
49
|
+
el.off = function(events, fn) {
|
50
|
+
if (events == '*') callbacks = {}
|
51
|
+
else {
|
52
|
+
events.replace(/\S+/g, function(name) {
|
53
|
+
if (fn) {
|
54
|
+
var arr = callbacks[name]
|
55
|
+
for (var i = 0, cb; (cb = arr && arr[i]); ++i) {
|
56
|
+
if (cb._id == fn._id) arr.splice(i--, 1)
|
57
|
+
}
|
58
|
+
} else {
|
59
|
+
callbacks[name] = []
|
60
|
+
}
|
61
|
+
})
|
62
|
+
}
|
63
|
+
return el
|
64
|
+
}
|
65
|
+
|
66
|
+
// only single event supported
|
67
|
+
el.one = function(name, fn) {
|
68
|
+
function on() {
|
69
|
+
el.off(name, on)
|
70
|
+
fn.apply(el, arguments)
|
71
|
+
}
|
72
|
+
return el.on(name, on)
|
73
|
+
}
|
74
|
+
|
75
|
+
el.trigger = function(name) {
|
76
|
+
var args = [].slice.call(arguments, 1),
|
77
|
+
fns = callbacks[name] || []
|
78
|
+
|
79
|
+
for (var i = 0, fn; (fn = fns[i]); ++i) {
|
80
|
+
if (!fn.busy) {
|
81
|
+
fn.busy = 1
|
82
|
+
fn.apply(el, fn.typed ? [name].concat(args) : args)
|
83
|
+
if (fns[i] !== fn) { i-- }
|
84
|
+
fn.busy = 0
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
if (callbacks.all && name != 'all') {
|
89
|
+
el.trigger.apply(el, ['all', name].concat(args))
|
90
|
+
}
|
91
|
+
|
92
|
+
return el
|
93
|
+
}
|
94
|
+
|
95
|
+
return el
|
96
|
+
|
97
|
+
}
|
98
|
+
riot.mixin = (function() {
|
99
|
+
var mixins = {}
|
100
|
+
|
101
|
+
return function(name, mixin) {
|
102
|
+
if (!mixin) return mixins[name]
|
103
|
+
mixins[name] = mixin
|
104
|
+
}
|
105
|
+
|
106
|
+
})()
|
107
|
+
|
108
|
+
;(function(riot, evt, win) {
|
109
|
+
|
110
|
+
// browsers only
|
111
|
+
if (!win) return
|
112
|
+
|
113
|
+
var loc = win.location,
|
114
|
+
fns = riot.observable(),
|
115
|
+
started = false,
|
116
|
+
current
|
117
|
+
|
118
|
+
function hash() {
|
119
|
+
return loc.href.split('#')[1] || '' // why not loc.hash.splice(1) ?
|
120
|
+
}
|
121
|
+
|
122
|
+
function parser(path) {
|
123
|
+
return path.split('/')
|
124
|
+
}
|
125
|
+
|
126
|
+
function emit(path) {
|
127
|
+
if (path.type) path = hash()
|
128
|
+
|
129
|
+
if (path != current) {
|
130
|
+
fns.trigger.apply(null, ['H'].concat(parser(path)))
|
131
|
+
current = path
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
var r = riot.route = function(arg) {
|
136
|
+
// string
|
137
|
+
if (arg[0]) {
|
138
|
+
loc.hash = arg
|
139
|
+
emit(arg)
|
140
|
+
|
141
|
+
// function
|
142
|
+
} else {
|
143
|
+
fns.on('H', arg)
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
r.exec = function(fn) {
|
148
|
+
fn.apply(null, parser(hash()))
|
149
|
+
}
|
150
|
+
|
151
|
+
r.parser = function(fn) {
|
152
|
+
parser = fn
|
153
|
+
}
|
154
|
+
|
155
|
+
r.stop = function () {
|
156
|
+
if (started) {
|
157
|
+
if (win.removeEventListener) win.removeEventListener(evt, emit, false) //@IE8 - the if()
|
158
|
+
else win.detachEvent('on' + evt, emit) //@IE8
|
159
|
+
fns.off('*')
|
160
|
+
started = false
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
r.start = function () {
|
165
|
+
if (!started) {
|
166
|
+
if (win.addEventListener) win.addEventListener(evt, emit, false) //@IE8 - the if()
|
167
|
+
else win.attachEvent('on' + evt, emit) //IE8
|
168
|
+
started = true
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
// autostart the router
|
173
|
+
r.start()
|
174
|
+
|
175
|
+
})(riot, 'hashchange', window)
|
176
|
+
/*
|
177
|
+
|
178
|
+
//// How it works?
|
179
|
+
|
180
|
+
|
181
|
+
Three ways:
|
182
|
+
|
183
|
+
1. Expressions: tmpl('{ value }', data).
|
184
|
+
Returns the result of evaluated expression as a raw object.
|
185
|
+
|
186
|
+
2. Templates: tmpl('Hi { name } { surname }', data).
|
187
|
+
Returns a string with evaluated expressions.
|
188
|
+
|
189
|
+
3. Filters: tmpl('{ show: !done, highlight: active }', data).
|
190
|
+
Returns a space separated list of trueish keys (mainly
|
191
|
+
used for setting html classes), e.g. "show highlight".
|
192
|
+
|
193
|
+
|
194
|
+
// Template examples
|
195
|
+
|
196
|
+
tmpl('{ title || "Untitled" }', data)
|
197
|
+
tmpl('Results are { results ? "ready" : "loading" }', data)
|
198
|
+
tmpl('Today is { new Date() }', data)
|
199
|
+
tmpl('{ message.length > 140 && "Message is too long" }', data)
|
200
|
+
tmpl('This item got { Math.round(rating) } stars', data)
|
201
|
+
tmpl('<h1>{ title }</h1>{ body }', data)
|
202
|
+
|
203
|
+
|
204
|
+
// Falsy expressions in templates
|
205
|
+
|
206
|
+
In templates (as opposed to single expressions) all falsy values
|
207
|
+
except zero (undefined/null/false) will default to empty string:
|
208
|
+
|
209
|
+
tmpl('{ undefined } - { false } - { null } - { 0 }', {})
|
210
|
+
// will return: " - - - 0"
|
211
|
+
|
212
|
+
*/
|
213
|
+
|
214
|
+
|
215
|
+
var brackets = (function(orig) {
|
216
|
+
|
217
|
+
var cachedBrackets,
|
218
|
+
r,
|
219
|
+
b,
|
220
|
+
re = /[{}]/g
|
221
|
+
|
222
|
+
return function(x) {
|
223
|
+
|
224
|
+
// make sure we use the current setting
|
225
|
+
var s = riot.settings.brackets || orig
|
226
|
+
|
227
|
+
// recreate cached vars if needed
|
228
|
+
if (cachedBrackets !== s) {
|
229
|
+
cachedBrackets = s
|
230
|
+
b = s.split(' ')
|
231
|
+
r = b.map(function (e) { return e.replace(/(?=.)/g, '\\') })
|
232
|
+
}
|
233
|
+
|
234
|
+
// if regexp given, rewrite it with current brackets (only if differ from default)
|
235
|
+
return x instanceof RegExp ? (
|
236
|
+
s === orig ? x :
|
237
|
+
new RegExp(x.source.replace(re, function(b) { return r[~~(b === '}')] }), x.global ? 'g' : '')
|
238
|
+
) :
|
239
|
+
// else, get specific bracket
|
240
|
+
b[x]
|
241
|
+
}
|
242
|
+
})('{ }')
|
243
|
+
|
244
|
+
|
245
|
+
var tmpl = (function() {
|
246
|
+
|
247
|
+
var cache = {},
|
248
|
+
OGLOB = '"in d?d:' + (window ? 'window).' : 'global).'),
|
249
|
+
reVars =
|
250
|
+
/(['"\/])(?:[^\\]*?|\\.|.)*?\1|\.\w*|\w*:|\b(?:(?:new|typeof|in|instanceof) |(?:this|true|false|null|undefined)\b|function\s*\()|([A-Za-z_$]\w*)/g
|
251
|
+
|
252
|
+
// build a template (or get it from cache), render with data
|
253
|
+
return function(str, data) {
|
254
|
+
return str && (cache[str] || (cache[str] = tmpl(str)))(data)
|
255
|
+
}
|
256
|
+
|
257
|
+
|
258
|
+
// create a template instance
|
259
|
+
|
260
|
+
function tmpl(s, p) {
|
261
|
+
|
262
|
+
if (s.indexOf(brackets(0)) < 0) {
|
263
|
+
// return raw text
|
264
|
+
s = s.replace(/\n|\r\n?/g, '\n')
|
265
|
+
return function () { return s }
|
266
|
+
}
|
267
|
+
|
268
|
+
// temporarily convert \{ and \} to a non-character
|
269
|
+
s = s
|
270
|
+
.replace(brackets(/\\{/g), '\uFFF0')
|
271
|
+
.replace(brackets(/\\}/g), '\uFFF1')
|
272
|
+
|
273
|
+
// split string to expression and non-expresion parts
|
274
|
+
p = split(s, extract(s, brackets(/{/), brackets(/}/)))
|
275
|
+
|
276
|
+
// is it a single expression or a template? i.e. {x} or <b>{x}</b>
|
277
|
+
s = (p.length === 2 && !p[0]) ?
|
278
|
+
|
279
|
+
// if expression, evaluate it
|
280
|
+
expr(p[1]) :
|
281
|
+
|
282
|
+
// if template, evaluate all expressions in it
|
283
|
+
'[' + p.map(function(s, i) {
|
284
|
+
|
285
|
+
// is it an expression or a string (every second part is an expression)
|
286
|
+
return i % 2 ?
|
287
|
+
|
288
|
+
// evaluate the expressions
|
289
|
+
expr(s, true) :
|
290
|
+
|
291
|
+
// process string parts of the template:
|
292
|
+
'"' + s
|
293
|
+
|
294
|
+
// preserve new lines
|
295
|
+
.replace(/\n|\r\n?/g, '\\n')
|
296
|
+
|
297
|
+
// escape quotes
|
298
|
+
.replace(/"/g, '\\"') +
|
299
|
+
|
300
|
+
'"'
|
301
|
+
|
302
|
+
}).join(',') + '].join("")'
|
303
|
+
|
304
|
+
return new Function('d', 'return ' + s
|
305
|
+
// bring escaped { and } back
|
306
|
+
.replace(/\uFFF0/g, brackets(0))
|
307
|
+
.replace(/\uFFF1/g, brackets(1)) + ';')
|
308
|
+
|
309
|
+
}
|
310
|
+
|
311
|
+
|
312
|
+
// parse { ... } expression
|
313
|
+
|
314
|
+
function expr(s, n) {
|
315
|
+
s = s
|
316
|
+
|
317
|
+
// convert new lines to spaces
|
318
|
+
.replace(/\n|\r\n?/g, ' ')
|
319
|
+
|
320
|
+
// trim whitespace, brackets, strip comments
|
321
|
+
.replace(brackets(/^[{ ]+|[ }]+$|\/\*.+?\*\//g), '')
|
322
|
+
|
323
|
+
// is it an object literal? i.e. { key : value }
|
324
|
+
return /^\s*[\w- "']+ *:/.test(s) ?
|
325
|
+
|
326
|
+
// if object literal, return trueish keys
|
327
|
+
// e.g.: { show: isOpen(), done: item.done } -> "show done"
|
328
|
+
'[' +
|
329
|
+
|
330
|
+
// extract key:val pairs, ignoring any nested objects
|
331
|
+
extract(s,
|
332
|
+
|
333
|
+
// name part: name:, "name":, 'name':, name :
|
334
|
+
/["' ]*[\w- ]+["' ]*:/,
|
335
|
+
|
336
|
+
// expression part: everything upto a comma followed by a name (see above) or end of line
|
337
|
+
/,(?=["' ]*[\w- ]+["' ]*:)|}|$/
|
338
|
+
).map(function(pair) {
|
339
|
+
|
340
|
+
// get key, val parts
|
341
|
+
return pair.replace(/^[ "']*(.+?)[ "']*: *(.+?),? *$/, function(_, k, v) {
|
342
|
+
|
343
|
+
// wrap all conditional parts to ignore errors
|
344
|
+
return v.replace(/[^&|=!><]+/g, wrap) + '?"' + k + '":"",'
|
345
|
+
|
346
|
+
})
|
347
|
+
|
348
|
+
}).join('') +
|
349
|
+
|
350
|
+
'].join(" ").trim()' :
|
351
|
+
|
352
|
+
// if js expression, evaluate as javascript
|
353
|
+
wrap(s, n)
|
354
|
+
|
355
|
+
}
|
356
|
+
|
357
|
+
|
358
|
+
// execute js w/o breaking on errors or undefined vars
|
359
|
+
|
360
|
+
function wrap(s, nonull) {
|
361
|
+
s = s.trim()
|
362
|
+
return !s ? '' : '(function(v){try{v=' +
|
363
|
+
|
364
|
+
// prefix vars (name => data.name)
|
365
|
+
s.replace(reVars, function(s, _, v) { return v ? '(("' + v + OGLOB + v + ')' : s }) +
|
366
|
+
|
367
|
+
// default to empty string for falsy values except zero
|
368
|
+
'}catch(e){}return ' + (nonull === true ? '!v&&v!==0?"":v' : 'v') + '}).call(d)'
|
369
|
+
}
|
370
|
+
|
371
|
+
|
372
|
+
// split string by an array of substrings
|
373
|
+
|
374
|
+
function split(str, substrings) {
|
375
|
+
var parts = []
|
376
|
+
substrings.map(function(sub, i) {
|
377
|
+
|
378
|
+
// push matched expression and part before it
|
379
|
+
i = str.indexOf(sub)
|
380
|
+
parts.push(str.slice(0, i), sub)
|
381
|
+
str = str.slice(i + sub.length)
|
382
|
+
})
|
383
|
+
if (str) parts.push(str)
|
384
|
+
|
385
|
+
// push the remaining part
|
386
|
+
return parts
|
387
|
+
}
|
388
|
+
|
389
|
+
|
390
|
+
// match strings between opening and closing regexp, skipping any inner/nested matches
|
391
|
+
|
392
|
+
function extract(str, open, close) {
|
393
|
+
|
394
|
+
var start,
|
395
|
+
level = 0,
|
396
|
+
matches = [],
|
397
|
+
re = new RegExp('(' + open.source + ')|(' + close.source + ')', 'g')
|
398
|
+
|
399
|
+
str.replace(re, function(_, open, close, pos) {
|
400
|
+
|
401
|
+
// if outer inner bracket, mark position
|
402
|
+
if (!level && open) start = pos
|
403
|
+
|
404
|
+
// in(de)crease bracket level
|
405
|
+
level += open ? 1 : -1
|
406
|
+
|
407
|
+
// if outer closing bracket, grab the match
|
408
|
+
if (!level && close != null) matches.push(str.slice(start, pos + close.length))
|
409
|
+
|
410
|
+
})
|
411
|
+
|
412
|
+
return matches
|
413
|
+
}
|
414
|
+
|
415
|
+
})()
|
416
|
+
|
417
|
+
/*
|
418
|
+
lib/browser/tag/mkdom.js
|
419
|
+
|
420
|
+
Includes hacks needed for the Internet Explorer version 9 and bellow
|
421
|
+
|
422
|
+
*/
|
423
|
+
// http://kangax.github.io/compat-table/es5/#ie8
|
424
|
+
// http://codeplanet.io/dropping-ie8/
|
425
|
+
|
426
|
+
var mkdom = (function (checkIE) {
|
427
|
+
|
428
|
+
var rootEls = {
|
429
|
+
'tr': 'tbody',
|
430
|
+
'th': 'tr',
|
431
|
+
'td': 'tr',
|
432
|
+
'tbody': 'table',
|
433
|
+
'col': 'colgroup'
|
434
|
+
},
|
435
|
+
GENERIC = 'div'
|
436
|
+
|
437
|
+
checkIE = checkIE && checkIE < 10
|
438
|
+
|
439
|
+
// creates any dom element in a div, table, or colgroup container
|
440
|
+
function _mkdom(html) {
|
441
|
+
|
442
|
+
var match = html && html.match(/^\s*<([-\w]+)/),
|
443
|
+
tagName = match && match[1].toLowerCase(),
|
444
|
+
rootTag = rootEls[tagName] || GENERIC,
|
445
|
+
el = mkEl(rootTag)
|
446
|
+
|
447
|
+
el.stub = true
|
448
|
+
|
449
|
+
if (checkIE && tagName && (match = tagName.match(SPECIAL_TAGS_REGEX)))
|
450
|
+
ie9elem(el, html, tagName, !!match[1])
|
451
|
+
else
|
452
|
+
el.innerHTML = html
|
453
|
+
|
454
|
+
return el
|
455
|
+
}
|
456
|
+
|
457
|
+
// creates tr, th, td, option, optgroup element for IE8-9
|
458
|
+
/* istanbul ignore next */
|
459
|
+
function ie9elem(el, html, tagName, select) {
|
460
|
+
|
461
|
+
var div = mkEl(GENERIC),
|
462
|
+
tag = select ? 'select>' : 'table>',
|
463
|
+
child
|
464
|
+
|
465
|
+
div.innerHTML = '<' + tag + html + '</' + tag
|
466
|
+
|
467
|
+
child = div.getElementsByTagName(tagName)[0]
|
468
|
+
if (child)
|
469
|
+
el.appendChild(child)
|
470
|
+
|
471
|
+
}
|
472
|
+
// end ie9elem()
|
473
|
+
|
474
|
+
return _mkdom
|
475
|
+
|
476
|
+
})(IE_VERSION)
|
477
|
+
|
478
|
+
// { key, i in items} -> { key, i, items }
|
479
|
+
function loopKeys(expr) {
|
480
|
+
var b0 = brackets(0),
|
481
|
+
els = expr.trim().slice(b0.length).match(/^\s*(\S+?)\s*(?:,\s*(\S+))?\s+in\s+(.+)$/)
|
482
|
+
return els ? { key: els[1], pos: els[2], val: b0 + els[3] } : { val: expr }
|
483
|
+
}
|
484
|
+
|
485
|
+
function mkitem(expr, key, val) {
|
486
|
+
var item = {}
|
487
|
+
item[expr.key] = key
|
488
|
+
if (expr.pos) item[expr.pos] = val
|
489
|
+
return item
|
490
|
+
}
|
491
|
+
|
492
|
+
|
493
|
+
/* Beware: heavy stuff */
|
494
|
+
function _each(dom, parent, expr) {
|
495
|
+
|
496
|
+
remAttr(dom, 'each')
|
497
|
+
|
498
|
+
var tagName = getTagName(dom),
|
499
|
+
template = dom.outerHTML,
|
500
|
+
hasImpl = !!tagImpl[tagName],
|
501
|
+
impl = tagImpl[tagName] || {
|
502
|
+
tmpl: template
|
503
|
+
},
|
504
|
+
root = dom.parentNode,
|
505
|
+
placeholder = document.createComment('riot placeholder'),
|
506
|
+
tags = [],
|
507
|
+
child = getTag(dom),
|
508
|
+
checksum
|
509
|
+
|
510
|
+
root.insertBefore(placeholder, dom)
|
511
|
+
|
512
|
+
expr = loopKeys(expr)
|
513
|
+
|
514
|
+
// clean template code
|
515
|
+
parent
|
516
|
+
.one('premount', function () {
|
517
|
+
if (root.stub) root = parent.root
|
518
|
+
// remove the original DOM node
|
519
|
+
dom.parentNode.removeChild(dom)
|
520
|
+
})
|
521
|
+
.on('update', function () {
|
522
|
+
var items = tmpl(expr.val, parent)
|
523
|
+
|
524
|
+
// object loop. any changes cause full redraw
|
525
|
+
if (!isArray(items)) {
|
526
|
+
|
527
|
+
checksum = items ? JSON.stringify(items) : ''
|
528
|
+
|
529
|
+
items = !items ? [] :
|
530
|
+
Object.keys(items).map(function (key) {
|
531
|
+
return mkitem(expr, key, items[key])
|
532
|
+
})
|
533
|
+
}
|
534
|
+
|
535
|
+
var frag = document.createDocumentFragment(),
|
536
|
+
i = tags.length,
|
537
|
+
j = items.length
|
538
|
+
|
539
|
+
// unmount leftover items
|
540
|
+
while (i > j) {
|
541
|
+
tags[--i].unmount()
|
542
|
+
tags.splice(i, 1)
|
543
|
+
}
|
544
|
+
|
545
|
+
for (i = 0; i < j; ++i) {
|
546
|
+
var _item = !checksum && !!expr.key ? mkitem(expr, items[i], i) : items[i]
|
547
|
+
|
548
|
+
if (!tags[i]) {
|
549
|
+
// mount new
|
550
|
+
(tags[i] = new Tag(impl, {
|
551
|
+
parent: parent,
|
552
|
+
isLoop: true,
|
553
|
+
hasImpl: hasImpl,
|
554
|
+
root: SPECIAL_TAGS_REGEX.test(tagName) ? root : dom.cloneNode(),
|
555
|
+
item: _item
|
556
|
+
}, dom.innerHTML)
|
557
|
+
).mount()
|
558
|
+
|
559
|
+
frag.appendChild(tags[i].root)
|
560
|
+
} else
|
561
|
+
tags[i].update(_item)
|
562
|
+
|
563
|
+
tags[i]._item = _item
|
564
|
+
|
565
|
+
}
|
566
|
+
|
567
|
+
root.insertBefore(frag, placeholder)
|
568
|
+
|
569
|
+
if (child) parent.tags[tagName] = tags
|
570
|
+
|
571
|
+
}).one('updated', function() {
|
572
|
+
var keys = Object.keys(parent)// only set new values
|
573
|
+
walk(root, function(node) {
|
574
|
+
// only set element node and not isLoop
|
575
|
+
if (node.nodeType == 1 && !node.isLoop && !node._looped) {
|
576
|
+
node._visited = false // reset _visited for loop node
|
577
|
+
node._looped = true // avoid set multiple each
|
578
|
+
setNamed(node, parent, keys)
|
579
|
+
}
|
580
|
+
})
|
581
|
+
})
|
582
|
+
|
583
|
+
}
|
584
|
+
|
585
|
+
|
586
|
+
function parseNamedElements(root, tag, childTags) {
|
587
|
+
|
588
|
+
walk(root, function(dom) {
|
589
|
+
if (dom.nodeType == 1) {
|
590
|
+
dom.isLoop = dom.isLoop || (dom.parentNode && dom.parentNode.isLoop || dom.getAttribute('each')) ? 1 : 0
|
591
|
+
|
592
|
+
// custom child tag
|
593
|
+
var child = getTag(dom)
|
594
|
+
|
595
|
+
if (child && !dom.isLoop) {
|
596
|
+
childTags.push(initChildTag(child, dom, tag))
|
597
|
+
}
|
598
|
+
|
599
|
+
if (!dom.isLoop)
|
600
|
+
setNamed(dom, tag, [])
|
601
|
+
}
|
602
|
+
|
603
|
+
})
|
604
|
+
|
605
|
+
}
|
606
|
+
|
607
|
+
function parseExpressions(root, tag, expressions) {
|
608
|
+
|
609
|
+
function addExpr(dom, val, extra) {
|
610
|
+
if (val.indexOf(brackets(0)) >= 0) {
|
611
|
+
var expr = { dom: dom, expr: val }
|
612
|
+
expressions.push(extend(expr, extra))
|
613
|
+
}
|
614
|
+
}
|
615
|
+
|
616
|
+
walk(root, function(dom) {
|
617
|
+
var type = dom.nodeType
|
618
|
+
|
619
|
+
// text node
|
620
|
+
if (type == 3 && dom.parentNode.tagName != 'STYLE') addExpr(dom, dom.nodeValue)
|
621
|
+
if (type != 1) return
|
622
|
+
|
623
|
+
/* element */
|
624
|
+
|
625
|
+
// loop
|
626
|
+
var attr = dom.getAttribute('each')
|
627
|
+
|
628
|
+
if (attr) { _each(dom, tag, attr); return false }
|
629
|
+
|
630
|
+
// attribute expressions
|
631
|
+
each(dom.attributes, function(attr) {
|
632
|
+
var name = attr.name,
|
633
|
+
bool = name.split('__')[1]
|
634
|
+
|
635
|
+
addExpr(dom, attr.value, { attr: bool || name, bool: bool })
|
636
|
+
if (bool) { remAttr(dom, name); return false }
|
637
|
+
|
638
|
+
})
|
639
|
+
|
640
|
+
// skip custom tags
|
641
|
+
if (getTag(dom)) return false
|
642
|
+
|
643
|
+
})
|
644
|
+
|
645
|
+
}
|
646
|
+
function Tag(impl, conf, innerHTML) {
|
647
|
+
|
648
|
+
var self = riot.observable(this),
|
649
|
+
opts = inherit(conf.opts) || {},
|
650
|
+
dom = mkdom(impl.tmpl),
|
651
|
+
parent = conf.parent,
|
652
|
+
isLoop = conf.isLoop,
|
653
|
+
hasImpl = conf.hasImpl,
|
654
|
+
item = cleanUpData(conf.item),
|
655
|
+
expressions = [],
|
656
|
+
childTags = [],
|
657
|
+
root = conf.root,
|
658
|
+
fn = impl.fn,
|
659
|
+
tagName = root.tagName.toLowerCase(),
|
660
|
+
attr = {},
|
661
|
+
propsInSyncWithParent = []
|
662
|
+
|
663
|
+
if (fn && root._tag) {
|
664
|
+
root._tag.unmount(true)
|
665
|
+
}
|
666
|
+
|
667
|
+
// not yet mounted
|
668
|
+
this.isMounted = false
|
669
|
+
root.isLoop = isLoop
|
670
|
+
|
671
|
+
// keep a reference to the tag just created
|
672
|
+
// so we will be able to mount this tag multiple times
|
673
|
+
root._tag = this
|
674
|
+
|
675
|
+
// create a unique id to this tag
|
676
|
+
// it could be handy to use it also to improve the virtual dom rendering speed
|
677
|
+
this._id = __uid++
|
678
|
+
|
679
|
+
extend(this, { parent: parent, root: root, opts: opts, tags: {} }, item)
|
680
|
+
|
681
|
+
// grab attributes
|
682
|
+
each(root.attributes, function(el) {
|
683
|
+
var val = el.value
|
684
|
+
// remember attributes with expressions only
|
685
|
+
if (brackets(/{.*}/).test(val)) attr[el.name] = val
|
686
|
+
})
|
687
|
+
|
688
|
+
if (dom.innerHTML && !/^(select|optgroup|table|tbody|tr|col(?:group)?)$/.test(tagName))
|
689
|
+
// replace all the yield tags with the tag inner html
|
690
|
+
dom.innerHTML = replaceYield(dom.innerHTML, innerHTML)
|
691
|
+
|
692
|
+
// options
|
693
|
+
function updateOpts() {
|
694
|
+
var ctx = hasImpl && isLoop ? self : parent || self
|
695
|
+
|
696
|
+
// update opts from current DOM attributes
|
697
|
+
each(root.attributes, function(el) {
|
698
|
+
opts[el.name] = tmpl(el.value, ctx)
|
699
|
+
})
|
700
|
+
// recover those with expressions
|
701
|
+
each(Object.keys(attr), function(name) {
|
702
|
+
opts[name] = tmpl(attr[name], ctx)
|
703
|
+
})
|
704
|
+
}
|
705
|
+
|
706
|
+
function normalizeData(data) {
|
707
|
+
for (var key in item) {
|
708
|
+
if (typeof self[key] !== T_UNDEF)
|
709
|
+
self[key] = data[key]
|
710
|
+
}
|
711
|
+
}
|
712
|
+
|
713
|
+
function inheritFromParent () {
|
714
|
+
if (!self.parent || !isLoop) return
|
715
|
+
each(Object.keys(self.parent), function(k) {
|
716
|
+
// some properties must be always in sync with the parent tag
|
717
|
+
var mustSync = !~RESERVED_WORDS_BLACKLIST.indexOf(k) && ~propsInSyncWithParent.indexOf(k)
|
718
|
+
if (typeof self[k] === T_UNDEF || mustSync) {
|
719
|
+
// track the property to keep in sync
|
720
|
+
// so we can keep it updated
|
721
|
+
if (!mustSync) propsInSyncWithParent.push(k)
|
722
|
+
self[k] = self.parent[k]
|
723
|
+
}
|
724
|
+
})
|
725
|
+
}
|
726
|
+
|
727
|
+
this.update = function(data) {
|
728
|
+
// make sure the data passed will not override
|
729
|
+
// the component core methods
|
730
|
+
data = cleanUpData(data)
|
731
|
+
// inherit properties from the parent
|
732
|
+
inheritFromParent()
|
733
|
+
// normalize the tag properties in case an item object was initially passed
|
734
|
+
if (data && typeof item === T_OBJECT) {
|
735
|
+
normalizeData(data)
|
736
|
+
item = data
|
737
|
+
}
|
738
|
+
extend(self, data)
|
739
|
+
updateOpts()
|
740
|
+
self.trigger('update', data)
|
741
|
+
update(expressions, self)
|
742
|
+
self.trigger('updated')
|
743
|
+
}
|
744
|
+
|
745
|
+
this.mixin = function() {
|
746
|
+
each(arguments, function(mix) {
|
747
|
+
mix = typeof mix === T_STRING ? riot.mixin(mix) : mix
|
748
|
+
each(Object.keys(mix), function(key) {
|
749
|
+
// bind methods to self
|
750
|
+
if (key != 'init')
|
751
|
+
self[key] = isFunction(mix[key]) ? mix[key].bind(self) : mix[key]
|
752
|
+
})
|
753
|
+
// init method will be called automatically
|
754
|
+
if (mix.init) mix.init.bind(self)()
|
755
|
+
})
|
756
|
+
}
|
757
|
+
|
758
|
+
this.mount = function() {
|
759
|
+
|
760
|
+
updateOpts()
|
761
|
+
|
762
|
+
// initialiation
|
763
|
+
if (fn) fn.call(self, opts)
|
764
|
+
|
765
|
+
// parse layout after init. fn may calculate args for nested custom tags
|
766
|
+
parseExpressions(dom, self, expressions)
|
767
|
+
|
768
|
+
// mount the child tags
|
769
|
+
toggle(true)
|
770
|
+
|
771
|
+
// update the root adding custom attributes coming from the compiler
|
772
|
+
// it fixes also #1087
|
773
|
+
if (impl.attrs || hasImpl) {
|
774
|
+
walkAttributes(impl.attrs, function (k, v) { root.setAttribute(k, v) })
|
775
|
+
parseExpressions(self.root, self, expressions)
|
776
|
+
}
|
777
|
+
|
778
|
+
if (!self.parent || isLoop) self.update(item)
|
779
|
+
|
780
|
+
// internal use only, fixes #403
|
781
|
+
self.trigger('premount')
|
782
|
+
|
783
|
+
if (isLoop && !hasImpl) {
|
784
|
+
// update the root attribute for the looped elements
|
785
|
+
self.root = root = dom.firstChild
|
786
|
+
|
787
|
+
} else {
|
788
|
+
while (dom.firstChild) root.appendChild(dom.firstChild)
|
789
|
+
if (root.stub) self.root = root = parent.root
|
790
|
+
}
|
791
|
+
// if it's not a child tag we can trigger its mount event
|
792
|
+
if (!self.parent || self.parent.isMounted) {
|
793
|
+
self.isMounted = true
|
794
|
+
self.trigger('mount')
|
795
|
+
}
|
796
|
+
// otherwise we need to wait that the parent event gets triggered
|
797
|
+
else self.parent.one('mount', function() {
|
798
|
+
// avoid to trigger the `mount` event for the tags
|
799
|
+
// not visible included in an if statement
|
800
|
+
if (!isInStub(self.root)) {
|
801
|
+
self.parent.isMounted = self.isMounted = true
|
802
|
+
self.trigger('mount')
|
803
|
+
}
|
804
|
+
})
|
805
|
+
}
|
806
|
+
|
807
|
+
|
808
|
+
this.unmount = function(keepRootTag) {
|
809
|
+
var el = root,
|
810
|
+
p = el.parentNode,
|
811
|
+
ptag
|
812
|
+
|
813
|
+
if (p) {
|
814
|
+
|
815
|
+
if (parent) {
|
816
|
+
ptag = getImmediateCustomParentTag(parent)
|
817
|
+
// remove this tag from the parent tags object
|
818
|
+
// if there are multiple nested tags with same name..
|
819
|
+
// remove this element form the array
|
820
|
+
if (isArray(ptag.tags[tagName]))
|
821
|
+
each(ptag.tags[tagName], function(tag, i) {
|
822
|
+
if (tag._id == self._id)
|
823
|
+
ptag.tags[tagName].splice(i, 1)
|
824
|
+
})
|
825
|
+
else
|
826
|
+
// otherwise just delete the tag instance
|
827
|
+
ptag.tags[tagName] = undefined
|
828
|
+
}
|
829
|
+
|
830
|
+
else
|
831
|
+
while (el.firstChild) el.removeChild(el.firstChild)
|
832
|
+
|
833
|
+
if (!keepRootTag)
|
834
|
+
p.removeChild(el)
|
835
|
+
else
|
836
|
+
// the riot-tag attribute isn't needed anymore, remove it
|
837
|
+
p.removeAttribute('riot-tag')
|
838
|
+
}
|
839
|
+
|
840
|
+
|
841
|
+
self.trigger('unmount')
|
842
|
+
toggle()
|
843
|
+
self.off('*')
|
844
|
+
// somehow ie8 does not like `delete root._tag`
|
845
|
+
root._tag = null
|
846
|
+
|
847
|
+
}
|
848
|
+
|
849
|
+
function toggle(isMount) {
|
850
|
+
|
851
|
+
// mount/unmount children
|
852
|
+
each(childTags, function(child) { child[isMount ? 'mount' : 'unmount']() })
|
853
|
+
|
854
|
+
// listen/unlisten parent (events flow one way from parent to children)
|
855
|
+
if (parent) {
|
856
|
+
var evt = isMount ? 'on' : 'off'
|
857
|
+
|
858
|
+
// the loop tags will be always in sync with the parent automatically
|
859
|
+
if (isLoop)
|
860
|
+
parent[evt]('unmount', self.unmount)
|
861
|
+
else
|
862
|
+
parent[evt]('update', self.update)[evt]('unmount', self.unmount)
|
863
|
+
}
|
864
|
+
}
|
865
|
+
|
866
|
+
// named elements available for fn
|
867
|
+
parseNamedElements(dom, this, childTags)
|
868
|
+
|
869
|
+
|
870
|
+
}
|
871
|
+
|
872
|
+
function setEventHandler(name, handler, dom, tag) {
|
873
|
+
|
874
|
+
dom[name] = function(e) {
|
875
|
+
|
876
|
+
var item = tag._item,
|
877
|
+
ptag = tag.parent,
|
878
|
+
el
|
879
|
+
|
880
|
+
if (!item)
|
881
|
+
while (ptag && !item) {
|
882
|
+
item = ptag._item
|
883
|
+
ptag = ptag.parent
|
884
|
+
}
|
885
|
+
|
886
|
+
// cross browser event fix
|
887
|
+
e = e || window.event
|
888
|
+
|
889
|
+
// ignore error on some browsers
|
890
|
+
try {
|
891
|
+
e.currentTarget = dom
|
892
|
+
if (!e.target) e.target = e.srcElement
|
893
|
+
if (!e.which) e.which = e.charCode || e.keyCode
|
894
|
+
} catch (ignored) { /**/ }
|
895
|
+
|
896
|
+
e.item = item
|
897
|
+
|
898
|
+
// prevent default behaviour (by default)
|
899
|
+
if (handler.call(tag, e) !== true && !/radio|check/.test(dom.type)) {
|
900
|
+
if (e.preventDefault) e.preventDefault()
|
901
|
+
e.returnValue = false
|
902
|
+
}
|
903
|
+
|
904
|
+
if (!e.preventUpdate) {
|
905
|
+
el = item ? getImmediateCustomParentTag(ptag) : tag
|
906
|
+
el.update()
|
907
|
+
}
|
908
|
+
|
909
|
+
}
|
910
|
+
|
911
|
+
}
|
912
|
+
|
913
|
+
// used by if- attribute
|
914
|
+
function insertTo(root, node, before) {
|
915
|
+
if (root) {
|
916
|
+
root.insertBefore(before, node)
|
917
|
+
root.removeChild(node)
|
918
|
+
}
|
919
|
+
}
|
920
|
+
|
921
|
+
function update(expressions, tag) {
|
922
|
+
|
923
|
+
each(expressions, function(expr, i) {
|
924
|
+
|
925
|
+
var dom = expr.dom,
|
926
|
+
attrName = expr.attr,
|
927
|
+
value = tmpl(expr.expr, tag),
|
928
|
+
parent = expr.dom.parentNode
|
929
|
+
|
930
|
+
if (expr.bool)
|
931
|
+
value = value ? attrName : false
|
932
|
+
else if (value == null)
|
933
|
+
value = ''
|
934
|
+
|
935
|
+
// leave out riot- prefixes from strings inside textarea
|
936
|
+
// fix #815: any value -> string
|
937
|
+
if (parent && parent.tagName == 'TEXTAREA') value = ('' + value).replace(/riot-/g, '')
|
938
|
+
|
939
|
+
// no change
|
940
|
+
if (expr.value === value) return
|
941
|
+
expr.value = value
|
942
|
+
|
943
|
+
// text node
|
944
|
+
if (!attrName) {
|
945
|
+
dom.nodeValue = '' + value // #815 related
|
946
|
+
return
|
947
|
+
}
|
948
|
+
|
949
|
+
// remove original attribute
|
950
|
+
remAttr(dom, attrName)
|
951
|
+
// event handler
|
952
|
+
if (isFunction(value)) {
|
953
|
+
setEventHandler(attrName, value, dom, tag)
|
954
|
+
|
955
|
+
// if- conditional
|
956
|
+
} else if (attrName == 'if') {
|
957
|
+
var stub = expr.stub,
|
958
|
+
add = function() { insertTo(stub.parentNode, stub, dom) },
|
959
|
+
remove = function() { insertTo(dom.parentNode, dom, stub) }
|
960
|
+
|
961
|
+
// add to DOM
|
962
|
+
if (value) {
|
963
|
+
if (stub) {
|
964
|
+
add()
|
965
|
+
dom.inStub = false
|
966
|
+
// avoid to trigger the mount event if the tags is not visible yet
|
967
|
+
// maybe we can optimize this avoiding to mount the tag at all
|
968
|
+
if (!isInStub(dom)) {
|
969
|
+
walk(dom, function(el) {
|
970
|
+
if (el._tag && !el._tag.isMounted) el._tag.isMounted = !!el._tag.trigger('mount')
|
971
|
+
})
|
972
|
+
}
|
973
|
+
}
|
974
|
+
// remove from DOM
|
975
|
+
} else {
|
976
|
+
stub = expr.stub = stub || document.createTextNode('')
|
977
|
+
// if the parentNode is defined we can easily replace the tag
|
978
|
+
if (dom.parentNode)
|
979
|
+
remove()
|
980
|
+
else
|
981
|
+
// otherwise we need to wait the updated event
|
982
|
+
(tag.parent || tag).one('updated', remove)
|
983
|
+
|
984
|
+
dom.inStub = true
|
985
|
+
}
|
986
|
+
// show / hide
|
987
|
+
} else if (/^(show|hide)$/.test(attrName)) {
|
988
|
+
if (attrName == 'hide') value = !value
|
989
|
+
dom.style.display = value ? '' : 'none'
|
990
|
+
|
991
|
+
// field value
|
992
|
+
} else if (attrName == 'value') {
|
993
|
+
dom.value = value
|
994
|
+
|
995
|
+
// <img src="{ expr }">
|
996
|
+
} else if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
|
997
|
+
if (value)
|
998
|
+
dom.setAttribute(attrName.slice(RIOT_PREFIX.length), value)
|
999
|
+
|
1000
|
+
} else {
|
1001
|
+
if (expr.bool) {
|
1002
|
+
dom[attrName] = value
|
1003
|
+
if (!value) return
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
if (typeof value !== T_OBJECT) dom.setAttribute(attrName, value)
|
1007
|
+
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
})
|
1011
|
+
|
1012
|
+
}
|
1013
|
+
function each(els, fn) {
|
1014
|
+
for (var i = 0, len = (els || []).length, el; i < len; i++) {
|
1015
|
+
el = els[i]
|
1016
|
+
// return false -> remove current item during loop
|
1017
|
+
if (el != null && fn(el, i) === false) i--
|
1018
|
+
}
|
1019
|
+
return els
|
1020
|
+
}
|
1021
|
+
|
1022
|
+
function isFunction(v) {
|
1023
|
+
return typeof v === T_FUNCTION || false // avoid IE problems
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
function remAttr(dom, name) {
|
1027
|
+
dom.removeAttribute(name)
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
function getTag(dom) {
|
1031
|
+
return dom.tagName && tagImpl[dom.getAttribute(RIOT_TAG) || dom.tagName.toLowerCase()]
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
function initChildTag(child, dom, parent) {
|
1035
|
+
var tag = new Tag(child, { root: dom, parent: parent }, dom.innerHTML),
|
1036
|
+
tagName = getTagName(dom),
|
1037
|
+
ptag = getImmediateCustomParentTag(parent),
|
1038
|
+
cachedTag
|
1039
|
+
|
1040
|
+
// fix for the parent attribute in the looped elements
|
1041
|
+
tag.parent = ptag
|
1042
|
+
|
1043
|
+
cachedTag = ptag.tags[tagName]
|
1044
|
+
|
1045
|
+
// if there are multiple children tags having the same name
|
1046
|
+
if (cachedTag) {
|
1047
|
+
// if the parent tags property is not yet an array
|
1048
|
+
// create it adding the first cached tag
|
1049
|
+
if (!isArray(cachedTag))
|
1050
|
+
ptag.tags[tagName] = [cachedTag]
|
1051
|
+
// add the new nested tag to the array
|
1052
|
+
if (!~ptag.tags[tagName].indexOf(tag))
|
1053
|
+
ptag.tags[tagName].push(tag)
|
1054
|
+
} else {
|
1055
|
+
ptag.tags[tagName] = tag
|
1056
|
+
}
|
1057
|
+
|
1058
|
+
// empty the child node once we got its template
|
1059
|
+
// to avoid that its children get compiled multiple times
|
1060
|
+
dom.innerHTML = ''
|
1061
|
+
|
1062
|
+
return tag
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
function getImmediateCustomParentTag(tag) {
|
1066
|
+
var ptag = tag
|
1067
|
+
while (!getTag(ptag.root)) {
|
1068
|
+
if (!ptag.parent) break
|
1069
|
+
ptag = ptag.parent
|
1070
|
+
}
|
1071
|
+
return ptag
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
function getTagName(dom) {
|
1075
|
+
var child = getTag(dom),
|
1076
|
+
namedTag = dom.getAttribute('name'),
|
1077
|
+
tagName = namedTag && namedTag.indexOf(brackets(0)) < 0 ? namedTag : child ? child.name : dom.tagName.toLowerCase()
|
1078
|
+
|
1079
|
+
return tagName
|
1080
|
+
}
|
1081
|
+
|
1082
|
+
function extend(src) {
|
1083
|
+
var obj, args = arguments
|
1084
|
+
for (var i = 1; i < args.length; ++i) {
|
1085
|
+
if ((obj = args[i])) {
|
1086
|
+
for (var key in obj) { // eslint-disable-line guard-for-in
|
1087
|
+
src[key] = obj[key]
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
return src
|
1092
|
+
}
|
1093
|
+
|
1094
|
+
// with this function we avoid that the current Tag methods get overridden
|
1095
|
+
function cleanUpData(data) {
|
1096
|
+
if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION)) return data
|
1097
|
+
|
1098
|
+
var o = {}
|
1099
|
+
for (var key in data) {
|
1100
|
+
if (!~RESERVED_WORDS_BLACKLIST.indexOf(key))
|
1101
|
+
o[key] = data[key]
|
1102
|
+
}
|
1103
|
+
return o
|
1104
|
+
}
|
1105
|
+
|
1106
|
+
function walk(dom, fn) {
|
1107
|
+
if (dom) {
|
1108
|
+
if (fn(dom) === false) return
|
1109
|
+
else {
|
1110
|
+
dom = dom.firstChild
|
1111
|
+
|
1112
|
+
while (dom) {
|
1113
|
+
walk(dom, fn)
|
1114
|
+
dom = dom.nextSibling
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
|
1120
|
+
// minimize risk: only zero or one _space_ between attr & value
|
1121
|
+
function walkAttributes(html, fn) {
|
1122
|
+
var m,
|
1123
|
+
re = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g
|
1124
|
+
|
1125
|
+
while ((m = re.exec(html))) {
|
1126
|
+
fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
|
1127
|
+
}
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
function isInStub(dom) {
|
1131
|
+
while (dom) {
|
1132
|
+
if (dom.inStub) return true
|
1133
|
+
dom = dom.parentNode
|
1134
|
+
}
|
1135
|
+
return false
|
1136
|
+
}
|
1137
|
+
|
1138
|
+
function mkEl(name) {
|
1139
|
+
return document.createElement(name)
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
function replaceYield(tmpl, innerHTML) {
|
1143
|
+
return tmpl.replace(/<(yield)\/?>(<\/\1>)?/gi, innerHTML || '')
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
function $$(selector, ctx) {
|
1147
|
+
return (ctx || document).querySelectorAll(selector)
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
function $(selector, ctx) {
|
1151
|
+
return (ctx || document).querySelector(selector)
|
1152
|
+
}
|
1153
|
+
|
1154
|
+
function inherit(parent) {
|
1155
|
+
function Child() {}
|
1156
|
+
Child.prototype = parent
|
1157
|
+
return new Child()
|
1158
|
+
}
|
1159
|
+
|
1160
|
+
function setNamed(dom, parent, keys) {
|
1161
|
+
if (dom._visited) return
|
1162
|
+
var p,
|
1163
|
+
v = dom.getAttribute('id') || dom.getAttribute('name')
|
1164
|
+
|
1165
|
+
if (v) {
|
1166
|
+
if (keys.indexOf(v) < 0) {
|
1167
|
+
p = parent[v]
|
1168
|
+
if (!p)
|
1169
|
+
parent[v] = dom
|
1170
|
+
else if (isArray(p))
|
1171
|
+
p.push(dom)
|
1172
|
+
else
|
1173
|
+
parent[v] = [p, dom]
|
1174
|
+
}
|
1175
|
+
dom._visited = true
|
1176
|
+
}
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
// faster String startsWith alternative
|
1180
|
+
function startsWith(src, str) {
|
1181
|
+
return src.slice(0, str.length) === str
|
1182
|
+
}
|
1183
|
+
|
1184
|
+
/*
|
1185
|
+
Virtual dom is an array of custom tags on the document.
|
1186
|
+
Updates and unmounts propagate downwards from parent to children.
|
1187
|
+
*/
|
1188
|
+
|
1189
|
+
var virtualDom = [],
|
1190
|
+
tagImpl = {},
|
1191
|
+
styleNode
|
1192
|
+
|
1193
|
+
function injectStyle(css) {
|
1194
|
+
|
1195
|
+
if (riot.render) return // skip injection on the server
|
1196
|
+
|
1197
|
+
if (!styleNode) {
|
1198
|
+
styleNode = mkEl('style')
|
1199
|
+
styleNode.setAttribute('type', 'text/css')
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
var head = document.head || document.getElementsByTagName('head')[0]
|
1203
|
+
|
1204
|
+
if (styleNode.styleSheet)
|
1205
|
+
styleNode.styleSheet.cssText += css
|
1206
|
+
else
|
1207
|
+
styleNode.innerHTML += css
|
1208
|
+
|
1209
|
+
if (!styleNode._rendered)
|
1210
|
+
if (styleNode.styleSheet) {
|
1211
|
+
document.body.appendChild(styleNode)
|
1212
|
+
} else {
|
1213
|
+
var rs = $('style[type=riot]')
|
1214
|
+
if (rs) {
|
1215
|
+
rs.parentNode.insertBefore(styleNode, rs)
|
1216
|
+
rs.parentNode.removeChild(rs)
|
1217
|
+
} else head.appendChild(styleNode)
|
1218
|
+
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
styleNode._rendered = true
|
1222
|
+
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
function mountTo(root, tagName, opts) {
|
1226
|
+
var tag = tagImpl[tagName],
|
1227
|
+
// cache the inner HTML to fix #855
|
1228
|
+
innerHTML = root._innerHTML = root._innerHTML || root.innerHTML
|
1229
|
+
|
1230
|
+
// clear the inner html
|
1231
|
+
root.innerHTML = ''
|
1232
|
+
|
1233
|
+
if (tag && root) tag = new Tag(tag, { root: root, opts: opts }, innerHTML)
|
1234
|
+
|
1235
|
+
if (tag && tag.mount) {
|
1236
|
+
tag.mount()
|
1237
|
+
virtualDom.push(tag)
|
1238
|
+
return tag.on('unmount', function() {
|
1239
|
+
virtualDom.splice(virtualDom.indexOf(tag), 1)
|
1240
|
+
})
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
}
|
1244
|
+
|
1245
|
+
riot.tag = function(name, html, css, attrs, fn) {
|
1246
|
+
if (isFunction(attrs)) {
|
1247
|
+
fn = attrs
|
1248
|
+
if (/^[\w\-]+\s?=/.test(css)) {
|
1249
|
+
attrs = css
|
1250
|
+
css = ''
|
1251
|
+
} else attrs = ''
|
1252
|
+
}
|
1253
|
+
if (css) {
|
1254
|
+
if (isFunction(css)) fn = css
|
1255
|
+
else injectStyle(css)
|
1256
|
+
}
|
1257
|
+
tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
|
1258
|
+
return name
|
1259
|
+
}
|
1260
|
+
|
1261
|
+
riot.mount = function(selector, tagName, opts) {
|
1262
|
+
|
1263
|
+
var els,
|
1264
|
+
allTags,
|
1265
|
+
tags = []
|
1266
|
+
|
1267
|
+
// helper functions
|
1268
|
+
|
1269
|
+
function addRiotTags(arr) {
|
1270
|
+
var list = ''
|
1271
|
+
each(arr, function (e) {
|
1272
|
+
list += ', *[' + RIOT_TAG + '="' + e.trim() + '"]'
|
1273
|
+
})
|
1274
|
+
return list
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
function selectAllTags() {
|
1278
|
+
var keys = Object.keys(tagImpl)
|
1279
|
+
return keys + addRiotTags(keys)
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
function pushTags(root) {
|
1283
|
+
var last
|
1284
|
+
if (root.tagName) {
|
1285
|
+
if (tagName && (!(last = root.getAttribute(RIOT_TAG)) || last != tagName))
|
1286
|
+
root.setAttribute(RIOT_TAG, tagName)
|
1287
|
+
|
1288
|
+
var tag = mountTo(root,
|
1289
|
+
tagName || root.getAttribute(RIOT_TAG) || root.tagName.toLowerCase(), opts)
|
1290
|
+
|
1291
|
+
if (tag) tags.push(tag)
|
1292
|
+
}
|
1293
|
+
else if (root.length) {
|
1294
|
+
each(root, pushTags) // assume nodeList
|
1295
|
+
}
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
// ----- mount code -----
|
1299
|
+
|
1300
|
+
if (typeof tagName === T_OBJECT) {
|
1301
|
+
opts = tagName
|
1302
|
+
tagName = 0
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
// crawl the DOM to find the tag
|
1306
|
+
if (typeof selector === T_STRING) {
|
1307
|
+
if (selector === '*')
|
1308
|
+
// select all the tags registered
|
1309
|
+
// and also the tags found with the riot-tag attribute set
|
1310
|
+
selector = allTags = selectAllTags()
|
1311
|
+
else
|
1312
|
+
// or just the ones named like the selector
|
1313
|
+
selector += addRiotTags(selector.split(','))
|
1314
|
+
|
1315
|
+
els = $$(selector)
|
1316
|
+
}
|
1317
|
+
else
|
1318
|
+
// probably you have passed already a tag or a NodeList
|
1319
|
+
els = selector
|
1320
|
+
|
1321
|
+
// select all the registered and mount them inside their root elements
|
1322
|
+
if (tagName === '*') {
|
1323
|
+
// get all custom tags
|
1324
|
+
tagName = allTags || selectAllTags()
|
1325
|
+
// if the root els it's just a single tag
|
1326
|
+
if (els.tagName)
|
1327
|
+
els = $$(tagName, els)
|
1328
|
+
else {
|
1329
|
+
// select all the children for all the different root elements
|
1330
|
+
var nodeList = []
|
1331
|
+
each(els, function (_el) {
|
1332
|
+
nodeList.push($$(tagName, _el))
|
1333
|
+
})
|
1334
|
+
els = nodeList
|
1335
|
+
}
|
1336
|
+
// get rid of the tagName
|
1337
|
+
tagName = 0
|
1338
|
+
}
|
1339
|
+
|
1340
|
+
if (els.tagName)
|
1341
|
+
pushTags(els)
|
1342
|
+
else
|
1343
|
+
each(els, pushTags)
|
1344
|
+
|
1345
|
+
return tags
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
// update everything
|
1349
|
+
riot.update = function() {
|
1350
|
+
return each(virtualDom, function(tag) {
|
1351
|
+
tag.update()
|
1352
|
+
})
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
// @deprecated
|
1356
|
+
riot.mountTo = riot.mount
|
1357
|
+
|
1358
|
+
// share methods for other riot parts, e.g. compiler
|
1359
|
+
riot.util = { brackets: brackets, tmpl: tmpl }
|
1360
|
+
|
1361
|
+
// support CommonJS, AMD & browser
|
1362
|
+
/* istanbul ignore next */
|
1363
|
+
if (typeof exports === T_OBJECT)
|
1364
|
+
module.exports = riot
|
1365
|
+
else if (typeof define === 'function' && define.amd)
|
1366
|
+
define(function() { return (window.riot = riot) })
|
1367
|
+
else
|
1368
|
+
window.riot = riot
|
1369
|
+
|
1370
|
+
})(typeof window != 'undefined' ? window : void 0);
|