2016-01-29 01:27:15 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Martin Donath <martin.donath@squidfunk.com>
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2016-09-30 14:29:45 +03:00
|
|
|
import FastClick from "fastclick"
|
2016-10-06 13:14:33 +03:00
|
|
|
import Material from "./components/Material"
|
2016-09-30 14:29:45 +03:00
|
|
|
|
2016-01-29 01:27:15 +03:00
|
|
|
/* ----------------------------------------------------------------------------
|
2016-08-07 19:01:56 +03:00
|
|
|
* Application
|
2016-01-29 01:27:15 +03:00
|
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
export default class Application {
|
2016-10-06 13:14:33 +03:00
|
|
|
|
2016-10-23 12:35:41 +03:00
|
|
|
/**
|
2016-12-15 17:55:40 +03:00
|
|
|
* Create the application
|
2016-10-23 12:35:41 +03:00
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {object} config Configuration object
|
|
|
|
*/
|
|
|
|
constructor(config) {
|
2016-11-02 21:21:14 +03:00
|
|
|
this.config_ = config
|
2016-10-23 12:35:41 +03:00
|
|
|
}
|
|
|
|
|
2016-10-06 13:14:33 +03:00
|
|
|
/**
|
2016-12-15 17:55:40 +03:00
|
|
|
* Initialize all components and listeners
|
2016-10-06 13:14:33 +03:00
|
|
|
*/
|
|
|
|
initialize() {
|
2016-10-23 12:35:41 +03:00
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Initialize Modernizr and Fastclick */
|
|
|
|
new Material.Event.Listener(document, "DOMContentLoaded", () => {
|
2016-11-02 21:21:14 +03:00
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Test for iOS */
|
|
|
|
Modernizr.addTest("ios", () => {
|
|
|
|
return !!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)
|
|
|
|
})
|
2016-11-02 21:21:14 +03:00
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Test for web application context */
|
|
|
|
Modernizr.addTest("standalone", () => {
|
|
|
|
return !!navigator.standalone
|
|
|
|
})
|
2016-11-02 21:21:14 +03:00
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Attack FastClick to mitigate 300ms delay on touch devices */
|
|
|
|
FastClick.attach(document.body)
|
2016-12-18 15:29:03 +03:00
|
|
|
|
|
|
|
/* Wrap all data tables */
|
|
|
|
const tables = document.querySelectorAll("table:not([class])")
|
2016-12-28 20:54:26 +03:00
|
|
|
Array.prototype.forEach.call(tables, table => {
|
2016-12-18 15:29:03 +03:00
|
|
|
const wrap = document.createElement("div")
|
|
|
|
wrap.classList.add("md-typeset__table")
|
|
|
|
if (table.nextSibling) {
|
|
|
|
table.parentNode.insertBefore(wrap, table.nextSibling)
|
|
|
|
} else {
|
|
|
|
table.parentNode.appendChild(wrap)
|
|
|
|
}
|
|
|
|
wrap.appendChild(table)
|
2016-12-28 20:54:26 +03:00
|
|
|
})
|
2016-12-29 16:19:26 +03:00
|
|
|
|
|
|
|
/* Force 1px scroll offset to trigger overflow scrolling */
|
|
|
|
if (Modernizr.ios) {
|
|
|
|
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
|
|
|
|
Array.prototype.forEach.call(scrollable, item => {
|
|
|
|
item.addEventListener("touchstart", () => {
|
|
|
|
const top = item.scrollTop
|
|
|
|
|
|
|
|
/* We're at the top of the container */
|
|
|
|
if (top === 0) {
|
|
|
|
item.scrollTop = 1
|
|
|
|
|
|
|
|
/* We're at the bottom of the container */
|
|
|
|
} else if (top + item.offsetHeight === item.scrollHeight) {
|
|
|
|
item.scrollTop = top - 1
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2016-12-15 17:55:40 +03:00
|
|
|
}).listen()
|
2016-11-02 21:21:14 +03:00
|
|
|
|
2016-12-24 10:10:55 +03:00
|
|
|
/* Component: sidebar container */
|
2016-12-26 16:31:16 +03:00
|
|
|
if (!Modernizr.csscalc)
|
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener(window, [
|
|
|
|
"resize", "orientationchange"
|
2016-12-26 16:47:50 +03:00
|
|
|
], new Material.Sidebar.Container("[data-md-component=container]")))
|
2016-12-24 10:10:55 +03:00
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Component: sidebar with navigation */
|
|
|
|
new Material.Event.MatchMedia("(min-width: 1200px)",
|
|
|
|
new Material.Event.Listener(window, [
|
|
|
|
"scroll", "resize", "orientationchange"
|
2016-12-26 16:47:50 +03:00
|
|
|
], new Material.Sidebar.Position("[data-md-component=navigation]")))
|
2016-12-15 17:55:40 +03:00
|
|
|
|
|
|
|
/* Component: sidebar with table of contents */
|
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener(window, [
|
|
|
|
"scroll", "resize", "orientationchange"
|
2016-12-26 16:47:50 +03:00
|
|
|
], new Material.Sidebar.Position("[data-md-component=toc]")))
|
2016-12-15 17:55:40 +03:00
|
|
|
|
|
|
|
/* Component: link blurring for table of contents */
|
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener(window, "scroll",
|
2016-12-26 16:47:50 +03:00
|
|
|
new Material.Nav.Blur("[data-md-component=toc] .md-nav__link")))
|
2016-12-15 17:55:40 +03:00
|
|
|
|
|
|
|
/* Component: collapsible elements for navigation */
|
2016-12-28 13:49:57 +03:00
|
|
|
const collapsibles =
|
|
|
|
document.querySelectorAll("[data-md-component=collapsible]")
|
2016-12-28 20:54:26 +03:00
|
|
|
Array.prototype.forEach.call(collapsibles, collapse => {
|
2016-12-15 17:55:40 +03:00
|
|
|
new Material.Event.MatchMedia("(min-width: 1200px)",
|
|
|
|
new Material.Event.Listener(collapse.previousElementSibling, "click",
|
|
|
|
new Material.Nav.Collapse(collapse)))
|
2016-12-28 20:54:26 +03:00
|
|
|
})
|
2016-12-15 17:55:40 +03:00
|
|
|
|
2016-12-28 13:49:57 +03:00
|
|
|
/* Component: pane monitor for iOS scrolling fixes */
|
|
|
|
new Material.Event.MatchMedia("(max-width: 1199px)",
|
|
|
|
new Material.Event.Listener(
|
|
|
|
"[data-md-component=navigation] [data-md-toggle]", "change",
|
|
|
|
new Material.Nav.Scrolling("[data-md-component=navigation] nav")))
|
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Component: search body lock for mobile */
|
|
|
|
new Material.Event.MatchMedia("(max-width: 959px)",
|
|
|
|
new Material.Event.Listener("[data-md-toggle=search]", "change",
|
|
|
|
new Material.Search.Lock("[data-md-toggle=search]")))
|
|
|
|
|
|
|
|
/* Component: search results */
|
|
|
|
new Material.Event.Listener(document.forms.search.query, [
|
|
|
|
"focus", "keyup"
|
2016-12-26 16:47:50 +03:00
|
|
|
], new Material.Search.Result("[data-md-component=result]", () => {
|
2017-01-06 20:19:15 +03:00
|
|
|
return fetch(`${this.config_.url.base}/mkdocs/search_index.json`, {
|
|
|
|
credentials: "same-origin"
|
|
|
|
}).then(response => response.json())
|
2016-12-15 17:55:40 +03:00
|
|
|
.then(data => {
|
|
|
|
return data.docs.map(doc => {
|
|
|
|
doc.location = this.config_.url.base + doc.location
|
|
|
|
return doc
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})).listen()
|
|
|
|
|
|
|
|
/* Listener: prevent touches on overlay if navigation is active */
|
|
|
|
new Material.Event.MatchMedia("(max-width: 1199px)",
|
2016-12-26 16:47:50 +03:00
|
|
|
new Material.Event.Listener("[data-md-component=overlay]", "touchstart",
|
2016-12-15 17:55:40 +03:00
|
|
|
ev => ev.preventDefault()))
|
|
|
|
|
|
|
|
/* Listener: close drawer when anchor links are clicked */
|
|
|
|
new Material.Event.MatchMedia("(max-width: 959px)",
|
2016-12-26 16:47:50 +03:00
|
|
|
new Material.Event.Listener("[data-md-component=navigation] [href^='#']",
|
2016-12-15 17:55:40 +03:00
|
|
|
"click", () => {
|
|
|
|
const toggle = document.querySelector("[data-md-toggle=drawer]")
|
|
|
|
if (toggle.checked) {
|
|
|
|
toggle.checked = false
|
2016-12-29 12:50:19 +03:00
|
|
|
toggle.dispatchEvent(new CustomEvent("change"))
|
2016-12-15 17:55:40 +03:00
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
2016-12-29 12:50:19 +03:00
|
|
|
/* Listener: focus input after opening search */
|
2016-12-15 17:55:40 +03:00
|
|
|
new Material.Event.Listener("[data-md-toggle=search]", "change", ev => {
|
|
|
|
setTimeout(toggle => {
|
|
|
|
const query = document.forms.search.query
|
|
|
|
if (toggle.checked)
|
|
|
|
query.focus()
|
|
|
|
}, 400, ev.target)
|
|
|
|
}).listen()
|
|
|
|
|
2016-12-29 12:50:19 +03:00
|
|
|
/* Listener: open search on focus */
|
2016-12-15 17:55:40 +03:00
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener(document.forms.search.query, "focus", () => {
|
|
|
|
const toggle = document.querySelector("[data-md-toggle=search]")
|
|
|
|
if (!toggle.checked) {
|
|
|
|
toggle.checked = true
|
2016-12-29 12:50:19 +03:00
|
|
|
toggle.dispatchEvent(new CustomEvent("change"))
|
2016-12-15 17:55:40 +03:00
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
2016-12-29 12:50:19 +03:00
|
|
|
/* Listener: close search when clicking outside */
|
2016-12-15 17:55:40 +03:00
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener(document.body, "click", () => {
|
|
|
|
const toggle = document.querySelector("[data-md-toggle=search]")
|
|
|
|
if (toggle.checked) {
|
|
|
|
toggle.checked = false
|
2016-12-29 12:50:19 +03:00
|
|
|
toggle.dispatchEvent(new CustomEvent("change"))
|
2016-12-15 17:55:40 +03:00
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
2016-12-28 19:32:56 +03:00
|
|
|
/* Listener: disable search when ESC key is pressed */
|
|
|
|
new Material.Event.Listener(window, "keyup", ev => {
|
|
|
|
const code = ev.keyCode || ev.which
|
|
|
|
if (code === 27) {
|
|
|
|
const toggle = document.querySelector("[data-md-toggle=search]")
|
|
|
|
if (toggle.checked) {
|
|
|
|
toggle.checked = false
|
2016-12-29 12:50:19 +03:00
|
|
|
toggle.dispatchEvent(new CustomEvent("change"))
|
2016-12-28 19:32:56 +03:00
|
|
|
document.forms.search.query.blur()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).listen()
|
|
|
|
|
2016-12-15 17:55:40 +03:00
|
|
|
/* Listener: fix unclickable toggle due to blur handler */
|
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
|
|
|
new Material.Event.Listener("[data-md-toggle=search]", "click",
|
|
|
|
ev => ev.stopPropagation()))
|
|
|
|
|
|
|
|
/* Listener: prevent search from closing when clicking */
|
|
|
|
new Material.Event.MatchMedia("(min-width: 960px)",
|
2016-12-26 16:47:50 +03:00
|
|
|
new Material.Event.Listener("[data-md-component=search]", "click",
|
2016-12-15 17:55:40 +03:00
|
|
|
ev => ev.stopPropagation()))
|
|
|
|
|
|
|
|
/* Retrieve the facts for the given repository type */
|
|
|
|
;(() => {
|
|
|
|
const el = document.querySelector("[data-md-source]")
|
2016-12-19 07:08:40 +03:00
|
|
|
if (!el) return Promise.resolve([])
|
2016-12-15 17:55:40 +03:00
|
|
|
switch (el.dataset.mdSource) {
|
|
|
|
case "github": return new Material.Source.Adapter.GitHub(el).fetch()
|
|
|
|
default: return Promise.resolve([])
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Render repository source information */
|
|
|
|
})().then(facts => {
|
|
|
|
const sources = document.querySelectorAll("[data-md-source]")
|
2016-12-28 20:54:26 +03:00
|
|
|
Array.prototype.forEach.call(sources, source => {
|
2016-12-15 17:55:40 +03:00
|
|
|
new Material.Source.Repository(source)
|
|
|
|
.initialize(facts)
|
2016-12-28 20:54:26 +03:00
|
|
|
})
|
2016-12-15 17:55:40 +03:00
|
|
|
})
|
2016-10-06 13:14:33 +03:00
|
|
|
}
|
|
|
|
}
|