This commit is contained in:
McShelby 2021-10-16 23:18:04 +00:00
parent cf7ccd5d7b
commit 0162850ac8
285 changed files with 37648 additions and 0 deletions

3
js/auto-complete.js Normal file

File diff suppressed because one or more lines are too long

7
js/clipboard.min.js vendored Normal file

File diff suppressed because one or more lines are too long

9
js/featherlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

637
js/jquery.svg.pan.zoom.js Normal file
View file

@ -0,0 +1,637 @@
// Generated by CoffeeScript 1.10.0
/*
jQuery SVG Pan Zoom v1.0.2, June 2015
Author: Daniel Hoffmann Bernardes (daniel.hoffmann.bernardes@gmail.com)
Repository: https://github.com/DanielHoffmann/jquery-svg-pan-zoom/
jQuery plugin to enable pan and zoom in SVG images either programmatically or through mouse/touch events.
[Demo page](http://danielhoffmann.github.io/jquery-svg-pan-zoom/)
* Features
- Programmatically manipulate the SVG viewBox
- Mouse and touch events to pan the SVG viewBox
- Mousewheel events to zoom in or out the SVG viewBox
- Animations
- Mousewheel zooming keeps the cursor over the same coordinates relative to the image (A.K.A. GoogleMaps-like zoom)
- Limiting the navigable area
* Requirements
jQuery
SVG-enabled browser (does not work with SVG work-arounds that use Flash)
* The viewBox
The viewBox is an attribute of SVG images that defines the area of the SVG that is visible, it is defined by 4 numbers: X, Y, Width, Height. These numbers together specify the visible area. This plugin works by manipulating these four numbers. For example, moving the image to the right alters the X value while zooming in reduces Width and Height.
* Usage
```javascript
var svgPanZoom= $("svg").svgPanZoom(options)
```
If the selection has more than one element `svgPanZoom` will return an array with a SvgPanZoom object for each image in the same order of the selection. If only one element is selected then the return is a single SvgPanZoom object. If no elements are selected the above call returns `null`
The returned SvgPanZoom object contains all options, these options can be overriden at any time directly, for example to disable mouseWheel events simply:
```javascript
svgPanZoom.events.mouseWheel= false
```
the SvgPanZoom object also has methods for manipulating the viewBox programmatically. For example:
```javascript
svgPanZoom.zoomIn()
```
will zoomIn the image using options.zoomFactor.
* Building
This project requires coffeescript to be installed in order to build.
`coffee -m --compile --output compiled/ src/`
* Options
```javascript
Options:
{
events: {
mouseWheel: boolean (true), // enables mouse wheel zooming events
doubleClick: boolean (true), // enables double-click to zoom-in events
drag: boolean (true), // enables drag and drop to move the SVG events
dragCursor: string "move" // cursor to use while dragging the SVG
},
animationTime: number (300), // time in milliseconds to use as default for animations. Set 0 to remove the animation
zoomFactor: number (0.25), // how much to zoom-in or zoom-out
maxZoom: number (3), //maximum zoom in, must be a number bigger than 1
panFactor: (number (100), // how much to move the viewBox when calling .panDirection() methods
initialViewBox: { // the initial viewBox, if null or undefined will try to use the viewBox set in the svg tag. Also accepts string in the format "X Y Width Height"
x: number (0) // the top-left corner X coordinate
y: number (0) // the top-left corner Y coordinate
width: number (1000) // the width of the viewBox
height: number (1000) // the height of the viewBox
},
limits: { // the limits in which the image can be moved. If null or undefined will use the initialViewBox plus 15% in each direction
x: number (-150)
y: number (-150)
x2: number (1150)
y2: number (1150)
}
}
```
* Methods
- pan
```javascript
svgPanZoom.panLeft(amount, animationTime)
svgPanZoom.panRight(amount, animationTime)
svgPanZoom.panUp(amount, animationTime)
svgPanZoom.panDown(amount, animationTime)
```
Moves the SVG viewBox in the specified direction. Parameters:
- amount: Number, optional. How much to move the viewBox, defaults to options.panFactor.
- animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
- zoom
```javascript
svgPanZoom.zoomIn(animationTime)
svgPanZoom.zoomOut(animationTime)
```
Zooms the viewBox. Parameters:
- animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
- reset
```javascript
svgPanZoom.reset()
```
Resets the SVG to options.initialViewBox values.
- getViewBox
```javascript
svgPanZoom.getViewBox()
```
Returns the viewbox in this format:
```javascript
{
x: number
y: number
width: number
height: number
}
```
- setViewBox
```javascript
svgPanZoom.setViewBox(x, y, width, height, animationTime)
```
Changes the viewBox to the specified coordinates. Will respect the `options.limits` adapting the viewBox if needed (moving or reducing it to fit into `options.limits`
- x: Number, the new x coodinate of the top-left corner
- y: Number, the new y coodinate of the top-left corner
- width: Number, the new width of the viewBox
- height: Number, the new height of the viewBox
- animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
- setCenter
```javascript
svgPanZoom.setCenter(x, y, animationTime)
```
Sets the center of the SVG. Parameters:
- x: Number, the new x coordinate of the center
- y: Number, the new y coordinate of the center
- animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
* Notes:
- Only works in SVGs inlined in the HTML. You can use $.load() to load the SVG image in the page using AJAX and call $().svgPanZoom() in the callback
- Touch pinch events to zoom not yet supported
- This plugin does not create any controls (like arrows to move the image) on top of the SVG. These controls are simple to create manually and they can call the methods to move the image.
- Do not manipulate the SVG viewBox attribute manually, use SvgPanZoom.setViewBox() instead
Copyright (C) 2014 Daniel Hoffmann Bernardes, Ícaro Technologies
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 NONINFRINGEMENT. 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.
*/
(function() {
var hasProp = {}.hasOwnProperty;
(function($) {
var checkLimits, defaultOptions, defaultViewBox, getViewBoxCoordinatesFromEvent, parseViewBoxString;
defaultOptions = {
events: {
mouseWheel: true,
doubleClick: true,
drag: true,
dragCursor: "move"
},
animationTime: 300,
zoomFactor: 0.25,
maxZoom: 3,
panFactor: 100,
initialViewBox: null,
limits: null
};
defaultViewBox = {
x: 0,
y: 0,
width: 1000,
height: 1000
};
/**
* Check the limits of the view box, return a new viewBox that respects the limits while keeping
* the original view box size if possible. If the view box needs to be reduced, the returned view
* box will keep the aspect ratio of the original view box.
*
* @param {Object} viewBox
* The original view box. Takes numbers, in the format `{x, y, width, height}`.
*
* @param {Object} limits
* Extents which can be shown, in the view box coordinate system. Takes numbers in the format
* `{x, y, x2, y2}`.
*
* @return {Object} viewBox
* A new view box object, squeezed into the limits. Contains numbers, in the format `{x, y,
* width, height}`.
*/
checkLimits = function(viewBox, limits) {
var limitsHeight, limitsWidth, reductionFactor, vb;
vb = $.extend({}, viewBox);
limitsWidth = Math.abs(limits.x2 - limits.x);
limitsHeight = Math.abs(limits.y2 - limits.y);
if (vb.width > limitsWidth) {
if (vb.height > limitsHeight) {
if (limitsWidth > limitsHeight) {
reductionFactor = limitsHeight / vb.height;
vb.height = limitsHeight;
vb.width = vb.width * reductionFactor;
} else {
reductionFactor = limitsWidth / vb.width;
vb.width = limitsWidth;
vb.height = vb.height * reductionFactor;
}
} else {
reductionFactor = limitsWidth / vb.width;
vb.width = limitsWidth;
vb.height = vb.height * reductionFactor;
}
} else if (vb.height > limitsHeight) {
reductionFactor = limitsHeight / vb.height;
vb.height = limitsHeight;
vb.width = vb.width * reductionFactor;
}
if (vb.x < limits.x) {
vb.x = limits.x;
}
if (vb.y < limits.y) {
vb.y = limits.y;
}
if (vb.x + vb.width > limits.x2) {
vb.x = limits.x2 - vb.width;
}
if (vb.y + vb.height > limits.y2) {
vb.y = limits.y2 - vb.height;
}
return vb;
};
/**
* Parse the viewbox string as defined in the spec for the svg tag.
*
* @param {String} viewBoxString
* A valid value of the `viewBox` attribute.
*
* @return {Object} viewBox
* A view box object. Contains numbers, in the format `{x, y, width, height}`.
*/
parseViewBoxString = function(string) {
var vb;
vb = string.replace("\s+", " ").split(" ");
return vb = {
x: parseFloat(vb[0]),
y: parseFloat(vb[1]),
width: parseFloat(vb[2]),
height: parseFloat(vb[3])
};
};
/**
* Get the mouse or first touch position from the `event`, relative to the SVG viewBox.
*
* @param {SVGElement} svgRoot
* The `<svg>` DOM object
*
* @param {MouseEvent|TouchEvent|jQueryEvent} event
* The DOM or jQuery event.
*
* @return {Object}
* Coordinates of the event. Contains numbers, in the format `{x, y}`.
*/
getViewBoxCoordinatesFromEvent = function(svgRoot, event) {
var ctm, foo, pos;
foo = {
x: null,
y: null
};
if (event.type === "touchstart" || event.type === "touchmove") {
if ((event.originalEvent != null) && (event.touches == null)) {
foo.x = event.originalEvent.touches[0].clientX;
foo.y = event.originalEvent.touches[0].clientY;
} else {
foo.x = event.touches[0].clientX;
foo.y = event.touches[0].clientY;
}
} else {
if (event.clientX != null) {
foo.x = event.clientX;
foo.y = event.clientY;
} else {
foo.x = event.originalEvent.clientX;
foo.y = event.originalEvent.clientY;
}
}
pos = svgRoot.createSVGPoint();
pos.x = parseInt(foo.x, 10);
pos.y = parseInt(foo.y, 10);
ctm = svgRoot.getScreenCTM();
ctm = ctm.inverse();
pos = pos.matrixTransform(ctm);
return pos;
};
return $.fn.svgPanZoom = function(options) {
var ret;
ret = [];
this.each(function() {
var $animationDiv, dragStarted, horizontalSizeIncrement, key, opts, preventClick, value, vb, verticalSizeIncrement, viewBox;
opts = $.extend(true, {}, defaultOptions, options);
opts.$svg = $(this);
if (opts.animationTime == null) {
opts.animationTime = 0;
}
opts.$svg[0].setAttribute("preserveAspectRatio", "xMidYMid meet");
vb = $.extend({}, this.viewBox.baseVal);
if (vb.x == null) {
vb.x = 0;
}
if (vb.y == null) {
vb.y = 0;
}
if (vb.width == null) {
vb.width = 0;
}
if (vb.height == null) {
vb.height = 0;
}
if (opts.initialViewBox != null) {
if (typeof opts.initialViewBox === "string") {
vb = parseViewBoxString(opts.initialViewBox);
} else if (typeof opts.initialViewBox === "object") {
vb = $.extend({}, defaultViewBox, opts.initialViewBox);
} else {
throw "initialViewBox is of invalid type";
}
} else if (vb.x === 0 && vb.y === 0 && vb.width === 0 && vb.height === 0) {
vb = defaultViewBox;
}
viewBox = vb;
opts.initialViewBox = $.extend({}, viewBox);
if (opts.limits == null) {
horizontalSizeIncrement = viewBox.width * 0.15;
verticalSizeIncrement = viewBox.height * 0.15;
opts.limits = {
x: viewBox.x - horizontalSizeIncrement,
y: viewBox.y - verticalSizeIncrement,
x2: viewBox.x + viewBox.width + horizontalSizeIncrement,
y2: viewBox.y + viewBox.height + verticalSizeIncrement
};
}
opts.reset = function() {
var inivb;
inivb = this.initialViewBox;
this.setViewBox(inivb.x, inivb.y, inivb.width, inivb.height, 0);
};
opts.getViewBox = function() {
return $.extend({}, viewBox);
};
$animationDiv = $("<div></div>");
opts.setViewBox = function(x, y, width, height, animationTime) {
if (animationTime == null) {
animationTime = this.animationTime;
}
if (animationTime > 0) {
$animationDiv.css({
left: viewBox.x + "px",
top: viewBox.y + "px",
width: viewBox.width + "px",
height: viewBox.height + "px"
});
}
viewBox = {
x: x != null ? x : viewBox.x,
y: y != null ? y : viewBox.y,
width: width ? width : viewBox.width,
height: height ? height : viewBox.height
};
viewBox = checkLimits(viewBox, this.limits);
if (animationTime > 0) {
$animationDiv.stop().animate({
left: viewBox.x,
top: viewBox.y,
width: viewBox.width,
height: viewBox.height
}, {
duration: animationTime,
easing: "linear",
step: (function(value, properties) {
var $div;
$div = $animationDiv;
this.$svg[0].setAttribute("viewBox", ($div.css("left").slice(0, -2)) + " " + ($div.css("top").slice(0, -2)) + " " + ($div.css("width").slice(0, -2)) + " " + ($div.css("height").slice(0, -2)));
}).bind(this)
});
} else {
this.$svg[0].setAttribute("viewBox", viewBox.x + " " + viewBox.y + " " + viewBox.width + " " + viewBox.height);
}
};
opts.panLeft = function(amount, animationTime) {
if (amount == null) {
amount = this.panFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
this.panRight(-amount, animationTime);
};
opts.panRight = function(amount, animationTime) {
if (amount == null) {
amount = this.panFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
this.setViewBox(viewBox.x + amount, null, null, null, animationTime);
};
opts.panUp = function(amount, animationTime) {
if (amount == null) {
amount = this.panFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
this.panDown(-amount, animationTime);
};
opts.panDown = function(amount, animationTime) {
if (amount == null) {
amount = this.panFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
this.setViewBox(null, viewBox.y + amount, null, null, animationTime);
};
opts.zoomIn = function(amount, animationTime) {
if (amount == null) {
amount = this.zoomFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
this.zoomOut(-amount, animationTime);
};
opts.zoomOut = function(amount, animationTime) {
var center, newHeight, newWidth;
if (amount == null) {
amount = this.zoomFactor;
}
if (animationTime == null) {
animationTime = this.animationTime;
}
if (amount === 0) {
return;
} else if (amount < 0) {
amount = Math.abs(amount);
newWidth = viewBox.width / (1 + amount);
newHeight = viewBox.height / (1 + amount);
} else {
newWidth = viewBox.width * (1 + amount);
newHeight = viewBox.height * (1 + amount);
}
center = {
x: viewBox.x + viewBox.width / 2,
y: viewBox.y + viewBox.height / 2
};
this.setViewBox(center.x - newWidth / 2, center.y - newWidth / 2, newWidth, newHeight, animationTime);
};
opts.setCenter = function(x, y, animationTime) {
if (animationTime == null) {
animationTime = this.animationTime;
}
this.setViewBox(x - viewBox.width / 2, y - viewBox.height / 2, viewBox.width, viewBox.height, animationTime);
};
for (key in opts) {
if (!hasProp.call(opts, key)) continue;
value = opts[key];
if (typeof value === "function") {
opts.key = value.bind(opts);
}
}
opts.$svg.on("mousewheel DOMMouseScroll MozMousePixelScroll", (function(ev) {
var delta, minHeight, minWidth, newMousePosition, newViewBox, newcenter, oldDistanceFromCenter, oldMousePosition, oldViewBox, oldcenter, reductionFactor;
delta = parseInt(ev.originalEvent.wheelDelta || -ev.originalEvent.detail);
if (delta === 0 || opts.events.mouseWheel !== true) {
return;
}
oldViewBox = this.getViewBox();
ev.preventDefault();
ev.stopPropagation();
oldMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
oldcenter = {
x: viewBox.x + viewBox.width / 2,
y: viewBox.y + viewBox.height / 2
};
oldDistanceFromCenter = {
x: oldcenter.x - oldMousePosition.x,
y: oldcenter.y - oldMousePosition.y
};
if (delta > 0) {
this.zoomIn(void 0, 0);
minWidth = this.initialViewBox.width / this.maxZoom;
minHeight = this.initialViewBox.height / this.maxZoom;
if (viewBox.width < minWidth) {
reductionFactor = minWidth / viewBox.width;
viewBox.width = minWidth;
viewBox.height = viewBox.height * reductionFactor;
}
if (viewBox.height < minHeight) {
reductionFactor = minHeight / viewBox.height;
viewBox.height = minHeight;
viewBox.width = viewBox.width * reductionFactor;
}
} else {
this.zoomOut(void 0, 0);
}
newMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
newcenter = {
x: oldcenter.x + (oldMousePosition.x - newMousePosition.x),
y: oldcenter.y + (oldMousePosition.y - newMousePosition.y)
};
this.setCenter(newcenter.x, newcenter.y, 0);
newViewBox = this.getViewBox();
this.setViewBox(oldViewBox.x, oldViewBox.y, oldViewBox.width, oldViewBox.height, 0);
this.setViewBox(newViewBox.x, newViewBox.y, newViewBox.width, newViewBox.height);
}).bind(opts));
opts.$svg.dblclick((function(ev) {
if (opts.events.doubleClick !== true) {
return;
}
ev.preventDefault();
ev.stopPropagation();
return this.zoomIn();
}).bind(opts));
opts.$svg[0].addEventListener("click", function(ev) {
var preventClick;
if (preventClick) {
preventClick = false;
ev.stopPropagation();
return ev.preventDefault();
}
}, true);
dragStarted = false;
preventClick = false;
opts.$svg.on("mousedown touchstart", (function(ev) {
var $body, domBody, initialViewBox, mouseMoveCallback, mouseUpCallback, oldCursor;
if (dragStarted) {
return;
}
if (opts.events.drag !== true || (ev.type === "mousedown" && ev.which !== 1)) {
return;
}
dragStarted = true;
preventClick = false;
ev.preventDefault();
ev.stopPropagation();
initialViewBox = $.extend({}, viewBox);
$body = $(window.document.body);
domBody = $body[0];
oldCursor = $body.css("cursor");
if (this.events.dragCursor != null) {
$body.css("cursor", this.events.dragCursor);
}
mouseMoveCallback = (function(ev2) {
var currentMousePosition, initialMousePosition;
ev2.preventDefault();
ev2.stopPropagation();
initialMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
currentMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev2);
if (Math.sqrt(Math.pow(ev.pageX + ev2.pageX, 2) + Math.pow(ev.pageY + ev2.pageY, 2)) > 3) {
preventClick = true;
}
this.setViewBox(initialViewBox.x + initialMousePosition.x - currentMousePosition.x, initialViewBox.y + initialMousePosition.y - currentMousePosition.y, null, null, 0);
}).bind(opts);
mouseUpCallback = (function(ev2) {
if (ev2.type === "mouseout" && ev2.target !== ev2.currentTarget) {
return;
}
ev2.preventDefault();
ev2.stopPropagation();
domBody.removeEventListener("mousemove", mouseMoveCallback, true);
domBody.removeEventListener("touchmove", mouseMoveCallback, true);
domBody.removeEventListener("mouseup", mouseUpCallback, true);
domBody.removeEventListener("touchend", mouseUpCallback, true);
domBody.removeEventListener("touchcancel", mouseUpCallback, true);
domBody.removeEventListener("mouseout", mouseUpCallback, true);
if (this.events.dragCursor != null) {
$body.css("cursor", oldCursor);
}
dragStarted = false;
}).bind(opts);
domBody.addEventListener("mousemove", mouseMoveCallback, true);
domBody.addEventListener("touchmove", mouseMoveCallback, true);
domBody.addEventListener("mouseup", mouseUpCallback, true);
domBody.addEventListener("touchend", mouseUpCallback, true);
domBody.addEventListener("touchcancel", mouseUpCallback, true);
domBody.addEventListener("mouseout", mouseUpCallback, true);
}).bind(opts));
opts.setViewBox(vb.x, vb.y, vb.width, vb.height, 0);
ret.push(opts);
});
if (ret.length === 0) {
return null;
}
if (ret.length === 1) {
return ret[0];
} else {
return ret;
}
};
})(jQuery);
}).call(this);
//# sourceMappingURL=jquery.svg.pan.zoom.js.map

6
js/lunr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

32
js/mermaid.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
js/perfect-scrollbar.jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
js/perfect-scrollbar.min.js vendored Normal file

File diff suppressed because one or more lines are too long

616
js/relearn.js Normal file
View file

@ -0,0 +1,616 @@
// Scrollbar Width function
function getScrollBarWidth() {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild(inner);
document.body.appendChild(outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild(outer);
return (w1 - w2);
};
function setMenuHeight() {
$('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40);
$('#sidebar .highlightable').perfectScrollbar('update');
}
function fallbackMessage(action) {
var actionMsg = '';
var actionKey = (action === 'cut' ? 'X' : 'C');
if (/iPhone|iPad/i.test(navigator.userAgent)) {
actionMsg = 'No support :(';
}
else if (/Mac/i.test(navigator.userAgent)) {
actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
}
else {
actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
}
return actionMsg;
}
function switchTab(tabGroup, tabId) {
allTabItems = jQuery("[data-tab-group='"+tabGroup+"']");
targetTabItems = jQuery("[data-tab-group='"+tabGroup+"'][data-tab-item='"+tabId+"']");
// if event is undefined then switchTab was called from restoreTabSelection
// so it's not a button event and we don't need to safe the selction or
// prevent page jump
var isButtonEvent = event != undefined;
if(isButtonEvent){
// save button position relative to viewport
var yposButton = event.target.getBoundingClientRect().top;
}
allTabItems.removeClass("active");
targetTabItems.addClass("active");
if(isButtonEvent){
// reset screen to the same position relative to clicked button to prevent page jump
var yposButtonDiff = event.target.getBoundingClientRect().top - yposButton;
window.scrollTo(window.scrollX, window.scrollY+yposButtonDiff);
// Store the selection to make it persistent
if(window.localStorage){
var selectionsJSON = window.localStorage.getItem("tabSelections");
if(selectionsJSON){
var tabSelections = JSON.parse(selectionsJSON);
}else{
var tabSelections = {};
}
tabSelections[tabGroup] = tabId;
window.localStorage.setItem("tabSelections", JSON.stringify(tabSelections));
}
}
}
function restoreTabSelections() {
if(window.localStorage){
var selectionsJSON = window.localStorage.getItem("tabSelections");
if(selectionsJSON){
var tabSelections = JSON.parse(selectionsJSON);
}else{
var tabSelections = {};
}
Object.keys(tabSelections).forEach(function(tabGroup) {
var tabItem = tabSelections[tabGroup];
switchTab(tabGroup, tabItem);
});
}
}
function initMermaid() {
$('code.language-mermaid').each(function(index, element) {
var content = $(element).html().replace(/&amp;/g, '&');
$(element).parent().replaceWith('<div class="mermaid" align="center">' + content + '</div>');
});
if (typeof mermaid != 'undefined') {
mermaid.mermaidAPI.initialize( Object.assign( {}, mermaid.mermaidAPI.getSiteConfig(), { startOnLoad: true } ) );
mermaid.contentLoaded();
$(".mermaid svg").svgPanZoom({})
}
}
function scrollToActiveMenu() {
window.setTimeout(function(){
var e = $("#sidebar ul.topics li.active")[0];
if( e && e.scrollIntoView ){
e.scrollIntoView({
block: 'center',
});
}
}, 200);
}
// Get Parameters from some url
var getUrlParameter = function getUrlParameter(sPageURL) {
var url = sPageURL.split('?');
var obj = {};
if (url.length == 2) {
var sURLVariables = url[1].split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
obj[sParameterName[0]] = sParameterName[1];
}
}
return obj;
};
// Execute actions on images generated from Markdown pages
var images = $("main#body-inner img").not(".inline");
// Wrap image inside a featherlight (to get a full size view in a popup)
images.wrap(function(){
var image =$(this);
var o = getUrlParameter(image[0].src);
var f = o['featherlight'];
// IF featherlight is false, do not use feather light
if (f != 'false') {
if (!image.parent("a").length) {
var html = $( "<a>" ).attr("href", image[0].src).attr("data-featherlight", "image").get(0).outerHTML;
return html;
}
}
});
// Change styles, depending on parameters set to the image
images.each(function(index){
var image = $(this)
var o = getUrlParameter(image[0].src);
if (typeof o !== "undefined") {
var h = o["height"];
var w = o["width"];
var c = o["classes"];
image.css("width", function() {
if (typeof w !== "undefined") {
return w;
} else {
return "auto";
}
});
image.css("height", function() {
if (typeof h !== "undefined") {
return h;
} else {
return "auto";
}
});
if (typeof c !== "undefined") {
var classes = c.split(',');
for (i = 0; i < classes.length; i++) {
image.addClass(classes[i]);
}
}
}
});
// for the window resize
$(window).resize(function() {
setMenuHeight();
});
// for the sticky header
$(window).scroll(function() {
// add shadow when not in top position
if ($(this).scrollTop() == 0) {
$('#top-bar').removeClass("is-sticky");
}
else {
$('#top-bar').addClass("is-sticky");
}
});
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
(function($, sr) {
var debounce = function(func, threshold, execAsap) {
var timeout;
return function debounced() {
var obj = this, args = arguments;
function delayed() {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery, 'smartresize');
jQuery(function() {
restoreTabSelections();
initMermaid();
scrollToActiveMenu();
jQuery('#sidebar .category-icon').on('click', function() {
$( this ).toggleClass("fa-angle-down fa-angle-right") ;
$( this ).parent().parent().children('ul').toggle() ;
return false;
});
var sidebarStatus = searchStatus = 'open';
$('#sidebar .highlightable').perfectScrollbar();
setMenuHeight();
jQuery('#overlay').on('click', function() {
jQuery(document.body).toggleClass('sidebar-hidden');
sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
return false;
});
jQuery('[data-sidebar-toggle]').on('click', function() {
jQuery(document.body).toggleClass('sidebar-hidden');
sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
return false;
});
jQuery('[data-clear-history-toggle]').on('click', function() {
sessionStorage.clear();
location.reload();
return false;
});
jQuery('[data-search-toggle]').on('click', function() {
if (sidebarStatus == 'closed') {
jQuery('[data-sidebar-toggle]').trigger('click');
jQuery(document.body).removeClass('searchbox-hidden');
searchStatus = 'open';
return false;
}
jQuery(document.body).toggleClass('searchbox-hidden');
searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open');
return false;
});
var ajax;
jQuery('[data-search-input]').on('input', function() {
var input = jQuery(this),
value = input.val(),
items = jQuery('[data-nav-id]');
items.removeClass('search-match');
if (!value.length) {
$('ul.topics').removeClass('searched');
items.css('display', 'block');
sessionStorage.removeItem('search-value');
$("mark").parents(".expand-marked").removeClass("expand-marked");
$(".highlightable").unhighlight({ element: 'mark' })
return;
}
sessionStorage.setItem('search-value', value);
$("mark").parents(".expand-marked").removeClass("expand-marked");
$(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' });
$("mark").parents(".expand").addClass("expand-marked");
if (ajax && ajax.abort) ajax.abort();
jQuery('[data-search-clear]').on('click', function() {
jQuery('[data-search-input]').val('').trigger('input');
sessionStorage.removeItem('search-input');
$("mark").parents(".expand-marked").removeClass("expand-marked");
$(".highlightable").unhighlight({ element: 'mark' })
});
});
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
if (sessionStorage.getItem('search-value')) {
var searchValue = sessionStorage.getItem('search-value')
$(document.body).removeClass('searchbox-hidden');
$('[data-search-input]').val(searchValue);
$('[data-search-input]').trigger('input');
var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0);
if (searchedElem) {
searchedElem.scrollIntoView(true);
var scrolledY = window.scrollY;
if(scrolledY){
window.scroll(0, scrolledY - 125);
}
}
}
$(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' });
$("mark").parents(".expand").addClass("expand-marked");
// clipboard
var clipInit = false;
$('code').each(function() {
var code = $(this),
text = code.text();
if (text.length > 5) {
if (!clipInit) {
var text, clip = new ClipboardJS('.copy-to-clipboard', {
text: function(trigger) {
text = $(trigger).prev('code').text();
return text.replace(/^\$\s/gm, '');
}
});
var inPre;
clip.on('success', function(e) {
e.clearSelection();
inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
$(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
});
clip.on('error', function(e) {
inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
$(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
$(document).one('copy', function(){
$(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
});
});
clipInit = true;
}
code.addClass('copy-to-clipboard-inline');
code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />');
code.next('.copy-to-clipboard').on('mouseleave', function() {
$(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w');
});
}
});
// allow keyboard control for prev/next links
jQuery(function() {
jQuery('.nav-prev').click(function(){
location.href = jQuery(this).attr('href');
});
jQuery('.nav-next').click(function() {
location.href = jQuery(this).attr('href');
});
});
jQuery('input, textarea').keydown(function (e) {
// left and right arrow keys
if (e.which == '37' || e.which == '39') {
e.stopPropagation();
}
});
jQuery(document).keydown(function(e) {
// prev links - left arrow key
if(e.which == '37') {
jQuery('.nav.nav-prev').click();
}
// next links - right arrow key
if(e.which == '39') {
jQuery('.nav.nav-next').click();
}
});
$('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight');
$('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight');
var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
if (!touchsupport){ // browser doesn't support touch
$('#toc-menu').hover(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
$('.progress').hover(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
}
if (touchsupport){ // browser does support touch
$('#toc-menu').click(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
$('.progress').click(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
}
/**
* Fix anchor scrolling that hides behind top nav bar
* Courtesy of https://stackoverflow.com/a/13067009/28106
*
* We could use pure css for this if only heading anchors were
* involved, but this works for any anchor, including footnotes
**/
(function (document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function () {
this.scrollToCurrent();
$(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function () {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function (href, pushToHistory) {
var match, anchorOffset;
if (!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if (match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').animate({ scrollTop: anchorOffset });
// Add the state to history as-per normal anchor links
if (HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function (e) {
if (this.scrollIfAnchor(window.location.hash) && e) {
e.preventDefault();
}
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function (e) {
var elem = e.target;
if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);
// Add link button for every
var text, clip = new ClipboardJS('.anchor');
$("h1~h2,h1~h3,h1~h4,h1~h5,h1~h6").append(function(index, html){
var element = $(this);
var url = encodeURI(document.location.origin + document.location.pathname);
var link = url + "#"+element[0].id;
var html = " " + $( "<span>" ).addClass("anchor").attr("data-clipboard-text", link).append("<i class='fas fa-link fa-lg'></i>").get(0).outerHTML;
return html;
});
$(".anchor").on('mouseleave', function(e) {
$(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w');
});
clip.on('success', function(e) {
e.clearSelection();
$(e.trigger).attr('aria-label', 'Link copied to clipboard!').addClass('tooltipped tooltipped-s');
});
$('a[rel="lightbox"]').featherlight({
root: 'div#body'
});
sessionStorage.setItem(jQuery('body').data('url'), 1);
// loop through the sessionStorage and see if something should be marked as visited
for (var url in sessionStorage) {
if (sessionStorage.getItem(url) == 1){
// in case we have `relativeURLs=true` we have to strip the
// relative path to root
url = url.replace( /\.\.\//g, '/' ).replace( /^\/+\//, '/' );
jQuery('[data-nav-id="' + url + '"]').addClass('visited');
}
}
});
jQuery.extend({
highlight: function(node, re, nodeName, className) {
if (node.nodeType === 3 && node.parentElement && node.parentElement.namespaceURI == 'http://www.w3.org/1999/xhtml') { // text nodes
var match = node.data.match(re);
if (match) {
var highlight = document.createElement(nodeName || 'span');
highlight.className = className || 'highlight';
var wordNode = node.splitText(match.index);
wordNode.splitText(match[0].length);
var wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
return 1; //skip added node in parent
}
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
for (var i = 0; i < node.childNodes.length; i++) {
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
}
}
return 0;
}
});
jQuery.fn.unhighlight = function(options) {
var settings = {
className: 'highlight',
element: 'span'
};
jQuery.extend(settings, options);
return this.find(settings.element + "." + settings.className).each(function() {
var parent = this.parentNode;
parent.replaceChild(this.firstChild, this);
parent.normalize();
}).end();
};
jQuery.fn.highlight = function(words, options) {
var settings = {
className: 'highlight',
element: 'span',
caseSensitive: false,
wordsOnly: false
};
jQuery.extend(settings, options);
if (!words) { return; }
if (words.constructor === String) {
words = [words];
}
words = jQuery.grep(words, function(word, i) {
return word != '';
});
words = jQuery.map(words, function(word, i) {
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
});
if (words.length == 0) { return this; }
;
var flag = settings.caseSensitive ? "" : "i";
var pattern = "(" + words.join("|") + ")";
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);
return this.each(function() {
jQuery.highlight(this, re, settings.element, settings.className);
});
};

104
js/search.js Normal file
View file

@ -0,0 +1,104 @@
var lunrIndex, pagesIndex;
// Initialize lunrjs using our generated index file
function initLunr() {
// First retrieve the index file
$.getJSON(index_url)
.done(function(index) {
pagesIndex = index;
// Set up lunrjs by declaring the fields we use
// Also provide their boost level for the ranking
lunrIndex = lunr(function() {
this.ref('index');
this.field('title', {
boost: 15
});
this.field('tags', {
boost: 10
});
this.field('content', {
boost: 5
});
this.pipeline.remove(lunr.stemmer);
this.searchPipeline.remove(lunr.stemmer);
// Feed lunr with each file and let lunr actually index them
pagesIndex.forEach(function(page, idx) {
page.index = idx;
this.add(page);
}, this);
})
})
.fail(function(jqxhr, textStatus, error) {
var err = textStatus + ', ' + error;
console.error('Error getting Hugo index file:', err);
});
}
/**
* Trigger a search in lunr and transform the result
*
* @param {String} term
* @return {Array} results
*/
function search(term) {
// Find the item in our index corresponding to the lunr one to have more info
// Remove Lunr special search characters: https://lunrjs.com/guides/searching.html
var searchTerm = lunr.tokenizer(term.replace(/[*:^~+-]/, ' ')).reduce( function(a,token){return a.concat(searchPatterns(token.str))}, []).join(' ');
return !searchTerm ? [] : lunrIndex.search(searchTerm).map(function(result) {
return { index: result.ref, matches: Object.keys(result.matchData.metadata) }
});
}
function searchPatterns(word) {
return [
word + '^100',
word + '*^10',
'*' + word + '^10',
word + '~' + Math.floor(word.length / 4) + '^1' // allow 1 in 4 letters to have a typo
];
}
// Let's get started
initLunr();
$(function() {
var searchList = new autoComplete({
/* selector for the search box element */
selector: $('#search-by').get(0),
/* source is the callback to perform the search */
source: function(term, response) {
response(search(term));
},
/* renderItem displays individual search results */
renderItem: function(item, term) {
var page = pagesIndex[item.index];
var numContextWords = 2;
var contextPattern = '(?:\\S+ +){0,' + numContextWords + '}\\S*\\b(?:' +
item.matches.map( function(match){return match.replace(/\W/g, '\\$&')} ).join('|') +
')\\b\\S*(?: +\\S+){0,' + numContextWords + '}';
var context = page.content.match(new RegExp(contextPattern, 'i'));
var divcontext = document.createElement('div');
divcontext.className = 'context';
divcontext.innerText = (context || '');
var divsuggestion = document.createElement('div');
divsuggestion.className = 'autocomplete-suggestion';
divsuggestion.setAttribute('data-term', term);
divsuggestion.setAttribute('data-title', page.title);
divsuggestion.setAttribute('data-uri', baseUri + page.uri);
divsuggestion.setAttribute('data-context', context);
divsuggestion.innerText = '» ' + page.title;
divsuggestion.appendChild(divcontext);
return divsuggestion.outerHTML;
},
/* onSelect callback fires when a search suggestion is chosen */
onSelect: function(e, term, item) {
location.href = item.getAttribute('data-uri');
}
});
// JavaScript-autoComplete only registers the focus event when minChars is 0 which doesn't make sense, let's do it ourselves
// https://github.com/Pixabay/JavaScript-autoComplete/blob/master/auto-complete.js#L191
var selector = $('#search-by').get(0);
$(selector).focus(selector.focusHandler);
});