bc-SmartTOC

可以为github、简书、cnblog等网站生成TOC

  1. // ==UserScript==
  2. // @name bc-SmartTOC
  3. // @name:zh-CN bc-SmartTOC
  4. // @icon data:image/png;base64,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
  5. // @namespace BC
  6. // @version 0.1
  7. // @description TOC can be generated for github、jianshu.com、cnblogs.com and other sites
  8. // @description:zh-cn 可以为github、简书、cnblog等网站生成TOC
  9. // @collaborator ChandlerVer5
  10. // @match http*://coursehunters.online/t/*
  11. // @match *://docs.spring.io/*
  12. // @match *flaviocopes.com/*
  13. // @match http*://*/book/*
  14. // @match http*://www.freecodecamp.org/news/*
  15. // @match http*://www.cnblogs.com/*/p/*
  16. // @match http*://*/doc*/*
  17. // @match http*://blog.csdn.net/*
  18. // @match http*://www.jianshu.com/p/*
  19. // @match http*://juejin.im/post/*
  20. // @match http*://sspai.com/*
  21. // @match http*://zhuanlan.zhihu.com/p/*
  22. // @match http*://mp.weixin.qq.com/s*
  23. // @match http*://cnodejs.org/topic/*
  24. // @match http*://div.io/topic/*
  25. // @match http*://www.runoob.com/*/*
  26. // @match http*://www.code4app.com/article/*
  27. // @match http*://www.zcfy.cc/article/*
  28. // @match http*://github.com/*
  29. // @match http*://gist.github.com/*
  30. // @grant none
  31. // ==/UserScript==
  32.  
  33. (function () {
  34. 'use strict';
  35.  
  36. function getRootWindow() {
  37. let w = window
  38. while (w !== w.parent) {
  39. w = w.parent
  40. }
  41. return w
  42. }
  43.  
  44. function getMaster(root) {
  45. const iframes = [].slice.apply(root.document.getElementsByTagName('iframe'))
  46.  
  47. if (iframes.length === 0) {
  48. return root
  49. } else {
  50. const largestChild = iframes
  51. .map(f => ({
  52. elem: f,
  53. area: f.offsetWidth * f.offsetHeight
  54. }))
  55. .sort((a, b) => b.area - a.area)[0]
  56. const html = root.document.documentElement
  57. return largestChild.area / (html.offsetWidth * html.offsetHeight) > 0.5
  58. ? largestChild.elem.contentWindow
  59. : root
  60. }
  61. }
  62.  
  63. function isMasterFrame(w) {
  64. const root = getRootWindow()
  65. const master = getMaster(root)
  66. return w === master
  67. }
  68.  
  69. var toastCSS = "#smarttoc-toast {\n all: initial;\n}\n\n#smarttoc-toast * {\n all: unset;\n}\n\n#smarttoc-toast {\n display: none;\n position: fixed;\n right: 0;\n top: 0;\n margin: 1em 2em;\n padding: 1em;\n z-index: 10000;\n box-sizing: border-box;\n background-color: #fff;\n border: 1px solid rgba(158, 158, 158, 0.22);\n color: gray;\n font-size: calc(12px + 0.15vw);\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-weight: normal;\n -webkit-font-smoothing: subpixel-antialiased;\n font-smoothing: subpixel-antialiased;\n transition: opacity 200ms ease-out, transform 200ms ease-out;\n}\n\n#smarttoc-toast.enter {\n display: block;\n opacity: 0.01;\n transform: translate3d(0, -2em, 0);\n}\n\n#smarttoc-toast.enter.enter-active {\n display: block;\n opacity: 1;\n transform: translate3d(0, 0, 0);\n}\n\n#smarttoc-toast.leave {\n display: block;\n opacity: 1;\n transform: translate3d(0, 0, 0);\n}\n\n#smarttoc-toast.leave.leave-active {\n display: block;\n opacity: 0.01;\n transform: translate3d(0, -2em, 0);\n}\n";
  70.  
  71. function log() {
  72. if (false) {}
  73. }
  74.  
  75. function draw(elem, color = 'red') {
  76. if (false && elem) {}
  77. }
  78.  
  79. function assert(condition, error) {
  80. if (!condition) {
  81. throw new Error(error)
  82. }
  83. }
  84.  
  85. // '12px' => 12
  86. const num = (size = '0') =>
  87. typeof size === 'number' ? size : +size.replace(/px/, '')
  88.  
  89. // '12px' <= 12
  90. const px = (size = 0) => num(size) + 'px'
  91.  
  92. function throttle(fn, delay) {
  93. if (delay) {
  94. let timer
  95. return function timerThrottled(...args) {
  96. clearTimeout(timer)
  97. timer = setTimeout(function() {
  98. fn(...args)
  99. }, delay)
  100. }
  101. } else {
  102. let request
  103. return function rafThrottled(...args) {
  104. cancelAnimationFrame(request)
  105. request = requestAnimationFrame(function() {
  106. fn(...args)
  107. })
  108. }
  109. }
  110. }
  111.  
  112. const safe = str => str.replace(/\s+/g, '-')
  113.  
  114. const unique = (function uniqueGenerator() {
  115. let set = new Set()
  116. return function unique(str) {
  117. let id = 1
  118. while (set.has(str)) {
  119. str = str.replace(/(\$\d+)?$/, '') + '$' + id
  120. id++
  121. }
  122. set.add(str)
  123. return str
  124. }
  125. })()
  126.  
  127. const getScroll = (elem, direction = 'top') => {
  128. if (elem === document.body) {
  129. return direction === 'top'
  130. ? document.documentElement.scrollTop || document.body.scrollTop
  131. : document.documentElement.scrollLeft || document.body.scrollLeft
  132. } else {
  133. return direction === 'top' ? elem.scrollTop : elem.scrollLeft
  134. }
  135. }
  136.  
  137. const setScroll = (elem, val, direction = 'top') => {
  138. if (elem === document.body) {
  139. if (direction === 'top') {
  140. document.documentElement.scrollTop = val
  141. window.scrollTo(window.scrollX, val)
  142. } else {
  143. document.documentElement.scrollLeft = val
  144. window.scrollTo(val, window.scrollY)
  145. }
  146. } else {
  147. if (direction === 'top') {
  148. elem.scrollTop = val
  149. } else {
  150. elem.scrollLeft = val
  151. }
  152. }
  153. }
  154.  
  155. const scrollTo = (function scrollToFactory() {
  156. let request
  157. const easeOutQuad = function(t, b, c, d) {
  158. t /= d
  159. return -c * t * (t - 2) + b
  160. }
  161. return function scrollTo({
  162. targetElem,
  163. scrollElem,
  164. topMargin = 0,
  165. maxDuration = 300,
  166. easeFn,
  167. callback
  168. }) {
  169. cancelAnimationFrame(request)
  170. let rect = targetElem.getBoundingClientRect()
  171. let endScrollTop =
  172. rect.top -
  173. (scrollElem === document.body
  174. ? 0
  175. : scrollElem.getBoundingClientRect().top) +
  176. getScroll(scrollElem) -
  177. topMargin
  178. let startScrollTop = getScroll(scrollElem)
  179. let distance = endScrollTop - startScrollTop
  180. let startTime
  181. let ease = easeFn || easeOutQuad
  182. let distanceRatio = Math.min(Math.abs(distance) / 10000, 1)
  183. let duration = Math.max(
  184. maxDuration * distanceRatio * (2 - distanceRatio),
  185. 10
  186. )
  187. if (!maxDuration) {
  188. setScroll(scrollElem, endScrollTop)
  189. if (callback) {
  190. callback()
  191. }
  192. } else {
  193. requestAnimationFrame(update)
  194. }
  195.  
  196. function update(timestamp) {
  197. if (!startTime) {
  198. startTime = timestamp
  199. }
  200. let progress = (timestamp - startTime) / duration
  201. if (progress < 1) {
  202. setScroll(
  203. scrollElem,
  204. ease(timestamp - startTime, startScrollTop, distance, duration)
  205. )
  206. requestAnimationFrame(update)
  207. } else {
  208. setScroll(scrollElem, endScrollTop)
  209. if (callback) {
  210. callback()
  211. }
  212. }
  213. }
  214. }
  215. })()
  216.  
  217. function toDash(str) {
  218. return str.replace(/([A-Z])/g, (match, p1) => '-' + p1.toLowerCase())
  219. }
  220.  
  221. function applyStyle(elem, style = {}, reset = false) {
  222. if (reset) {
  223. elem.style = ''
  224. }
  225. if (typeof style === 'string') {
  226. elem.style = style
  227. } else {
  228. for (let prop in style) {
  229. if (typeof style[prop] === 'number') {
  230. elem.style.setProperty(toDash(prop), px(style[prop]), 'important')
  231. } else {
  232. elem.style.setProperty(toDash(prop), style[prop], 'important')
  233. }
  234. }
  235. }
  236. }
  237.  
  238. function translate3d(x = 0, y = 0, z = 0) {
  239. return `translate3d(${Math.round(x)}px, ${Math.round(y)}px, ${Math.round(
  240. z
  241. )}px)` // 0.5px => blurred text
  242. }
  243.  
  244. function setClass(elem, names, delay) {
  245. if (delay === undefined) {
  246. elem.classList = names
  247. } else {
  248. return setTimeout(() => {
  249. elem.classList = names
  250. }, delay)
  251. }
  252. }
  253.  
  254. const toast = (function toastFactory() {
  255. let timers = []
  256. return function toast(msg) {
  257. let toast
  258. insertCSS(toastCSS, 'smarttoc-toast__css')
  259. if (document.getElementById('smarttoc-toast')) {
  260. toast = document.getElementById('smarttoc-toast')
  261. } else {
  262. toast = document.createElement('DIV')
  263. toast.id = 'smarttoc-toast'
  264. document.body.appendChild(toast)
  265. }
  266. toast.textContent = msg
  267.  
  268. timers.forEach(clearTimeout)
  269. toast.classList = ''
  270.  
  271. const set = setClass.bind(null, toast)
  272.  
  273. toast.classList = 'enter'
  274. timers = [
  275. set('enter enter-active', 0),
  276. set('leave', 3000),
  277. set('leave leave-active', 3000),
  278. set('', 3000 + 200)
  279. ]
  280. }
  281. })()
  282.  
  283. const insertCSS = function(css, id) {
  284. if (!document.getElementById(id)) {
  285. let style = document.createElement('STYLE')
  286. style.type = 'text/css'
  287. style.id = id
  288. style.textContent = css
  289. document.head.appendChild(style)
  290. return
  291. }
  292. }
  293.  
  294. var tocCSS = "/* EVERYTHING HERE WILL BE '!IMPORTANT' */\n\n/* reset */\n\n#smarttoc {\n all: initial;\n}\n\n#smarttoc * {\n all: unset;\n}\n\n/* container */\n\n#smarttoc {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: fixed;\n max-width: 22em;\n min-width: 14em;\n max-height: calc(100vh - 100px);\n z-index: 10000;\n box-sizing: border-box;\n background-color: #fff;\n color: gray;\n font-size: calc(12px + 0.1vw);\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n line-height: 1.5;\n font-weight: normal;\n border: 1px solid rgba(158, 158, 158, 0.22);\n -webkit-font-smoothing: subpixel-antialiased;\n font-smoothing: subpixel-antialiased;\n overflow: hidden;\n will-change: transform, max-width;\n transition: max-width 0.3s;\n contain: content;\n}\n\n#smarttoc:hover {\n max-width: 33vw;\n}\n\n#smarttoc.hidden {\n display: none;\n}\n\n#smarttoc .handle {\n -webkit-user-select: none;\n user-select: none;\n\n border-bottom: 1px solid rgba(158, 158, 158, 0.22);\n padding: 0.1em 0.7em;\n font-variant-caps: inherit;\n font-variant: small-caps;\n font-size: 0.9em;\n color: #bbb;\n cursor: pointer;\n text-align: center;\n opacity: 0;\n will-change: opacity;\n transition: opacity 0.3s;\n}\n\n#smarttoc:hover .handle {\n max-width: 33vw;\n opacity: 1;\n}\n\n#smarttoc .handle:hover,\n#smarttoc .handle:active {\n cursor: move;\n}\n\n#smarttoc .handle:active {\n background: #f9f9f9;\n}\n\n#smarttoc > ul {\n flex-grow: 1;\n padding: 0 1.3em 1.3em 1em;\n overflow-y: auto;\n}\n\n/* all headings */\n\n#smarttoc ul,\n#smarttoc li {\n list-style: none;\n display: block;\n}\n\n#smarttoc a {\n text-decoration: none;\n color: gray;\n display: block;\n line-height: 1.3;\n padding-top: 0.2em;\n padding-bottom: 0.2em;\n text-overflow: ellipsis;\n overflow-x: hidden;\n white-space: nowrap;\n}\n\n#smarttoc a:hover,\n#smarttoc a:active {\n border-left-color: rgba(86, 61, 124, 0.5);\n color: #563d7c;\n}\n\n#smarttoc li.active > a {\n border-left-color: #563d7c;\n color: #563d7c;\n}\n\n/* heading level: 1 */\n\n#smarttoc ul {\n line-height: 2;\n}\n\n#smarttoc ul a {\n font-size: 1em;\n padding-left: 1.3em;\n cursor:pointer \n}\n\n#smarttoc ul a:hover,\n#smarttoc ul a:active,\n#smarttoc ul li.active > a {\n border-left-width: 3px;\n border-left-style: solid;\n padding-left: calc(1.3em - 3px);\n}\n\n#smarttoc ul li.active > a {\n font-weight: 700;\n}\n\n/* heading level: 2 (hidden only when there are too many headings) */\n\n#smarttoc ul ul {\n line-height: 1.8;\n}\n\n#smarttoc.lengthy ul ul {\n display: none;\n}\n\n#smarttoc.lengthy ul li.active > ul {\n display: block;\n}\n\n#smarttoc ul ul a {\n font-size: 1em;\n padding-left: 2.7em;\n}\n\n#smarttoc ul ul a:hover,\n#smarttoc ul ul a:active,\n#smarttoc ul ul li.active > a {\n border-left-width: 2px;\n border-left-style: solid;\n padding-left: calc(2.7em - 2px);\n font-weight: normal;\n}\n\n/* heading level: 3 (hidden unless parent is active) */\n\n#smarttoc ul ul ul {\n line-height: 1.7;\n display: none;\n}\n\n#smarttoc ul ul li.active > ul {\n display: block;\n}\n\n#smarttoc ul ul ul a {\n font-size: 1em;\n padding-left: 4em;\n}\n\n#smarttoc ul ul ul a:hover,\n#smarttoc ul ul ul a:active,\n#smarttoc ul ul ul li.active > a {\n border-left-width: 1px;\n border-left-style: solid;\n padding-left: calc(4em - 1px);\n font-weight: normal;\n}\n";
  295.  
  296. const proto = {
  297. subscribe(cb, emitOnSubscribe = true) {
  298. if (emitOnSubscribe && this.value !== undefined) {
  299. cb(this.value)
  300. }
  301. this.listeners.push(cb)
  302. },
  303. addDependent(dependent) {
  304. this.dependents.push(dependent)
  305. },
  306. update(val) {
  307. this.value = val
  308. this.changed = true
  309. this.dependents.forEach(dep => dep.update(val))
  310. },
  311. flush() {
  312. if (this.changed) {
  313. this.changed = false
  314. this.listeners.forEach(l => l(this.value))
  315. this.dependents.forEach(dep => dep.flush())
  316. }
  317. },
  318. unique() {
  319. let lastValue = this.value
  320. let $unique = Stream(lastValue)
  321. this.subscribe(val => {
  322. if (val !== lastValue) {
  323. $unique(val)
  324. lastValue = val
  325. }
  326. })
  327. return $unique
  328. },
  329. map(f) {
  330. return Stream.combine(this, f)
  331. },
  332. filter(f) {
  333. return this.map(output => (f(output) ? output : undefined))
  334. },
  335. throttle(delay) {
  336. let $throttled = Stream(this.value)
  337. const emit = throttle($throttled, delay)
  338. this.subscribe(emit)
  339. return $throttled
  340. },
  341. log(name) {
  342. this.subscribe(e => console.log(`[${name}]: `, e))
  343. return this
  344. }
  345. }
  346.  
  347. function Stream(init) {
  348. let s = function(val) {
  349. if (val === undefined) return s.value
  350. s.update(val)
  351. s.flush(val)
  352. }
  353.  
  354. s.value = init
  355. s.changed = false
  356. s.listeners = []
  357. s.dependents = []
  358.  
  359. return Object.assign(s, proto)
  360. }
  361.  
  362. Stream.combine = function(...streams) {
  363. const combiner = streams.pop()
  364. let cached = streams.map(s => s())
  365. const combined = Stream(combiner(...cached))
  366.  
  367. streams.forEach((s, i) => {
  368. const dependent = {
  369. update(val) {
  370. cached[i] = val
  371. combined.update(combiner(...cached))
  372. },
  373. flush() {
  374. combined.flush()
  375. }
  376. }
  377. s.addDependent(dependent)
  378. })
  379.  
  380. return combined
  381. }
  382.  
  383. Stream.interval = function(int) {
  384. let $interval = Stream()
  385. setInterval(() => $interval(null), int)
  386. return $interval
  387. }
  388.  
  389. Stream.fromEvent = function(elem, type) {
  390. let $event = Stream()
  391. elem.addEventListener(type, $event)
  392. return $event
  393. }
  394.  
  395. var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  396.  
  397. function createCommonjsModule(fn, module) {
  398. return module = { exports: {} }, fn(module, module.exports), module.exports;
  399. }
  400.  
  401. var mithril = createCommonjsModule(function (module) {
  402. ;(function() {
  403. "use strict"
  404. function Vnode(tag, key, attrs0, children, text, dom) {
  405. return {tag: tag, key: key, attrs: attrs0, children: children, text: text, dom: dom, domSize: undefined, state: undefined, _state: undefined, events: undefined, instance: undefined, skip: false}
  406. }
  407. Vnode.normalize = function(node) {
  408. if (Array.isArray(node)) return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined)
  409. if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined)
  410. return node
  411. }
  412. Vnode.normalizeChildren = function normalizeChildren(children) {
  413. for (var i = 0; i < children.length; i++) {
  414. children[i] = Vnode.normalize(children[i])
  415. }
  416. return children
  417. }
  418. var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g
  419. var selectorCache = {}
  420. var hasOwn = {}.hasOwnProperty
  421. function compileSelector(selector) {
  422. var match, tag = "div", classes = [], attrs = {}
  423. while (match = selectorParser.exec(selector)) {
  424. var type = match[1], value = match[2]
  425. if (type === "" && value !== "") tag = value
  426. else if (type === "#") attrs.id = value
  427. else if (type === ".") classes.push(value)
  428. else if (match[3][0] === "[") {
  429. var attrValue = match[6]
  430. if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\")
  431. if (match[4] === "class") classes.push(attrValue)
  432. else attrs[match[4]] = attrValue === "" ? attrValue : attrValue || true
  433. }
  434. }
  435. if (classes.length > 0) attrs.className = classes.join(" ")
  436. return selectorCache[selector] = {tag: tag, attrs: attrs}
  437. }
  438. function execSelector(state, attrs, children) {
  439. var hasAttrs = false, childList, text
  440. var className = attrs.className || attrs.class
  441. for (var key in state.attrs) {
  442. if (hasOwn.call(state.attrs, key)) {
  443. attrs[key] = state.attrs[key]
  444. }
  445. }
  446. if (className !== undefined) {
  447. if (attrs.class !== undefined) {
  448. attrs.class = undefined
  449. attrs.className = className
  450. }
  451. if (state.attrs.className != null) {
  452. attrs.className = state.attrs.className + " " + className
  453. }
  454. }
  455. for (let key in attrs) {
  456. if (hasOwn.call(attrs, key) && key !== "key") {
  457. hasAttrs = true
  458. break
  459. }
  460. }
  461. if (Array.isArray(children) && children.length === 1 && children[0] != null && children[0].tag === "#") {
  462. text = children[0].children
  463. } else {
  464. childList = children
  465. }
  466. return Vnode(state.tag, attrs.key, hasAttrs ? attrs : undefined, childList, text)
  467. }
  468. function hyperscript(selector) {
  469. // Because sloppy mode sucks
  470. var attrs = arguments[1], start = 2, children
  471. if (selector == null || typeof selector !== "string" && typeof selector !== "function" && typeof selector.view !== "function") {
  472. throw Error("The selector must be either a string or a component.");
  473. }
  474. if (typeof selector === "string") {
  475. var cached = selectorCache[selector] || compileSelector(selector)
  476. }
  477. if (attrs == null) {
  478. attrs = {}
  479. } else if (typeof attrs !== "object" || attrs.tag != null || Array.isArray(attrs)) {
  480. attrs = {}
  481. start = 1
  482. }
  483. if (arguments.length === start + 1) {
  484. children = arguments[start]
  485. if (!Array.isArray(children)) children = [children]
  486. } else {
  487. children = []
  488. while (start < arguments.length) children.push(arguments[start++])
  489. }
  490. var normalized = Vnode.normalizeChildren(children)
  491. if (typeof selector === "string") {
  492. return execSelector(cached, attrs, normalized)
  493. } else {
  494. return Vnode(selector, attrs.key, attrs, normalized)
  495. }
  496. }
  497. hyperscript.trust = function(html) {
  498. if (html == null) html = ""
  499. return Vnode("<", undefined, undefined, html, undefined, undefined)
  500. }
  501. hyperscript.fragment = function(attrs1, children) {
  502. return Vnode("[", attrs1.key, attrs1, Vnode.normalizeChildren(children), undefined, undefined)
  503. }
  504. var m = hyperscript
  505. /** @constructor */
  506. var PromisePolyfill = function(executor) {
  507. if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`")
  508. if (typeof executor !== "function") throw new TypeError("executor must be a function")
  509. var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false)
  510. var instance = self._instance = {resolvers: resolvers, rejectors: rejectors}
  511. var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
  512. function handler(list, shouldAbsorb) {
  513. return function execute(value) {
  514. var then
  515. try {
  516. if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") {
  517. if (value === self) throw new TypeError("Promise can't be resolved w/ itself")
  518. executeOnce(then.bind(value))
  519. }
  520. else {
  521. callAsync(function() {
  522. if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value)
  523. for (var i = 0; i < list.length; i++) list[i](value)
  524. resolvers.length = 0, rejectors.length = 0
  525. instance.state = shouldAbsorb
  526. instance.retry = function() {execute(value)}
  527. })
  528. }
  529. }
  530. catch (e) {
  531. rejectCurrent(e)
  532. }
  533. }
  534. }
  535. function executeOnce(then) {
  536. var runs = 0
  537. function run(fn) {
  538. return function(value) {
  539. if (runs++ > 0) return
  540. fn(value)
  541. }
  542. }
  543. var onerror = run(rejectCurrent)
  544. try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)}
  545. }
  546. executeOnce(executor)
  547. }
  548. PromisePolyfill.prototype.then = function(onFulfilled, onRejection) {
  549. var self = this, instance = self._instance
  550. function handle(callback, list, next, state) {
  551. list.push(function(value) {
  552. if (typeof callback !== "function") next(value)
  553. else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)}
  554. })
  555. if (typeof instance.retry === "function" && state === instance.state) instance.retry()
  556. }
  557. var resolveNext, rejectNext
  558. var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject})
  559. handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false)
  560. return promise
  561. }
  562. PromisePolyfill.prototype.catch = function(onRejection) {
  563. return this.then(null, onRejection)
  564. }
  565. PromisePolyfill.resolve = function(value) {
  566. if (value instanceof PromisePolyfill) return value
  567. return new PromisePolyfill(function(resolve) {resolve(value)})
  568. }
  569. PromisePolyfill.reject = function(value) {
  570. return new PromisePolyfill(function(resolve, reject) {reject(value)})
  571. }
  572. PromisePolyfill.all = function(list) {
  573. return new PromisePolyfill(function(resolve, reject) {
  574. var total = list.length, count = 0, values = []
  575. if (list.length === 0) resolve([])
  576. else for (var i = 0; i < list.length; i++) {
  577. (function(i) {
  578. function consume(value) {
  579. count++
  580. values[i] = value
  581. if (count === total) resolve(values)
  582. }
  583. if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") {
  584. list[i].then(consume, reject)
  585. }
  586. else consume(list[i])
  587. })(i)
  588. }
  589. })
  590. }
  591. PromisePolyfill.race = function(list) {
  592. return new PromisePolyfill(function(resolve, reject) {
  593. for (var i = 0; i < list.length; i++) {
  594. list[i].then(resolve, reject)
  595. }
  596. })
  597. }
  598. if (typeof window !== "undefined") {
  599. if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill
  600. let PromisePolyfill = window.Promise
  601. } else if (typeof commonjsGlobal !== "undefined") {
  602. if (typeof commonjsGlobal.Promise === "undefined") commonjsGlobal.Promise = PromisePolyfill
  603. let PromisePolyfill = commonjsGlobal.Promise
  604. } else {
  605. }
  606. var buildQueryString = function(object) {
  607. if (Object.prototype.toString.call(object) !== "[object Object]") return ""
  608. var args = []
  609. for (var key0 in object) {
  610. destructure(key0, object[key0])
  611. }
  612. return args.join("&")
  613. function destructure(key0, value) {
  614. if (Array.isArray(value)) {
  615. for (var i = 0; i < value.length; i++) {
  616. destructure(key0 + "[" + i + "]", value[i])
  617. }
  618. }
  619. else if (Object.prototype.toString.call(value) === "[object Object]") {
  620. for (let i in value) {
  621. destructure(key0 + "[" + i + "]", value[i])
  622. }
  623. }
  624. else args.push(encodeURIComponent(key0) + (value != null && value !== "" ? "=" + encodeURIComponent(value) : ""))
  625. }
  626. }
  627. var FILE_PROTOCOL_REGEX = new RegExp("^file://", "i")
  628. var _8 = function($window, Promise) {
  629. var callbackCount = 0
  630. var oncompletion
  631. function setCompletionCallback(callback) {oncompletion = callback}
  632. function finalizer() {
  633. var count = 0
  634. function complete() {if (--count === 0 && typeof oncompletion === "function") oncompletion()}
  635. return function finalize(promise0) {
  636. var then0 = promise0.then
  637. promise0.then = function() {
  638. count++
  639. var next = then0.apply(promise0, arguments)
  640. next.then(complete, function(e) {
  641. complete()
  642. if (count === 0) throw e
  643. })
  644. return finalize(next)
  645. }
  646. return promise0
  647. }
  648. }
  649. function normalize(args, extra) {
  650. if (typeof args === "string") {
  651. var url = args
  652. args = extra || {}
  653. if (args.url == null) args.url = url
  654. }
  655. return args
  656. }
  657. function request(args, extra) {
  658. var finalize = finalizer()
  659. args = normalize(args, extra)
  660. var promise0 = new Promise(function(resolve, reject) {
  661. if (args.method == null) args.method = "GET"
  662. args.method = args.method.toUpperCase()
  663. var useBody = (args.method === "GET" || args.method === "TRACE") ? false : (typeof args.useBody === "boolean" ? args.useBody : true)
  664. if (typeof args.serialize !== "function") args.serialize = typeof FormData !== "undefined" && args.data instanceof FormData ? function(value) {return value} : JSON.stringify
  665. if (typeof args.deserialize !== "function") args.deserialize = deserialize
  666. if (typeof args.extract !== "function") args.extract = extract
  667. args.url = interpolate(args.url, args.data)
  668. if (useBody) args.data = args.serialize(args.data)
  669. else args.url = assemble(args.url, args.data)
  670. var xhr = new $window.XMLHttpRequest(),
  671. aborted = false,
  672. _abort = xhr.abort
  673. xhr.abort = function abort() {
  674. aborted = true
  675. _abort.call(xhr)
  676. }
  677. xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined)
  678. if (args.serialize === JSON.stringify && useBody) {
  679. xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8")
  680. }
  681. if (args.deserialize === deserialize) {
  682. xhr.setRequestHeader("Accept", "application/json, text/*")
  683. }
  684. if (args.withCredentials) xhr.withCredentials = args.withCredentials
  685. for (var key in args.headers) if ({}.hasOwnProperty.call(args.headers, key)) {
  686. xhr.setRequestHeader(key, args.headers[key])
  687. }
  688. if (typeof args.config === "function") xhr = args.config(xhr, args) || xhr
  689. xhr.onreadystatechange = function() {
  690. // Don't throw errors on xhr.abort().
  691. if(aborted) return
  692. if (xhr.readyState === 4) {
  693. try {
  694. var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args))
  695. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || FILE_PROTOCOL_REGEX.test(args.url)) {
  696. resolve(cast(args.type, response))
  697. }
  698. else {
  699. var error = new Error(xhr.responseText)
  700. for (var key in response) error[key] = response[key]
  701. reject(error)
  702. }
  703. }
  704. catch (e) {
  705. reject(e)
  706. }
  707. }
  708. }
  709. if (useBody && (args.data != null)) xhr.send(args.data)
  710. else xhr.send()
  711. })
  712. return args.background === true ? promise0 : finalize(promise0)
  713. }
  714. function jsonp(args, extra) {
  715. var finalize = finalizer()
  716. args = normalize(args, extra)
  717. var promise0 = new Promise(function(resolve, reject) {
  718. var callbackName = args.callbackName || "_mithril_" + Math.round(Math.random() * 1e16) + "_" + callbackCount++
  719. var script = $window.document.createElement("script")
  720. $window[callbackName] = function(data) {
  721. script.parentNode.removeChild(script)
  722. resolve(cast(args.type, data))
  723. delete $window[callbackName]
  724. }
  725. script.onerror = function() {
  726. script.parentNode.removeChild(script)
  727. reject(new Error("JSONP request failed"))
  728. delete $window[callbackName]
  729. }
  730. if (args.data == null) args.data = {}
  731. args.url = interpolate(args.url, args.data)
  732. args.data[args.callbackKey || "callback"] = callbackName
  733. script.src = assemble(args.url, args.data)
  734. $window.document.documentElement.appendChild(script)
  735. })
  736. return args.background === true? promise0 : finalize(promise0)
  737. }
  738. function interpolate(url, data) {
  739. if (data == null) return url
  740. var tokens = url.match(/:[^\/]+/gi) || []
  741. for (var i = 0; i < tokens.length; i++) {
  742. var key = tokens[i].slice(1)
  743. if (data[key] != null) {
  744. url = url.replace(tokens[i], data[key])
  745. }
  746. }
  747. return url
  748. }
  749. function assemble(url, data) {
  750. var querystring = buildQueryString(data)
  751. if (querystring !== "") {
  752. var prefix = url.indexOf("?") < 0 ? "?" : "&"
  753. url += prefix + querystring
  754. }
  755. return url
  756. }
  757. function deserialize(data) {
  758. try {return data !== "" ? JSON.parse(data) : null}
  759. catch (e) {throw new Error(data)}
  760. }
  761. function extract(xhr) {return xhr.responseText}
  762. function cast(type0, data) {
  763. if (typeof type0 === "function") {
  764. if (Array.isArray(data)) {
  765. for (var i = 0; i < data.length; i++) {
  766. data[i] = new type0(data[i])
  767. }
  768. }
  769. else return new type0(data)
  770. }
  771. return data
  772. }
  773. return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback}
  774. }
  775. var requestService = _8(window, PromisePolyfill)
  776. var coreRenderer = function($window) {
  777. var $doc = $window.document
  778. var $emptyFragment = $doc.createDocumentFragment()
  779. var nameSpace = {
  780. svg: "http://www.w3.org/2000/svg",
  781. math: "http://www.w3.org/1998/Math/MathML"
  782. }
  783. var onevent
  784. function setEventCallback(callback) {return onevent = callback}
  785. function getNameSpace(vnode) {
  786. return vnode.attrs && vnode.attrs.xmlns || nameSpace[vnode.tag]
  787. }
  788. //create
  789. function createNodes(parent, vnodes, start, end, hooks, nextSibling, ns) {
  790. for (var i = start; i < end; i++) {
  791. var vnode = vnodes[i]
  792. if (vnode != null) {
  793. createNode(parent, vnode, hooks, ns, nextSibling)
  794. }
  795. }
  796. }
  797. function createNode(parent, vnode, hooks, ns, nextSibling) {
  798. var tag = vnode.tag
  799. if (typeof tag === "string") {
  800. vnode.state = {}
  801. if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
  802. switch (tag) {
  803. case "#": return createText(parent, vnode, nextSibling)
  804. case "<": return createHTML(parent, vnode, nextSibling)
  805. case "[": return createFragment(parent, vnode, hooks, ns, nextSibling)
  806. default: return createElement(parent, vnode, hooks, ns, nextSibling)
  807. }
  808. }
  809. else return createComponent(parent, vnode, hooks, ns, nextSibling)
  810. }
  811. function createText(parent, vnode, nextSibling) {
  812. vnode.dom = $doc.createTextNode(vnode.children)
  813. insertNode(parent, vnode.dom, nextSibling)
  814. return vnode.dom
  815. }
  816. function createHTML(parent, vnode, nextSibling) {
  817. var match1 = vnode.children.match(/^\s*?<(\w+)/im) || []
  818. var parent1 = {caption: "table", thead: "table", tbody: "table", tfoot: "table", tr: "tbody", th: "tr", td: "tr", colgroup: "table", col: "colgroup"}[match1[1]] || "div"
  819. var temp = $doc.createElement(parent1)
  820. temp.innerHTML = vnode.children
  821. vnode.dom = temp.firstChild
  822. vnode.domSize = temp.childNodes.length
  823. var fragment = $doc.createDocumentFragment()
  824. var child
  825. while (child = temp.firstChild) {
  826. fragment.appendChild(child)
  827. }
  828. insertNode(parent, fragment, nextSibling)
  829. return fragment
  830. }
  831. function createFragment(parent, vnode, hooks, ns, nextSibling) {
  832. var fragment = $doc.createDocumentFragment()
  833. if (vnode.children != null) {
  834. var children = vnode.children
  835. createNodes(fragment, children, 0, children.length, hooks, null, ns)
  836. }
  837. vnode.dom = fragment.firstChild
  838. vnode.domSize = fragment.childNodes.length
  839. insertNode(parent, fragment, nextSibling)
  840. return fragment
  841. }
  842. function createElement(parent, vnode, hooks, ns, nextSibling) {
  843. var tag = vnode.tag
  844. var attrs2 = vnode.attrs
  845. var is = attrs2 && attrs2.is
  846. ns = getNameSpace(vnode) || ns
  847. var element = ns ?
  848. is ? $doc.createElementNS(ns, tag, {is: is}) : $doc.createElementNS(ns, tag) :
  849. is ? $doc.createElement(tag, {is: is}) : $doc.createElement(tag)
  850. vnode.dom = element
  851. if (attrs2 != null) {
  852. setAttrs(vnode, attrs2, ns)
  853. }
  854. insertNode(parent, element, nextSibling)
  855. if (vnode.attrs != null && vnode.attrs.contenteditable != null) {
  856. setContentEditable(vnode)
  857. }
  858. else {
  859. if (vnode.text != null) {
  860. if (vnode.text !== "") element.textContent = vnode.text
  861. else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)]
  862. }
  863. if (vnode.children != null) {
  864. var children = vnode.children
  865. createNodes(element, children, 0, children.length, hooks, null, ns)
  866. setLateAttrs(vnode)
  867. }
  868. }
  869. return element
  870. }
  871. function initComponent(vnode, hooks) {
  872. var sentinel
  873. if (typeof vnode.tag.view === "function") {
  874. vnode.state = Object.create(vnode.tag)
  875. sentinel = vnode.state.view
  876. if (sentinel.$$reentrantLock$$ != null) return $emptyFragment
  877. sentinel.$$reentrantLock$$ = true
  878. } else {
  879. vnode.state = void 0
  880. sentinel = vnode.tag
  881. if (sentinel.$$reentrantLock$$ != null) return $emptyFragment
  882. sentinel.$$reentrantLock$$ = true
  883. vnode.state = (vnode.tag.prototype != null && typeof vnode.tag.prototype.view === "function") ? new vnode.tag(vnode) : vnode.tag(vnode)
  884. }
  885. vnode._state = vnode.state
  886. if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
  887. initLifecycle(vnode._state, vnode, hooks)
  888. vnode.instance = Vnode.normalize(vnode._state.view.call(vnode.state, vnode))
  889. if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument")
  890. sentinel.$$reentrantLock$$ = null
  891. }
  892. function createComponent(parent, vnode, hooks, ns, nextSibling) {
  893. initComponent(vnode, hooks)
  894. if (vnode.instance != null) {
  895. var element = createNode(parent, vnode.instance, hooks, ns, nextSibling)
  896. vnode.dom = vnode.instance.dom
  897. vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0
  898. insertNode(parent, element, nextSibling)
  899. return element
  900. }
  901. else {
  902. vnode.domSize = 0
  903. return $emptyFragment
  904. }
  905. }
  906. //update
  907. function updateNodes(parent, old, vnodes, recycling, hooks, nextSibling, ns) {
  908. if (old === vnodes || old == null && vnodes == null) return
  909. else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns)
  910. else if (vnodes == null) removeNodes(old, 0, old.length, vnodes)
  911. else {
  912. if (old.length === vnodes.length) {
  913. var isUnkeyed = false
  914. for (var i = 0; i < vnodes.length; i++) {
  915. if (vnodes[i] != null && old[i] != null) {
  916. isUnkeyed = vnodes[i].key == null && old[i].key == null
  917. break
  918. }
  919. }
  920. if (isUnkeyed) {
  921. for (let i = 0; i < old.length; i++) {
  922. if (old[i] === vnodes[i]) continue
  923. else if (old[i] == null && vnodes[i] != null) createNode(parent, vnodes[i], hooks, ns, getNextSibling(old, i + 1, nextSibling))
  924. else if (vnodes[i] == null) removeNodes(old, i, i + 1, vnodes)
  925. else updateNode(parent, old[i], vnodes[i], hooks, getNextSibling(old, i + 1, nextSibling), recycling, ns)
  926. }
  927. return
  928. }
  929. }
  930. recycling = recycling || isRecyclable(old, vnodes)
  931. if (recycling) {
  932. var pool = old.pool
  933. old = old.concat(old.pool)
  934. }
  935. var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
  936. while (oldEnd >= oldStart && end >= start) {
  937. var o = old[oldStart], v = vnodes[start]
  938. if (o === v && !recycling) oldStart++, start++
  939. else if (o == null) oldStart++
  940. else if (v == null) start++
  941. else if (o.key === v.key) {
  942. var shouldRecycle = (pool != null && oldStart >= old.length - pool.length) || ((pool == null) && recycling)
  943. oldStart++, start++
  944. updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), shouldRecycle, ns)
  945. if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
  946. }
  947. else {
  948. let o = old[oldEnd]
  949. if (o === v && !recycling) oldEnd--, start++
  950. else if (o == null) oldEnd--
  951. else if (v == null) start++
  952. else if (o.key === v.key) {
  953. let shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling)
  954. updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns)
  955. if (recycling || start < end) insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
  956. oldEnd--, start++
  957. }
  958. else break
  959. }
  960. }
  961. while (oldEnd >= oldStart && end >= start) {
  962. let o = old[oldEnd], v = vnodes[end]
  963. if (o === v && !recycling) oldEnd--, end--
  964. else if (o == null) oldEnd--
  965. else if (v == null) end--
  966. else if (o.key === v.key) {
  967. let shouldRecycle = (pool != null && oldEnd >= old.length - pool.length) || ((pool == null) && recycling)
  968. updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), shouldRecycle, ns)
  969. if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
  970. if (o.dom != null) nextSibling = o.dom
  971. oldEnd--, end--
  972. }
  973. else {
  974. if (!map) map = getKeyMap(old, oldEnd)
  975. if (v != null) {
  976. var oldIndex = map[v.key]
  977. if (oldIndex != null) {
  978. let movable = old[oldIndex]
  979. let shouldRecycle = (pool != null && oldIndex >= old.length - pool.length) || ((pool == null) && recycling)
  980. updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
  981. insertNode(parent, toFragment(movable), nextSibling)
  982. old[oldIndex].skip = true
  983. if (movable.dom != null) nextSibling = movable.dom
  984. }
  985. else {
  986. var dom = createNode(parent, v, hooks, ns, nextSibling)
  987. nextSibling = dom
  988. }
  989. }
  990. end--
  991. }
  992. if (end < start) break
  993. }
  994. createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
  995. removeNodes(old, oldStart, oldEnd + 1, vnodes)
  996. }
  997. }
  998. function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) {
  999. var oldTag = old.tag, tag = vnode.tag
  1000. if (oldTag === tag) {
  1001. vnode.state = old.state
  1002. vnode._state = old._state
  1003. vnode.events = old.events
  1004. if (!recycling && shouldNotUpdate(vnode, old)) return
  1005. if (typeof oldTag === "string") {
  1006. if (vnode.attrs != null) {
  1007. if (recycling) {
  1008. vnode.state = {}
  1009. initLifecycle(vnode.attrs, vnode, hooks)
  1010. }
  1011. else updateLifecycle(vnode.attrs, vnode, hooks)
  1012. }
  1013. switch (oldTag) {
  1014. case "#": updateText(old, vnode); break
  1015. case "<": updateHTML(parent, old, vnode, nextSibling); break
  1016. case "[": updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns); break
  1017. default: updateElement(old, vnode, recycling, hooks, ns)
  1018. }
  1019. }
  1020. else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns)
  1021. }
  1022. else {
  1023. removeNode(old, null)
  1024. createNode(parent, vnode, hooks, ns, nextSibling)
  1025. }
  1026. }
  1027. function updateText(old, vnode) {
  1028. if (old.children.toString() !== vnode.children.toString()) {
  1029. old.dom.nodeValue = vnode.children
  1030. }
  1031. vnode.dom = old.dom
  1032. }
  1033. function updateHTML(parent, old, vnode, nextSibling) {
  1034. if (old.children !== vnode.children) {
  1035. toFragment(old)
  1036. createHTML(parent, vnode, nextSibling)
  1037. }
  1038. else vnode.dom = old.dom, vnode.domSize = old.domSize
  1039. }
  1040. function updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns) {
  1041. updateNodes(parent, old.children, vnode.children, recycling, hooks, nextSibling, ns)
  1042. var domSize = 0, children = vnode.children
  1043. vnode.dom = null
  1044. if (children != null) {
  1045. for (var i = 0; i < children.length; i++) {
  1046. var child = children[i]
  1047. if (child != null && child.dom != null) {
  1048. if (vnode.dom == null) vnode.dom = child.dom
  1049. domSize += child.domSize || 1
  1050. }
  1051. }
  1052. if (domSize !== 1) vnode.domSize = domSize
  1053. }
  1054. }
  1055. function updateElement(old, vnode, recycling, hooks, ns) {
  1056. var element = vnode.dom = old.dom
  1057. ns = getNameSpace(vnode) || ns
  1058. if (vnode.tag === "textarea") {
  1059. if (vnode.attrs == null) vnode.attrs = {}
  1060. if (vnode.text != null) {
  1061. vnode.attrs.value = vnode.text //FIXME handle0 multiple children
  1062. vnode.text = undefined
  1063. }
  1064. }
  1065. updateAttrs(vnode, old.attrs, vnode.attrs, ns)
  1066. if (vnode.attrs != null && vnode.attrs.contenteditable != null) {
  1067. setContentEditable(vnode)
  1068. }
  1069. else if (old.text != null && vnode.text != null && vnode.text !== "") {
  1070. if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text
  1071. }
  1072. else {
  1073. if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)]
  1074. if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)]
  1075. updateNodes(element, old.children, vnode.children, recycling, hooks, null, ns)
  1076. }
  1077. }
  1078. function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) {
  1079. if (recycling) {
  1080. initComponent(vnode, hooks)
  1081. } else {
  1082. vnode.instance = Vnode.normalize(vnode._state.view.call(vnode.state, vnode))
  1083. if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument")
  1084. if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks)
  1085. updateLifecycle(vnode._state, vnode, hooks)
  1086. }
  1087. if (vnode.instance != null) {
  1088. if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling)
  1089. else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns)
  1090. vnode.dom = vnode.instance.dom
  1091. vnode.domSize = vnode.instance.domSize
  1092. }
  1093. else if (old.instance != null) {
  1094. removeNode(old.instance, null)
  1095. vnode.dom = undefined
  1096. vnode.domSize = 0
  1097. }
  1098. else {
  1099. vnode.dom = old.dom
  1100. vnode.domSize = old.domSize
  1101. }
  1102. }
  1103. function isRecyclable(old, vnodes) {
  1104. if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) {
  1105. var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0
  1106. var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0
  1107. var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0
  1108. if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) {
  1109. return true
  1110. }
  1111. }
  1112. return false
  1113. }
  1114. function getKeyMap(vnodes, end) {
  1115. var map = {}, i = 0
  1116. for (let i = 0; i < end; i++) {
  1117. let vnode = vnodes[i]
  1118. if (vnode != null) {
  1119. let key2 = vnode.key
  1120. if (key2 != null) map[key2] = i
  1121. }
  1122. }
  1123. return map
  1124. }
  1125. function toFragment(vnode) {
  1126. var count0 = vnode.domSize
  1127. if (count0 != null || vnode.dom == null) {
  1128. var fragment = $doc.createDocumentFragment()
  1129. if (count0 > 0) {
  1130. var dom = vnode.dom
  1131. while (--count0) fragment.appendChild(dom.nextSibling)
  1132. fragment.insertBefore(dom, fragment.firstChild)
  1133. }
  1134. return fragment
  1135. }
  1136. else return vnode.dom
  1137. }
  1138. function getNextSibling(vnodes, i, nextSibling) {
  1139. for (; i < vnodes.length; i++) {
  1140. if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom
  1141. }
  1142. return nextSibling
  1143. }
  1144. function insertNode(parent, dom, nextSibling) {
  1145. if (nextSibling && nextSibling.parentNode) parent.insertBefore(dom, nextSibling)
  1146. else parent.appendChild(dom)
  1147. }
  1148. function setContentEditable(vnode) {
  1149. var children = vnode.children
  1150. if (children != null && children.length === 1 && children[0].tag === "<") {
  1151. var content = children[0].children
  1152. if (vnode.dom.innerHTML !== content) vnode.dom.innerHTML = content
  1153. }
  1154. else if (vnode.text != null || children != null && children.length !== 0) throw new Error("Child node of a contenteditable must be trusted")
  1155. }
  1156. //remove
  1157. function removeNodes(vnodes, start, end, context) {
  1158. for (var i = start; i < end; i++) {
  1159. var vnode = vnodes[i]
  1160. if (vnode != null) {
  1161. if (vnode.skip) vnode.skip = false
  1162. else removeNode(vnode, context)
  1163. }
  1164. }
  1165. }
  1166. function removeNode(vnode, context) {
  1167. var expected = 1, called = 0
  1168. if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function") {
  1169. var result = vnode.attrs.onbeforeremove.call(vnode.state, vnode)
  1170. if (result != null && typeof result.then === "function") {
  1171. expected++
  1172. result.then(continuation, continuation)
  1173. }
  1174. }
  1175. if (typeof vnode.tag !== "string" && typeof vnode._state.onbeforeremove === "function") {
  1176. let result = vnode._state.onbeforeremove.call(vnode.state, vnode)
  1177. if (result != null && typeof result.then === "function") {
  1178. expected++
  1179. result.then(continuation, continuation)
  1180. }
  1181. }
  1182. continuation()
  1183. function continuation() {
  1184. if (++called === expected) {
  1185. onremove(vnode)
  1186. if (vnode.dom) {
  1187. var count0 = vnode.domSize || 1
  1188. if (count0 > 1) {
  1189. var dom = vnode.dom
  1190. while (--count0) {
  1191. removeNodeFromDOM(dom.nextSibling)
  1192. }
  1193. }
  1194. removeNodeFromDOM(vnode.dom)
  1195. if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements
  1196. if (!context.pool) context.pool = [vnode]
  1197. else context.pool.push(vnode)
  1198. }
  1199. }
  1200. }
  1201. }
  1202. }
  1203. function removeNodeFromDOM(node) {
  1204. var parent = node.parentNode
  1205. if (parent != null) parent.removeChild(node)
  1206. }
  1207. function onremove(vnode) {
  1208. if (vnode.attrs && typeof vnode.attrs.onremove === "function") vnode.attrs.onremove.call(vnode.state, vnode)
  1209. if (typeof vnode.tag !== "string" && typeof vnode._state.onremove === "function") vnode._state.onremove.call(vnode.state, vnode)
  1210. if (vnode.instance != null) onremove(vnode.instance)
  1211. else {
  1212. var children = vnode.children
  1213. if (Array.isArray(children)) {
  1214. for (var i = 0; i < children.length; i++) {
  1215. var child = children[i]
  1216. if (child != null) onremove(child)
  1217. }
  1218. }
  1219. }
  1220. }
  1221. //attrs2
  1222. function setAttrs(vnode, attrs2, ns) {
  1223. for (var key2 in attrs2) {
  1224. setAttr(vnode, key2, null, attrs2[key2], ns)
  1225. }
  1226. }
  1227. function setAttr(vnode, key2, old, value, ns) {
  1228. var element = vnode.dom
  1229. if (key2 === "key" || key2 === "is" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return
  1230. var nsLastIndex = key2.indexOf(":")
  1231. if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") {
  1232. element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value)
  1233. }
  1234. else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value)
  1235. else if (key2 === "style") updateStyle(element, old, value)
  1236. else if (key2 in element && !isAttribute(key2) && ns === undefined && !isCustomElement(vnode)) {
  1237. if (key2 === "value") {
  1238. var normalized0 = "" + value // eslint-disable-line no-implicit-coercion
  1239. //setting input[value] to same value by typing on focused element moves cursor to end in Chrome
  1240. if ((vnode.tag === "input" || vnode.tag === "textarea") && vnode.dom.value === normalized0 && vnode.dom === $doc.activeElement) return
  1241. //setting select[value] to same value while having select open blinks select dropdown in Chrome
  1242. if (vnode.tag === "select") {
  1243. if (value === null) {
  1244. if (vnode.dom.selectedIndex === -1 && vnode.dom === $doc.activeElement) return
  1245. } else {
  1246. if (old !== null && vnode.dom.value === normalized0 && vnode.dom === $doc.activeElement) return
  1247. }
  1248. }
  1249. //setting option[value] to same value while having select open blinks select dropdown in Chrome
  1250. if (vnode.tag === "option" && old != null && vnode.dom.value === normalized0) return
  1251. }
  1252. // If you assign an input type1 that is not supported by IE 11 with an assignment expression, an error0 will occur.
  1253. if (vnode.tag === "input" && key2 === "type") {
  1254. element.setAttribute(key2, value)
  1255. return
  1256. }
  1257. element[key2] = value
  1258. }
  1259. else {
  1260. if (typeof value === "boolean") {
  1261. if (value) element.setAttribute(key2, "")
  1262. else element.removeAttribute(key2)
  1263. }
  1264. else element.setAttribute(key2 === "className" ? "class" : key2, value)
  1265. }
  1266. }
  1267. function setLateAttrs(vnode) {
  1268. var attrs2 = vnode.attrs
  1269. if (vnode.tag === "select" && attrs2 != null) {
  1270. if ("value" in attrs2) setAttr(vnode, "value", null, attrs2.value, undefined)
  1271. if ("selectedIndex" in attrs2) setAttr(vnode, "selectedIndex", null, attrs2.selectedIndex, undefined)
  1272. }
  1273. }
  1274. function updateAttrs(vnode, old, attrs2, ns) {
  1275. if (attrs2 != null) {
  1276. for (let key2 in attrs2) {
  1277. setAttr(vnode, key2, old && old[key2], attrs2[key2], ns)
  1278. }
  1279. }
  1280. if (old != null) {
  1281. for (var key2 in old) {
  1282. if (attrs2 == null || !(key2 in attrs2)) {
  1283. if (key2 === "className") key2 = "class"
  1284. if (key2[0] === "o" && key2[1] === "n" && !isLifecycleMethod(key2)) updateEvent(vnode, key2, undefined)
  1285. else if (key2 !== "key") vnode.dom.removeAttribute(key2)
  1286. }
  1287. }
  1288. }
  1289. }
  1290. function isFormAttribute(vnode, attr) {
  1291. return attr === "value" || attr === "checked" || attr === "selectedIndex" || attr === "selected" && vnode.dom === $doc.activeElement
  1292. }
  1293. function isLifecycleMethod(attr) {
  1294. return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove" || attr === "onbeforeupdate"
  1295. }
  1296. function isAttribute(attr) {
  1297. return attr === "href" || attr === "list" || attr === "form" || attr === "width" || attr === "height"// || attr === "type"
  1298. }
  1299. function isCustomElement(vnode){
  1300. return vnode.attrs.is || vnode.tag.indexOf("-") > -1
  1301. }
  1302. function hasIntegrationMethods(source) {
  1303. return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove)
  1304. }
  1305. //style
  1306. function updateStyle(element, old, style) {
  1307. if (old === style) element.style.cssText = "", old = null
  1308. if (style == null) element.style.cssText = ""
  1309. else if (typeof style === "string") element.style.cssText = style
  1310. else {
  1311. if (typeof old === "string") element.style.cssText = ""
  1312. for (var key2 in style) {
  1313. element.style[key2] = style[key2]
  1314. }
  1315. if (old != null && typeof old !== "string") {
  1316. for (var key3 in old) {
  1317. if (!(key3 in style)) element.style[key3] = ""
  1318. }
  1319. }
  1320. }
  1321. }
  1322. //event
  1323. function updateEvent(vnode, key2, value) {
  1324. var element = vnode.dom
  1325. var callback = typeof onevent !== "function" ? value : function(e) {
  1326. var result = value.call(element, e)
  1327. onevent.call(element, e)
  1328. return result
  1329. }
  1330. if (key2 in element) element[key2] = typeof value === "function" ? callback : null
  1331. else {
  1332. var eventName = key2.slice(2)
  1333. if (vnode.events === undefined) vnode.events = {}
  1334. if (vnode.events[key2] === callback) return
  1335. if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false)
  1336. if (typeof value === "function") {
  1337. vnode.events[key2] = callback
  1338. element.addEventListener(eventName, vnode.events[key2], false)
  1339. }
  1340. }
  1341. }
  1342. //lifecycle
  1343. function initLifecycle(source, vnode, hooks) {
  1344. if (typeof source.oninit === "function") source.oninit.call(vnode.state, vnode)
  1345. if (typeof source.oncreate === "function") hooks.push(source.oncreate.bind(vnode.state, vnode))
  1346. }
  1347. function updateLifecycle(source, vnode, hooks) {
  1348. if (typeof source.onupdate === "function") hooks.push(source.onupdate.bind(vnode.state, vnode))
  1349. }
  1350. function shouldNotUpdate(vnode, old) {
  1351. var forceVnodeUpdate, forceComponentUpdate
  1352. if (vnode.attrs != null && typeof vnode.attrs.onbeforeupdate === "function") forceVnodeUpdate = vnode.attrs.onbeforeupdate.call(vnode.state, vnode, old)
  1353. if (typeof vnode.tag !== "string" && typeof vnode._state.onbeforeupdate === "function") forceComponentUpdate = vnode._state.onbeforeupdate.call(vnode.state, vnode, old)
  1354. if (!(forceVnodeUpdate === undefined && forceComponentUpdate === undefined) && !forceVnodeUpdate && !forceComponentUpdate) {
  1355. vnode.dom = old.dom
  1356. vnode.domSize = old.domSize
  1357. vnode.instance = old.instance
  1358. return true
  1359. }
  1360. return false
  1361. }
  1362. function render(dom, vnodes) {
  1363. if (!dom) throw new Error("Ensure the DOM element being passed to m.route/m.mount/m.render is not undefined.")
  1364. var hooks = []
  1365. var active = $doc.activeElement
  1366. var namespace = dom.namespaceURI
  1367. // First time0 rendering into a node clears it out
  1368. if (dom.vnodes == null) dom.textContent = ""
  1369. if (!Array.isArray(vnodes)) vnodes = [vnodes]
  1370. updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), false, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace)
  1371. dom.vnodes = vnodes
  1372. for (var i = 0; i < hooks.length; i++) hooks[i]()
  1373. if ($doc.activeElement !== active) active.focus()
  1374. }
  1375. return {render: render, setEventCallback: setEventCallback}
  1376. }
  1377. function throttle(callback) {
  1378. //60fps translates to 16.6ms, round it down since setTimeout requires int
  1379. var time = 16
  1380. var last = 0, pending = null
  1381. var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
  1382. return function() {
  1383. var now = Date.now()
  1384. if (last === 0 || now - last >= time) {
  1385. last = now
  1386. callback()
  1387. }
  1388. else if (pending === null) {
  1389. pending = timeout(function() {
  1390. pending = null
  1391. callback()
  1392. last = Date.now()
  1393. }, time - (now - last))
  1394. }
  1395. }
  1396. }
  1397. var _11 = function($window) {
  1398. var renderService = coreRenderer($window)
  1399. renderService.setEventCallback(function(e) {
  1400. if (e.redraw === false) e.redraw = undefined
  1401. else redraw()
  1402. })
  1403. var callbacks = []
  1404. function subscribe(key1, callback) {
  1405. unsubscribe(key1)
  1406. callbacks.push(key1, throttle(callback))
  1407. }
  1408. function unsubscribe(key1) {
  1409. var index = callbacks.indexOf(key1)
  1410. if (index > -1) callbacks.splice(index, 2)
  1411. }
  1412. function redraw() {
  1413. for (var i = 1; i < callbacks.length; i += 2) {
  1414. callbacks[i]()
  1415. }
  1416. }
  1417. return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render}
  1418. }
  1419. var redrawService = _11(window)
  1420. requestService.setCompletionCallback(redrawService.redraw)
  1421. var _16 = function(redrawService0) {
  1422. return function(root, component) {
  1423. if (component === null) {
  1424. redrawService0.render(root, [])
  1425. redrawService0.unsubscribe(root)
  1426. return
  1427. }
  1428.  
  1429. if (component.view == null && typeof component !== "function") throw new Error("m.mount(element, component) expects a component, not a vnode")
  1430.  
  1431. var run0 = function() {
  1432. redrawService0.render(root, Vnode(component))
  1433. }
  1434. redrawService0.subscribe(root, run0)
  1435. redrawService0.redraw()
  1436. }
  1437. }
  1438. m.mount = _16(redrawService)
  1439. var Promise = PromisePolyfill
  1440. var parseQueryString = function(string) {
  1441. if (string === "" || string == null) return {}
  1442. if (string.charAt(0) === "?") string = string.slice(1)
  1443. var entries = string.split("&"), data0 = {}, counters = {}
  1444. for (var i = 0; i < entries.length; i++) {
  1445. var entry = entries[i].split("=")
  1446. var key5 = decodeURIComponent(entry[0])
  1447. var value = entry.length === 2 ? decodeURIComponent(entry[1]) : ""
  1448. if (value === "true") value = true
  1449. else if (value === "false") value = false
  1450. var levels = key5.split(/\]\[?|\[/)
  1451. var cursor = data0
  1452. if (key5.indexOf("[") > -1) levels.pop()
  1453. for (var j = 0; j < levels.length; j++) {
  1454. var level = levels[j], nextLevel = levels[j + 1]
  1455. var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10))
  1456. var isValue = j === levels.length - 1
  1457. if (level === "") {
  1458. var key6 = levels.slice(0, j).join()
  1459. if (counters[key6] == null) counters[key6] = 0
  1460. level = counters[key6]++
  1461. }
  1462. if (cursor[level] == null) {
  1463. cursor[level] = isValue ? value : isNumber ? [] : {}
  1464. }
  1465. cursor = cursor[level]
  1466. }
  1467. }
  1468. return data0
  1469. }
  1470. var coreRouter = function($window) {
  1471. var supportsPushState = typeof $window.history.pushState === "function"
  1472. var callAsync0 = typeof setImmediate === "function" ? setImmediate : setTimeout
  1473. function normalize1(fragment0) {
  1474. var data = $window.location[fragment0].replace(/(?:%[a-f89][a-f0-9])+/gim, decodeURIComponent)
  1475. if (fragment0 === "pathname" && data[0] !== "/") data = "/" + data
  1476. return data
  1477. }
  1478. var asyncId
  1479. function debounceAsync(callback0) {
  1480. return function() {
  1481. if (asyncId != null) return
  1482. asyncId = callAsync0(function() {
  1483. asyncId = null
  1484. callback0()
  1485. })
  1486. }
  1487. }
  1488. function parsePath(path, queryData, hashData) {
  1489. var queryIndex = path.indexOf("?")
  1490. var hashIndex = path.indexOf("#")
  1491. var pathEnd = queryIndex > -1 ? queryIndex : hashIndex > -1 ? hashIndex : path.length
  1492. if (queryIndex > -1) {
  1493. var queryEnd = hashIndex > -1 ? hashIndex : path.length
  1494. var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd))
  1495. for (var key4 in queryParams) queryData[key4] = queryParams[key4]
  1496. }
  1497. if (hashIndex > -1) {
  1498. var hashParams = parseQueryString(path.slice(hashIndex + 1))
  1499. for (var key5 in hashParams) hashData[key5] = hashParams[key5]
  1500. }
  1501. return path.slice(0, pathEnd)
  1502. }
  1503. var router = {prefix: "#!"}
  1504. router.getPath = function() {
  1505. var type2 = router.prefix.charAt(0)
  1506. switch (type2) {
  1507. case "#": return normalize1("hash").slice(router.prefix.length)
  1508. case "?": return normalize1("search").slice(router.prefix.length) + normalize1("hash")
  1509. default: return normalize1("pathname").slice(router.prefix.length) + normalize1("search") + normalize1("hash")
  1510. }
  1511. }
  1512. router.setPath = function(path, data, options) {
  1513. var queryData = {}, hashData = {}
  1514. path = parsePath(path, queryData, hashData)
  1515. if (data != null) {
  1516. for (var key4 in data) queryData[key4] = data[key4]
  1517. path = path.replace(/:([^\/]+)/g, function(match2, token) {
  1518. delete queryData[token]
  1519. return data[token]
  1520. })
  1521. }
  1522. var query = buildQueryString(queryData)
  1523. if (query) path += "?" + query
  1524. var hash = buildQueryString(hashData)
  1525. if (hash) path += "#" + hash
  1526. if (supportsPushState) {
  1527. var state = options ? options.state : null
  1528. var title = options ? options.title : null
  1529. $window.onpopstate()
  1530. if (options && options.replace) $window.history.replaceState(state, title, router.prefix + path)
  1531. else $window.history.pushState(state, title, router.prefix + path)
  1532. }
  1533. else $window.location.href = router.prefix + path
  1534. }
  1535. router.defineRoutes = function(routes, resolve, reject) {
  1536. function resolveRoute() {
  1537. var path = router.getPath()
  1538. var params = {}
  1539. var pathname = parsePath(path, params, params)
  1540. var state = $window.history.state
  1541. if (state != null) {
  1542. for (var k in state) params[k] = state[k]
  1543. }
  1544. for (var route0 in routes) {
  1545. var matcher = new RegExp("^" + route0.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
  1546. if (matcher.test(pathname)) {
  1547. pathname.replace(matcher, function() {
  1548. var keys = route0.match(/:[^\/]+/g) || []
  1549. var values = [].slice.call(arguments, 1, -2)
  1550. for (var i = 0; i < keys.length; i++) {
  1551. params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i])
  1552. }
  1553. resolve(routes[route0], params, path, route0)
  1554. })
  1555. return
  1556. }
  1557. }
  1558. reject(path, params)
  1559. }
  1560. if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
  1561. else if (router.prefix.charAt(0) === "#") $window.onhashchange = resolveRoute
  1562. resolveRoute()
  1563. }
  1564. return router
  1565. }
  1566. var _20 = function($window, redrawService0) {
  1567. var routeService = coreRouter($window)
  1568. var identity = function(v) {return v}
  1569. var render1, component, attrs3, currentPath, lastUpdate
  1570. var route = function(root, defaultRoute, routes) {
  1571. if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
  1572. var run1 = function() {
  1573. if (render1 != null) redrawService0.render(root, render1(Vnode(component, attrs3.key, attrs3)))
  1574. }
  1575. var bail = function(path) {
  1576. if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true})
  1577. else throw new Error("Could not resolve default route " + defaultRoute)
  1578. }
  1579. routeService.defineRoutes(routes, function(payload, params, path) {
  1580. var update = lastUpdate = function(routeResolver, comp) {
  1581. if (update !== lastUpdate) return
  1582. component = comp != null && (typeof comp.view === "function" || typeof comp === "function")? comp : "div"
  1583. attrs3 = params, currentPath = path, lastUpdate = null
  1584. render1 = (routeResolver.render || identity).bind(routeResolver)
  1585. run1()
  1586. }
  1587. if (payload.view || typeof payload === "function") update({}, payload)
  1588. else {
  1589. if (payload.onmatch) {
  1590. Promise.resolve(payload.onmatch(params, path)).then(function(resolved) {
  1591. update(payload, resolved)
  1592. }, bail)
  1593. }
  1594. else update(payload, "div")
  1595. }
  1596. }, bail)
  1597. redrawService0.subscribe(root, run1)
  1598. }
  1599. route.set = function(path, data, options) {
  1600. if (lastUpdate != null) {
  1601. options = options || {}
  1602. options.replace = true
  1603. }
  1604. lastUpdate = null
  1605. routeService.setPath(path, data, options)
  1606. }
  1607. route.get = function() {return currentPath}
  1608. route.prefix = function(prefix0) {routeService.prefix = prefix0}
  1609. route.link = function(vnode1) {
  1610. vnode1.dom.setAttribute("href", routeService.prefix + vnode1.attrs.href)
  1611. vnode1.dom.onclick = function(e) {
  1612. if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
  1613. e.preventDefault()
  1614. e.redraw = false
  1615. var href = this.getAttribute("href")
  1616. if (href.indexOf(routeService.prefix) === 0) href = href.slice(routeService.prefix.length)
  1617. route.set(href, undefined, undefined)
  1618. }
  1619. }
  1620. route.param = function(key3) {
  1621. if(typeof attrs3 !== "undefined" && typeof key3 !== "undefined") return attrs3[key3]
  1622. return attrs3
  1623. }
  1624. return route
  1625. }
  1626. m.route = _20(window, redrawService)
  1627. m.withAttr = function(attrName, callback1, context) {
  1628. return function(e) {
  1629. callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
  1630. }
  1631. }
  1632. var _28 = coreRenderer(window)
  1633. m.render = _28.render
  1634. m.redraw = redrawService.redraw
  1635. m.request = requestService.request
  1636. m.jsonp = requestService.jsonp
  1637. m.parseQueryString = parseQueryString
  1638. m.buildQueryString = buildQueryString
  1639. m.version = "1.1.3"
  1640. m.vnode = Vnode
  1641. if ('object' !== "undefined") module["exports"] = m
  1642. else {}
  1643. }());
  1644. });
  1645.  
  1646. const restrictScroll = function(e) {
  1647. const toc = e.currentTarget
  1648. const maxScroll = toc.scrollHeight - toc.offsetHeight
  1649. if (toc.scrollTop + e.deltaY < 0) {
  1650. toc.scrollTop = 0
  1651. e.preventDefault()
  1652. } else if (toc.scrollTop + e.deltaY > maxScroll) {
  1653. toc.scrollTop = maxScroll
  1654. e.preventDefault()
  1655. }
  1656. e.redraw = false
  1657. }
  1658.  
  1659. const TOC = function({ $headings, $activeHeading, onClickHeading }) {
  1660. // $activeHeading.subscribe(activeIndex => {})
  1661. const toTree = function(headings) {
  1662. let i = 0
  1663. let tree = { level: 0, children: [] }
  1664. let stack = [tree]
  1665. const top = arr => arr.slice(-1)[0]
  1666.  
  1667. while (i < headings.length) {
  1668. let { level, isActive } = headings[i]
  1669. if (level === stack.length) {
  1670. const node = {
  1671. heading: headings[i],
  1672. children: []
  1673. }
  1674. top(stack).children.push(node)
  1675. stack.push(node)
  1676. if (isActive) {
  1677. stack.forEach(node => {
  1678. if (node.heading) {
  1679. node.heading.isActive = true
  1680. }
  1681. })
  1682. }
  1683. i++
  1684. } else if (level < stack.length) {
  1685. stack.pop()
  1686. } else if (level > stack.length) {
  1687. const node = {
  1688. heading: null,
  1689. children: []
  1690. }
  1691. top(stack).children.push(node)
  1692. stack.push(node)
  1693. }
  1694. }
  1695. return tree
  1696. }
  1697.  
  1698. const UL = (children, { isRoot = false } = {}) =>
  1699. mithril(
  1700. 'ul',
  1701. { onwheel: isRoot && restrictScroll, onclick: isRoot && onClickHeading },
  1702. children.map(LI)
  1703. )
  1704.  
  1705. const LI = ({ heading, children }, index) =>
  1706. mithril(
  1707. 'li',
  1708. { class: heading && heading.isActive ? 'active' : '', key: index },
  1709. [
  1710. heading &&
  1711. mithril('a', { href: `#${heading.anchor}` }, heading.node.textContent),
  1712. children && children.length && UL(children)
  1713. ].filter(Boolean)
  1714. )
  1715.  
  1716. return {
  1717. oncreate({ dom }) {
  1718. // scroll to heading if out of view
  1719. $activeHeading.subscribe(index => {
  1720. const target = [].slice.apply(dom.querySelectorAll('.active')).pop()
  1721. if (target) {
  1722. const targetRect = target.getBoundingClientRect()
  1723. const containerRect = dom.getBoundingClientRect()
  1724. const outOfView =
  1725. targetRect.top > containerRect.bottom ||
  1726. targetRect.bottom < containerRect.top
  1727. if (outOfView) {
  1728. scrollTo({
  1729. targetElem: target,
  1730. scrollElem: dom,
  1731. maxDuration: 0,
  1732. topMargin: dom.offsetHeight / 2 - target.offsetHeight / 2
  1733. })
  1734. }
  1735. }
  1736. })
  1737. Stream.combine($headings, $activeHeading, () => null).subscribe(_ =>
  1738. mithril.redraw()
  1739. )
  1740. },
  1741. view() {
  1742. $headings().forEach((h, i) => (h.isActive = i === $activeHeading()))
  1743. const tree = toTree($headings())
  1744. return UL(tree.children, { isRoot: true })
  1745. }
  1746. }
  1747. }
  1748.  
  1749. const stop = e => {
  1750. e.stopPropagation()
  1751. e.preventDefault()
  1752. }
  1753.  
  1754. const Handle = function({ $userOffset }) {
  1755. let [sClientX, sClientY] = [0, 0]
  1756. let [sOffsetX, sOffsetY] = [0, 0]
  1757.  
  1758. const onDrag = throttle(e => {
  1759. stop(e)
  1760. let [dX, dY] = [e.clientX - sClientX, e.clientY - sClientY]
  1761. $userOffset([sOffsetX + dX, sOffsetY + dY])
  1762. e.redraw = false
  1763. })
  1764.  
  1765. const onDragEnd = e => {
  1766. window.removeEventListener('mousemove', onDrag)
  1767. window.removeEventListener('mouseup', onDragEnd)
  1768. e.redraw = false
  1769. }
  1770.  
  1771. const onDragStart = e => {
  1772. if (e.button === 0) {
  1773. stop(e)
  1774. sClientX = e.clientX
  1775. sClientY = e.clientY
  1776. sOffsetX = $userOffset()[0]
  1777. sOffsetY = $userOffset()[1]
  1778. window.addEventListener('mousemove', onDrag)
  1779. window.addEventListener('mouseup', onDragEnd)
  1780. }
  1781. e.redraw = false
  1782. }
  1783.  
  1784. return {
  1785. view() {
  1786. return mithril(
  1787. '.handle',
  1788. {
  1789. onmousedown: onDragStart
  1790. },
  1791. 'table of contents'
  1792. )
  1793. }
  1794. }
  1795. }
  1796.  
  1797. const ARTICLE_TOC_GAP = 150
  1798.  
  1799. const makeSticky = function(options) {
  1800. let {
  1801. ref,
  1802. scrollable,
  1803. popper,
  1804. direction,
  1805. gap,
  1806. $refChange,
  1807. $scroll,
  1808. $offset,
  1809. $topMargin
  1810. } = options
  1811. let $refRect = Stream.combine($refChange, () => {
  1812. let refRect = ref.getBoundingClientRect()
  1813. let refStyle = window.getComputedStyle(ref)
  1814. let scrollTop = getScroll(scrollable, 'top')
  1815. let scrollLeft = getScroll(scrollable, 'left')
  1816. let refFullRect = {
  1817. top: refRect.top - scrollTop,
  1818. right: refRect.right - scrollLeft,
  1819. bottom: refRect.bottom - scrollTop,
  1820. left: refRect.left - scrollLeft,
  1821. width: refRect.width,
  1822. height: refRect.height
  1823. }
  1824. if (refStyle['box-sizing'] === 'border-box') {
  1825. refFullRect.left += num(refStyle['padding-left'])
  1826. refFullRect.right -= num(refStyle['padding-right'])
  1827. refFullRect.width -=
  1828. num(refStyle['padding-left']) + num(refStyle['padding-right'])
  1829. }
  1830. return refFullRect
  1831. })
  1832. let popperMetric = popper.getBoundingClientRect()
  1833. const scrollableTop =
  1834. scrollable === document.body ? 0 : scrollable.getBoundingClientRect().top
  1835. return Stream.combine(
  1836. $refRect,
  1837. $scroll,
  1838. $offset,
  1839. $topMargin,
  1840. (ref, [scrollX, scrollY], [offsetX, offsetY], topMargin) => {
  1841. let x =
  1842. direction === 'right'
  1843. ? ref.right + gap
  1844. : ref.left - gap - popperMetric.width
  1845. x = Math.min(Math.max(0, x), window.innerWidth - popperMetric.width) // restrict to visible area
  1846. let y = Math.max(scrollableTop + topMargin, ref.top - scrollY)
  1847. return {
  1848. position: 'fixed',
  1849. left: 0,
  1850. top: 0,
  1851. transform: translate3d(x + offsetX, y + offsetY)
  1852. }
  1853. }
  1854. )
  1855. }
  1856.  
  1857. const getOptimalContainerPos = function(article) {
  1858. const {
  1859. top,
  1860. left,
  1861. right,
  1862. bottom,
  1863. height,
  1864. width
  1865. } = article.getBoundingClientRect()
  1866.  
  1867. const depthOf = function(elem) {
  1868. let depth = 0
  1869. while (elem) {
  1870. elem = elem.parentElement
  1871. depth++
  1872. }
  1873. return depth
  1874. }
  1875. const depthOfPoint = function([x, y]) {
  1876. const elem = document.elementFromPoint(x, y)
  1877. return elem && depthOf(elem)
  1878. }
  1879. const gap = ARTICLE_TOC_GAP
  1880. const testWidth = 200
  1881. const testHeight = 400
  1882. const leftSlotTestPoints = [
  1883. left - gap - testWidth,
  1884. left - gap - testWidth / 2,
  1885. left - gap
  1886. ]
  1887. .map(x => [top, top + testHeight / 2, top + testHeight].map(y => [x, y]))
  1888. .reduce((prev, cur) => prev.concat(cur), [])
  1889. const rightSlotTestPoints = [
  1890. right + gap,
  1891. right + gap + testWidth / 2,
  1892. right + gap + testWidth
  1893. ]
  1894. .map(x => [top, top + testHeight / 2, top + testHeight].map(y => [x, y]))
  1895. .reduce((prev, cur) => prev.concat(cur), [])
  1896. const leftDepths = leftSlotTestPoints.map(depthOfPoint).filter(Boolean)
  1897. const rightDepths = rightSlotTestPoints.map(depthOfPoint).filter(Boolean)
  1898. const leftAvgDepth = leftDepths.length
  1899. ? leftDepths.reduce((a, b) => a + b, 0) / leftDepths.length
  1900. : null
  1901. const rightAvgDepth = rightDepths.length
  1902. ? rightDepths.reduce((a, b) => a + b, 0) / rightDepths.length
  1903. : null
  1904.  
  1905. if (!leftAvgDepth) return { direction: 'right' }
  1906. if (!rightAvgDepth) return { direction: 'left' }
  1907. const spaceDiff = document.documentElement.offsetWidth - right - left
  1908. const scoreDiff =
  1909. spaceDiff * 1 + (rightAvgDepth - leftAvgDepth) * 9 * -10 + 20 // I do like right better
  1910. return scoreDiff > 0 ? { direction: 'right' } : { direction: 'left' }
  1911. }
  1912.  
  1913. const Container = function({
  1914. article,
  1915. scrollable,
  1916. $headings,
  1917. theme,
  1918. $activeHeading,
  1919. $isShow,
  1920. $userOffset,
  1921. $relayout,
  1922. $scroll,
  1923. $topbarHeight,
  1924. onClickHeading
  1925. }) {
  1926. const handle = Handle({ $userOffset })
  1927. const toc = TOC({ $headings, $activeHeading, onClickHeading })
  1928. return {
  1929. oncreate({ dom }) {
  1930. const { direction } = getOptimalContainerPos(article)
  1931. this.$style = makeSticky({
  1932. ref: article,
  1933. scrollable: scrollable,
  1934. popper: dom,
  1935. direction: direction,
  1936. gap: ARTICLE_TOC_GAP,
  1937. $topMargin: $topbarHeight.map(h => (h || 0) + 50),
  1938. $refChange: $relayout,
  1939. $scroll: $scroll,
  1940. $offset: $userOffset
  1941. })
  1942. this.$style.subscribe(_ => mithril.redraw())
  1943. },
  1944. view() {
  1945. return mithril(
  1946. '#smarttoc',
  1947. {
  1948. class: [
  1949. theme || 'light',
  1950. $headings().filter(h => h.level <= 2).length > 50 && 'lengthy',
  1951. $isShow() ? '' : 'hidden'
  1952. ]
  1953. .filter(Boolean)
  1954. .join(' '),
  1955. style: this.$style && this.$style()
  1956. },
  1957. [mithril(handle), mithril(toc)]
  1958. )
  1959. }
  1960. }
  1961. }
  1962.  
  1963. const Extender = function({ $headings, scrollable, $isShow, $relayout }) {
  1964. const $extender = Stream()
  1965. // toc: extend body height so we can scroll to the last heading
  1966. let extender = document.createElement('DIV')
  1967. extender.id = 'smarttoc-extender'
  1968. Stream.combine($isShow, $relayout, $headings, (isShow, _, headings) => {
  1969. setTimeout(() => {
  1970. // some delay to ensure page is stable ?
  1971. let lastHeading = headings.slice(-1)[0].node
  1972. let lastRect = lastHeading.getBoundingClientRect()
  1973. let extenderHeight = 0
  1974. if (scrollable === document.body) {
  1975. let heightBelowLastRect =
  1976. document.documentElement.scrollHeight -
  1977. (lastRect.bottom + document.documentElement.scrollTop) -
  1978. num(extender.style.height) // in case we are there already
  1979. extenderHeight = isShow
  1980. ? Math.max(
  1981. window.innerHeight - lastRect.height - heightBelowLastRect,
  1982. 0
  1983. )
  1984. : 0
  1985. } else {
  1986. let scrollRect = scrollable.getBoundingClientRect()
  1987. let heightBelowLastRect =
  1988. scrollRect.top +
  1989. scrollable.scrollHeight -
  1990. getScroll(scrollable) - // bottom of scrollable relative to viewport
  1991. lastRect.bottom -
  1992. num(extender.style.height) // in case we are there already
  1993. extenderHeight = isShow
  1994. ? Math.max(
  1995. scrollRect.height - lastRect.height - heightBelowLastRect,
  1996. 0
  1997. )
  1998. : 0
  1999. }
  2000. $extender({
  2001. height: extenderHeight
  2002. })
  2003. }, 300)
  2004. })
  2005. $extender.subscribe(style => applyStyle(extender, style))
  2006. return extender
  2007. }
  2008.  
  2009. const relayoutStream = function(article, $resize, $isShow) {
  2010. const readableStyle = function(article) {
  2011. let computed = window.getComputedStyle(article)
  2012. let fontSize = num(computed.fontSize)
  2013. let bestWidth = Math.min(Math.max(fontSize, 12), 16) * 66
  2014. if (computed['box-sizing'] === 'border-box') {
  2015. bestWidth +=
  2016. num(computed['padding-left']) + num(computed['padding-right'])
  2017. }
  2018.  
  2019. return Object.assign(
  2020. num(computed.marginLeft) || num(computed.marginRight)
  2021. ? {}
  2022. : {
  2023. marginLeft: 'auto',
  2024. marginRight: 'auto'
  2025. },
  2026. num(computed.maxWidth)
  2027. ? {}
  2028. : {
  2029. maxWidth: bestWidth
  2030. }
  2031. )
  2032. }
  2033. let oldStyle = article.style.cssText
  2034. let newStyle = readableStyle(article)
  2035. let $relayout = $isShow.map(isShow => {
  2036. if (isShow) {
  2037. applyStyle(article, newStyle)
  2038. return article
  2039. } else {
  2040. applyStyle(article, oldStyle)
  2041. }
  2042. })
  2043. return Stream.combine($relayout, $resize, () => null)
  2044. }
  2045.  
  2046. const addAnchors = function(headings) {
  2047. const anchoredHeadings = headings.map(function({ node, level, anchor }) {
  2048. if (!anchor) {
  2049. anchor =
  2050. node.id ||
  2051. [].slice
  2052. .apply(node.children)
  2053. .filter(elem => elem.tagName === 'A')
  2054. .map(a => {
  2055. let href = a.getAttribute('href') || ''
  2056. return href.startsWith('#') ? href.substr(1) : a.id
  2057. })
  2058. .filter(Boolean)[0]
  2059. if (!anchor) {
  2060. anchor = node.id = unique(safe(node.textContent))
  2061. } else {
  2062. anchor = unique(anchor)
  2063. }
  2064. }
  2065. return { node, level, anchor }
  2066. })
  2067. return anchoredHeadings
  2068. }
  2069.  
  2070. const getScrollParent = function(elem) {
  2071. const canScroll = el =>
  2072. ['auto', 'scroll'].includes(window.getComputedStyle(el).overflowY) &&
  2073. el.clientHeight + 1 < el.scrollHeight
  2074. while (elem && elem !== document.body && !canScroll(elem)) {
  2075. elem = elem.parentElement
  2076. }
  2077. log('scrollable', elem)
  2078. draw(elem, 'purple')
  2079. return elem
  2080. }
  2081.  
  2082. const scrollStream = function(scrollable, $isShow) {
  2083. let $scroll = Stream([getScroll(scrollable, 'left'), getScroll(scrollable)])
  2084. let source = scrollable === document.body ? window : scrollable
  2085. Stream.fromEvent(source, 'scroll')
  2086. .filter(() => $isShow())
  2087. .throttle()
  2088. .subscribe(() => {
  2089. $scroll([getScroll(scrollable, 'left'), getScroll(scrollable)])
  2090. })
  2091. return $scroll
  2092. }
  2093.  
  2094. const activeHeadingStream = function(
  2095. $headings,
  2096. scrollable,
  2097. $scroll,
  2098. $relayout,
  2099. $topbarHeight
  2100. ) {
  2101. const $headingScrollYs = Stream.combine(
  2102. $relayout,
  2103. $headings,
  2104. (_, headings) => {
  2105. const scrollableTop =
  2106. (scrollable === document.body
  2107. ? 0
  2108. : scrollable.getBoundingClientRect().top) -
  2109. getScroll(scrollable, 'top')
  2110. return headings.map(
  2111. ({ node }) => node.getBoundingClientRect().top - scrollableTop
  2112. )
  2113. }
  2114. )
  2115.  
  2116. let $curIndex = Stream.combine(
  2117. $headingScrollYs,
  2118. $scroll,
  2119. $topbarHeight,
  2120. function(headingScrollYs, [scrollX, scrollY], topbarHeight = 0) {
  2121. let i = 0
  2122. for (let len = headingScrollYs.length; i < len; i++) {
  2123. if (headingScrollYs[i] > scrollY + topbarHeight + 20) {
  2124. break
  2125. }
  2126. }
  2127. return Math.max(0, i - 1)
  2128. }
  2129. )
  2130.  
  2131. return $curIndex.unique()
  2132. }
  2133.  
  2134. const scrollToHeading = function(
  2135. { node },
  2136. scrollElem,
  2137. onScrollEnd,
  2138. topMargin = 0
  2139. ) {
  2140. scrollTo({
  2141. targetElem: node,
  2142. scrollElem: scrollElem,
  2143. topMargin: topMargin,
  2144. maxDuration: 300,
  2145. callback: onScrollEnd && onScrollEnd.bind(null, node)
  2146. })
  2147. }
  2148.  
  2149. const getTopBarHeight = function(topElem) {
  2150. const findFixedParent = function(elem) {
  2151. const isFixed = elem => {
  2152. let { position, zIndex } = window.getComputedStyle(elem)
  2153. return position === 'fixed' && zIndex
  2154. }
  2155. while (elem !== document.body && !isFixed(elem)) {
  2156. elem = elem.parentElement
  2157. }
  2158. return elem === document.body ? null : elem
  2159. }
  2160. let { left, right, top } = topElem.getBoundingClientRect()
  2161. let leftTopmost = document.elementFromPoint(left + 1, top + 1)
  2162. let rightTopmost = document.elementFromPoint(right - 1, top + 1)
  2163. if (
  2164. leftTopmost &&
  2165. rightTopmost &&
  2166. leftTopmost !== topElem &&
  2167. rightTopmost !== topElem
  2168. ) {
  2169. let leftFixed = findFixedParent(leftTopmost)
  2170. let rightFixed = findFixedParent(rightTopmost)
  2171. if (leftFixed && leftFixed === rightFixed) {
  2172. return leftFixed.offsetHeight
  2173. } else {
  2174. return 0
  2175. }
  2176. } else {
  2177. return 0
  2178. }
  2179. }
  2180.  
  2181. const getTheme = function(article) {
  2182. let elem = article
  2183. try {
  2184. const parseColor = str =>
  2185. str.replace(/rgba?\(/, '').replace(/\).*/, '').split(/, ?/)
  2186. const getBgColor = elem =>
  2187. parseColor(window.getComputedStyle(elem)['background-color'])
  2188. const isTransparent = ([r, g, b, a]) => a === 0
  2189. const isLight = ([r, g, b, a]) => r + g + b > 255 / 2 * 3
  2190. while (elem && elem.parentElement) {
  2191. const color = getBgColor(elem)
  2192. if (isTransparent(color)) {
  2193. elem = elem.parentElement
  2194. } else {
  2195. return isLight(color) ? 'light' : 'dark'
  2196. }
  2197. }
  2198. return 'light'
  2199. } catch (e) {
  2200. return 'light'
  2201. }
  2202. }
  2203.  
  2204. const getRoot = function() {
  2205. let root = document.getElementById('smarttoc_wrapper')
  2206. if (!root) {
  2207. root = document.body.appendChild(document.createElement('DIV'))
  2208. root.id = 'smarttoc_wrapper'
  2209. }
  2210. return root
  2211. }
  2212.  
  2213.  
  2214. // 生成目录
  2215. function createTOC({
  2216. article,
  2217. $headings: $headings_,
  2218. userOffset = [0, 0]
  2219. }) {
  2220. const $headings = $headings_.map(addAnchors)
  2221. insertCSS(tocCSS, 'smarttoc__css')
  2222.  
  2223. const scrollable = getScrollParent(article)
  2224. const theme = getTheme(article)
  2225. log('theme', theme)
  2226.  
  2227. const $isShow = Stream(true)
  2228. const $topbarHeight = Stream()
  2229. const $resize = Stream.combine(
  2230. Stream.fromEvent(window, 'resize'),
  2231. Stream.fromEvent(document, 'readystatechange'),
  2232. Stream.fromEvent(document, 'load'),
  2233. Stream.fromEvent(document, 'DOMContentLoaded'),
  2234. () => null
  2235. )
  2236. .filter(() => $isShow())
  2237. .throttle()
  2238. const $scroll = scrollStream(scrollable, $isShow)
  2239. const $relayout = relayoutStream(article, $resize, $isShow)
  2240. const $activeHeading = activeHeadingStream(
  2241. $headings,
  2242. scrollable,
  2243. $scroll,
  2244. $relayout,
  2245. $topbarHeight
  2246. )
  2247. const $userOffset = Stream(userOffset)
  2248.  
  2249. /*
  2250. scrollable.appendChild(
  2251. Extender({ $headings, scrollable, $isShow, $relayout })
  2252. )
  2253. */
  2254.  
  2255. const onScrollEnd = function(node) {
  2256. if ($topbarHeight() == null) {
  2257. setTimeout(() => {
  2258. $topbarHeight(getTopBarHeight(node))
  2259. log('topBarHeight', $topbarHeight())
  2260. if ($topbarHeight()) {
  2261. scrollToHeading({ node }, scrollable, null, $topbarHeight() + 10)
  2262. }
  2263. }, 300)
  2264. }
  2265. }
  2266.  
  2267. const onClickHeading = function(e) {
  2268. e.preventDefault()
  2269. e.stopPropagation()
  2270. const anchor = e.target.getAttribute('href').substr(1)
  2271. const heading = $headings().find(heading => heading.anchor === anchor)
  2272. scrollToHeading(
  2273. heading,
  2274. scrollable,
  2275. onScrollEnd,
  2276. ($topbarHeight() || 0) + 10
  2277. )
  2278. }
  2279.  
  2280. mithril.mount(
  2281. getRoot(),
  2282. Container({
  2283. article,
  2284. scrollable,
  2285. $headings,
  2286. theme,
  2287. $activeHeading,
  2288. $isShow,
  2289. $userOffset,
  2290. $relayout,
  2291. $scroll,
  2292. $topbarHeight,
  2293. onClickHeading
  2294. })
  2295. )
  2296.  
  2297. // now show what we've found
  2298. if (article.getBoundingClientRect().top > window.innerHeight - 50) {
  2299. scrollToHeading(
  2300. $headings()[0],
  2301. scrollable,
  2302. onScrollEnd,
  2303. ($topbarHeight() || 0) + 10
  2304. )
  2305. }
  2306.  
  2307. return {
  2308. isValid: () =>
  2309. document.body.contains(article) && article.contains($headings()[0].node),
  2310.  
  2311. isShow: () => $isShow(),
  2312.  
  2313. toggle: () => $isShow(!$isShow()),
  2314.  
  2315. next: () => {
  2316. if ($isShow()) {
  2317. let nextIdx = Math.min($headings().length - 1, $activeHeading() + 1)
  2318. scrollToHeading(
  2319. $headings()[nextIdx],
  2320. scrollable,
  2321. onScrollEnd,
  2322. ($topbarHeight() || 0) + 10
  2323. )
  2324. }
  2325. },
  2326.  
  2327. prev: () => {
  2328. if ($isShow()) {
  2329. let prevIdx = Math.max(0, $activeHeading() - 1)
  2330. scrollToHeading(
  2331. $headings()[prevIdx],
  2332. scrollable,
  2333. onScrollEnd,
  2334. ($topbarHeight() || 0) + 10
  2335. )
  2336. }
  2337. },
  2338.  
  2339. dispose: () => {
  2340. log('dispose')
  2341. $isShow(false)
  2342. mithril.render(getRoot(), mithril(''))
  2343. return { userOffset: $userOffset() }
  2344. }
  2345. }
  2346. }
  2347.  
  2348. const pathToTop = function(elem, maxLvl = -1) {
  2349. assert(elem, 'no element given')
  2350. const path = []
  2351. while (elem && maxLvl--) {
  2352. path.push(elem)
  2353. elem = elem.parentElement
  2354. }
  2355. return path
  2356. }
  2357.  
  2358. const isStrongAlsoHeading = function(rootElement = document) {
  2359. return false
  2360. // return rootElement.querySelectorAll('p > strong:only-child').length > 3
  2361. }
  2362.  
  2363. const extractArticle = function(rootElement = document) {
  2364. log('extracting article')
  2365.  
  2366. const scores = new Map()
  2367.  
  2368. function addScore(elem, inc) {
  2369. scores.set(elem, (scores.get(elem) || 0) + inc)
  2370. }
  2371.  
  2372. function updateScore(elem, weight) {
  2373. let path = pathToTop(elem, weight.length)
  2374. path.forEach((elem, distance) => addScore(elem, weight[distance]))
  2375. }
  2376.  
  2377. // weigh nodes by factor: "selector", "distance from this node"
  2378. const weights = {
  2379. h1: [0, 100, 60, 40, 30, 25, 22].map(s => s * 0.4),
  2380. h2: [0, 100, 60, 40, 30, 25, 22],
  2381. h3: [0, 100, 60, 40, 30, 25, 22].map(s => s * 0.5),
  2382. h4: [0, 100, 60, 40, 30, 25, 22].map(s => s * 0.5 * 0.5),
  2383. h5: [0, 100, 60, 40, 30, 25, 22].map(s => s * 0.5 * 0.5 * 0.5),
  2384. h6: [0, 100, 60, 40, 30, 25, 22].map(s => s * 0.5 * 0.5 * 0.5 * 0.5),
  2385. article: [500],
  2386. '.article': [500],
  2387. '.content': [101],
  2388. sidebar: [-500],
  2389. '.sidebar': [-500],
  2390. aside: [-500],
  2391. '.aside': [-500],
  2392. nav: [-500],
  2393. '.nav': [-500],
  2394. '.navigation': [-500],
  2395. '.toc': [-500],
  2396. '.table-of-contents': [-500]
  2397. }
  2398. const selectors = Object.keys(weights)
  2399. selectors
  2400. .map(selector => ({
  2401. selector: selector,
  2402. elems: [].slice.apply(rootElement.querySelectorAll(selector))
  2403. }))
  2404. .forEach(({ selector, elems }) =>
  2405. elems.forEach(elem => updateScore(elem, weights[selector]))
  2406. )
  2407. const sorted = [...scores].sort((a, b) => b[1] - a[1])
  2408.  
  2409. // reweigh top 5 nodes by factor: "take-lots-vertical-space", "contain-less-links", "too-narrow"
  2410. let candicates = sorted
  2411. .slice(0, 5)
  2412. .filter(Boolean)
  2413. .map(([elem, score]) => ({ elem, score }))
  2414.  
  2415. let isTooNarrow = e => e.scrollWidth < 400 // rule out sidebars
  2416.  
  2417. candicates.forEach(c => {
  2418. if (isTooNarrow(c.elem)) {
  2419. c.isNarrow = true
  2420. candicates.forEach(parent => {
  2421. if (parent.elem.contains(c.elem)) {
  2422. parent.score *= 0.7
  2423. }
  2424. })
  2425. }
  2426. })
  2427. candicates = candicates.filter(c => !c.isNarrow)
  2428.  
  2429. const reweighted = candicates
  2430. .map(({ elem, score }) => [
  2431. elem,
  2432. score *
  2433. Math.log(
  2434. elem.scrollHeight *
  2435. elem.scrollHeight /
  2436. (elem.querySelectorAll('a').length || 1)
  2437. ),
  2438. elem.scrollHeight,
  2439. elem.querySelectorAll('a').length
  2440. ])
  2441. .sort((a, b) => b[1] - a[1])
  2442.  
  2443. const article = reweighted.length ? reweighted[0][0] : null
  2444. return article
  2445. }
  2446.  
  2447. const extractHeadings = function(article) {
  2448. log('extracting heading')
  2449.  
  2450. // what to be considered as headings
  2451. const tags = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].concat(
  2452. isStrongAlsoHeading(article) ? 'STRONG' : []
  2453. )
  2454. const tagWeight = tag =>
  2455. ({ H1: 4, H2: 9, H3: 9, H4: 10, H5: 10, H6: 10, STRONG: 10 }[tag])
  2456. const isVisible = elem => elem.offsetHeight !== 0
  2457. const isGroupVisible = headings =>
  2458. headings.filter(isVisible).length >= headings.length * 0.5
  2459. const headingGroup = tags
  2460. .map(tag => [].slice.apply(article.getElementsByTagName(tag)))
  2461. .map((headings, i) => ({
  2462. elems: headings,
  2463. tag: tags[i],
  2464. score: headings.length * tagWeight(tags[i])
  2465. }))
  2466. .filter(heading => heading.score >= 10)
  2467. .filter(heading => isGroupVisible(heading.elems))
  2468. .slice(0, 3)
  2469.  
  2470. // use document sequence
  2471. const validTags = headingGroup.map(headings => headings.tag)
  2472. const acceptNode = node =>
  2473. validTags.includes(node.tagName) && isVisible(node)
  2474. ? NodeFilter.FILTER_ACCEPT
  2475. : NodeFilter.FILTER_SKIP
  2476. const treeWalker = document.createTreeWalker(
  2477. article,
  2478. NodeFilter.SHOW_ELEMENT,
  2479. { acceptNode }
  2480. )
  2481. const headings = []
  2482. while (treeWalker.nextNode()) {
  2483. let node = treeWalker.currentNode
  2484. headings.push({
  2485. node,
  2486. level: validTags.indexOf(node.tagName) + 1
  2487. })
  2488. }
  2489. if (false) {}
  2490. return headings
  2491. }
  2492.  
  2493. function extract() {
  2494. const article = extractArticle(document)
  2495. let $headings
  2496. if (article) {
  2497. $headings = Stream(extractHeadings(article))
  2498.  
  2499. const $articleChange = Stream(null)
  2500. const observer = new MutationObserver(_ => $articleChange(null))
  2501. observer.observe(article, { childList: true })
  2502.  
  2503. $articleChange.throttle(200).subscribe(_ => {
  2504. let headings = extractHeadings(article)
  2505. if (headings && headings.length) {
  2506. $headings(headings)
  2507. }
  2508. })
  2509. }
  2510.  
  2511. return [article, $headings]
  2512. }
  2513.  
  2514. if (isMasterFrame(window)) {
  2515. let toc
  2516.  
  2517. const generate = function(option = {}) {
  2518. let [article, $headings] = extract()
  2519. if (article && $headings && $headings().length) {
  2520. return createTOC(Object.assign({ article, $headings }, option))
  2521. } else {
  2522. toast('No article/headings are detected.')
  2523. return null
  2524. }
  2525. }
  2526.  
  2527. toc = generate()
  2528.  
  2529. setInterval(() => {
  2530. if (toc && !toc.isValid()) {
  2531. let lastState = toc.dispose()
  2532. toc = generate(lastState)
  2533. }
  2534. }, 3000)
  2535.  
  2536. /*chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  2537. try {
  2538. if (toc) {
  2539. toc[request]()
  2540. } else {
  2541. toc = generate()
  2542. }
  2543. } catch (e) {
  2544. console.error(e)
  2545. }
  2546. sendResponse(true)
  2547. })*/
  2548. }
  2549.  
  2550. }());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址