aboutsummaryrefslogtreecommitdiff
path: root/haddock-api/resources/html/js-src
diff options
context:
space:
mode:
authoralexbiehl <alex.biehl@gmail.com>2017-10-31 21:48:55 +0100
committeralexbiehl <alex.biehl@gmail.com>2017-10-31 21:48:55 +0100
commit08c9e19236770811caf571321f5ece271d1fccff (patch)
treebeb3f6407d14abcab32f9d54811cabd319c356a4 /haddock-api/resources/html/js-src
parent3896bff411596ef50b5ca2f2be425e89878410aa (diff)
parente5fe98530d9c70f5197494da9de07f42dd7fe334 (diff)
Merge remote-tracking branch 'origin/master' into ghc-head
Diffstat (limited to 'haddock-api/resources/html/js-src')
-rw-r--r--haddock-api/resources/html/js-src/cookies.ts20
-rw-r--r--haddock-api/resources/html/js-src/details-helper.ts106
-rw-r--r--haddock-api/resources/html/js-src/init.ts9
-rw-r--r--haddock-api/resources/html/js-src/quick-jump.tsx29
-rw-r--r--haddock-api/resources/html/js-src/style-menu.ts (renamed from haddock-api/resources/html/js-src/haddock-util.ts)75
5 files changed, 161 insertions, 78 deletions
diff --git a/haddock-api/resources/html/js-src/cookies.ts b/haddock-api/resources/html/js-src/cookies.ts
new file mode 100644
index 00000000..3b68d6d8
--- /dev/null
+++ b/haddock-api/resources/html/js-src/cookies.ts
@@ -0,0 +1,20 @@
+export function setCookie(name: string, value: string) {
+ document.cookie = name + "=" + encodeURIComponent(value) + ";path=/;";
+}
+
+export function clearCookie(name: string) {
+ document.cookie = name + "=;path=/;expires=Thu, 01-Jan-1970 00:00:01 GMT;";
+}
+
+export function getCookie(name: string) {
+ const nameEQ = name + "=";
+ const ca = document.cookie.split(';');
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) {
+ return decodeURIComponent(c.substring(nameEQ.length,c.length));
+ }
+ }
+ return null;
+} \ No newline at end of file
diff --git a/haddock-api/resources/html/js-src/details-helper.ts b/haddock-api/resources/html/js-src/details-helper.ts
new file mode 100644
index 00000000..f13ac905
--- /dev/null
+++ b/haddock-api/resources/html/js-src/details-helper.ts
@@ -0,0 +1,106 @@
+import {getCookie} from "./cookies";
+
+interface HTMLDetailsElement extends HTMLElement {
+ open: boolean
+}
+
+interface DetailsInfo {
+ element: HTMLDetailsElement
+ openByDefault: boolean
+ toggles: HTMLElement[]
+}
+
+// Global state
+const detailsRegistry: { [id: string]: DetailsInfo } = {};
+const toggled: { [id: string]: true } = {}; /* stores which <details> are not in their default state */
+
+function lookupDetailsRegistry(id: string): DetailsInfo {
+ const info = detailsRegistry[id];
+ if (info == undefined) { throw new Error(`could not find <details> element with id '${id}'`); }
+ return info;
+}
+
+function onDetailsToggle(ev: Event) {
+ const element = ev.target as HTMLDetailsElement;
+ const id = element.id;
+ const info = lookupDetailsRegistry(id);
+ const isOpen = info.element.open;
+ for (const toggle of info.toggles) {
+ if (toggle.classList.contains('details-toggle-control')) {
+ toggle.classList.add(isOpen ? 'collapser' : 'expander');
+ toggle.classList.remove(isOpen ? 'expander' : 'collapser');
+ }
+ }
+ if (element.open == info.openByDefault) {
+ delete toggled[id];
+ } else {
+ toggled[id] = true;
+ }
+ rememberToggled();
+}
+
+function gatherDetailsElements() {
+ const els: HTMLDetailsElement[] = Array.prototype.slice.call(document.getElementsByTagName('details'));
+ for (const el of els) {
+ if (typeof el.id == "string" && el.id.length > 0) {
+ detailsRegistry[el.id] = {
+ element: el,
+ openByDefault: !!el.open,
+ toggles: [] // added later
+ };
+ el.addEventListener('toggle', onDetailsToggle);
+ }
+ }
+}
+
+function toggleDetails(id: string) {
+ const {element} = lookupDetailsRegistry(id);
+ element.open = !element.open;
+}
+
+function rememberToggled() {
+ const sections: string[] = Object.keys(toggled);
+ // cookie specific to this page; don't use setCookie which sets path=/
+ document.cookie = "toggled=" + encodeURIComponent(sections.join('+'));
+}
+
+function restoreToggled() {
+ const cookie = getCookie("toggled");
+ if (!cookie) { return; }
+ const ids = cookie.split('+');
+ for (const id of ids) {
+ const info = detailsRegistry[id];
+ toggled[id] = true;
+ if (info) {
+ info.element.open = !info.element.open;
+ }
+ }
+}
+
+function onToggleClick(ev: MouseEvent) {
+ ev.preventDefault();
+ const toggle = ev.currentTarget as HTMLElement;
+ const id = toggle.getAttribute('data-details-id');
+ if (!id) { throw new Error("element with class 'details-toggle' has no 'data-details-id' attribute!"); }
+ toggleDetails(id);
+}
+
+function initCollapseToggles() {
+ const toggles: HTMLElement[] = Array.prototype.slice.call(document.getElementsByClassName('details-toggle'));
+ toggles.forEach(toggle => {
+ const id = toggle.getAttribute('data-details-id');
+ if (!id) { throw new Error("element with class 'details-toggle' has no 'data-details-id' attribute!"); }
+ const info = lookupDetailsRegistry(id);
+ info.toggles.push(toggle);
+ toggle.addEventListener('click', onToggleClick);
+ if (toggle.classList.contains('details-toggle-control')) {
+ toggle.classList.add(info.element.open ? 'collapser' : 'expander');
+ }
+ });
+}
+
+export function init() {
+ gatherDetailsElements();
+ restoreToggled();
+ initCollapseToggles();
+} \ No newline at end of file
diff --git a/haddock-api/resources/html/js-src/init.ts b/haddock-api/resources/html/js-src/init.ts
index 0619dfc3..877874ae 100644
--- a/haddock-api/resources/html/js-src/init.ts
+++ b/haddock-api/resources/html/js-src/init.ts
@@ -1,4 +1,5 @@
-import * as util from "./haddock-util";
+import * as styleMenu from "./style-menu";
+import * as detailsHelper from "./details-helper";
import * as quickJump from "./quick-jump";
function onDomReady(callback: () => void) {
@@ -14,8 +15,8 @@ function onDomReady(callback: () => void) {
}
onDomReady(() => {
- util.addStyleMenu();
- util.resetStyle();
- util.restoreCollapsed();
+ document.body.classList.add('js-enabled');
+ styleMenu.init();
+ detailsHelper.init();
quickJump.init();
}); \ No newline at end of file
diff --git a/haddock-api/resources/html/js-src/quick-jump.tsx b/haddock-api/resources/html/js-src/quick-jump.tsx
index a2bcdb64..97ac14af 100644
--- a/haddock-api/resources/html/js-src/quick-jump.tsx
+++ b/haddock-api/resources/html/js-src/quick-jump.tsx
@@ -3,10 +3,6 @@ import preact = require("preact");
const { h, Component } = preact;
-declare interface ObjectConstructor {
- assign(target: any, ...sources: any[]): any;
-}
-
type DocItem = {
display_html: string
name: string
@@ -19,8 +15,13 @@ function loadJSON(path: string, success: (json: DocItem[]) => void, error: (xhr:
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
- if (success)
- success(JSON.parse(xhr.responseText));
+ if (success) {
+ try {
+ success(JSON.parse(xhr.responseText));
+ } catch (exc) {
+ error(xhr);
+ }
+ }
} else {
if (error) { error(xhr); }
}
@@ -102,7 +103,7 @@ class QuickJump extends Component<QuickJumpProps, QuickJumpState> {
loadJSON(this.props.baseUrl + "/doc-index.json", (data) => {
this.setState({
fuse: new Fuse(data, {
- threshold: 0.4,
+ threshold: 0.25,
caseSensitive: true,
includeScore: true,
tokenize: true,
@@ -218,7 +219,19 @@ class QuickJump extends Component<QuickJumpProps, QuickJumpState> {
}
render(props: any, state: QuickJumpState) {
- if (state.failedLoading) { return null; }
+ if (state.failedLoading) {
+ const usingFileProtocol = window.location.protocol == 'file:';
+ return <div id="search" class={state.isVisible ? '' : 'hidden'}>
+ <div id="search-results">
+ <p class="error">Failed to load file 'doc-index.json' containing definitions in this package.</p>
+ {usingFileProtocol ? <p class="error">
+ To use quick jump, load this page with HTTP (from a local static file web server) instead of using the <code>file://</code> protocol.
+ (For security reasons, it is not possible to fetch auxiliary files using JS in a HTML page opened with <code>file://</code>.)
+ </p> : []
+ }
+ </div>
+ </div>;
+ }
this.linkIndex = 0;
diff --git a/haddock-api/resources/html/js-src/haddock-util.ts b/haddock-api/resources/html/js-src/style-menu.ts
index 257ceb6a..3911655b 100644
--- a/haddock-api/resources/html/js-src/haddock-util.ts
+++ b/haddock-api/resources/html/js-src/style-menu.ts
@@ -1,5 +1,7 @@
// Haddock JavaScript utilities
+import {getCookie, setCookie, clearCookie} from "./cookies";
+
const rspace = /\s\s+/g,
rtrim = /^\s+|\s+$/g;
@@ -37,7 +39,6 @@ function toggleClass(elem: Element, valueOn: string, valueOff: string, bool?: bo
return bool;
}
-
function makeClassToggle(valueOn: string, valueOff: string): (elem: Element, bool?: boolean) => boolean {
return function(elem, bool) {
return toggleClass(elem, valueOn, valueOff, bool);
@@ -45,69 +46,6 @@ function makeClassToggle(valueOn: string, valueOff: string): (elem: Element, boo
}
const toggleShow = makeClassToggle("show", "hide");
-const toggleCollapser = makeClassToggle("collapser", "expander");
-
-function toggleSection(id: string): boolean {
- const b = toggleShow(document.getElementById("section." + id) as Element);
- toggleCollapser(document.getElementById("control." + id) as Element, b);
- rememberCollapsed(id);
- return b;
-}
-
-// TODO: get rid of global variables
-if (typeof window !== 'undefined') {
- (window as any).toggleSection = toggleSection;
-}
-
-const collapsed: { [id: string]: boolean } = {};
-function rememberCollapsed(id: string) {
- if(collapsed[id])
- delete collapsed[id]
- else
- collapsed[id] = true;
-
- const sections: string[] = [];
- for(let i in collapsed) {
- if(collapsed.hasOwnProperty(i))
- sections.push(i);
- }
- // cookie specific to this page; don't use setCookie which sets path=/
- document.cookie = "collapsed=" + encodeURIComponent(sections.join('+'));
-}
-
-export function restoreCollapsed() {
- const cookie = getCookie("collapsed");
- if(!cookie)
- return;
-
- const ids = cookie.split('+');
- for(const i in ids)
- {
- if(document.getElementById("section." + ids[i]))
- toggleSection(ids[i]);
- }
-}
-
-function setCookie(name: string, value: string) {
- document.cookie = name + "=" + encodeURIComponent(value) + ";path=/;";
-}
-
-function clearCookie(name: string) {
- document.cookie = name + "=;path=/;expires=Thu, 01-Jan-1970 00:00:01 GMT;";
-}
-
-function getCookie(name: string) {
- const nameEQ = name + "=";
- const ca = document.cookie.split(';');
- for (let i = 0; i < ca.length; i++) {
- let c = ca[i];
- while (c.charAt(0)==' ') c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) == 0) {
- return decodeURIComponent(c.substring(nameEQ.length,c.length));
- }
- }
- return null;
-}
function addMenuItem(html: string) {
const menu = document.getElementById("page-menu");
@@ -123,7 +61,7 @@ function styles(): HTMLLinkElement[] {
return es.filter((a: HTMLLinkElement) => a.rel.indexOf("style") != -1 && a.title);
}
-export function addStyleMenu() {
+function addStyleMenu() {
const as = styles();
let btns = "";
as.forEach((a) => {
@@ -162,7 +100,7 @@ function setActiveStyleSheet(title: string) {
styleMenu(false);
}
-export function resetStyle() {
+function resetStyle() {
const s = getCookie("haddock-style");
if (s) setActiveStyleSheet(s);
}
@@ -170,4 +108,9 @@ export function resetStyle() {
function styleMenu(show?: boolean) {
const m = document.getElementById('style-menu');
if (m) toggleShow(m, show);
+}
+
+export function init() {
+ addStyleMenu();
+ resetStyle();
} \ No newline at end of file