UNPKG

chevrotain

Version:

Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers

224 lines (197 loc) 7.3 kB
(function (root, factory) { if (typeof define === "function" && define.amd) { // AMD. Register as an anonymous module. define([], factory); } else if (typeof module === "object" && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { // Browser globals (root is window) root.diagrams_behavior = factory(); } })(this, function () { /** * @param [scrollingEnabled=true] {boolean} - Is the scrolling from a non-terminal usage to it's definition * enabled. it is enabled by default, but this flow is not relevant in all use cases (playground) and thus * it is parametrized. */ function initDiagramsBehavior(scrollingEnabled) { if (scrollingEnabled === undefined) { scrollingEnabled = true; } var diagramHeaders = toArr( document.getElementsByClassName("diagramHeader"), ); diagramHeaders.forEach(function (header) { header.addEventListener( "mouseover", toggleNonTerminalUsageAndDef_fromHeader, ); header.addEventListener( "mouseout", toggleNonTerminalUsageAndDef_fromHeader, ); }); var noneTerminals = toArr(document.getElementsByClassName("non-terminal")); var noneTerminalsText = findDomChildrenByTagName(noneTerminals, "text"); noneTerminalsText.forEach(function (nonTerminal) { nonTerminal.addEventListener( "mouseover", toggleNonTerminalUsageAndDef_fromNoneTerminal, ); nonTerminal.addEventListener( "mouseout", toggleNonTerminalUsageAndDef_fromNoneTerminal, ); if (scrollingEnabled) { nonTerminal.addEventListener("click", jumpToNoneTerminalDef); } }); var terminals = toArr(document.getElementsByClassName("terminal")); var terminalsText = findDomChildrenByTagName(terminals, "text"); terminalsText.forEach(function (terminal) { terminal.addEventListener("mouseover", toggleTerminalUsage); terminal.addEventListener("mouseout", toggleTerminalUsage); }); } function toggleTerminalUsage(mouseEvent) { var terminalName = mouseEvent.target.getAttribute("label"); var rects = getUsageSvgRect(terminalName, "terminal", "label"); toggleClassForNodes(rects, "diagramRectUsage"); } function toggleNonTerminalUsageAndDef_fromNoneTerminal(mouseEvent) { var rectsHeaderAndRuleName = getUsageRectAndDefHeader(mouseEvent.target); toggleClassForNodes(rectsHeaderAndRuleName.rects, "diagramRectUsage"); toggleClass(rectsHeaderAndRuleName.header, "diagramHeaderDef"); } function jumpToNoneTerminalDef(mouseEvent) { var header = findHeader(mouseEvent.target.getAttribute("rulename")); scrollToY(header.offsetTop, 666, "easeInOutQuint"); } function toggleNonTerminalUsageAndDef_fromHeader(mouseEvent) { toggleClass(mouseEvent.target, "diagramHeaderDef"); // this does not work on an svg DOM element so its ok to use innerHTML. var definitionName = mouseEvent.target.innerHTML; var rects = getUsageSvgRect(definitionName, "non-terminal", "rulename"); toggleClassForNodes(rects, "diagramRectUsage"); } function getUsageSvgRect(definitionName, className, attributeName) { var classDomElements = toArr(document.getElementsByClassName(className)); var rects = findDomChildrenByTagName(classDomElements, "rect"); return rects.filter(function (currRect) { var textNode = currRect.parentNode.getElementsByTagName("text")[0]; return textNode.getAttribute(attributeName) === definitionName; }); } function findHeader(headerName) { var headers = toArr(document.getElementsByClassName("diagramHeader")); var header = headers.find(function (currHeader) { // this works on H2 dom elements and not SVG elements so innerHTML usage is safe. return currHeader.innerHTML === headerName; }); return header; } function getUsageRectAndDefHeader(target) { var headerName = target.getAttribute("rulename"); var rects = getUsageSvgRect(headerName, "non-terminal", "rulename"); var header = findHeader(headerName); return { rects: rects, header: header, ruleName: headerName, }; } // utils // IE 10/11 does not support this on svg elements. // I'm uncertain I really care... :) // https://developer.mozilla.org/en-US/docs/Web/API/Element/classList function toggleClass(domNode, className) { if (domNode.classList.contains(className)) { domNode.classList.remove(className); } else { domNode.classList.add(className); } } function toggleClassForNodes(domNodes, className) { domNodes.forEach(function (currDomNode) { toggleClass(currDomNode, className); }); } function toArr(htmlCollection) { return Array.prototype.slice.call(htmlCollection); } // first add raf shim // http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ var requestAnimFrame = (function () { return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); } ); })(); // https://stackoverflow.com/questions/8917921/cross-browser-javascript-not-jquery-scroll-to-top-animation function scrollToY(scrollTargetY, speed, easing) { // scrollTargetY: the target scrollY property of the window // speed: time in pixels per second // easing: easing equation to use var scrollY = window.scrollY, scrollTargetY = scrollTargetY || 0, speed = speed || 2000, easing = easing || "easeOutSine", currentTime = 0; // min time .1, max time .8 seconds var time = Math.max( 0.1, Math.min(Math.abs(scrollY - scrollTargetY) / speed, 0.8), ); // easing equations from https://github.com/danro/easing-js/blob/master/easing.js var PI_D2 = Math.PI / 2, easingEquations = { easeOutSine: function (pos) { return Math.sin(pos * (Math.PI / 2)); }, easeInOutSine: function (pos) { return -0.5 * (Math.cos(Math.PI * pos) - 1); }, easeInOutQuint: function (pos) { if ((pos /= 0.5) < 1) { return 0.5 * Math.pow(pos, 5); } return 0.5 * (Math.pow(pos - 2, 5) + 2); }, }; // add animation loop function tick() { currentTime += 1 / 60; var p = currentTime / time; var t = easingEquations[easing](p); if (p < 1) { requestAnimFrame(tick); window.scrollTo(0, scrollY + (scrollTargetY - scrollY) * t); } else { window.scrollTo(0, scrollTargetY); } } // call it once to get started tick(); } function findDomChildrenByTagName(domElements, tagName) { var elemsFound = []; domElements.forEach(function (currDomNode) { toArr(currDomNode.children).forEach(function (currChild) { if (currChild.tagName === tagName) { elemsFound.push(currChild); } }); }); return elemsFound; } return { initDiagramsBehavior: initDiagramsBehavior, }; });