riot_js-rails 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/riot_js/rails/processors/sprockets_processor_v3.rb +4 -2
- data/lib/riot_js/rails/railtie.rb +2 -2
- data/lib/riot_js/rails/version.rb +1 -1
- data/vendor/assets/javascripts/compiler/brackets.js +252 -0
- data/vendor/assets/javascripts/compiler/compiler.js +968 -10
- data/vendor/assets/javascripts/compiler/parsers.js +195 -21
- data/vendor/assets/javascripts/riot.js +1632 -569
- metadata +3 -3
- data/vendor/assets/javascripts/compiler/compiler_core.js +0 -234
@@ -1,32 +1,206 @@
|
|
1
|
-
|
1
|
+
/**
|
2
|
+
* The compiler.parsers object holds the compiler's predefined parsers
|
3
|
+
* @module
|
4
|
+
*/
|
5
|
+
|
6
|
+
var path = require('path') // for sass
|
7
|
+
|
8
|
+
// dummy function for the none and javascript parsers
|
9
|
+
function _none (src) { return src }
|
10
|
+
|
11
|
+
/** Cache of required modules */
|
12
|
+
var _mods = {
|
13
|
+
none: _none,
|
14
|
+
javascript: _none
|
15
|
+
}
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Returns the module name for the given parser's name.
|
19
|
+
*
|
20
|
+
* @param {string} name - one of the `html`, `css`, or `js` parsers.
|
21
|
+
* @returns {string} The module name for using with `require()`
|
22
|
+
* @static
|
23
|
+
*/
|
24
|
+
function _modname (name) {
|
25
|
+
switch (name) {
|
26
|
+
case 'es6':
|
27
|
+
return 'babel'
|
28
|
+
case 'babel':
|
29
|
+
return 'babel-core'
|
30
|
+
case 'javascript':
|
31
|
+
return 'none'
|
32
|
+
case 'coffee':
|
33
|
+
case 'coffeescript':
|
34
|
+
return 'coffee-script'
|
35
|
+
case 'scss':
|
36
|
+
case 'sass':
|
37
|
+
return 'node-sass'
|
38
|
+
case 'typescript':
|
39
|
+
return 'typescript-simple'
|
40
|
+
default:
|
41
|
+
return name
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Loads a parser instance via `require`, without generating error.
|
47
|
+
*
|
48
|
+
* @param {string} name - one of the `html`, `css`, or `js` parsers.
|
49
|
+
* @param {string} [req=name] - name for `require()`
|
50
|
+
* @returns {function} parser function, or null if error
|
51
|
+
*/
|
52
|
+
function _try (name, req) {
|
53
|
+
|
54
|
+
function fn (r) {
|
55
|
+
try { return require(r) } catch (_) {/**/}
|
56
|
+
return null
|
57
|
+
}
|
58
|
+
|
59
|
+
var p = _mods[name] = fn(req || _modname(name))
|
60
|
+
|
61
|
+
// istanbul ignore next: babel-core v5.8.x is not loaded by CI
|
62
|
+
if (!p && name === 'es6') {
|
63
|
+
p = _mods[name] = fn('babel-core')
|
64
|
+
}
|
65
|
+
return p
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Returns a parser instance by its name, require the module if necessary.
|
70
|
+
* Public through the `parsers._req` function.
|
71
|
+
*
|
72
|
+
* @param {string} name - The parser's name, as registered in the parsers object
|
73
|
+
* @param {string} [req] - To be used by `_try` with `require`
|
74
|
+
* @returns {function} The parser instance, null if the parser is not found
|
75
|
+
* @static
|
76
|
+
*/
|
77
|
+
function _req (name, req) {
|
78
|
+
return name in _mods ? _mods[name] : _try(name, req)
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Merge the properties of the first object with the properties of the second.
|
83
|
+
*
|
84
|
+
* @param {object} target - Target object
|
85
|
+
* @param {object} source - Source of the extra properties
|
86
|
+
* @returns {object} Target object containing the new properties
|
87
|
+
*/
|
88
|
+
function extend (target, source) {
|
89
|
+
if (source) {
|
90
|
+
for (var prop in source) {
|
91
|
+
/* istanbul ignore next */
|
92
|
+
if (source.hasOwnProperty(prop)) {
|
93
|
+
target[prop] = source[prop]
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
return target
|
98
|
+
}
|
99
|
+
|
100
|
+
module.exports = {
|
101
|
+
/**
|
102
|
+
* The HTML parsers.
|
103
|
+
* @prop {function} jade - http://jade-lang.com
|
104
|
+
*/
|
2
105
|
html: {
|
3
|
-
jade: function(html) {
|
4
|
-
|
106
|
+
jade: function (html, opts, url) {
|
107
|
+
opts = extend({
|
108
|
+
pretty: true,
|
109
|
+
filename: url,
|
110
|
+
doctype: 'html'
|
111
|
+
}, opts)
|
112
|
+
return _req('jade').render(html, opts)
|
113
|
+
}
|
114
|
+
},
|
115
|
+
/**
|
116
|
+
* Style parsers. In browsers, only less is supported.
|
117
|
+
* @prop {function} sass - http://sass-lang.com
|
118
|
+
* @prop {function} scss - http://sass-lang.com
|
119
|
+
* @prop {function} less - http://lesscss.org
|
120
|
+
* @prop {function} stylus - http://stylus-lang.com
|
121
|
+
*/
|
122
|
+
css: {
|
123
|
+
sass: function (tag, css, opts, url) {
|
124
|
+
opts = extend({
|
125
|
+
data: css,
|
126
|
+
includePaths: [path.dirname(url)],
|
127
|
+
indentedSyntax: true,
|
128
|
+
omitSourceMapUrl: true,
|
129
|
+
outputStyle: 'compact'
|
130
|
+
}, opts)
|
131
|
+
return _req('sass').renderSync(opts).css + ''
|
132
|
+
},
|
133
|
+
scss: function (tag, css, opts, url) {
|
134
|
+
opts = extend({
|
135
|
+
data: css,
|
136
|
+
includePaths: [path.dirname(url)],
|
137
|
+
indentedSyntax: false,
|
138
|
+
omitSourceMapUrl: true,
|
139
|
+
outputStyle: 'compact'
|
140
|
+
}, opts)
|
141
|
+
return _req('scss').renderSync(opts).css + ''
|
142
|
+
},
|
143
|
+
less: function (tag, css, opts, url) {
|
144
|
+
var ret
|
145
|
+
|
146
|
+
opts = extend({
|
147
|
+
sync: true,
|
148
|
+
syncImport: true,
|
149
|
+
filename: url
|
150
|
+
}, opts)
|
151
|
+
_req('less').render(css, opts, function (err, result) {
|
152
|
+
/* istanbul ignore next */
|
153
|
+
if (err) throw err
|
154
|
+
ret = result.css
|
155
|
+
})
|
156
|
+
return ret
|
157
|
+
},
|
158
|
+
stylus: function (tag, css, opts, url) {
|
159
|
+
var
|
160
|
+
stylus = _req('stylus'),
|
161
|
+
nib = _req('nib') // optional nib support
|
162
|
+
|
163
|
+
opts = extend({ filename: url }, opts)
|
164
|
+
/* istanbul ignore next: can't run both */
|
165
|
+
return nib
|
166
|
+
? stylus(css, opts).use(nib()).import('nib').render() : stylus.render(css, opts)
|
5
167
|
}
|
6
168
|
},
|
7
|
-
|
169
|
+
/**
|
170
|
+
* The JavaScript parsers.
|
171
|
+
* @prop {function} es6 - https://babeljs.io - babel or babel-core up to v5.8
|
172
|
+
* @prop {function} babel - https://babeljs.io - for v6.x or later
|
173
|
+
* @prop {function} coffee - http://coffeescript.org
|
174
|
+
* @prop {function} livescript - http://livescript.net
|
175
|
+
* @prop {function} typescript - http://www.typescriptlang.org
|
176
|
+
*/
|
8
177
|
js: {
|
9
|
-
|
10
|
-
|
178
|
+
es6: function (js, opts) {
|
179
|
+
opts = extend({
|
180
|
+
blacklist: ['useStrict', 'strict', 'react'],
|
181
|
+
sourceMaps: false,
|
182
|
+
comments: false
|
183
|
+
}, opts)
|
184
|
+
return _req('es6').transform(js, opts).code
|
11
185
|
},
|
12
|
-
|
13
|
-
return
|
186
|
+
babel: function (js, opts, url) {
|
187
|
+
return _req('babel').transform(js, extend({ filename: url }, opts)).code
|
14
188
|
},
|
15
|
-
|
16
|
-
return
|
189
|
+
coffee: function (js, opts) {
|
190
|
+
return _req('coffee').compile(js, extend({ bare: true }, opts))
|
17
191
|
},
|
18
|
-
|
19
|
-
return
|
192
|
+
livescript: function (js, opts) {
|
193
|
+
return _req('livescript').compile(js, extend({ bare: true, header: false }, opts))
|
20
194
|
},
|
21
|
-
|
22
|
-
return
|
23
|
-
}
|
24
|
-
|
195
|
+
typescript: function (js, opts) {
|
196
|
+
return _req('typescript')(js, opts)
|
197
|
+
},
|
198
|
+
none: _none, javascript: _none
|
199
|
+
},
|
200
|
+
_modname: _modname,
|
201
|
+
_req: _req
|
25
202
|
}
|
26
203
|
|
27
|
-
|
28
|
-
|
29
|
-
// 4 the nostalgics
|
30
|
-
parsers.js.coffeescript = parsers.js.coffee
|
204
|
+
exports = module.exports
|
205
|
+
exports.js.coffeescript = exports.js.coffee // 4 the nostalgics
|
31
206
|
|
32
|
-
module.exports = parsers
|
@@ -1,13 +1,21 @@
|
|
1
|
-
/* Riot v2.
|
1
|
+
/* Riot v2.3.15, @license MIT, (c) 2015 Muut Inc. + contributors */
|
2
2
|
|
3
3
|
;(function(window, undefined) {
|
4
4
|
'use strict';
|
5
|
-
var riot = { version: 'v2.
|
6
|
-
|
5
|
+
var riot = { version: 'v2.3.15', settings: {} },
|
6
|
+
// be aware, internal usage
|
7
|
+
// ATTENTION: prefix the global dynamic variables with `__`
|
7
8
|
|
8
9
|
// counter to give a unique id to all the Tag instances
|
9
10
|
__uid = 0,
|
10
|
-
|
11
|
+
// tags instances cache
|
12
|
+
__virtualDom = [],
|
13
|
+
// tags implementation cache
|
14
|
+
__tagImpl = {},
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Const
|
18
|
+
*/
|
11
19
|
// riot specific prefixes
|
12
20
|
RIOT_PREFIX = 'riot-',
|
13
21
|
RIOT_TAG = RIOT_PREFIX + 'tag',
|
@@ -18,470 +26,984 @@ var riot = { version: 'v2.2.4', settings: {} },
|
|
18
26
|
T_UNDEF = 'undefined',
|
19
27
|
T_FUNCTION = 'function',
|
20
28
|
// special native tags that cannot be treated like the others
|
21
|
-
SPECIAL_TAGS_REGEX = /^(?:
|
22
|
-
RESERVED_WORDS_BLACKLIST = ['_item', '_id', 'update', 'root', 'mount', 'unmount', 'mixin', 'isMounted', 'isLoop', 'tags', 'parent', 'opts', 'trigger', 'on', 'off', 'one'],
|
29
|
+
SPECIAL_TAGS_REGEX = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/,
|
30
|
+
RESERVED_WORDS_BLACKLIST = ['_item', '_id', '_parent', 'update', 'root', 'mount', 'unmount', 'mixin', 'isMounted', 'isLoop', 'tags', 'parent', 'opts', 'trigger', 'on', 'off', 'one'],
|
23
31
|
|
24
32
|
// 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
|
-
|
33
|
+
IE_VERSION = (window && window.document || {}).documentMode | 0
|
34
|
+
/* istanbul ignore next */
|
30
35
|
riot.observable = function(el) {
|
31
36
|
|
37
|
+
/**
|
38
|
+
* Extend the original object or create a new empty one
|
39
|
+
* @type { Object }
|
40
|
+
*/
|
41
|
+
|
32
42
|
el = el || {}
|
33
43
|
|
44
|
+
/**
|
45
|
+
* Private variables and methods
|
46
|
+
*/
|
34
47
|
var callbacks = {},
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
fn.typed = pos > 0
|
48
|
+
slice = Array.prototype.slice,
|
49
|
+
onEachEvent = function(e, fn) { e.replace(/\S+/g, fn) },
|
50
|
+
defineProperty = function (key, value) {
|
51
|
+
Object.defineProperty(el, key, {
|
52
|
+
value: value,
|
53
|
+
enumerable: false,
|
54
|
+
writable: false,
|
55
|
+
configurable: false
|
44
56
|
})
|
45
57
|
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Listen to the given space separated list of `events` and execute the `callback` each time an event is triggered.
|
61
|
+
* @param { String } events - events ids
|
62
|
+
* @param { Function } fn - callback function
|
63
|
+
* @returns { Object } el
|
64
|
+
*/
|
65
|
+
defineProperty('on', function(events, fn) {
|
66
|
+
if (typeof fn != 'function') return el
|
67
|
+
|
68
|
+
onEachEvent(events, function(name, pos) {
|
69
|
+
(callbacks[name] = callbacks[name] || []).push(fn)
|
70
|
+
fn.typed = pos > 0
|
71
|
+
})
|
72
|
+
|
46
73
|
return el
|
47
|
-
}
|
74
|
+
})
|
48
75
|
|
49
|
-
|
50
|
-
|
76
|
+
/**
|
77
|
+
* Removes the given space separated list of `events` listeners
|
78
|
+
* @param { String } events - events ids
|
79
|
+
* @param { Function } fn - callback function
|
80
|
+
* @returns { Object } el
|
81
|
+
*/
|
82
|
+
defineProperty('off', function(events, fn) {
|
83
|
+
if (events == '*' && !fn) callbacks = {}
|
51
84
|
else {
|
52
|
-
events
|
85
|
+
onEachEvent(events, function(name) {
|
53
86
|
if (fn) {
|
54
87
|
var arr = callbacks[name]
|
55
|
-
for (var i = 0, cb;
|
56
|
-
if (cb
|
88
|
+
for (var i = 0, cb; cb = arr && arr[i]; ++i) {
|
89
|
+
if (cb == fn) arr.splice(i--, 1)
|
57
90
|
}
|
58
|
-
} else
|
59
|
-
callbacks[name] = []
|
60
|
-
}
|
91
|
+
} else delete callbacks[name]
|
61
92
|
})
|
62
93
|
}
|
63
94
|
return el
|
64
|
-
}
|
95
|
+
})
|
65
96
|
|
66
|
-
|
67
|
-
|
97
|
+
/**
|
98
|
+
* Listen to the given space separated list of `events` and execute the `callback` at most once
|
99
|
+
* @param { String } events - events ids
|
100
|
+
* @param { Function } fn - callback function
|
101
|
+
* @returns { Object } el
|
102
|
+
*/
|
103
|
+
defineProperty('one', function(events, fn) {
|
68
104
|
function on() {
|
69
|
-
el.off(
|
105
|
+
el.off(events, on)
|
70
106
|
fn.apply(el, arguments)
|
71
107
|
}
|
72
|
-
return el.on(
|
73
|
-
}
|
108
|
+
return el.on(events, on)
|
109
|
+
})
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Execute all callback functions that listen to the given space separated list of `events`
|
113
|
+
* @param { String } events - events ids
|
114
|
+
* @returns { Object } el
|
115
|
+
*/
|
116
|
+
defineProperty('trigger', function(events) {
|
117
|
+
|
118
|
+
// getting the arguments
|
119
|
+
// skipping the first one
|
120
|
+
var args = slice.call(arguments, 1),
|
121
|
+
fns
|
122
|
+
|
123
|
+
onEachEvent(events, function(name) {
|
74
124
|
|
75
|
-
|
76
|
-
var args = [].slice.call(arguments, 1),
|
77
|
-
fns = callbacks[name] || []
|
125
|
+
fns = slice.call(callbacks[name] || [], 0)
|
78
126
|
|
79
|
-
|
80
|
-
|
127
|
+
for (var i = 0, fn; fn = fns[i]; ++i) {
|
128
|
+
if (fn.busy) return
|
81
129
|
fn.busy = 1
|
82
130
|
fn.apply(el, fn.typed ? [name].concat(args) : args)
|
83
131
|
if (fns[i] !== fn) { i-- }
|
84
132
|
fn.busy = 0
|
85
133
|
}
|
86
|
-
}
|
87
134
|
|
88
|
-
|
89
|
-
|
90
|
-
|
135
|
+
if (callbacks['*'] && name != '*')
|
136
|
+
el.trigger.apply(el, ['*', name].concat(args))
|
137
|
+
|
138
|
+
})
|
91
139
|
|
92
140
|
return el
|
93
|
-
}
|
141
|
+
})
|
94
142
|
|
95
143
|
return el
|
96
144
|
|
97
145
|
}
|
98
|
-
|
99
|
-
|
146
|
+
/* istanbul ignore next */
|
147
|
+
;(function(riot) {
|
148
|
+
|
149
|
+
/**
|
150
|
+
* Simple client-side router
|
151
|
+
* @module riot-route
|
152
|
+
*/
|
153
|
+
|
154
|
+
|
155
|
+
var RE_ORIGIN = /^.+?\/+[^\/]+/,
|
156
|
+
EVENT_LISTENER = 'EventListener',
|
157
|
+
REMOVE_EVENT_LISTENER = 'remove' + EVENT_LISTENER,
|
158
|
+
ADD_EVENT_LISTENER = 'add' + EVENT_LISTENER,
|
159
|
+
HAS_ATTRIBUTE = 'hasAttribute',
|
160
|
+
REPLACE = 'replace',
|
161
|
+
POPSTATE = 'popstate',
|
162
|
+
HASHCHANGE = 'hashchange',
|
163
|
+
TRIGGER = 'trigger',
|
164
|
+
MAX_EMIT_STACK_LEVEL = 3,
|
165
|
+
win = typeof window != 'undefined' && window,
|
166
|
+
doc = typeof document != 'undefined' && document,
|
167
|
+
hist = win && history,
|
168
|
+
loc = win && (hist.location || win.location), // see html5-history-api
|
169
|
+
prot = Router.prototype, // to minify more
|
170
|
+
clickEvent = doc && doc.ontouchstart ? 'touchstart' : 'click',
|
171
|
+
started = false,
|
172
|
+
central = riot.observable(),
|
173
|
+
routeFound = false,
|
174
|
+
debouncedEmit,
|
175
|
+
base, current, parser, secondParser, emitStack = [], emitStackLevel = 0
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Default parser. You can replace it via router.parser method.
|
179
|
+
* @param {string} path - current path (normalized)
|
180
|
+
* @returns {array} array
|
181
|
+
*/
|
182
|
+
function DEFAULT_PARSER(path) {
|
183
|
+
return path.split(/[/?#]/)
|
184
|
+
}
|
100
185
|
|
101
|
-
|
102
|
-
|
103
|
-
|
186
|
+
/**
|
187
|
+
* Default parser (second). You can replace it via router.parser method.
|
188
|
+
* @param {string} path - current path (normalized)
|
189
|
+
* @param {string} filter - filter string (normalized)
|
190
|
+
* @returns {array} array
|
191
|
+
*/
|
192
|
+
function DEFAULT_SECOND_PARSER(path, filter) {
|
193
|
+
var re = new RegExp('^' + filter[REPLACE](/\*/g, '([^/?#]+?)')[REPLACE](/\.\./, '.*') + '$'),
|
194
|
+
args = path.match(re)
|
195
|
+
|
196
|
+
if (args) return args.slice(1)
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* Simple/cheap debounce implementation
|
201
|
+
* @param {function} fn - callback
|
202
|
+
* @param {number} delay - delay in seconds
|
203
|
+
* @returns {function} debounced function
|
204
|
+
*/
|
205
|
+
function debounce(fn, delay) {
|
206
|
+
var t
|
207
|
+
return function () {
|
208
|
+
clearTimeout(t)
|
209
|
+
t = setTimeout(fn, delay)
|
104
210
|
}
|
211
|
+
}
|
105
212
|
|
106
|
-
|
213
|
+
/**
|
214
|
+
* Set the window listeners to trigger the routes
|
215
|
+
* @param {boolean} autoExec - see route.start
|
216
|
+
*/
|
217
|
+
function start(autoExec) {
|
218
|
+
debouncedEmit = debounce(emit, 1)
|
219
|
+
win[ADD_EVENT_LISTENER](POPSTATE, debouncedEmit)
|
220
|
+
win[ADD_EVENT_LISTENER](HASHCHANGE, debouncedEmit)
|
221
|
+
doc[ADD_EVENT_LISTENER](clickEvent, click)
|
222
|
+
if (autoExec) emit(true)
|
223
|
+
}
|
107
224
|
|
108
|
-
|
225
|
+
/**
|
226
|
+
* Router class
|
227
|
+
*/
|
228
|
+
function Router() {
|
229
|
+
this.$ = []
|
230
|
+
riot.observable(this) // make it observable
|
231
|
+
central.on('stop', this.s.bind(this))
|
232
|
+
central.on('emit', this.e.bind(this))
|
233
|
+
}
|
109
234
|
|
110
|
-
|
111
|
-
|
235
|
+
function normalize(path) {
|
236
|
+
return path[REPLACE](/^\/|\/$/, '')
|
237
|
+
}
|
112
238
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
current
|
239
|
+
function isString(str) {
|
240
|
+
return typeof str == 'string'
|
241
|
+
}
|
117
242
|
|
118
|
-
|
119
|
-
|
120
|
-
|
243
|
+
/**
|
244
|
+
* Get the part after domain name
|
245
|
+
* @param {string} href - fullpath
|
246
|
+
* @returns {string} path from root
|
247
|
+
*/
|
248
|
+
function getPathFromRoot(href) {
|
249
|
+
return (href || loc.href || '')[REPLACE](RE_ORIGIN, '')
|
250
|
+
}
|
121
251
|
|
122
|
-
|
123
|
-
|
124
|
-
|
252
|
+
/**
|
253
|
+
* Get the part after base
|
254
|
+
* @param {string} href - fullpath
|
255
|
+
* @returns {string} path from base
|
256
|
+
*/
|
257
|
+
function getPathFromBase(href) {
|
258
|
+
return base[0] == '#'
|
259
|
+
? (href || loc.href || '').split(base)[1] || ''
|
260
|
+
: getPathFromRoot(href)[REPLACE](base, '')
|
261
|
+
}
|
125
262
|
|
126
|
-
|
127
|
-
|
263
|
+
function emit(force) {
|
264
|
+
// the stack is needed for redirections
|
265
|
+
var isRoot = emitStackLevel == 0
|
266
|
+
if (MAX_EMIT_STACK_LEVEL <= emitStackLevel) return
|
128
267
|
|
129
|
-
|
130
|
-
|
268
|
+
emitStackLevel++
|
269
|
+
emitStack.push(function() {
|
270
|
+
var path = getPathFromBase()
|
271
|
+
if (force || path != current) {
|
272
|
+
central[TRIGGER]('emit', path)
|
131
273
|
current = path
|
132
274
|
}
|
133
|
-
}
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
loc.hash = arg
|
139
|
-
emit(arg)
|
140
|
-
|
141
|
-
// function
|
142
|
-
} else {
|
143
|
-
fns.on('H', arg)
|
275
|
+
})
|
276
|
+
if (isRoot) {
|
277
|
+
while (emitStack.length) {
|
278
|
+
emitStack[0]()
|
279
|
+
emitStack.shift()
|
144
280
|
}
|
281
|
+
emitStackLevel = 0
|
145
282
|
}
|
283
|
+
}
|
146
284
|
|
147
|
-
|
148
|
-
|
285
|
+
function click(e) {
|
286
|
+
if (
|
287
|
+
e.which != 1 // not left click
|
288
|
+
|| e.metaKey || e.ctrlKey || e.shiftKey // or meta keys
|
289
|
+
|| e.defaultPrevented // or default prevented
|
290
|
+
) return
|
291
|
+
|
292
|
+
var el = e.target
|
293
|
+
while (el && el.nodeName != 'A') el = el.parentNode
|
294
|
+
if (
|
295
|
+
!el || el.nodeName != 'A' // not A tag
|
296
|
+
|| el[HAS_ATTRIBUTE]('download') // has download attr
|
297
|
+
|| !el[HAS_ATTRIBUTE]('href') // has no href attr
|
298
|
+
|| el.target && el.target != '_self' // another window or frame
|
299
|
+
|| el.href.indexOf(loc.href.match(RE_ORIGIN)[0]) == -1 // cross origin
|
300
|
+
) return
|
301
|
+
|
302
|
+
if (el.href != loc.href) {
|
303
|
+
if (
|
304
|
+
el.href.split('#')[0] == loc.href.split('#')[0] // internal jump
|
305
|
+
|| base != '#' && getPathFromRoot(el.href).indexOf(base) !== 0 // outside of base
|
306
|
+
|| !go(getPathFromBase(el.href), el.title || doc.title) // route not found
|
307
|
+
) return
|
149
308
|
}
|
150
309
|
|
151
|
-
|
152
|
-
|
153
|
-
}
|
310
|
+
e.preventDefault()
|
311
|
+
}
|
154
312
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
313
|
+
/**
|
314
|
+
* Go to the path
|
315
|
+
* @param {string} path - destination path
|
316
|
+
* @param {string} title - page title
|
317
|
+
* @param {boolean} shouldReplace - use replaceState or pushState
|
318
|
+
* @returns {boolean} - route not found flag
|
319
|
+
*/
|
320
|
+
function go(path, title, shouldReplace) {
|
321
|
+
if (hist) { // if a browser
|
322
|
+
path = base + normalize(path)
|
323
|
+
title = title || doc.title
|
324
|
+
// browsers ignores the second parameter `title`
|
325
|
+
shouldReplace
|
326
|
+
? hist.replaceState(null, title, path)
|
327
|
+
: hist.pushState(null, title, path)
|
328
|
+
// so we need to set it manually
|
329
|
+
doc.title = title
|
330
|
+
routeFound = false
|
331
|
+
emit()
|
332
|
+
return routeFound
|
162
333
|
}
|
163
334
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
335
|
+
// Server-side usage: directly execute handlers for the path
|
336
|
+
return central[TRIGGER]('emit', getPathFromBase(path))
|
337
|
+
}
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Go to path or set action
|
341
|
+
* a single string: go there
|
342
|
+
* two strings: go there with setting a title
|
343
|
+
* two strings and boolean: replace history with setting a title
|
344
|
+
* a single function: set an action on the default route
|
345
|
+
* a string/RegExp and a function: set an action on the route
|
346
|
+
* @param {(string|function)} first - path / action / filter
|
347
|
+
* @param {(string|RegExp|function)} second - title / action
|
348
|
+
* @param {boolean} third - replace flag
|
349
|
+
*/
|
350
|
+
prot.m = function(first, second, third) {
|
351
|
+
if (isString(first) && (!second || isString(second))) go(first, second, third || false)
|
352
|
+
else if (second) this.r(first, second)
|
353
|
+
else this.r('@', first)
|
354
|
+
}
|
355
|
+
|
356
|
+
/**
|
357
|
+
* Stop routing
|
358
|
+
*/
|
359
|
+
prot.s = function() {
|
360
|
+
this.off('*')
|
361
|
+
this.$ = []
|
362
|
+
}
|
363
|
+
|
364
|
+
/**
|
365
|
+
* Emit
|
366
|
+
* @param {string} path - path
|
367
|
+
*/
|
368
|
+
prot.e = function(path) {
|
369
|
+
this.$.concat('@').some(function(filter) {
|
370
|
+
var args = (filter == '@' ? parser : secondParser)(normalize(path), normalize(filter))
|
371
|
+
if (typeof args != 'undefined') {
|
372
|
+
this[TRIGGER].apply(null, [filter].concat(args))
|
373
|
+
return routeFound = true // exit from loop
|
169
374
|
}
|
375
|
+
}, this)
|
376
|
+
}
|
377
|
+
|
378
|
+
/**
|
379
|
+
* Register route
|
380
|
+
* @param {string} filter - filter for matching to url
|
381
|
+
* @param {function} action - action to register
|
382
|
+
*/
|
383
|
+
prot.r = function(filter, action) {
|
384
|
+
if (filter != '@') {
|
385
|
+
filter = '/' + normalize(filter)
|
386
|
+
this.$.push(filter)
|
170
387
|
}
|
388
|
+
this.on(filter, action)
|
389
|
+
}
|
171
390
|
|
172
|
-
|
173
|
-
|
391
|
+
var mainRouter = new Router()
|
392
|
+
var route = mainRouter.m.bind(mainRouter)
|
393
|
+
|
394
|
+
/**
|
395
|
+
* Create a sub router
|
396
|
+
* @returns {function} the method of a new Router object
|
397
|
+
*/
|
398
|
+
route.create = function() {
|
399
|
+
var newSubRouter = new Router()
|
400
|
+
// stop only this sub-router
|
401
|
+
newSubRouter.m.stop = newSubRouter.s.bind(newSubRouter)
|
402
|
+
// return sub-router's main method
|
403
|
+
return newSubRouter.m.bind(newSubRouter)
|
404
|
+
}
|
174
405
|
|
175
|
-
|
176
|
-
|
406
|
+
/**
|
407
|
+
* Set the base of url
|
408
|
+
* @param {(str|RegExp)} arg - a new base or '#' or '#!'
|
409
|
+
*/
|
410
|
+
route.base = function(arg) {
|
411
|
+
base = arg || '#'
|
412
|
+
current = getPathFromBase() // recalculate current path
|
413
|
+
}
|
177
414
|
|
178
|
-
|
415
|
+
/** Exec routing right now **/
|
416
|
+
route.exec = function() {
|
417
|
+
emit(true)
|
418
|
+
}
|
419
|
+
|
420
|
+
/**
|
421
|
+
* Replace the default router to yours
|
422
|
+
* @param {function} fn - your parser function
|
423
|
+
* @param {function} fn2 - your secondParser function
|
424
|
+
*/
|
425
|
+
route.parser = function(fn, fn2) {
|
426
|
+
if (!fn && !fn2) {
|
427
|
+
// reset parser for testing...
|
428
|
+
parser = DEFAULT_PARSER
|
429
|
+
secondParser = DEFAULT_SECOND_PARSER
|
430
|
+
}
|
431
|
+
if (fn) parser = fn
|
432
|
+
if (fn2) secondParser = fn2
|
433
|
+
}
|
434
|
+
|
435
|
+
/**
|
436
|
+
* Helper function to get url query as an object
|
437
|
+
* @returns {object} parsed query
|
438
|
+
*/
|
439
|
+
route.query = function() {
|
440
|
+
var q = {}
|
441
|
+
var href = loc.href || current
|
442
|
+
href[REPLACE](/[?&](.+?)=([^&]*)/g, function(_, k, v) { q[k] = v })
|
443
|
+
return q
|
444
|
+
}
|
445
|
+
|
446
|
+
/** Stop routing **/
|
447
|
+
route.stop = function () {
|
448
|
+
if (started) {
|
449
|
+
if (win) {
|
450
|
+
win[REMOVE_EVENT_LISTENER](POPSTATE, debouncedEmit)
|
451
|
+
win[REMOVE_EVENT_LISTENER](HASHCHANGE, debouncedEmit)
|
452
|
+
doc[REMOVE_EVENT_LISTENER](clickEvent, click)
|
453
|
+
}
|
454
|
+
central[TRIGGER]('stop')
|
455
|
+
started = false
|
456
|
+
}
|
457
|
+
}
|
179
458
|
|
459
|
+
/**
|
460
|
+
* Start routing
|
461
|
+
* @param {boolean} autoExec - automatically exec after starting if true
|
462
|
+
*/
|
463
|
+
route.start = function (autoExec) {
|
464
|
+
if (!started) {
|
465
|
+
if (win) {
|
466
|
+
if (document.readyState == 'complete') start(autoExec)
|
467
|
+
// the timeout is needed to solve
|
468
|
+
// a weird safari bug https://github.com/riot/route/issues/33
|
469
|
+
else win[ADD_EVENT_LISTENER]('load', function() {
|
470
|
+
setTimeout(function() { start(autoExec) }, 1)
|
471
|
+
})
|
472
|
+
}
|
473
|
+
started = true
|
474
|
+
}
|
475
|
+
}
|
180
476
|
|
181
|
-
|
477
|
+
/** Prepare the router **/
|
478
|
+
route.base()
|
479
|
+
route.parser()
|
480
|
+
|
481
|
+
riot.route = route
|
482
|
+
})(riot)
|
483
|
+
/* istanbul ignore next */
|
484
|
+
|
485
|
+
/**
|
486
|
+
* The riot template engine
|
487
|
+
* @version v2.3.21
|
488
|
+
*/
|
489
|
+
|
490
|
+
/**
|
491
|
+
* riot.util.brackets
|
492
|
+
*
|
493
|
+
* - `brackets ` - Returns a string or regex based on its parameter
|
494
|
+
* - `brackets.set` - Change the current riot brackets
|
495
|
+
*
|
496
|
+
* @module
|
497
|
+
*/
|
498
|
+
|
499
|
+
var brackets = (function (UNDEF) {
|
500
|
+
|
501
|
+
var
|
502
|
+
REGLOB = 'g',
|
503
|
+
|
504
|
+
R_MLCOMMS = /\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,
|
505
|
+
|
506
|
+
R_STRINGS = /"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'/g,
|
507
|
+
|
508
|
+
S_QBLOCKS = R_STRINGS.source + '|' +
|
509
|
+
/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source + '|' +
|
510
|
+
/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?(\/)[gim]*/.source,
|
511
|
+
|
512
|
+
FINDBRACES = {
|
513
|
+
'(': RegExp('([()])|' + S_QBLOCKS, REGLOB),
|
514
|
+
'[': RegExp('([[\\]])|' + S_QBLOCKS, REGLOB),
|
515
|
+
'{': RegExp('([{}])|' + S_QBLOCKS, REGLOB)
|
516
|
+
},
|
517
|
+
|
518
|
+
DEFAULT = '{ }'
|
519
|
+
|
520
|
+
var _pairs = [
|
521
|
+
'{', '}',
|
522
|
+
'{', '}',
|
523
|
+
/{[^}]*}/,
|
524
|
+
/\\([{}])/g,
|
525
|
+
/\\({)|{/g,
|
526
|
+
RegExp('\\\\(})|([[({])|(})|' + S_QBLOCKS, REGLOB),
|
527
|
+
DEFAULT,
|
528
|
+
/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,
|
529
|
+
/(^|[^\\]){=[\S\s]*?}/
|
530
|
+
]
|
531
|
+
|
532
|
+
var
|
533
|
+
cachedBrackets = UNDEF,
|
534
|
+
_regex,
|
535
|
+
_cache = [],
|
536
|
+
_settings
|
537
|
+
|
538
|
+
function _loopback (re) { return re }
|
539
|
+
|
540
|
+
function _rewrite (re, bp) {
|
541
|
+
if (!bp) bp = _cache
|
542
|
+
return new RegExp(
|
543
|
+
re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : ''
|
544
|
+
)
|
545
|
+
}
|
182
546
|
|
183
|
-
|
184
|
-
|
547
|
+
function _create (pair) {
|
548
|
+
if (pair === DEFAULT) return _pairs
|
185
549
|
|
186
|
-
|
187
|
-
Returns a string with evaluated expressions.
|
550
|
+
var arr = pair.split(' ')
|
188
551
|
|
189
|
-
|
190
|
-
|
191
|
-
|
552
|
+
if (arr.length !== 2 || /[\x00-\x1F<>a-zA-Z0-9'",;\\]/.test(pair)) {
|
553
|
+
throw new Error('Unsupported brackets "' + pair + '"')
|
554
|
+
}
|
555
|
+
arr = arr.concat(pair.replace(/(?=[[\]()*+?.^$|])/g, '\\').split(' '))
|
556
|
+
|
557
|
+
arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr)
|
558
|
+
arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr)
|
559
|
+
arr[6] = _rewrite(_pairs[6], arr)
|
560
|
+
arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCKS, REGLOB)
|
561
|
+
arr[8] = pair
|
562
|
+
return arr
|
563
|
+
}
|
192
564
|
|
565
|
+
function _brackets (reOrIdx) {
|
566
|
+
return reOrIdx instanceof RegExp ? _regex(reOrIdx) : _cache[reOrIdx]
|
567
|
+
}
|
193
568
|
|
194
|
-
|
569
|
+
_brackets.split = function split (str, tmpl, _bp) {
|
570
|
+
// istanbul ignore next: _bp is for the compiler
|
571
|
+
if (!_bp) _bp = _cache
|
195
572
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
573
|
+
var
|
574
|
+
parts = [],
|
575
|
+
match,
|
576
|
+
isexpr,
|
577
|
+
start,
|
578
|
+
pos,
|
579
|
+
re = _bp[6]
|
202
580
|
|
581
|
+
isexpr = start = re.lastIndex = 0
|
203
582
|
|
204
|
-
|
583
|
+
while (match = re.exec(str)) {
|
205
584
|
|
206
|
-
|
207
|
-
except zero (undefined/null/false) will default to empty string:
|
585
|
+
pos = match.index
|
208
586
|
|
209
|
-
|
210
|
-
// will return: " - - - 0"
|
587
|
+
if (isexpr) {
|
211
588
|
|
212
|
-
|
589
|
+
if (match[2]) {
|
590
|
+
re.lastIndex = skipBraces(str, match[2], re.lastIndex)
|
591
|
+
continue
|
592
|
+
}
|
593
|
+
if (!match[3])
|
594
|
+
continue
|
595
|
+
}
|
213
596
|
|
597
|
+
if (!match[1]) {
|
598
|
+
unescapeStr(str.slice(start, pos))
|
599
|
+
start = re.lastIndex
|
600
|
+
re = _bp[6 + (isexpr ^= 1)]
|
601
|
+
re.lastIndex = start
|
602
|
+
}
|
603
|
+
}
|
214
604
|
|
215
|
-
|
605
|
+
if (str && start < str.length) {
|
606
|
+
unescapeStr(str.slice(start))
|
607
|
+
}
|
216
608
|
|
217
|
-
|
218
|
-
r,
|
219
|
-
b,
|
220
|
-
re = /[{}]/g
|
609
|
+
return parts
|
221
610
|
|
222
|
-
|
611
|
+
function unescapeStr (s) {
|
612
|
+
if (tmpl || isexpr)
|
613
|
+
parts.push(s && s.replace(_bp[5], '$1'))
|
614
|
+
else
|
615
|
+
parts.push(s)
|
616
|
+
}
|
223
617
|
|
224
|
-
|
225
|
-
|
618
|
+
function skipBraces (s, ch, ix) {
|
619
|
+
var
|
620
|
+
match,
|
621
|
+
recch = FINDBRACES[ch]
|
226
622
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
623
|
+
recch.lastIndex = ix
|
624
|
+
ix = 1
|
625
|
+
while (match = recch.exec(s)) {
|
626
|
+
if (match[1] &&
|
627
|
+
!(match[1] === ch ? ++ix : --ix)) break
|
628
|
+
}
|
629
|
+
return ix ? s.length : recch.lastIndex
|
232
630
|
}
|
631
|
+
}
|
233
632
|
|
234
|
-
|
235
|
-
return
|
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]
|
633
|
+
_brackets.hasExpr = function hasExpr (str) {
|
634
|
+
return _cache[4].test(str)
|
241
635
|
}
|
242
|
-
})('{ }')
|
243
636
|
|
637
|
+
_brackets.loopKeys = function loopKeys (expr) {
|
638
|
+
var m = expr.match(_cache[9])
|
639
|
+
return m
|
640
|
+
? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
|
641
|
+
: { val: expr.trim() }
|
642
|
+
}
|
244
643
|
|
245
|
-
|
644
|
+
_brackets.hasRaw = function (src) {
|
645
|
+
return _cache[10].test(src)
|
646
|
+
}
|
246
647
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
/(['"\/])(?:[^\\]*?|\\.|.)*?\1|\.\w*|\w*:|\b(?:(?:new|typeof|in|instanceof) |(?:this|true|false|null|undefined)\b|function\s*\()|([A-Za-z_$]\w*)/g
|
648
|
+
_brackets.array = function array (pair) {
|
649
|
+
return pair ? _create(pair) : _cache
|
650
|
+
}
|
251
651
|
|
252
|
-
|
253
|
-
|
254
|
-
|
652
|
+
function _reset (pair) {
|
653
|
+
if ((pair || (pair = DEFAULT)) !== _cache[8]) {
|
654
|
+
_cache = _create(pair)
|
655
|
+
_regex = pair === DEFAULT ? _loopback : _rewrite
|
656
|
+
_cache[9] = _regex(_pairs[9])
|
657
|
+
_cache[10] = _regex(_pairs[10])
|
658
|
+
}
|
659
|
+
cachedBrackets = pair
|
255
660
|
}
|
256
661
|
|
662
|
+
function _setSettings (o) {
|
663
|
+
var b
|
664
|
+
o = o || {}
|
665
|
+
b = o.brackets
|
666
|
+
Object.defineProperty(o, 'brackets', {
|
667
|
+
set: _reset,
|
668
|
+
get: function () { return cachedBrackets },
|
669
|
+
enumerable: true
|
670
|
+
})
|
671
|
+
_settings = o
|
672
|
+
_reset(b)
|
673
|
+
}
|
257
674
|
|
258
|
-
|
675
|
+
Object.defineProperty(_brackets, 'settings', {
|
676
|
+
set: _setSettings,
|
677
|
+
get: function () { return _settings }
|
678
|
+
})
|
259
679
|
|
260
|
-
|
680
|
+
/* istanbul ignore next: in the browser riot is always in the scope */
|
681
|
+
_brackets.settings = typeof riot !== 'undefined' && riot.settings || {}
|
682
|
+
_brackets.set = _reset
|
261
683
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
return function () { return s }
|
266
|
-
}
|
684
|
+
_brackets.R_STRINGS = R_STRINGS
|
685
|
+
_brackets.R_MLCOMMS = R_MLCOMMS
|
686
|
+
_brackets.S_QBLOCKS = S_QBLOCKS
|
267
687
|
|
268
|
-
|
269
|
-
s = s
|
270
|
-
.replace(brackets(/\\{/g), '\uFFF0')
|
271
|
-
.replace(brackets(/\\}/g), '\uFFF1')
|
688
|
+
return _brackets
|
272
689
|
|
273
|
-
|
274
|
-
p = split(s, extract(s, brackets(/{/), brackets(/}/)))
|
690
|
+
})()
|
275
691
|
|
276
|
-
|
277
|
-
|
692
|
+
/**
|
693
|
+
* @module tmpl
|
694
|
+
*
|
695
|
+
* tmpl - Root function, returns the template value, render with data
|
696
|
+
* tmpl.hasExpr - Test the existence of a expression inside a string
|
697
|
+
* tmpl.loopKeys - Get the keys for an 'each' loop (used by `_each`)
|
698
|
+
*/
|
278
699
|
|
279
|
-
|
280
|
-
expr(p[1]) :
|
700
|
+
var tmpl = (function () {
|
281
701
|
|
282
|
-
|
283
|
-
'[' + p.map(function(s, i) {
|
702
|
+
var _cache = {}
|
284
703
|
|
285
|
-
|
286
|
-
|
704
|
+
function _tmpl (str, data) {
|
705
|
+
if (!str) return str
|
287
706
|
|
288
|
-
|
289
|
-
|
707
|
+
return (_cache[str] || (_cache[str] = _create(str))).call(data, _logErr)
|
708
|
+
}
|
290
709
|
|
291
|
-
|
292
|
-
'"' + s
|
710
|
+
_tmpl.haveRaw = brackets.hasRaw
|
293
711
|
|
294
|
-
|
295
|
-
.replace(/\n|\r\n?/g, '\\n')
|
712
|
+
_tmpl.hasExpr = brackets.hasExpr
|
296
713
|
|
297
|
-
|
298
|
-
.replace(/"/g, '\\"') +
|
714
|
+
_tmpl.loopKeys = brackets.loopKeys
|
299
715
|
|
300
|
-
|
716
|
+
_tmpl.errorHandler = null
|
301
717
|
|
302
|
-
|
718
|
+
function _logErr (err, ctx) {
|
303
719
|
|
304
|
-
|
305
|
-
// bring escaped { and } back
|
306
|
-
.replace(/\uFFF0/g, brackets(0))
|
307
|
-
.replace(/\uFFF1/g, brackets(1)) + ';')
|
720
|
+
if (_tmpl.errorHandler) {
|
308
721
|
|
722
|
+
err.riotData = {
|
723
|
+
tagName: ctx && ctx.root && ctx.root.tagName,
|
724
|
+
_riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase
|
725
|
+
}
|
726
|
+
_tmpl.errorHandler(err)
|
727
|
+
}
|
309
728
|
}
|
310
729
|
|
730
|
+
function _create (str) {
|
311
731
|
|
312
|
-
|
732
|
+
var expr = _getTmpl(str)
|
733
|
+
if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
|
313
734
|
|
314
|
-
|
315
|
-
|
735
|
+
return new Function('E', expr + ';')
|
736
|
+
}
|
316
737
|
|
317
|
-
|
318
|
-
|
738
|
+
var
|
739
|
+
RE_QBLOCK = RegExp(brackets.S_QBLOCKS, 'g'),
|
740
|
+
RE_QBMARK = /\x01(\d+)~/g
|
319
741
|
|
320
|
-
|
321
|
-
|
742
|
+
function _getTmpl (str) {
|
743
|
+
var
|
744
|
+
qstr = [],
|
745
|
+
expr,
|
746
|
+
parts = brackets.split(str.replace(/\u2057/g, '"'), 1)
|
322
747
|
|
323
|
-
|
324
|
-
|
748
|
+
if (parts.length > 2 || parts[0]) {
|
749
|
+
var i, j, list = []
|
325
750
|
|
326
|
-
|
327
|
-
// e.g.: { show: isOpen(), done: item.done } -> "show done"
|
328
|
-
'[' +
|
751
|
+
for (i = j = 0; i < parts.length; ++i) {
|
329
752
|
|
330
|
-
|
331
|
-
extract(s,
|
753
|
+
expr = parts[i]
|
332
754
|
|
333
|
-
|
334
|
-
/["' ]*[\w- ]+["' ]*:/,
|
755
|
+
if (expr && (expr = i & 1 ?
|
335
756
|
|
336
|
-
|
337
|
-
/,(?=["' ]*[\w- ]+["' ]*:)|}|$/
|
338
|
-
).map(function(pair) {
|
757
|
+
_parseExpr(expr, 1, qstr) :
|
339
758
|
|
340
|
-
|
341
|
-
|
759
|
+
'"' + expr
|
760
|
+
.replace(/\\/g, '\\\\')
|
761
|
+
.replace(/\r\n?|\n/g, '\\n')
|
762
|
+
.replace(/"/g, '\\"') +
|
763
|
+
'"'
|
342
764
|
|
343
|
-
|
344
|
-
return v.replace(/[^&|=!><]+/g, wrap) + '?"' + k + '":"",'
|
765
|
+
)) list[j++] = expr
|
345
766
|
|
346
|
-
|
767
|
+
}
|
347
768
|
|
348
|
-
|
769
|
+
expr = j < 2 ? list[0] :
|
770
|
+
'[' + list.join(',') + '].join("")'
|
349
771
|
|
350
|
-
|
772
|
+
} else {
|
351
773
|
|
352
|
-
|
353
|
-
|
774
|
+
expr = _parseExpr(parts[1], 0, qstr)
|
775
|
+
}
|
354
776
|
|
777
|
+
if (qstr[0])
|
778
|
+
expr = expr.replace(RE_QBMARK, function (_, pos) {
|
779
|
+
return qstr[pos]
|
780
|
+
.replace(/\r/g, '\\r')
|
781
|
+
.replace(/\n/g, '\\n')
|
782
|
+
})
|
783
|
+
|
784
|
+
return expr
|
355
785
|
}
|
356
786
|
|
787
|
+
var
|
788
|
+
RE_BREND = {
|
789
|
+
'(': /[()]/g,
|
790
|
+
'[': /[[\]]/g,
|
791
|
+
'{': /[{}]/g
|
792
|
+
},
|
793
|
+
CS_IDENT = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\x01(\d+)~):/
|
357
794
|
|
358
|
-
|
795
|
+
function _parseExpr (expr, asText, qstr) {
|
359
796
|
|
360
|
-
|
361
|
-
s = s.trim()
|
362
|
-
return !s ? '' : '(function(v){try{v=' +
|
797
|
+
if (expr[0] === '=') expr = expr.slice(1)
|
363
798
|
|
364
|
-
|
365
|
-
|
799
|
+
expr = expr
|
800
|
+
.replace(RE_QBLOCK, function (s, div) {
|
801
|
+
return s.length > 2 && !div ? '\x01' + (qstr.push(s) - 1) + '~' : s
|
802
|
+
})
|
803
|
+
.replace(/\s+/g, ' ').trim()
|
804
|
+
.replace(/\ ?([[\({},?\.:])\ ?/g, '$1')
|
366
805
|
|
367
|
-
|
368
|
-
|
369
|
-
|
806
|
+
if (expr) {
|
807
|
+
var
|
808
|
+
list = [],
|
809
|
+
cnt = 0,
|
810
|
+
match
|
370
811
|
|
812
|
+
while (expr &&
|
813
|
+
(match = expr.match(CS_IDENT)) &&
|
814
|
+
!match.index
|
815
|
+
) {
|
816
|
+
var
|
817
|
+
key,
|
818
|
+
jsb,
|
819
|
+
re = /,|([[{(])|$/g
|
371
820
|
|
372
|
-
|
821
|
+
expr = RegExp.rightContext
|
822
|
+
key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1]
|
373
823
|
|
374
|
-
|
375
|
-
var parts = []
|
376
|
-
substrings.map(function(sub, i) {
|
824
|
+
while (jsb = (match = re.exec(expr))[1]) skipBraces(jsb, re)
|
377
825
|
|
378
|
-
|
379
|
-
|
380
|
-
parts.push(str.slice(0, i), sub)
|
381
|
-
str = str.slice(i + sub.length)
|
382
|
-
})
|
383
|
-
if (str) parts.push(str)
|
826
|
+
jsb = expr.slice(0, match.index)
|
827
|
+
expr = RegExp.rightContext
|
384
828
|
|
385
|
-
|
386
|
-
|
829
|
+
list[cnt++] = _wrapExpr(jsb, 1, key)
|
830
|
+
}
|
831
|
+
|
832
|
+
expr = !cnt ? _wrapExpr(expr, asText) :
|
833
|
+
cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
|
834
|
+
}
|
835
|
+
return expr
|
836
|
+
|
837
|
+
function skipBraces (ch, re) {
|
838
|
+
var
|
839
|
+
mm,
|
840
|
+
lv = 1,
|
841
|
+
ir = RE_BREND[ch]
|
842
|
+
|
843
|
+
ir.lastIndex = re.lastIndex
|
844
|
+
while (mm = ir.exec(expr)) {
|
845
|
+
if (mm[0] === ch) ++lv
|
846
|
+
else if (!--lv) break
|
847
|
+
}
|
848
|
+
re.lastIndex = lv ? expr.length : ir.lastIndex
|
849
|
+
}
|
387
850
|
}
|
388
851
|
|
852
|
+
// istanbul ignore next: not both
|
853
|
+
var
|
854
|
+
JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
|
855
|
+
JS_VARNAME = /[,{][$\w]+:|(^ *|[^$\w\.])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,
|
856
|
+
JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/
|
389
857
|
|
390
|
-
|
858
|
+
function _wrapExpr (expr, asText, key) {
|
859
|
+
var tb
|
391
860
|
|
392
|
-
|
861
|
+
expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) {
|
862
|
+
if (mvar) {
|
863
|
+
pos = tb ? 0 : pos + match.length
|
393
864
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
865
|
+
if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') {
|
866
|
+
match = p + '("' + mvar + JS_CONTEXT + mvar
|
867
|
+
if (pos) tb = (s = s[pos]) === '.' || s === '(' || s === '['
|
868
|
+
} else if (pos) {
|
869
|
+
tb = !JS_NOPROPS.test(s.slice(pos))
|
870
|
+
}
|
871
|
+
}
|
872
|
+
return match
|
873
|
+
})
|
398
874
|
|
399
|
-
|
875
|
+
if (tb) {
|
876
|
+
expr = 'try{return ' + expr + '}catch(e){E(e,this)}'
|
877
|
+
}
|
400
878
|
|
401
|
-
|
402
|
-
if (!level && open) start = pos
|
879
|
+
if (key) {
|
403
880
|
|
404
|
-
|
405
|
-
|
881
|
+
expr = (tb ?
|
882
|
+
'function(){' + expr + '}.call(this)' : '(' + expr + ')'
|
883
|
+
) + '?"' + key + '":""'
|
406
884
|
|
407
|
-
|
408
|
-
if (!level && close != null) matches.push(str.slice(start, pos + close.length))
|
885
|
+
} else if (asText) {
|
409
886
|
|
410
|
-
|
887
|
+
expr = 'function(v){' + (tb ?
|
888
|
+
expr.replace('return ', 'v=') : 'v=(' + expr + ')'
|
889
|
+
) + ';return v||v===0?v:""}.call(this)'
|
890
|
+
}
|
411
891
|
|
412
|
-
return
|
892
|
+
return expr
|
413
893
|
}
|
414
894
|
|
895
|
+
// istanbul ignore next: compatibility fix for beta versions
|
896
|
+
_tmpl.parse = function (s) { return s }
|
897
|
+
|
898
|
+
_tmpl.version = brackets.version = 'v2.3.21'
|
899
|
+
|
900
|
+
return _tmpl
|
901
|
+
|
415
902
|
})()
|
416
903
|
|
417
904
|
/*
|
418
905
|
lib/browser/tag/mkdom.js
|
419
906
|
|
420
|
-
Includes hacks needed for the Internet Explorer version 9 and
|
421
|
-
|
907
|
+
Includes hacks needed for the Internet Explorer version 9 and below
|
908
|
+
See: http://kangax.github.io/compat-table/es5/#ie8
|
909
|
+
http://codeplanet.io/dropping-ie8/
|
422
910
|
*/
|
423
|
-
// http://kangax.github.io/compat-table/es5/#ie8
|
424
|
-
// http://codeplanet.io/dropping-ie8/
|
425
|
-
|
426
911
|
var mkdom = (function (checkIE) {
|
427
912
|
|
428
|
-
var
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
'tbody': 'table',
|
433
|
-
'col': 'colgroup'
|
434
|
-
},
|
435
|
-
GENERIC = 'div'
|
913
|
+
var
|
914
|
+
reToSrc = /<yield\s+to=(['"])?@\1\s*>([\S\s]+?)<\/yield\s*>/.source,
|
915
|
+
rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' },
|
916
|
+
GENERIC = 'div'
|
436
917
|
|
437
918
|
checkIE = checkIE && checkIE < 10
|
919
|
+
var tblTags = checkIE
|
920
|
+
? SPECIAL_TAGS_REGEX : /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/
|
438
921
|
|
439
922
|
// creates any dom element in a div, table, or colgroup container
|
440
|
-
function _mkdom(html) {
|
923
|
+
function _mkdom(templ, html) {
|
441
924
|
|
442
|
-
var match =
|
443
|
-
|
444
|
-
|
445
|
-
el = mkEl(rootTag)
|
925
|
+
var match = templ && templ.match(/^\s*<([-\w]+)/),
|
926
|
+
tagName = match && match[1].toLowerCase(),
|
927
|
+
el = mkEl(GENERIC)
|
446
928
|
|
447
|
-
|
929
|
+
// replace all the yield tags with the tag inner html
|
930
|
+
templ = replaceYield(templ, html || '')
|
448
931
|
|
449
|
-
|
450
|
-
|
932
|
+
/* istanbul ignore next */
|
933
|
+
//if ((checkIE || !startsWith(tagName, 'opt')) && SPECIAL_TAGS_REGEX.test(tagName))
|
934
|
+
if (tblTags.test(tagName))
|
935
|
+
el = specialTags(el, templ, tagName)
|
451
936
|
else
|
452
|
-
el.innerHTML =
|
937
|
+
el.innerHTML = templ
|
938
|
+
|
939
|
+
el.stub = true
|
453
940
|
|
454
941
|
return el
|
455
942
|
}
|
456
943
|
|
457
|
-
// creates
|
458
|
-
|
459
|
-
function
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
944
|
+
// creates the root element for table and select child elements
|
945
|
+
// tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
|
946
|
+
function specialTags(el, templ, tagName) {
|
947
|
+
var
|
948
|
+
select = tagName[0] === 'o',
|
949
|
+
parent = select ? 'select>' : 'table>'
|
950
|
+
|
951
|
+
// trim() is important here, this ensures we don't have artifacts,
|
952
|
+
// so we can check if we have only one element inside the parent
|
953
|
+
el.innerHTML = '<' + parent + templ.trim() + '</' + parent
|
954
|
+
parent = el.firstChild
|
955
|
+
|
956
|
+
// returns the immediate parent if tr/th/td/col is the only element, if not
|
957
|
+
// returns the whole tree, as this can include additional elements
|
958
|
+
if (select) {
|
959
|
+
parent.selectedIndex = -1 // for IE9, compatible w/current riot behavior
|
960
|
+
} else {
|
961
|
+
var tname = rootEls[tagName]
|
962
|
+
if (tname && parent.children.length === 1) parent = $(tname, parent)
|
963
|
+
}
|
964
|
+
return parent
|
965
|
+
}
|
466
966
|
|
467
|
-
|
468
|
-
|
469
|
-
|
967
|
+
/**
|
968
|
+
* Replace the yield tag from any tag template with the innerHTML of the
|
969
|
+
* original tag in the page
|
970
|
+
* @param { String } templ - tag implementation template
|
971
|
+
* @param { String } html - original content of the tag in the DOM
|
972
|
+
* @returns { String } tag template updated without the yield tag
|
973
|
+
*/
|
974
|
+
function replaceYield(templ, html) {
|
975
|
+
// do nothing if no yield
|
976
|
+
if (!/<yield\b/i.test(templ)) return templ
|
977
|
+
|
978
|
+
// be careful with #1343 - string on the source having `$1`
|
979
|
+
var n = 0
|
980
|
+
templ = templ.replace(/<yield\s+from=['"]([-\w]+)['"]\s*(?:\/>|>\s*<\/yield\s*>)/ig,
|
981
|
+
function (str, ref) {
|
982
|
+
var m = html.match(RegExp(reToSrc.replace('@', ref), 'i'))
|
983
|
+
++n
|
984
|
+
return m && m[2] || ''
|
985
|
+
})
|
470
986
|
|
987
|
+
// yield without any "from", replace yield in templ with the innerHTML
|
988
|
+
return n ? templ : templ.replace(/<yield\s*(?:\/>|>\s*<\/yield\s*>)/gi, html)
|
471
989
|
}
|
472
|
-
// end ie9elem()
|
473
990
|
|
474
991
|
return _mkdom
|
475
992
|
|
476
993
|
})(IE_VERSION)
|
477
994
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
}
|
484
|
-
|
995
|
+
/**
|
996
|
+
* Convert the item looped into an object used to extend the child tag properties
|
997
|
+
* @param { Object } expr - object containing the keys used to extend the children tags
|
998
|
+
* @param { * } key - value to assign to the new object returned
|
999
|
+
* @param { * } val - value containing the position of the item in the array
|
1000
|
+
* @returns { Object } - new object containing the values of the original item
|
1001
|
+
*
|
1002
|
+
* The variables 'key' and 'val' are arbitrary.
|
1003
|
+
* They depend on the collection type looped (Array, Object)
|
1004
|
+
* and on the expression used on the each tag
|
1005
|
+
*
|
1006
|
+
*/
|
485
1007
|
function mkitem(expr, key, val) {
|
486
1008
|
var item = {}
|
487
1009
|
item[expr.key] = key
|
@@ -489,114 +1011,305 @@ function mkitem(expr, key, val) {
|
|
489
1011
|
return item
|
490
1012
|
}
|
491
1013
|
|
1014
|
+
/**
|
1015
|
+
* Unmount the redundant tags
|
1016
|
+
* @param { Array } items - array containing the current items to loop
|
1017
|
+
* @param { Array } tags - array containing all the children tags
|
1018
|
+
*/
|
1019
|
+
function unmountRedundant(items, tags) {
|
1020
|
+
|
1021
|
+
var i = tags.length,
|
1022
|
+
j = items.length,
|
1023
|
+
t
|
1024
|
+
|
1025
|
+
while (i > j) {
|
1026
|
+
t = tags[--i]
|
1027
|
+
tags.splice(i, 1)
|
1028
|
+
t.unmount()
|
1029
|
+
}
|
1030
|
+
}
|
492
1031
|
|
493
|
-
|
1032
|
+
/**
|
1033
|
+
* Move the nested custom tags in non custom loop tags
|
1034
|
+
* @param { Object } child - non custom loop tag
|
1035
|
+
* @param { Number } i - current position of the loop tag
|
1036
|
+
*/
|
1037
|
+
function moveNestedTags(child, i) {
|
1038
|
+
Object.keys(child.tags).forEach(function(tagName) {
|
1039
|
+
var tag = child.tags[tagName]
|
1040
|
+
if (isArray(tag))
|
1041
|
+
each(tag, function (t) {
|
1042
|
+
moveChildTag(t, tagName, i)
|
1043
|
+
})
|
1044
|
+
else
|
1045
|
+
moveChildTag(tag, tagName, i)
|
1046
|
+
})
|
1047
|
+
}
|
1048
|
+
|
1049
|
+
/**
|
1050
|
+
* Adds the elements for a virtual tag
|
1051
|
+
* @param { Tag } tag - the tag whose root's children will be inserted or appended
|
1052
|
+
* @param { Node } src - the node that will do the inserting or appending
|
1053
|
+
* @param { Tag } target - only if inserting, insert before this tag's first child
|
1054
|
+
*/
|
1055
|
+
function addVirtual(tag, src, target) {
|
1056
|
+
var el = tag._root, sib
|
1057
|
+
tag._virts = []
|
1058
|
+
while (el) {
|
1059
|
+
sib = el.nextSibling
|
1060
|
+
if (target)
|
1061
|
+
src.insertBefore(el, target._root)
|
1062
|
+
else
|
1063
|
+
src.appendChild(el)
|
1064
|
+
|
1065
|
+
tag._virts.push(el) // hold for unmounting
|
1066
|
+
el = sib
|
1067
|
+
}
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
/**
|
1071
|
+
* Move virtual tag and all child nodes
|
1072
|
+
* @param { Tag } tag - first child reference used to start move
|
1073
|
+
* @param { Node } src - the node that will do the inserting
|
1074
|
+
* @param { Tag } target - insert before this tag's first child
|
1075
|
+
* @param { Number } len - how many child nodes to move
|
1076
|
+
*/
|
1077
|
+
function moveVirtual(tag, src, target, len) {
|
1078
|
+
var el = tag._root, sib, i = 0
|
1079
|
+
for (; i < len; i++) {
|
1080
|
+
sib = el.nextSibling
|
1081
|
+
src.insertBefore(el, target._root)
|
1082
|
+
el = sib
|
1083
|
+
}
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
|
1087
|
+
/**
|
1088
|
+
* Manage tags having the 'each'
|
1089
|
+
* @param { Object } dom - DOM node we need to loop
|
1090
|
+
* @param { Tag } parent - parent tag instance where the dom node is contained
|
1091
|
+
* @param { String } expr - string contained in the 'each' attribute
|
1092
|
+
*/
|
494
1093
|
function _each(dom, parent, expr) {
|
495
1094
|
|
1095
|
+
// remove the each property from the original tag
|
496
1096
|
remAttr(dom, 'each')
|
497
1097
|
|
498
|
-
var
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
1098
|
+
var mustReorder = typeof getAttr(dom, 'no-reorder') !== T_STRING || remAttr(dom, 'no-reorder'),
|
1099
|
+
tagName = getTagName(dom),
|
1100
|
+
impl = __tagImpl[tagName] || { tmpl: dom.outerHTML },
|
1101
|
+
useRoot = SPECIAL_TAGS_REGEX.test(tagName),
|
1102
|
+
root = dom.parentNode,
|
1103
|
+
ref = document.createTextNode(''),
|
1104
|
+
child = getTag(dom),
|
1105
|
+
isOption = /^option$/i.test(tagName), // the option tags must be treated differently
|
1106
|
+
tags = [],
|
1107
|
+
oldItems = [],
|
1108
|
+
hasKeys,
|
1109
|
+
isVirtual = dom.tagName == 'VIRTUAL'
|
1110
|
+
|
1111
|
+
// parse the each expression
|
1112
|
+
expr = tmpl.loopKeys(expr)
|
1113
|
+
|
1114
|
+
// insert a marked where the loop tags will be injected
|
1115
|
+
root.insertBefore(ref, dom)
|
509
1116
|
|
510
|
-
|
1117
|
+
// clean template code
|
1118
|
+
parent.one('before-mount', function () {
|
511
1119
|
|
512
|
-
|
1120
|
+
// remove the original DOM node
|
1121
|
+
dom.parentNode.removeChild(dom)
|
1122
|
+
if (root.stub) root = parent.root
|
513
1123
|
|
514
|
-
|
515
|
-
|
516
|
-
.
|
517
|
-
|
518
|
-
|
519
|
-
dom.parentNode.removeChild(dom)
|
520
|
-
})
|
521
|
-
.on('update', function () {
|
522
|
-
var items = tmpl(expr.val, parent)
|
1124
|
+
}).on('update', function () {
|
1125
|
+
// get the new items collection
|
1126
|
+
var items = tmpl(expr.val, parent),
|
1127
|
+
// create a fragment to hold the new DOM nodes to inject in the parent tag
|
1128
|
+
frag = document.createDocumentFragment()
|
523
1129
|
|
524
|
-
// object loop. any changes cause full redraw
|
525
|
-
if (!isArray(items)) {
|
526
1130
|
|
527
|
-
checksum = items ? JSON.stringify(items) : ''
|
528
1131
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
1132
|
+
// object loop. any changes cause full redraw
|
1133
|
+
if (!isArray(items)) {
|
1134
|
+
hasKeys = items || false
|
1135
|
+
items = hasKeys ?
|
1136
|
+
Object.keys(items).map(function (key) {
|
1137
|
+
return mkitem(expr, key, items[key])
|
1138
|
+
}) : []
|
1139
|
+
}
|
534
1140
|
|
535
|
-
|
536
|
-
|
537
|
-
|
1141
|
+
// loop all the new items
|
1142
|
+
items.forEach(function(item, i) {
|
1143
|
+
// reorder only if the items are objects
|
1144
|
+
var _mustReorder = mustReorder && item instanceof Object,
|
1145
|
+
oldPos = oldItems.indexOf(item),
|
1146
|
+
pos = ~oldPos && _mustReorder ? oldPos : i,
|
1147
|
+
// does a tag exist in this position?
|
1148
|
+
tag = tags[pos]
|
1149
|
+
|
1150
|
+
item = !hasKeys && expr.key ? mkitem(expr, item, i) : item
|
1151
|
+
|
1152
|
+
// new tag
|
1153
|
+
if (
|
1154
|
+
!_mustReorder && !tag // with no-reorder we just update the old tags
|
1155
|
+
||
|
1156
|
+
_mustReorder && !~oldPos || !tag // by default we always try to reorder the DOM elements
|
1157
|
+
) {
|
1158
|
+
|
1159
|
+
tag = new Tag(impl, {
|
1160
|
+
parent: parent,
|
1161
|
+
isLoop: true,
|
1162
|
+
hasImpl: !!__tagImpl[tagName],
|
1163
|
+
root: useRoot ? root : dom.cloneNode(),
|
1164
|
+
item: item
|
1165
|
+
}, dom.innerHTML)
|
1166
|
+
|
1167
|
+
tag.mount()
|
1168
|
+
if (isVirtual) tag._root = tag.root.firstChild // save reference for further moves or inserts
|
1169
|
+
// this tag must be appended
|
1170
|
+
if (i == tags.length) {
|
1171
|
+
if (isVirtual)
|
1172
|
+
addVirtual(tag, frag)
|
1173
|
+
else frag.appendChild(tag.root)
|
1174
|
+
}
|
1175
|
+
// this tag must be insert
|
1176
|
+
else {
|
1177
|
+
if (isVirtual)
|
1178
|
+
addVirtual(tag, root, tags[i])
|
1179
|
+
else root.insertBefore(tag.root, tags[i].root)
|
1180
|
+
oldItems.splice(i, 0, item)
|
1181
|
+
}
|
538
1182
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
1183
|
+
tags.splice(i, 0, tag)
|
1184
|
+
pos = i // handled here so no move
|
1185
|
+
} else tag.update(item)
|
1186
|
+
|
1187
|
+
// reorder the tag if it's not located in its previous position
|
1188
|
+
if (pos !== i && _mustReorder) {
|
1189
|
+
// update the DOM
|
1190
|
+
if (isVirtual)
|
1191
|
+
moveVirtual(tag, root, tags[i], dom.childNodes.length)
|
1192
|
+
else root.insertBefore(tag.root, tags[i].root)
|
1193
|
+
// update the position attribute if it exists
|
1194
|
+
if (expr.pos)
|
1195
|
+
tag[expr.pos] = i
|
1196
|
+
// move the old tag instance
|
1197
|
+
tags.splice(i, 0, tags.splice(pos, 1)[0])
|
1198
|
+
// move the old item
|
1199
|
+
oldItems.splice(i, 0, oldItems.splice(pos, 1)[0])
|
1200
|
+
// if the loop tags are not custom
|
1201
|
+
// we need to move all their custom tags into the right position
|
1202
|
+
if (!child && tag.tags) moveNestedTags(tag, i)
|
543
1203
|
}
|
544
1204
|
|
545
|
-
|
546
|
-
|
1205
|
+
// cache the original item to use it in the events bound to this node
|
1206
|
+
// and its children
|
1207
|
+
tag._item = item
|
1208
|
+
// cache the real parent tag internally
|
1209
|
+
defineProperty(tag, '_parent', parent)
|
547
1210
|
|
548
|
-
|
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()
|
1211
|
+
})
|
558
1212
|
|
559
|
-
|
560
|
-
|
561
|
-
tags[i].update(_item)
|
1213
|
+
// remove the redundant tags
|
1214
|
+
unmountRedundant(items, tags)
|
562
1215
|
|
563
|
-
|
1216
|
+
// insert the new nodes
|
1217
|
+
if (isOption) root.appendChild(frag)
|
1218
|
+
else root.insertBefore(frag, ref)
|
564
1219
|
|
565
|
-
|
1220
|
+
// set the 'tags' property of the parent tag
|
1221
|
+
// if child is 'undefined' it means that we don't need to set this property
|
1222
|
+
// for example:
|
1223
|
+
// we don't need store the `myTag.tags['div']` property if we are looping a div tag
|
1224
|
+
// but we need to track the `myTag.tags['child']` property looping a custom child node named `child`
|
1225
|
+
if (child) parent.tags[tagName] = tags
|
566
1226
|
|
567
|
-
|
1227
|
+
// clone the items array
|
1228
|
+
oldItems = items.slice()
|
568
1229
|
|
569
|
-
|
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
|
-
})
|
1230
|
+
})
|
582
1231
|
|
583
1232
|
}
|
1233
|
+
/**
|
1234
|
+
* Object that will be used to inject and manage the css of every tag instance
|
1235
|
+
*/
|
1236
|
+
var styleManager = (function(_riot) {
|
1237
|
+
|
1238
|
+
if (!window) return { // skip injection on the server
|
1239
|
+
add: function () {},
|
1240
|
+
inject: function () {}
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
var styleNode = (function () {
|
1244
|
+
// create a new style element with the correct type
|
1245
|
+
var newNode = mkEl('style')
|
1246
|
+
setAttr(newNode, 'type', 'text/css')
|
1247
|
+
|
1248
|
+
// replace any user node or insert the new one into the head
|
1249
|
+
var userNode = $('style[type=riot]')
|
1250
|
+
if (userNode) {
|
1251
|
+
if (userNode.id) newNode.id = userNode.id
|
1252
|
+
userNode.parentNode.replaceChild(newNode, userNode)
|
1253
|
+
}
|
1254
|
+
else document.getElementsByTagName('head')[0].appendChild(newNode)
|
584
1255
|
|
1256
|
+
return newNode
|
1257
|
+
})()
|
585
1258
|
|
586
|
-
|
1259
|
+
// Create cache and shortcut to the correct property
|
1260
|
+
var cssTextProp = styleNode.styleSheet,
|
1261
|
+
stylesToInject = ''
|
1262
|
+
|
1263
|
+
// Expose the style node in a non-modificable property
|
1264
|
+
Object.defineProperty(_riot, 'styleNode', {
|
1265
|
+
value: styleNode,
|
1266
|
+
writable: true
|
1267
|
+
})
|
1268
|
+
|
1269
|
+
/**
|
1270
|
+
* Public api
|
1271
|
+
*/
|
1272
|
+
return {
|
1273
|
+
/**
|
1274
|
+
* Save a tag style to be later injected into DOM
|
1275
|
+
* @param { String } css [description]
|
1276
|
+
*/
|
1277
|
+
add: function(css) {
|
1278
|
+
stylesToInject += css
|
1279
|
+
},
|
1280
|
+
/**
|
1281
|
+
* Inject all previously saved tag styles into DOM
|
1282
|
+
* innerHTML seems slow: http://jsperf.com/riot-insert-style
|
1283
|
+
*/
|
1284
|
+
inject: function() {
|
1285
|
+
if (stylesToInject) {
|
1286
|
+
if (cssTextProp) cssTextProp.cssText += stylesToInject
|
1287
|
+
else styleNode.innerHTML += stylesToInject
|
1288
|
+
stylesToInject = ''
|
1289
|
+
}
|
1290
|
+
}
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
})(riot)
|
1294
|
+
|
1295
|
+
|
1296
|
+
function parseNamedElements(root, tag, childTags, forceParsingNamed) {
|
587
1297
|
|
588
1298
|
walk(root, function(dom) {
|
589
1299
|
if (dom.nodeType == 1) {
|
590
|
-
dom.isLoop = dom.isLoop ||
|
1300
|
+
dom.isLoop = dom.isLoop ||
|
1301
|
+
(dom.parentNode && dom.parentNode.isLoop || getAttr(dom, 'each'))
|
1302
|
+
? 1 : 0
|
591
1303
|
|
592
1304
|
// custom child tag
|
593
|
-
|
1305
|
+
if (childTags) {
|
1306
|
+
var child = getTag(dom)
|
594
1307
|
|
595
|
-
|
596
|
-
|
1308
|
+
if (child && !dom.isLoop)
|
1309
|
+
childTags.push(initChildTag(child, {root: dom, parent: tag}, dom.innerHTML, tag))
|
597
1310
|
}
|
598
1311
|
|
599
|
-
if (!dom.isLoop)
|
1312
|
+
if (!dom.isLoop || forceParsingNamed)
|
600
1313
|
setNamed(dom, tag, [])
|
601
1314
|
}
|
602
1315
|
|
@@ -607,14 +1320,14 @@ function parseNamedElements(root, tag, childTags) {
|
|
607
1320
|
function parseExpressions(root, tag, expressions) {
|
608
1321
|
|
609
1322
|
function addExpr(dom, val, extra) {
|
610
|
-
if (
|
611
|
-
|
612
|
-
expressions.push(extend(expr, extra))
|
1323
|
+
if (tmpl.hasExpr(val)) {
|
1324
|
+
expressions.push(extend({ dom: dom, expr: val }, extra))
|
613
1325
|
}
|
614
1326
|
}
|
615
1327
|
|
616
1328
|
walk(root, function(dom) {
|
617
|
-
var type = dom.nodeType
|
1329
|
+
var type = dom.nodeType,
|
1330
|
+
attr
|
618
1331
|
|
619
1332
|
// text node
|
620
1333
|
if (type == 3 && dom.parentNode.tagName != 'STYLE') addExpr(dom, dom.nodeValue)
|
@@ -623,7 +1336,7 @@ function parseExpressions(root, tag, expressions) {
|
|
623
1336
|
/* element */
|
624
1337
|
|
625
1338
|
// loop
|
626
|
-
|
1339
|
+
attr = getAttr(dom, 'each')
|
627
1340
|
|
628
1341
|
if (attr) { _each(dom, tag, attr); return false }
|
629
1342
|
|
@@ -646,23 +1359,21 @@ function parseExpressions(root, tag, expressions) {
|
|
646
1359
|
function Tag(impl, conf, innerHTML) {
|
647
1360
|
|
648
1361
|
var self = riot.observable(this),
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
if (fn && root._tag)
|
664
|
-
root._tag.unmount(true)
|
665
|
-
}
|
1362
|
+
opts = inherit(conf.opts) || {},
|
1363
|
+
parent = conf.parent,
|
1364
|
+
isLoop = conf.isLoop,
|
1365
|
+
hasImpl = conf.hasImpl,
|
1366
|
+
item = cleanUpData(conf.item),
|
1367
|
+
expressions = [],
|
1368
|
+
childTags = [],
|
1369
|
+
root = conf.root,
|
1370
|
+
fn = impl.fn,
|
1371
|
+
tagName = root.tagName.toLowerCase(),
|
1372
|
+
attr = {},
|
1373
|
+
propsInSyncWithParent = [],
|
1374
|
+
dom
|
1375
|
+
|
1376
|
+
if (fn && root._tag) root._tag.unmount(true)
|
666
1377
|
|
667
1378
|
// not yet mounted
|
668
1379
|
this.isMounted = false
|
@@ -674,7 +1385,7 @@ function Tag(impl, conf, innerHTML) {
|
|
674
1385
|
|
675
1386
|
// create a unique id to this tag
|
676
1387
|
// it could be handy to use it also to improve the virtual dom rendering speed
|
677
|
-
this
|
1388
|
+
defineProperty(this, '_riot_id', ++__uid) // base 1 allows test !t._riot_id
|
678
1389
|
|
679
1390
|
extend(this, { parent: parent, root: root, opts: opts, tags: {} }, item)
|
680
1391
|
|
@@ -682,12 +1393,10 @@ function Tag(impl, conf, innerHTML) {
|
|
682
1393
|
each(root.attributes, function(el) {
|
683
1394
|
var val = el.value
|
684
1395
|
// remember attributes with expressions only
|
685
|
-
if (
|
1396
|
+
if (tmpl.hasExpr(val)) attr[el.name] = val
|
686
1397
|
})
|
687
1398
|
|
688
|
-
|
689
|
-
// replace all the yield tags with the tag inner html
|
690
|
-
dom.innerHTML = replaceYield(dom.innerHTML, innerHTML)
|
1399
|
+
dom = mkdom(impl.tmpl, innerHTML)
|
691
1400
|
|
692
1401
|
// options
|
693
1402
|
function updateOpts() {
|
@@ -695,17 +1404,18 @@ function Tag(impl, conf, innerHTML) {
|
|
695
1404
|
|
696
1405
|
// update opts from current DOM attributes
|
697
1406
|
each(root.attributes, function(el) {
|
698
|
-
|
1407
|
+
var val = el.value
|
1408
|
+
opts[toCamel(el.name)] = tmpl.hasExpr(val) ? tmpl(val, ctx) : val
|
699
1409
|
})
|
700
1410
|
// recover those with expressions
|
701
1411
|
each(Object.keys(attr), function(name) {
|
702
|
-
opts[name] = tmpl(attr[name], ctx)
|
1412
|
+
opts[toCamel(name)] = tmpl(attr[name], ctx)
|
703
1413
|
})
|
704
1414
|
}
|
705
1415
|
|
706
1416
|
function normalizeData(data) {
|
707
1417
|
for (var key in item) {
|
708
|
-
if (typeof self[key] !== T_UNDEF)
|
1418
|
+
if (typeof self[key] !== T_UNDEF && isWritable(self, key))
|
709
1419
|
self[key] = data[key]
|
710
1420
|
}
|
711
1421
|
}
|
@@ -714,7 +1424,7 @@ function Tag(impl, conf, innerHTML) {
|
|
714
1424
|
if (!self.parent || !isLoop) return
|
715
1425
|
each(Object.keys(self.parent), function(k) {
|
716
1426
|
// some properties must be always in sync with the parent tag
|
717
|
-
var mustSync =
|
1427
|
+
var mustSync = !contains(RESERVED_WORDS_BLACKLIST, k) && contains(propsInSyncWithParent, k)
|
718
1428
|
if (typeof self[k] === T_UNDEF || mustSync) {
|
719
1429
|
// track the property to keep in sync
|
720
1430
|
// so we can keep it updated
|
@@ -724,7 +1434,8 @@ function Tag(impl, conf, innerHTML) {
|
|
724
1434
|
})
|
725
1435
|
}
|
726
1436
|
|
727
|
-
this
|
1437
|
+
defineProperty(this, 'update', function(data) {
|
1438
|
+
|
728
1439
|
// make sure the data passed will not override
|
729
1440
|
// the component core methods
|
730
1441
|
data = cleanUpData(data)
|
@@ -739,23 +1450,44 @@ function Tag(impl, conf, innerHTML) {
|
|
739
1450
|
updateOpts()
|
740
1451
|
self.trigger('update', data)
|
741
1452
|
update(expressions, self)
|
742
|
-
|
743
|
-
|
1453
|
+
// the updated event will be triggered
|
1454
|
+
// once the DOM will be ready and all the reflows are completed
|
1455
|
+
// this is useful if you want to get the "real" root properties
|
1456
|
+
// 4 ex: root.offsetWidth ...
|
1457
|
+
rAF(function() { self.trigger('updated') })
|
1458
|
+
return this
|
1459
|
+
})
|
744
1460
|
|
745
|
-
this
|
1461
|
+
defineProperty(this, 'mixin', function() {
|
746
1462
|
each(arguments, function(mix) {
|
1463
|
+
var instance
|
1464
|
+
|
747
1465
|
mix = typeof mix === T_STRING ? riot.mixin(mix) : mix
|
748
|
-
|
1466
|
+
|
1467
|
+
// check if the mixin is a function
|
1468
|
+
if (isFunction(mix)) {
|
1469
|
+
// create the new mixin instance
|
1470
|
+
instance = new mix()
|
1471
|
+
// save the prototype to loop it afterwards
|
1472
|
+
mix = mix.prototype
|
1473
|
+
} else instance = mix
|
1474
|
+
|
1475
|
+
// loop the keys in the function prototype or the all object keys
|
1476
|
+
each(Object.getOwnPropertyNames(mix), function(key) {
|
749
1477
|
// bind methods to self
|
750
1478
|
if (key != 'init')
|
751
|
-
self[key] = isFunction(
|
1479
|
+
self[key] = isFunction(instance[key]) ?
|
1480
|
+
instance[key].bind(self) :
|
1481
|
+
instance[key]
|
752
1482
|
})
|
1483
|
+
|
753
1484
|
// init method will be called automatically
|
754
|
-
if (
|
1485
|
+
if (instance.init) instance.init.bind(self)()
|
755
1486
|
})
|
756
|
-
|
1487
|
+
return this
|
1488
|
+
})
|
757
1489
|
|
758
|
-
this
|
1490
|
+
defineProperty(this, 'mount', function() {
|
759
1491
|
|
760
1492
|
updateOpts()
|
761
1493
|
|
@@ -771,14 +1503,14 @@ function Tag(impl, conf, innerHTML) {
|
|
771
1503
|
// update the root adding custom attributes coming from the compiler
|
772
1504
|
// it fixes also #1087
|
773
1505
|
if (impl.attrs || hasImpl) {
|
774
|
-
walkAttributes(impl.attrs, function (k, v) { root
|
1506
|
+
walkAttributes(impl.attrs, function (k, v) { setAttr(root, k, v) })
|
775
1507
|
parseExpressions(self.root, self, expressions)
|
776
1508
|
}
|
777
1509
|
|
778
1510
|
if (!self.parent || isLoop) self.update(item)
|
779
1511
|
|
780
1512
|
// internal use only, fixes #403
|
781
|
-
self.trigger('
|
1513
|
+
self.trigger('before-mount')
|
782
1514
|
|
783
1515
|
if (isLoop && !hasImpl) {
|
784
1516
|
// update the root attribute for the looped elements
|
@@ -788,6 +1520,12 @@ function Tag(impl, conf, innerHTML) {
|
|
788
1520
|
while (dom.firstChild) root.appendChild(dom.firstChild)
|
789
1521
|
if (root.stub) self.root = root = parent.root
|
790
1522
|
}
|
1523
|
+
|
1524
|
+
// parse the named dom nodes in the looped child
|
1525
|
+
// adding them to the parent as well
|
1526
|
+
if (isLoop)
|
1527
|
+
parseNamedElements(self.root, self.parent, null, true)
|
1528
|
+
|
791
1529
|
// if it's not a child tag we can trigger its mount event
|
792
1530
|
if (!self.parent || self.parent.isMounted) {
|
793
1531
|
self.isMounted = true
|
@@ -802,13 +1540,26 @@ function Tag(impl, conf, innerHTML) {
|
|
802
1540
|
self.trigger('mount')
|
803
1541
|
}
|
804
1542
|
})
|
805
|
-
}
|
1543
|
+
})
|
806
1544
|
|
807
1545
|
|
808
|
-
this
|
1546
|
+
defineProperty(this, 'unmount', function(keepRootTag) {
|
809
1547
|
var el = root,
|
810
|
-
|
811
|
-
|
1548
|
+
p = el.parentNode,
|
1549
|
+
ptag,
|
1550
|
+
tagIndex = __virtualDom.indexOf(self)
|
1551
|
+
|
1552
|
+
self.trigger('before-unmount')
|
1553
|
+
|
1554
|
+
// remove this tag instance from the global virtualDom variable
|
1555
|
+
if (~tagIndex)
|
1556
|
+
__virtualDom.splice(tagIndex, 1)
|
1557
|
+
|
1558
|
+
if (this._virts) {
|
1559
|
+
each(this._virts, function(v) {
|
1560
|
+
if (v.parentNode) v.parentNode.removeChild(v)
|
1561
|
+
})
|
1562
|
+
}
|
812
1563
|
|
813
1564
|
if (p) {
|
814
1565
|
|
@@ -819,7 +1570,7 @@ function Tag(impl, conf, innerHTML) {
|
|
819
1570
|
// remove this element form the array
|
820
1571
|
if (isArray(ptag.tags[tagName]))
|
821
1572
|
each(ptag.tags[tagName], function(tag, i) {
|
822
|
-
if (tag.
|
1573
|
+
if (tag._riot_id == self._riot_id)
|
823
1574
|
ptag.tags[tagName].splice(i, 1)
|
824
1575
|
})
|
825
1576
|
else
|
@@ -834,17 +1585,17 @@ function Tag(impl, conf, innerHTML) {
|
|
834
1585
|
p.removeChild(el)
|
835
1586
|
else
|
836
1587
|
// the riot-tag attribute isn't needed anymore, remove it
|
837
|
-
p
|
1588
|
+
remAttr(p, 'riot-tag')
|
838
1589
|
}
|
839
1590
|
|
840
1591
|
|
841
1592
|
self.trigger('unmount')
|
842
1593
|
toggle()
|
843
1594
|
self.off('*')
|
844
|
-
|
845
|
-
root._tag
|
1595
|
+
self.isMounted = false
|
1596
|
+
delete root._tag
|
846
1597
|
|
847
|
-
}
|
1598
|
+
})
|
848
1599
|
|
849
1600
|
function toggle(isMount) {
|
850
1601
|
|
@@ -852,46 +1603,48 @@ function Tag(impl, conf, innerHTML) {
|
|
852
1603
|
each(childTags, function(child) { child[isMount ? 'mount' : 'unmount']() })
|
853
1604
|
|
854
1605
|
// listen/unlisten parent (events flow one way from parent to children)
|
855
|
-
if (parent)
|
856
|
-
|
1606
|
+
if (!parent) return
|
1607
|
+
var evt = isMount ? 'on' : 'off'
|
857
1608
|
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
}
|
1609
|
+
// the loop tags will be always in sync with the parent automatically
|
1610
|
+
if (isLoop)
|
1611
|
+
parent[evt]('unmount', self.unmount)
|
1612
|
+
else
|
1613
|
+
parent[evt]('update', self.update)[evt]('unmount', self.unmount)
|
864
1614
|
}
|
865
1615
|
|
866
1616
|
// named elements available for fn
|
867
1617
|
parseNamedElements(dom, this, childTags)
|
868
1618
|
|
869
|
-
|
870
1619
|
}
|
871
|
-
|
1620
|
+
/**
|
1621
|
+
* Attach an event to a DOM node
|
1622
|
+
* @param { String } name - event name
|
1623
|
+
* @param { Function } handler - event callback
|
1624
|
+
* @param { Object } dom - dom node
|
1625
|
+
* @param { Tag } tag - tag instance
|
1626
|
+
*/
|
872
1627
|
function setEventHandler(name, handler, dom, tag) {
|
873
1628
|
|
874
1629
|
dom[name] = function(e) {
|
875
1630
|
|
876
|
-
var
|
877
|
-
|
878
|
-
|
1631
|
+
var ptag = tag._parent,
|
1632
|
+
item = tag._item,
|
1633
|
+
el
|
879
1634
|
|
880
1635
|
if (!item)
|
881
1636
|
while (ptag && !item) {
|
882
1637
|
item = ptag._item
|
883
|
-
ptag = ptag.
|
1638
|
+
ptag = ptag._parent
|
884
1639
|
}
|
885
1640
|
|
886
1641
|
// cross browser event fix
|
887
1642
|
e = e || window.event
|
888
1643
|
|
889
|
-
//
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
if (!e.which) e.which = e.charCode || e.keyCode
|
894
|
-
} catch (ignored) { /**/ }
|
1644
|
+
// override the event properties
|
1645
|
+
if (isWritable(e, 'currentTarget')) e.currentTarget = dom
|
1646
|
+
if (isWritable(e, 'target')) e.target = e.srcElement
|
1647
|
+
if (isWritable(e, 'which')) e.which = e.charCode || e.keyCode
|
895
1648
|
|
896
1649
|
e.item = item
|
897
1650
|
|
@@ -910,22 +1663,32 @@ function setEventHandler(name, handler, dom, tag) {
|
|
910
1663
|
|
911
1664
|
}
|
912
1665
|
|
913
|
-
|
1666
|
+
|
1667
|
+
/**
|
1668
|
+
* Insert a DOM node replacing another one (used by if- attribute)
|
1669
|
+
* @param { Object } root - parent node
|
1670
|
+
* @param { Object } node - node replaced
|
1671
|
+
* @param { Object } before - node added
|
1672
|
+
*/
|
914
1673
|
function insertTo(root, node, before) {
|
915
|
-
if (root)
|
916
|
-
|
917
|
-
|
918
|
-
}
|
1674
|
+
if (!root) return
|
1675
|
+
root.insertBefore(before, node)
|
1676
|
+
root.removeChild(node)
|
919
1677
|
}
|
920
1678
|
|
1679
|
+
/**
|
1680
|
+
* Update the expressions in a Tag instance
|
1681
|
+
* @param { Array } expressions - expression that must be re evaluated
|
1682
|
+
* @param { Tag } tag - tag instance
|
1683
|
+
*/
|
921
1684
|
function update(expressions, tag) {
|
922
1685
|
|
923
1686
|
each(expressions, function(expr, i) {
|
924
1687
|
|
925
1688
|
var dom = expr.dom,
|
926
|
-
|
927
|
-
|
928
|
-
|
1689
|
+
attrName = expr.attr,
|
1690
|
+
value = tmpl(expr.expr, tag),
|
1691
|
+
parent = expr.dom.parentNode
|
929
1692
|
|
930
1693
|
if (expr.bool)
|
931
1694
|
value = value ? attrName : false
|
@@ -934,7 +1697,11 @@ function update(expressions, tag) {
|
|
934
1697
|
|
935
1698
|
// leave out riot- prefixes from strings inside textarea
|
936
1699
|
// fix #815: any value -> string
|
937
|
-
if (parent && parent.tagName == 'TEXTAREA')
|
1700
|
+
if (parent && parent.tagName == 'TEXTAREA') {
|
1701
|
+
value = ('' + value).replace(/riot-/g, '')
|
1702
|
+
// change textarea's value
|
1703
|
+
parent.value = value
|
1704
|
+
}
|
938
1705
|
|
939
1706
|
// no change
|
940
1707
|
if (expr.value === value) return
|
@@ -955,8 +1722,8 @@ function update(expressions, tag) {
|
|
955
1722
|
// if- conditional
|
956
1723
|
} else if (attrName == 'if') {
|
957
1724
|
var stub = expr.stub,
|
958
|
-
|
959
|
-
|
1725
|
+
add = function() { insertTo(stub.parentNode, stub, dom) },
|
1726
|
+
remove = function() { insertTo(dom.parentNode, dom, stub) }
|
960
1727
|
|
961
1728
|
// add to DOM
|
962
1729
|
if (value) {
|
@@ -967,7 +1734,8 @@ function update(expressions, tag) {
|
|
967
1734
|
// maybe we can optimize this avoiding to mount the tag at all
|
968
1735
|
if (!isInStub(dom)) {
|
969
1736
|
walk(dom, function(el) {
|
970
|
-
if (el._tag && !el._tag.isMounted)
|
1737
|
+
if (el._tag && !el._tag.isMounted)
|
1738
|
+
el._tag.isMounted = !!el._tag.trigger('mount')
|
971
1739
|
})
|
972
1740
|
}
|
973
1741
|
}
|
@@ -977,9 +1745,8 @@ function update(expressions, tag) {
|
|
977
1745
|
// if the parentNode is defined we can easily replace the tag
|
978
1746
|
if (dom.parentNode)
|
979
1747
|
remove()
|
980
|
-
else
|
981
1748
|
// otherwise we need to wait the updated event
|
982
|
-
|
1749
|
+
else (tag.parent || tag).one('updated', remove)
|
983
1750
|
|
984
1751
|
dom.inStub = true
|
985
1752
|
}
|
@@ -995,7 +1762,7 @@ function update(expressions, tag) {
|
|
995
1762
|
// <img src="{ expr }">
|
996
1763
|
} else if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
|
997
1764
|
if (value)
|
998
|
-
dom
|
1765
|
+
setAttr(dom, attrName.slice(RIOT_PREFIX.length), value)
|
999
1766
|
|
1000
1767
|
} else {
|
1001
1768
|
if (expr.bool) {
|
@@ -1003,65 +1770,168 @@ function update(expressions, tag) {
|
|
1003
1770
|
if (!value) return
|
1004
1771
|
}
|
1005
1772
|
|
1006
|
-
if (typeof value !== T_OBJECT)
|
1773
|
+
if (value === 0 || value && typeof value !== T_OBJECT)
|
1774
|
+
setAttr(dom, attrName, value)
|
1007
1775
|
|
1008
1776
|
}
|
1009
1777
|
|
1010
1778
|
})
|
1011
1779
|
|
1012
1780
|
}
|
1781
|
+
/**
|
1782
|
+
* Specialized function for looping an array-like collection with `each={}`
|
1783
|
+
* @param { Array } els - collection of items
|
1784
|
+
* @param {Function} fn - callback function
|
1785
|
+
* @returns { Array } the array looped
|
1786
|
+
*/
|
1013
1787
|
function each(els, fn) {
|
1014
|
-
|
1788
|
+
var len = els ? els.length : 0
|
1789
|
+
|
1790
|
+
for (var i = 0, el; i < len; i++) {
|
1015
1791
|
el = els[i]
|
1016
|
-
// return false ->
|
1792
|
+
// return false -> current item was removed by fn during the loop
|
1017
1793
|
if (el != null && fn(el, i) === false) i--
|
1018
1794
|
}
|
1019
1795
|
return els
|
1020
1796
|
}
|
1021
1797
|
|
1798
|
+
/**
|
1799
|
+
* Detect if the argument passed is a function
|
1800
|
+
* @param { * } v - whatever you want to pass to this function
|
1801
|
+
* @returns { Boolean } -
|
1802
|
+
*/
|
1022
1803
|
function isFunction(v) {
|
1023
1804
|
return typeof v === T_FUNCTION || false // avoid IE problems
|
1024
1805
|
}
|
1025
1806
|
|
1807
|
+
/**
|
1808
|
+
* Remove any DOM attribute from a node
|
1809
|
+
* @param { Object } dom - DOM node we want to update
|
1810
|
+
* @param { String } name - name of the property we want to remove
|
1811
|
+
*/
|
1026
1812
|
function remAttr(dom, name) {
|
1027
1813
|
dom.removeAttribute(name)
|
1028
1814
|
}
|
1029
1815
|
|
1030
|
-
|
1031
|
-
|
1816
|
+
/**
|
1817
|
+
* Convert a string containing dashes to camel case
|
1818
|
+
* @param { String } string - input string
|
1819
|
+
* @returns { String } my-string -> myString
|
1820
|
+
*/
|
1821
|
+
function toCamel(string) {
|
1822
|
+
return string.replace(/-(\w)/g, function(_, c) {
|
1823
|
+
return c.toUpperCase()
|
1824
|
+
})
|
1032
1825
|
}
|
1033
1826
|
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1827
|
+
/**
|
1828
|
+
* Get the value of any DOM attribute on a node
|
1829
|
+
* @param { Object } dom - DOM node we want to parse
|
1830
|
+
* @param { String } name - name of the attribute we want to get
|
1831
|
+
* @returns { String | undefined } name of the node attribute whether it exists
|
1832
|
+
*/
|
1833
|
+
function getAttr(dom, name) {
|
1834
|
+
return dom.getAttribute(name)
|
1835
|
+
}
|
1039
1836
|
|
1040
|
-
|
1041
|
-
|
1837
|
+
/**
|
1838
|
+
* Set any DOM attribute
|
1839
|
+
* @param { Object } dom - DOM node we want to update
|
1840
|
+
* @param { String } name - name of the property we want to set
|
1841
|
+
* @param { String } val - value of the property we want to set
|
1842
|
+
*/
|
1843
|
+
function setAttr(dom, name, val) {
|
1844
|
+
dom.setAttribute(name, val)
|
1845
|
+
}
|
1042
1846
|
|
1043
|
-
|
1847
|
+
/**
|
1848
|
+
* Detect the tag implementation by a DOM node
|
1849
|
+
* @param { Object } dom - DOM node we need to parse to get its tag implementation
|
1850
|
+
* @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
|
1851
|
+
*/
|
1852
|
+
function getTag(dom) {
|
1853
|
+
return dom.tagName && __tagImpl[getAttr(dom, RIOT_TAG) || dom.tagName.toLowerCase()]
|
1854
|
+
}
|
1855
|
+
/**
|
1856
|
+
* Add a child tag to its parent into the `tags` object
|
1857
|
+
* @param { Object } tag - child tag instance
|
1858
|
+
* @param { String } tagName - key where the new tag will be stored
|
1859
|
+
* @param { Object } parent - tag instance where the new child tag will be included
|
1860
|
+
*/
|
1861
|
+
function addChildTag(tag, tagName, parent) {
|
1862
|
+
var cachedTag = parent.tags[tagName]
|
1044
1863
|
|
1045
1864
|
// if there are multiple children tags having the same name
|
1046
1865
|
if (cachedTag) {
|
1047
1866
|
// if the parent tags property is not yet an array
|
1048
1867
|
// create it adding the first cached tag
|
1049
1868
|
if (!isArray(cachedTag))
|
1050
|
-
|
1869
|
+
// don't add the same tag twice
|
1870
|
+
if (cachedTag !== tag)
|
1871
|
+
parent.tags[tagName] = [cachedTag]
|
1051
1872
|
// add the new nested tag to the array
|
1052
|
-
if (
|
1053
|
-
|
1873
|
+
if (!contains(parent.tags[tagName], tag))
|
1874
|
+
parent.tags[tagName].push(tag)
|
1054
1875
|
} else {
|
1055
|
-
|
1876
|
+
parent.tags[tagName] = tag
|
1056
1877
|
}
|
1878
|
+
}
|
1879
|
+
|
1880
|
+
/**
|
1881
|
+
* Move the position of a custom tag in its parent tag
|
1882
|
+
* @param { Object } tag - child tag instance
|
1883
|
+
* @param { String } tagName - key where the tag was stored
|
1884
|
+
* @param { Number } newPos - index where the new tag will be stored
|
1885
|
+
*/
|
1886
|
+
function moveChildTag(tag, tagName, newPos) {
|
1887
|
+
var parent = tag.parent,
|
1888
|
+
tags
|
1889
|
+
// no parent no move
|
1890
|
+
if (!parent) return
|
1891
|
+
|
1892
|
+
tags = parent.tags[tagName]
|
1893
|
+
|
1894
|
+
if (isArray(tags))
|
1895
|
+
tags.splice(newPos, 0, tags.splice(tags.indexOf(tag), 1)[0])
|
1896
|
+
else addChildTag(tag, tagName, parent)
|
1897
|
+
}
|
1057
1898
|
|
1899
|
+
/**
|
1900
|
+
* Create a new child tag including it correctly into its parent
|
1901
|
+
* @param { Object } child - child tag implementation
|
1902
|
+
* @param { Object } opts - tag options containing the DOM node where the tag will be mounted
|
1903
|
+
* @param { String } innerHTML - inner html of the child node
|
1904
|
+
* @param { Object } parent - instance of the parent tag including the child custom tag
|
1905
|
+
* @returns { Object } instance of the new child tag just created
|
1906
|
+
*/
|
1907
|
+
function initChildTag(child, opts, innerHTML, parent) {
|
1908
|
+
var tag = new Tag(child, opts, innerHTML),
|
1909
|
+
tagName = getTagName(opts.root),
|
1910
|
+
ptag = getImmediateCustomParentTag(parent)
|
1911
|
+
// fix for the parent attribute in the looped elements
|
1912
|
+
tag.parent = ptag
|
1913
|
+
// store the real parent tag
|
1914
|
+
// in some cases this could be different from the custom parent tag
|
1915
|
+
// for example in nested loops
|
1916
|
+
tag._parent = parent
|
1917
|
+
|
1918
|
+
// add this tag to the custom parent tag
|
1919
|
+
addChildTag(tag, tagName, ptag)
|
1920
|
+
// and also to the real parent tag
|
1921
|
+
if (ptag !== parent)
|
1922
|
+
addChildTag(tag, tagName, parent)
|
1058
1923
|
// empty the child node once we got its template
|
1059
1924
|
// to avoid that its children get compiled multiple times
|
1060
|
-
|
1925
|
+
opts.root.innerHTML = ''
|
1061
1926
|
|
1062
1927
|
return tag
|
1063
1928
|
}
|
1064
1929
|
|
1930
|
+
/**
|
1931
|
+
* Loop backward all the parents tree to detect the first custom parent tag
|
1932
|
+
* @param { Object } tag - a Tag instance
|
1933
|
+
* @returns { Object } the instance of the first custom parent tag found
|
1934
|
+
*/
|
1065
1935
|
function getImmediateCustomParentTag(tag) {
|
1066
1936
|
var ptag = tag
|
1067
1937
|
while (!getTag(ptag.root)) {
|
@@ -1071,40 +1941,117 @@ function getImmediateCustomParentTag(tag) {
|
|
1071
1941
|
return ptag
|
1072
1942
|
}
|
1073
1943
|
|
1944
|
+
/**
|
1945
|
+
* Helper function to set an immutable property
|
1946
|
+
* @param { Object } el - object where the new property will be set
|
1947
|
+
* @param { String } key - object key where the new property will be stored
|
1948
|
+
* @param { * } value - value of the new property
|
1949
|
+
* @param { Object } options - set the propery overriding the default options
|
1950
|
+
* @returns { Object } - the initial object
|
1951
|
+
*/
|
1952
|
+
function defineProperty(el, key, value, options) {
|
1953
|
+
Object.defineProperty(el, key, extend({
|
1954
|
+
value: value,
|
1955
|
+
enumerable: false,
|
1956
|
+
writable: false,
|
1957
|
+
configurable: false
|
1958
|
+
}, options))
|
1959
|
+
return el
|
1960
|
+
}
|
1961
|
+
|
1962
|
+
/**
|
1963
|
+
* Get the tag name of any DOM node
|
1964
|
+
* @param { Object } dom - DOM node we want to parse
|
1965
|
+
* @returns { String } name to identify this dom node in riot
|
1966
|
+
*/
|
1074
1967
|
function getTagName(dom) {
|
1075
1968
|
var child = getTag(dom),
|
1076
|
-
namedTag = dom
|
1077
|
-
tagName = namedTag &&
|
1969
|
+
namedTag = getAttr(dom, 'name'),
|
1970
|
+
tagName = namedTag && !tmpl.hasExpr(namedTag) ?
|
1971
|
+
namedTag :
|
1972
|
+
child ? child.name : dom.tagName.toLowerCase()
|
1078
1973
|
|
1079
1974
|
return tagName
|
1080
1975
|
}
|
1081
1976
|
|
1977
|
+
/**
|
1978
|
+
* Extend any object with other properties
|
1979
|
+
* @param { Object } src - source object
|
1980
|
+
* @returns { Object } the resulting extended object
|
1981
|
+
*
|
1982
|
+
* var obj = { foo: 'baz' }
|
1983
|
+
* extend(obj, {bar: 'bar', foo: 'bar'})
|
1984
|
+
* console.log(obj) => {bar: 'bar', foo: 'bar'}
|
1985
|
+
*
|
1986
|
+
*/
|
1082
1987
|
function extend(src) {
|
1083
1988
|
var obj, args = arguments
|
1084
1989
|
for (var i = 1; i < args.length; ++i) {
|
1085
|
-
if (
|
1086
|
-
for (var key in obj) {
|
1087
|
-
|
1990
|
+
if (obj = args[i]) {
|
1991
|
+
for (var key in obj) {
|
1992
|
+
// check if this property of the source object could be overridden
|
1993
|
+
if (isWritable(src, key))
|
1994
|
+
src[key] = obj[key]
|
1088
1995
|
}
|
1089
1996
|
}
|
1090
1997
|
}
|
1091
1998
|
return src
|
1092
1999
|
}
|
1093
2000
|
|
1094
|
-
|
2001
|
+
/**
|
2002
|
+
* Check whether an array contains an item
|
2003
|
+
* @param { Array } arr - target array
|
2004
|
+
* @param { * } item - item to test
|
2005
|
+
* @returns { Boolean } Does 'arr' contain 'item'?
|
2006
|
+
*/
|
2007
|
+
function contains(arr, item) {
|
2008
|
+
return ~arr.indexOf(item)
|
2009
|
+
}
|
2010
|
+
|
2011
|
+
/**
|
2012
|
+
* Check whether an object is a kind of array
|
2013
|
+
* @param { * } a - anything
|
2014
|
+
* @returns {Boolean} is 'a' an array?
|
2015
|
+
*/
|
2016
|
+
function isArray(a) { return Array.isArray(a) || a instanceof Array }
|
2017
|
+
|
2018
|
+
/**
|
2019
|
+
* Detect whether a property of an object could be overridden
|
2020
|
+
* @param { Object } obj - source object
|
2021
|
+
* @param { String } key - object property
|
2022
|
+
* @returns { Boolean } is this property writable?
|
2023
|
+
*/
|
2024
|
+
function isWritable(obj, key) {
|
2025
|
+
var props = Object.getOwnPropertyDescriptor(obj, key)
|
2026
|
+
return typeof obj[key] === T_UNDEF || props && props.writable
|
2027
|
+
}
|
2028
|
+
|
2029
|
+
|
2030
|
+
/**
|
2031
|
+
* With this function we avoid that the internal Tag methods get overridden
|
2032
|
+
* @param { Object } data - options we want to use to extend the tag instance
|
2033
|
+
* @returns { Object } clean object without containing the riot internal reserved words
|
2034
|
+
*/
|
1095
2035
|
function cleanUpData(data) {
|
1096
|
-
if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION))
|
2036
|
+
if (!(data instanceof Tag) && !(data && typeof data.trigger == T_FUNCTION))
|
2037
|
+
return data
|
1097
2038
|
|
1098
2039
|
var o = {}
|
1099
2040
|
for (var key in data) {
|
1100
|
-
if (
|
2041
|
+
if (!contains(RESERVED_WORDS_BLACKLIST, key))
|
1101
2042
|
o[key] = data[key]
|
1102
2043
|
}
|
1103
2044
|
return o
|
1104
2045
|
}
|
1105
2046
|
|
2047
|
+
/**
|
2048
|
+
* Walk down recursively all the children tags starting dom node
|
2049
|
+
* @param { Object } dom - starting node where we will start the recursion
|
2050
|
+
* @param { Function } fn - callback to transform the child node just found
|
2051
|
+
*/
|
1106
2052
|
function walk(dom, fn) {
|
1107
2053
|
if (dom) {
|
2054
|
+
// stop the recursion
|
1108
2055
|
if (fn(dom) === false) return
|
1109
2056
|
else {
|
1110
2057
|
dom = dom.firstChild
|
@@ -1117,16 +2064,25 @@ function walk(dom, fn) {
|
|
1117
2064
|
}
|
1118
2065
|
}
|
1119
2066
|
|
1120
|
-
|
2067
|
+
/**
|
2068
|
+
* Minimize risk: only zero or one _space_ between attr & value
|
2069
|
+
* @param { String } html - html string we want to parse
|
2070
|
+
* @param { Function } fn - callback function to apply on any attribute found
|
2071
|
+
*/
|
1121
2072
|
function walkAttributes(html, fn) {
|
1122
2073
|
var m,
|
1123
|
-
|
2074
|
+
re = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g
|
1124
2075
|
|
1125
|
-
while (
|
2076
|
+
while (m = re.exec(html)) {
|
1126
2077
|
fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
|
1127
2078
|
}
|
1128
2079
|
}
|
1129
2080
|
|
2081
|
+
/**
|
2082
|
+
* Check whether a DOM node is in stub mode, useful for the riot 'if' directive
|
2083
|
+
* @param { Object } dom - DOM node we want to parse
|
2084
|
+
* @returns { Boolean } -
|
2085
|
+
*/
|
1130
2086
|
function isInStub(dom) {
|
1131
2087
|
while (dom) {
|
1132
2088
|
if (dom.inStub) return true
|
@@ -1135,97 +2091,141 @@ function isInStub(dom) {
|
|
1135
2091
|
return false
|
1136
2092
|
}
|
1137
2093
|
|
2094
|
+
/**
|
2095
|
+
* Create a generic DOM node
|
2096
|
+
* @param { String } name - name of the DOM node we want to create
|
2097
|
+
* @returns { Object } DOM node just created
|
2098
|
+
*/
|
1138
2099
|
function mkEl(name) {
|
1139
2100
|
return document.createElement(name)
|
1140
2101
|
}
|
1141
2102
|
|
1142
|
-
|
1143
|
-
|
1144
|
-
}
|
1145
|
-
|
2103
|
+
/**
|
2104
|
+
* Shorter and fast way to select multiple nodes in the DOM
|
2105
|
+
* @param { String } selector - DOM selector
|
2106
|
+
* @param { Object } ctx - DOM node where the targets of our search will is located
|
2107
|
+
* @returns { Object } dom nodes found
|
2108
|
+
*/
|
1146
2109
|
function $$(selector, ctx) {
|
1147
2110
|
return (ctx || document).querySelectorAll(selector)
|
1148
2111
|
}
|
1149
2112
|
|
2113
|
+
/**
|
2114
|
+
* Shorter and fast way to select a single node in the DOM
|
2115
|
+
* @param { String } selector - unique dom selector
|
2116
|
+
* @param { Object } ctx - DOM node where the target of our search will is located
|
2117
|
+
* @returns { Object } dom node found
|
2118
|
+
*/
|
1150
2119
|
function $(selector, ctx) {
|
1151
2120
|
return (ctx || document).querySelector(selector)
|
1152
2121
|
}
|
1153
2122
|
|
2123
|
+
/**
|
2124
|
+
* Simple object prototypal inheritance
|
2125
|
+
* @param { Object } parent - parent object
|
2126
|
+
* @returns { Object } child instance
|
2127
|
+
*/
|
1154
2128
|
function inherit(parent) {
|
1155
2129
|
function Child() {}
|
1156
2130
|
Child.prototype = parent
|
1157
2131
|
return new Child()
|
1158
2132
|
}
|
1159
2133
|
|
2134
|
+
/**
|
2135
|
+
* Get the name property needed to identify a DOM node in riot
|
2136
|
+
* @param { Object } dom - DOM node we need to parse
|
2137
|
+
* @returns { String | undefined } give us back a string to identify this dom node
|
2138
|
+
*/
|
2139
|
+
function getNamedKey(dom) {
|
2140
|
+
return getAttr(dom, 'id') || getAttr(dom, 'name')
|
2141
|
+
}
|
2142
|
+
|
2143
|
+
/**
|
2144
|
+
* Set the named properties of a tag element
|
2145
|
+
* @param { Object } dom - DOM node we need to parse
|
2146
|
+
* @param { Object } parent - tag instance where the named dom element will be eventually added
|
2147
|
+
* @param { Array } keys - list of all the tag instance properties
|
2148
|
+
*/
|
1160
2149
|
function setNamed(dom, parent, keys) {
|
1161
|
-
|
1162
|
-
var
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
parent[
|
2150
|
+
// get the key value we want to add to the tag instance
|
2151
|
+
var key = getNamedKey(dom),
|
2152
|
+
isArr,
|
2153
|
+
// add the node detected to a tag instance using the named property
|
2154
|
+
add = function(value) {
|
2155
|
+
// avoid to override the tag properties already set
|
2156
|
+
if (contains(keys, key)) return
|
2157
|
+
// check whether this value is an array
|
2158
|
+
isArr = isArray(value)
|
2159
|
+
// if the key was never set
|
2160
|
+
if (!value)
|
2161
|
+
// set it once on the tag instance
|
2162
|
+
parent[key] = dom
|
2163
|
+
// if it was an array and not yet set
|
2164
|
+
else if (!isArr || isArr && !contains(value, dom)) {
|
2165
|
+
// add the dom node into the array
|
2166
|
+
if (isArr)
|
2167
|
+
value.push(dom)
|
2168
|
+
else
|
2169
|
+
parent[key] = [value, dom]
|
2170
|
+
}
|
1174
2171
|
}
|
1175
|
-
|
1176
|
-
|
2172
|
+
|
2173
|
+
// skip the elements with no named properties
|
2174
|
+
if (!key) return
|
2175
|
+
|
2176
|
+
// check whether this key has been already evaluated
|
2177
|
+
if (tmpl.hasExpr(key))
|
2178
|
+
// wait the first updated event only once
|
2179
|
+
parent.one('mount', function() {
|
2180
|
+
key = getNamedKey(dom)
|
2181
|
+
add(parent[key])
|
2182
|
+
})
|
2183
|
+
else
|
2184
|
+
add(parent[key])
|
2185
|
+
|
1177
2186
|
}
|
1178
2187
|
|
1179
|
-
|
2188
|
+
/**
|
2189
|
+
* Faster String startsWith alternative
|
2190
|
+
* @param { String } src - source string
|
2191
|
+
* @param { String } str - test string
|
2192
|
+
* @returns { Boolean } -
|
2193
|
+
*/
|
1180
2194
|
function startsWith(src, str) {
|
1181
2195
|
return src.slice(0, str.length) === str
|
1182
2196
|
}
|
1183
2197
|
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
*/
|
1188
|
-
|
1189
|
-
var
|
1190
|
-
|
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
|
2198
|
+
/**
|
2199
|
+
* requestAnimationFrame function
|
2200
|
+
* Adapted from https://gist.github.com/paulirish/1579671, license MIT
|
2201
|
+
*/
|
2202
|
+
var rAF = (function (w) {
|
2203
|
+
var raf = w.requestAnimationFrame ||
|
2204
|
+
w.mozRequestAnimationFrame || w.webkitRequestAnimationFrame
|
1208
2205
|
|
1209
|
-
if (!
|
1210
|
-
|
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)
|
2206
|
+
if (!raf || /iP(ad|hone|od).*OS 6/.test(w.navigator.userAgent)) { // buggy iOS6
|
2207
|
+
var lastTime = 0
|
1218
2208
|
|
2209
|
+
raf = function (cb) {
|
2210
|
+
var nowtime = Date.now(), timeout = Math.max(16 - (nowtime - lastTime), 0)
|
2211
|
+
setTimeout(function () { cb(lastTime = nowtime + timeout) }, timeout)
|
1219
2212
|
}
|
2213
|
+
}
|
2214
|
+
return raf
|
1220
2215
|
|
1221
|
-
|
1222
|
-
|
1223
|
-
}
|
2216
|
+
})(window || {})
|
1224
2217
|
|
2218
|
+
/**
|
2219
|
+
* Mount a tag creating new Tag instance
|
2220
|
+
* @param { Object } root - dom node where the tag will be mounted
|
2221
|
+
* @param { String } tagName - name of the riot tag we want to mount
|
2222
|
+
* @param { Object } opts - options to pass to the Tag instance
|
2223
|
+
* @returns { Tag } a new Tag instance
|
2224
|
+
*/
|
1225
2225
|
function mountTo(root, tagName, opts) {
|
1226
|
-
var tag =
|
1227
|
-
|
1228
|
-
|
2226
|
+
var tag = __tagImpl[tagName],
|
2227
|
+
// cache the inner HTML to fix #855
|
2228
|
+
innerHTML = root._innerHTML = root._innerHTML || root.innerHTML
|
1229
2229
|
|
1230
2230
|
// clear the inner html
|
1231
2231
|
root.innerHTML = ''
|
@@ -1234,14 +2234,47 @@ function mountTo(root, tagName, opts) {
|
|
1234
2234
|
|
1235
2235
|
if (tag && tag.mount) {
|
1236
2236
|
tag.mount()
|
1237
|
-
virtualDom
|
1238
|
-
|
1239
|
-
virtualDom.splice(virtualDom.indexOf(tag), 1)
|
1240
|
-
})
|
2237
|
+
// add this tag to the virtualDom variable
|
2238
|
+
if (!contains(__virtualDom, tag)) __virtualDom.push(tag)
|
1241
2239
|
}
|
1242
2240
|
|
2241
|
+
return tag
|
1243
2242
|
}
|
2243
|
+
/**
|
2244
|
+
* Riot public api
|
2245
|
+
*/
|
2246
|
+
|
2247
|
+
// share methods for other riot parts, e.g. compiler
|
2248
|
+
riot.util = { brackets: brackets, tmpl: tmpl }
|
2249
|
+
|
2250
|
+
/**
|
2251
|
+
* Create a mixin that could be globally shared across all the tags
|
2252
|
+
*/
|
2253
|
+
riot.mixin = (function() {
|
2254
|
+
var mixins = {}
|
1244
2255
|
|
2256
|
+
/**
|
2257
|
+
* Create/Return a mixin by its name
|
2258
|
+
* @param { String } name - mixin name
|
2259
|
+
* @param { Object } mixin - mixin logic
|
2260
|
+
* @returns { Object } the mixin logic
|
2261
|
+
*/
|
2262
|
+
return function(name, mixin) {
|
2263
|
+
if (!mixin) return mixins[name]
|
2264
|
+
mixins[name] = mixin
|
2265
|
+
}
|
2266
|
+
|
2267
|
+
})()
|
2268
|
+
|
2269
|
+
/**
|
2270
|
+
* Create a new riot tag implementation
|
2271
|
+
* @param { String } name - name/id of the new riot tag
|
2272
|
+
* @param { String } html - tag template
|
2273
|
+
* @param { String } css - custom tag css
|
2274
|
+
* @param { String } attrs - root tag attributes
|
2275
|
+
* @param { Function } fn - user function
|
2276
|
+
* @returns { String } name/id of the tag just created
|
2277
|
+
*/
|
1245
2278
|
riot.tag = function(name, html, css, attrs, fn) {
|
1246
2279
|
if (isFunction(attrs)) {
|
1247
2280
|
fn = attrs
|
@@ -1252,51 +2285,78 @@ riot.tag = function(name, html, css, attrs, fn) {
|
|
1252
2285
|
}
|
1253
2286
|
if (css) {
|
1254
2287
|
if (isFunction(css)) fn = css
|
1255
|
-
else
|
2288
|
+
else styleManager.add(css)
|
1256
2289
|
}
|
1257
|
-
|
2290
|
+
__tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
|
2291
|
+
return name
|
2292
|
+
}
|
2293
|
+
|
2294
|
+
/**
|
2295
|
+
* Create a new riot tag implementation (for use by the compiler)
|
2296
|
+
* @param { String } name - name/id of the new riot tag
|
2297
|
+
* @param { String } html - tag template
|
2298
|
+
* @param { String } css - custom tag css
|
2299
|
+
* @param { String } attrs - root tag attributes
|
2300
|
+
* @param { Function } fn - user function
|
2301
|
+
* @param { string } [bpair] - brackets used in the compilation
|
2302
|
+
* @returns { String } name/id of the tag just created
|
2303
|
+
*/
|
2304
|
+
riot.tag2 = function(name, html, css, attrs, fn, bpair) {
|
2305
|
+
if (css) styleManager.add(css)
|
2306
|
+
//if (bpair) riot.settings.brackets = bpair
|
2307
|
+
__tagImpl[name] = { name: name, tmpl: html, attrs: attrs, fn: fn }
|
1258
2308
|
return name
|
1259
2309
|
}
|
1260
2310
|
|
2311
|
+
/**
|
2312
|
+
* Mount a tag using a specific tag implementation
|
2313
|
+
* @param { String } selector - tag DOM selector
|
2314
|
+
* @param { String } tagName - tag implementation name
|
2315
|
+
* @param { Object } opts - tag logic
|
2316
|
+
* @returns { Array } new tags instances
|
2317
|
+
*/
|
1261
2318
|
riot.mount = function(selector, tagName, opts) {
|
1262
2319
|
|
1263
2320
|
var els,
|
1264
|
-
|
1265
|
-
|
2321
|
+
allTags,
|
2322
|
+
tags = []
|
1266
2323
|
|
1267
2324
|
// helper functions
|
1268
2325
|
|
1269
2326
|
function addRiotTags(arr) {
|
1270
2327
|
var list = ''
|
1271
2328
|
each(arr, function (e) {
|
1272
|
-
|
2329
|
+
if (!/[^-\w]/.test(e))
|
2330
|
+
list += ',*[' + RIOT_TAG + '=' + e.trim() + ']'
|
1273
2331
|
})
|
1274
2332
|
return list
|
1275
2333
|
}
|
1276
2334
|
|
1277
2335
|
function selectAllTags() {
|
1278
|
-
var keys = Object.keys(
|
2336
|
+
var keys = Object.keys(__tagImpl)
|
1279
2337
|
return keys + addRiotTags(keys)
|
1280
2338
|
}
|
1281
2339
|
|
1282
2340
|
function pushTags(root) {
|
1283
2341
|
var last
|
2342
|
+
|
1284
2343
|
if (root.tagName) {
|
1285
|
-
if (tagName && (!(last = root
|
1286
|
-
root
|
2344
|
+
if (tagName && (!(last = getAttr(root, RIOT_TAG)) || last != tagName))
|
2345
|
+
setAttr(root, RIOT_TAG, tagName)
|
1287
2346
|
|
1288
|
-
var tag = mountTo(root,
|
1289
|
-
tagName || root.getAttribute(RIOT_TAG) || root.tagName.toLowerCase(), opts)
|
2347
|
+
var tag = mountTo(root, tagName || root.getAttribute(RIOT_TAG) || root.tagName.toLowerCase(), opts)
|
1290
2348
|
|
1291
2349
|
if (tag) tags.push(tag)
|
1292
|
-
}
|
1293
|
-
else if (root.length) {
|
2350
|
+
} else if (root.length)
|
1294
2351
|
each(root, pushTags) // assume nodeList
|
1295
|
-
|
2352
|
+
|
1296
2353
|
}
|
1297
2354
|
|
1298
2355
|
// ----- mount code -----
|
1299
2356
|
|
2357
|
+
// inject styles into DOM
|
2358
|
+
styleManager.inject()
|
2359
|
+
|
1300
2360
|
if (typeof tagName === T_OBJECT) {
|
1301
2361
|
opts = tagName
|
1302
2362
|
tagName = 0
|
@@ -1312,7 +2372,9 @@ riot.mount = function(selector, tagName, opts) {
|
|
1312
2372
|
// or just the ones named like the selector
|
1313
2373
|
selector += addRiotTags(selector.split(','))
|
1314
2374
|
|
1315
|
-
|
2375
|
+
// make sure to pass always a selector
|
2376
|
+
// to the querySelectorAll function
|
2377
|
+
els = selector ? $$(selector) : []
|
1316
2378
|
}
|
1317
2379
|
else
|
1318
2380
|
// probably you have passed already a tag or a NodeList
|
@@ -1345,25 +2407,26 @@ riot.mount = function(selector, tagName, opts) {
|
|
1345
2407
|
return tags
|
1346
2408
|
}
|
1347
2409
|
|
1348
|
-
|
2410
|
+
/**
|
2411
|
+
* Update all the tags instances created
|
2412
|
+
* @returns { Array } all the tags instances
|
2413
|
+
*/
|
1349
2414
|
riot.update = function() {
|
1350
|
-
return each(
|
2415
|
+
return each(__virtualDom, function(tag) {
|
1351
2416
|
tag.update()
|
1352
2417
|
})
|
1353
2418
|
}
|
1354
2419
|
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
riot.util = { brackets: brackets, tmpl: tmpl }
|
1360
|
-
|
2420
|
+
/**
|
2421
|
+
* Export the Tag constructor
|
2422
|
+
*/
|
2423
|
+
riot.Tag = Tag
|
1361
2424
|
// support CommonJS, AMD & browser
|
1362
2425
|
/* istanbul ignore next */
|
1363
2426
|
if (typeof exports === T_OBJECT)
|
1364
2427
|
module.exports = riot
|
1365
|
-
else if (typeof define ===
|
1366
|
-
define(function() { return
|
2428
|
+
else if (typeof define === T_FUNCTION && typeof define.amd !== T_UNDEF)
|
2429
|
+
define(function() { return riot })
|
1367
2430
|
else
|
1368
2431
|
window.riot = riot
|
1369
2432
|
|