refactor(core): add router
This commit is contained in:
parent
8bee8f0213
commit
30da0d5d46
17 changed files with 316 additions and 88 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { merge, camelize, isPrimitive } from './util/core'
|
import { merge, hyphenate, isPrimitive } from './util/core'
|
||||||
|
|
||||||
const config = merge({
|
const config = merge({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
|
@ -23,7 +23,7 @@ const script = document.currentScript ||
|
||||||
|
|
||||||
if (script) {
|
if (script) {
|
||||||
for (const prop in config) {
|
for (const prop in config) {
|
||||||
const val = script.getAttribute('data-' + camelize(prop))
|
const val = script.getAttribute('data-' + hyphenate(prop))
|
||||||
|
|
||||||
if (isPrimitive(val)) {
|
if (isPrimitive(val)) {
|
||||||
config[prop] = val === '' ? true : val
|
config[prop] = val === '' ? true : val
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import { dom, on } from '../util/dom'
|
import { body, on } from '../util/dom'
|
||||||
import * as sidebar from './sidebar'
|
import * as sidebar from './sidebar'
|
||||||
|
|
||||||
export function eventMixin (Docsify) {
|
export function eventMixin (Docsify) {
|
||||||
|
|
@ -14,6 +14,6 @@ export function initEvent (vm) {
|
||||||
if (vm.config.coverpage) {
|
if (vm.config.coverpage) {
|
||||||
!isMobile && on('scroll', sidebar.sticky)
|
!isMobile && on('scroll', sidebar.sticky)
|
||||||
} else {
|
} else {
|
||||||
dom.body.classList.add('sticky')
|
body.classList.add('sticky')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import { getNode, on, dom } from '../util/dom'
|
import { getNode, on, body } from '../util/dom'
|
||||||
/**
|
/**
|
||||||
* Toggle button
|
* Toggle button
|
||||||
*/
|
*/
|
||||||
export function btn (el) {
|
export function btn (el) {
|
||||||
const toggle = () => dom.body.classList.toggle('close')
|
const toggle = () => body.classList.toggle('close')
|
||||||
|
|
||||||
el = getNode(el)
|
el = getNode(el)
|
||||||
on(el, 'click', toggle)
|
on(el, 'click', toggle)
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,33 @@
|
||||||
import progressbar from '../render/progressbar'
|
import progressbar from '../render/progressbar'
|
||||||
import { noop } from '../util/core'
|
import { noop } from '../util/core'
|
||||||
|
|
||||||
|
const cache = {}
|
||||||
|
const RUN_VERSION = Date.now()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple ajax get
|
* Simple ajax get
|
||||||
* @param {String} url
|
* @param {string} url
|
||||||
* @param {Boolean} [loading=false] has loading bar
|
* @param {boolean} [hasBar=false] has progress bar
|
||||||
* @return { then(resolve, reject), abort }
|
* @return { then(resolve, reject), abort }
|
||||||
*/
|
*/
|
||||||
export function get (url, hasLoading = false) {
|
export function get (url, hasBar = false) {
|
||||||
const xhr = new XMLHttpRequest()
|
const xhr = new XMLHttpRequest()
|
||||||
|
const on = function () {
|
||||||
|
xhr.addEventListener.apply(xhr, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
url += (/\?(\w+)=/g.test(url) ? '&' : '?') + `v=${RUN_VERSION}`
|
||||||
|
|
||||||
|
if (cache[url]) {
|
||||||
|
return { then: cb => cb(cache[url]), abort: noop }
|
||||||
|
}
|
||||||
|
|
||||||
xhr.open('GET', url)
|
xhr.open('GET', url)
|
||||||
xhr.send()
|
xhr.send()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
then: function (success, error = noop) {
|
then: function (success, error = noop) {
|
||||||
const on = xhr.addEventListener
|
if (hasBar) {
|
||||||
|
|
||||||
if (hasLoading) {
|
|
||||||
const id = setInterval(_ => progressbar({}), 500)
|
const id = setInterval(_ => progressbar({}), 500)
|
||||||
|
|
||||||
on('progress', progressbar)
|
on('progress', progressbar)
|
||||||
|
|
@ -29,9 +39,14 @@ export function get (url, hasLoading = false) {
|
||||||
|
|
||||||
on('error', error)
|
on('error', error)
|
||||||
on('load', ({ target }) => {
|
on('load', ({ target }) => {
|
||||||
target.status >= 400 ? error(target) : success(target.response)
|
if (target.status >= 400) {
|
||||||
|
error(target)
|
||||||
|
} else {
|
||||||
|
cache[url] = target.response
|
||||||
|
success(target.response)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
abort: () => xhr.readyState !== 4 && xhr.abort()
|
abort: _ => xhr.readyState !== 4 && xhr.abort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,43 @@
|
||||||
|
import { get } from './ajax'
|
||||||
import { callHook } from '../init/lifecycle'
|
import { callHook } from '../init/lifecycle'
|
||||||
|
import { getCurrentRoot } from '../route/util'
|
||||||
|
|
||||||
export function fetchMixin (Docsify) {
|
export function fetchMixin (Docsify) {
|
||||||
Docsify.prototype.$fetch = function (path) {
|
let last
|
||||||
// 加载侧边栏、导航、内容
|
|
||||||
|
Docsify.prototype._fetch = function (cb) {
|
||||||
|
const { path } = this.route
|
||||||
|
const { loadNavbar, loadSidebar } = this.config
|
||||||
|
const currentRoot = getCurrentRoot(path)
|
||||||
|
|
||||||
|
// Abort last request
|
||||||
|
last && last.abort && last.abort()
|
||||||
|
|
||||||
|
last = get(this.$getFile(path), true)
|
||||||
|
last.then(text => {
|
||||||
|
this._renderMain(text)
|
||||||
|
if (!loadSidebar) return cb()
|
||||||
|
|
||||||
|
const fn = result => { this._renderSidebar(result); cb() }
|
||||||
|
|
||||||
|
// Load sidebar
|
||||||
|
get(this.$getFile(currentRoot + loadSidebar))
|
||||||
|
.then(fn, _ => get(loadSidebar).then(fn))
|
||||||
|
},
|
||||||
|
_ => this._renderMain(null))
|
||||||
|
|
||||||
|
// Load nav
|
||||||
|
loadNavbar &&
|
||||||
|
get(this.$getFile(currentRoot + loadNavbar))
|
||||||
|
.then(
|
||||||
|
this._renderNav,
|
||||||
|
_ => get(loadNavbar).then(this._renderNav)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initFetch (vm) {
|
export function initFetch (vm) {
|
||||||
vm.$fetch(result => {
|
vm._fetch(result => {
|
||||||
vm.$resetEvents()
|
vm.$resetEvents()
|
||||||
callHook(vm, 'doneEach')
|
callHook(vm, 'doneEach')
|
||||||
})
|
})
|
||||||
|
|
|
||||||
13
src/core/global-api.js
Normal file
13
src/core/global-api.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import * as util from './util'
|
||||||
|
import * as dom from './util/dom'
|
||||||
|
import * as render from './render/compiler'
|
||||||
|
import * as route from './route/util'
|
||||||
|
import { get } from './fetch/ajax'
|
||||||
|
import marked from 'marked'
|
||||||
|
import prism from 'prismjs'
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
window.Docsify = { util, dom, render, route, get }
|
||||||
|
window.marked = marked
|
||||||
|
window.Prism = prism
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,7 @@ import { routeMixin } from './route'
|
||||||
import { renderMixin } from './render'
|
import { renderMixin } from './render'
|
||||||
import { fetchMixin } from './fetch'
|
import { fetchMixin } from './fetch'
|
||||||
import { eventMixin } from './event'
|
import { eventMixin } from './event'
|
||||||
import * as util from './util'
|
import initGlobalAPI from './global-api'
|
||||||
import { get as load } from './fetch/ajax'
|
|
||||||
import * as routeUtil from './route/util'
|
|
||||||
|
|
||||||
function Docsify () {
|
function Docsify () {
|
||||||
this._init()
|
this._init()
|
||||||
|
|
@ -20,9 +18,7 @@ eventMixin(Docsify)
|
||||||
/**
|
/**
|
||||||
* Global API
|
* Global API
|
||||||
*/
|
*/
|
||||||
window.Docsify = {
|
initGlobalAPI()
|
||||||
util: util.merge({ load }, util, routeUtil)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run Docsify
|
* Run Docsify
|
||||||
|
|
|
||||||
50
src/core/render/compiler.js
Normal file
50
src/core/render/compiler.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import marked from 'marked'
|
||||||
|
import Prism from 'prismjs'
|
||||||
|
|
||||||
|
export const renderer = new marked.Renderer()
|
||||||
|
|
||||||
|
export function markdown () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const toc = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render anchor tag
|
||||||
|
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
||||||
|
*/
|
||||||
|
renderer.heading = function (text, level) {
|
||||||
|
const slug = slugify(text)
|
||||||
|
let route = ''
|
||||||
|
|
||||||
|
route = `#/${getRoute()}`
|
||||||
|
toc.push({ level, slug: `${route}#${encodeURIComponent(slug)}`, title: text })
|
||||||
|
|
||||||
|
return `<h${level} id="${slug}"><a href="${route}#${slug}" data-id="${slug}" class="anchor"><span>${text}</span></a></h${level}>`
|
||||||
|
}
|
||||||
|
// highlight code
|
||||||
|
renderer.code = function (code, lang = '') {
|
||||||
|
const hl = Prism.highlight(code, Prism.languages[lang] || Prism.languages.markup)
|
||||||
|
|
||||||
|
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
|
||||||
|
}
|
||||||
|
renderer.link = function (href, title, text) {
|
||||||
|
if (!/:|(\/{2})/.test(href)) {
|
||||||
|
href = `#/${href}`.replace(/\/+/g, '/')
|
||||||
|
}
|
||||||
|
return `<a href="${href}" title="${title || ''}">${text}</a>`
|
||||||
|
}
|
||||||
|
renderer.paragraph = function (text) {
|
||||||
|
if (/^!>/.test(text)) {
|
||||||
|
return tpl.helper('tip', text)
|
||||||
|
} else if (/^\?>/.test(text)) {
|
||||||
|
return tpl.helper('warn', text)
|
||||||
|
}
|
||||||
|
return `<p>${text}</p>`
|
||||||
|
}
|
||||||
|
renderer.image = function (href, title, text) {
|
||||||
|
const url = /:|(\/{2})/.test(href) ? href : ($docsify.basePath + href).replace(/\/+/g, '/')
|
||||||
|
const titleHTML = title ? ` title="${title}"` : ''
|
||||||
|
|
||||||
|
return `<img src="${url}" alt="${text}"${titleHTML} />`
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,26 @@
|
||||||
import { getNode, dom } from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import cssVars from '../util/polyfill/css-vars'
|
import cssVars from '../util/polyfill/css-vars'
|
||||||
import * as tpl from './tpl'
|
import * as tpl from './tpl'
|
||||||
|
|
||||||
|
function renderMain () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNav () {
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSidebar () {
|
||||||
|
}
|
||||||
|
|
||||||
export function renderMixin (Docsify) {
|
export function renderMixin (Docsify) {
|
||||||
Docsify.prototype._renderTo = function (el, content, replace) {
|
Docsify.prototype._renderTo = function (el, content, replace) {
|
||||||
const node = getNode(el)
|
const node = dom.getNode(el)
|
||||||
if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content
|
if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Docsify.prototype._renderSidebar = renderSidebar
|
||||||
|
Docsify.prototype._renderNav = renderNav
|
||||||
|
Docsify.prototype._renderMain = renderMain
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRender (vm) {
|
export function initRender (vm) {
|
||||||
|
|
@ -40,7 +54,7 @@ export function initRender (vm) {
|
||||||
dom.body.insertBefore(navEl, dom.body.children[0])
|
dom.body.insertBefore(navEl, dom.body.children[0])
|
||||||
|
|
||||||
if (config.themeColor) {
|
if (config.themeColor) {
|
||||||
dom.head += tpl.theme(config.themeColor)
|
dom.$.head += tpl.theme(config.themeColor)
|
||||||
// Polyfll
|
// Polyfll
|
||||||
cssVars(config.themeColor)
|
cssVars(config.themeColor)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
import { dom } from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import { isPrimitive } from '../util/core'
|
import { isPrimitive } from '../util/core'
|
||||||
|
|
||||||
let loadingEl
|
let barEl
|
||||||
let timeId
|
let timeId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init progress component
|
* Init progress component
|
||||||
*/
|
*/
|
||||||
function init () {
|
function init () {
|
||||||
if (loadingEl) return
|
|
||||||
const div = dom.create('div')
|
const div = dom.create('div')
|
||||||
|
|
||||||
div.classList.add('progress')
|
div.classList.add('progress')
|
||||||
dom.appendTo(div, dom.body)
|
dom.appendTo(dom.body, div)
|
||||||
loadingEl = div
|
barEl = div
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Render progress bar
|
* Render progress bar
|
||||||
|
|
@ -21,26 +20,26 @@ function init () {
|
||||||
export default function ({ loaded, total, step }) {
|
export default function ({ loaded, total, step }) {
|
||||||
let num
|
let num
|
||||||
|
|
||||||
loadingEl = init()
|
!barEl && init()
|
||||||
|
|
||||||
if (!isPrimitive(step)) {
|
if (!isPrimitive(step)) {
|
||||||
step = Math.floor(Math.random() * 5 + 1)
|
step = Math.floor(Math.random() * 5 + 1)
|
||||||
}
|
}
|
||||||
if (step) {
|
if (step) {
|
||||||
num = parseInt(loadingEl.style.width, 10) + step
|
num = parseInt(barEl.style.width, 10) + step
|
||||||
num = num > 80 ? 80 : num
|
num = num > 80 ? 80 : num
|
||||||
} else {
|
} else {
|
||||||
num = Math.floor(loaded / total * 100)
|
num = Math.floor(loaded / total * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingEl.style.opacity = 1
|
barEl.style.opacity = 1
|
||||||
loadingEl.style.width = num >= 95 ? '100%' : num + '%'
|
barEl.style.width = num >= 95 ? '100%' : num + '%'
|
||||||
|
|
||||||
if (num >= 95) {
|
if (num >= 95) {
|
||||||
clearTimeout(timeId)
|
clearTimeout(timeId)
|
||||||
timeId = setTimeout(_ => {
|
timeId = setTimeout(_ => {
|
||||||
loadingEl.style.opacity = 0
|
barEl.style.opacity = 0
|
||||||
loadingEl.style.width = '0%'
|
barEl.style.width = '0%'
|
||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,27 @@
|
||||||
// import { cleanPath, getLocation } from './util'
|
import { parseQuery } from './util'
|
||||||
export function ensureSlash () {
|
|
||||||
const path = getHash()
|
function replaceHash (path) {
|
||||||
if (path.charAt(0) === '/') return
|
const i = window.location.href.indexOf('#')
|
||||||
|
window.location.replace(
|
||||||
|
window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize the current url
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* domain.com/docs/ => domain.com/docs/#/
|
||||||
|
* domain.com/docs/#/#slug => domain.com/docs/#/?id=slug
|
||||||
|
*/
|
||||||
|
export function normalize () {
|
||||||
|
let path = getHash()
|
||||||
|
|
||||||
|
path = path
|
||||||
|
.replace('#', '?id=')
|
||||||
|
.replace(/\?(\w+)=/g, (_, slug) => slug === 'id' ? '?id=' : `&${slug}=`)
|
||||||
|
|
||||||
|
if (path.charAt(0) === '/') return replaceHash(path)
|
||||||
replaceHash('/' + path)
|
replaceHash('/' + path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,11 +33,33 @@ export function getHash () {
|
||||||
return index === -1 ? '' : href.slice(index + 1)
|
return index === -1 ? '' : href.slice(index + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceHash (path) {
|
/**
|
||||||
const i = window.location.href.indexOf('#')
|
* Parse the current url
|
||||||
window.location.replace(
|
* @return {object} { path, query }
|
||||||
window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
|
*/
|
||||||
)
|
export function parse () {
|
||||||
|
let path = window.location.href
|
||||||
|
let query = ''
|
||||||
|
|
||||||
|
const queryIndex = path.indexOf('?')
|
||||||
|
if (queryIndex >= 0) {
|
||||||
|
query = path.slice(queryIndex + 1)
|
||||||
|
path = path.slice(0, queryIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashIndex = path.indexOf('#')
|
||||||
|
if (hashIndex) {
|
||||||
|
path = path.slice(hashIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { path, query: parseQuery(query) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 把第二个 hash 转成 ?id=
|
/**
|
||||||
|
* to URL
|
||||||
|
* @param {String} path
|
||||||
|
* @param {String} qs query string
|
||||||
|
*/
|
||||||
|
export function toURL (path, qs) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,42 @@
|
||||||
import { ensureSlash } from './hash'
|
import { normalize, parse } from './hash'
|
||||||
|
import { getBasePath, cleanPath } from './util'
|
||||||
|
import { on } from '../util/dom'
|
||||||
|
|
||||||
|
function getAlias (path, alias) {
|
||||||
|
if (alias[path]) return getAlias(alias[path], alias)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileName (path) {
|
||||||
|
return /\.(md|html)$/g.test(path)
|
||||||
|
? path
|
||||||
|
: /\/$/g.test(path)
|
||||||
|
? `${path}README.md`
|
||||||
|
: `${path}.md`
|
||||||
|
}
|
||||||
|
|
||||||
export function routeMixin (Docsify) {
|
export function routeMixin (Docsify) {
|
||||||
Docsify.prototype.$route = {
|
Docsify.prototype.route = {}
|
||||||
query: location.query || {},
|
Docsify.prototype.$getFile = function (path) {
|
||||||
path: location.path || '/',
|
const { config } = this
|
||||||
base: ''
|
const base = getBasePath(config.basePath)
|
||||||
|
|
||||||
|
path = getAlias(path, config.alias)
|
||||||
|
path = getFileName(path)
|
||||||
|
path = path === '/README.md' ? ('/' + config.homepage || path) : path
|
||||||
|
path = cleanPath(base + path)
|
||||||
|
|
||||||
|
return path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRoute (vm) {
|
export function initRoute (vm) {
|
||||||
ensureSlash()
|
normalize()
|
||||||
window.addEventListener('hashchange', () => {
|
vm.route = parse()
|
||||||
ensureSlash()
|
|
||||||
vm.$fetch()
|
on('hashchange', _ => {
|
||||||
|
normalize()
|
||||||
|
vm.route = parse()
|
||||||
|
vm._fetch()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,45 @@
|
||||||
|
import { cached } from '../util/core'
|
||||||
|
|
||||||
|
const decode = decodeURIComponent
|
||||||
|
|
||||||
|
export const parseQuery = cached(query => {
|
||||||
|
const res = {}
|
||||||
|
|
||||||
|
query = query.trim().replace(/^(\?|#|&)/, '')
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
query.split('&').forEach(function (param) {
|
||||||
|
const parts = param.replace(/\+/g, ' ').split('=')
|
||||||
|
const key = decode(parts.shift())
|
||||||
|
const val = parts.length > 0
|
||||||
|
? decode(parts.join('='))
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (res[key] === undefined) {
|
||||||
|
res[key] = val
|
||||||
|
} else if (Array.isArray(res[key])) {
|
||||||
|
res[key].push(val)
|
||||||
|
} else {
|
||||||
|
res[key] = [res[key], val]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
export function cleanPath (path) {
|
export function cleanPath (path) {
|
||||||
return path.replace(/\/+/g, '/')
|
return path.replace(/\/+/g, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocation (base) {
|
export function getBasePath (base) {
|
||||||
let path = window.location.pathname
|
return /^(\/|https?:)/g.test(base)
|
||||||
if (base && path.indexOf(base) === 0) {
|
? base
|
||||||
path = path.slice(base.length)
|
: cleanPath(window.location.pathname + '/' + base)
|
||||||
}
|
}
|
||||||
return (path || '/') + window.location.search + window.location.hash
|
|
||||||
|
export function getCurrentRoot (path) {
|
||||||
|
return /\/$/g.test(path) ? path : path.match(/(\S*\/)[^\/]+$/)[1]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Create a cached version of a pure function.
|
* Create a cached version of a pure function.
|
||||||
*/
|
*/
|
||||||
function cached (fn) {
|
export function cached (fn) {
|
||||||
const cache = Object.create(null)
|
const cache = Object.create(null)
|
||||||
return function cachedFn (str) {
|
return function cachedFn (str) {
|
||||||
const hit = cache[str]
|
const hit = cache[str]
|
||||||
|
|
@ -10,11 +10,10 @@ function cached (fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Camelize a hyphen-delimited string.
|
* Hyphenate a camelCase string.
|
||||||
*/
|
*/
|
||||||
const camelizeRE = /-(\w)/g
|
export const hyphenate = cached(str => {
|
||||||
export const camelize = cached((str) => {
|
return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase())
|
||||||
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,34 @@ const cacheNode = {}
|
||||||
*/
|
*/
|
||||||
export function getNode (el, noCache = false) {
|
export function getNode (el, noCache = false) {
|
||||||
if (typeof el === 'string') {
|
if (typeof el === 'string') {
|
||||||
el = noCache ? dom.find(el) : (cacheNode[el] || dom.find(el))
|
el = noCache ? find(el) : (cacheNode[el] || find(el))
|
||||||
}
|
}
|
||||||
|
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dom = {
|
export const $ = document
|
||||||
body: document.body,
|
|
||||||
head: document.head,
|
export const body = $.body
|
||||||
find: node => document.querySelector(node),
|
|
||||||
findAll: node => document.querySelectorAll(node),
|
export const head = $.head
|
||||||
create: (node, tpl) => {
|
|
||||||
node = document.createElement(node)
|
export function find (node) {
|
||||||
if (tpl) node.innerHTML = tpl
|
return $.querySelector(node)
|
||||||
},
|
}
|
||||||
appendTo: (target, el) => target.appendChild(el)
|
|
||||||
|
export function findAll (node) {
|
||||||
|
return [].clice.call($.querySelectorAll(node))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create (node, tpl) {
|
||||||
|
node = $.createElement(node)
|
||||||
|
if (tpl) node.innerHTML = tpl
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appendTo (target, el) {
|
||||||
|
return target.appendChild(el)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function on (el, type, handler) {
|
export function on (el, type, handler) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './core'
|
export * from './core'
|
||||||
export * from './env'
|
export * from './env'
|
||||||
export * from './dom'
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
import { dom } from '../dom'
|
import * as dom from '../dom'
|
||||||
import { get } from '../../fetch/ajax'
|
import { get } from '../../fetch/ajax'
|
||||||
|
|
||||||
function replaceVar (block, themeColor) {
|
function replaceVar (block, color) {
|
||||||
block.innerHTML = block.innerHTML
|
block.innerHTML = block.innerHTML
|
||||||
.replace(/var\(\s*--theme-color.*?\)/g, themeColor)
|
.replace(/var\(\s*--theme-color.*?\)/g, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (themeColor) {
|
export default function (color) {
|
||||||
// Variable support
|
// Variable support
|
||||||
if (window.CSS
|
if (window.CSS &&
|
||||||
&& window.CSS.supports
|
window.CSS.supports &&
|
||||||
&& window.CSS.supports('(--foo: red)')) return
|
window.CSS.supports('(--v:red)')) return
|
||||||
|
|
||||||
const styleBlocks = dom.findAll('style:not(.inserted),link')
|
const styleBlocks = dom.findAll('style:not(.inserted),link')
|
||||||
|
|
||||||
;[].forEach.call(styleBlocks, block => {
|
;[].forEach.call(styleBlocks, block => {
|
||||||
if (block.nodeName === 'STYLE') {
|
if (block.nodeName === 'STYLE') {
|
||||||
replaceVar(block, themeColor)
|
replaceVar(block, color)
|
||||||
} else if (block.nodeName === 'LINK') {
|
} else if (block.nodeName === 'LINK') {
|
||||||
const href = block.getAttribute('href')
|
const href = block.getAttribute('href')
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default function (themeColor) {
|
||||||
const style = dom.create('style', res)
|
const style = dom.create('style', res)
|
||||||
|
|
||||||
dom.head.appendChild(style)
|
dom.head.appendChild(style)
|
||||||
replaceVar(style, themeColor)
|
replaceVar(style, color)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue