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