import { LanguageService } from "./../services/languageService"; import { LocalizedStrings } from "./../services/localizedStringsService"; import { Operators } from "./../ast/operators"; /** * source: https://pawelgrzybek.com/page-scroll-in-vanilla-javascript/ * */ export function scrollIt ( destination, duration = 200, easing = "linear", callback = null ) { const easings = { linear (t) { return t; }, easeInQuad (t) { return t * t; }, easeOutQuad (t) { return t * (2 - t); }, easeInOutQuad (t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }, easeInCubic (t) { return t * t * t; }, easeOutCubic (t) { return --t * t * t + 1; }, easeInOutCubic (t) { return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; }, easeInQuart (t) { return t * t * t * t; }, easeOutQuart (t) { return 1 - --t * t * t * t; }, easeInOutQuart (t) { return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t; }, easeInQuint (t) { return t * t * t * t * t; }, easeOutQuint (t) { return 1 + --t * t * t * t * t; }, easeInOutQuint (t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t; }, }; const start = window.pageYOffset; const startTime = "now" in window.performance ? performance.now() : new Date().getTime(); const documentHeight = Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight ); const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName("body")[0].clientHeight; const destinationOffset = typeof destination === "number" ? destination : destination.offsetTop; const destinationOffsetToScroll = Math.round( documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset ); if ("requestAnimationFrame" in window === false) { window.scroll(0, destinationOffsetToScroll); if (callback) { callback(); } return; } function scroll () { const now = "now" in window.performance ? performance.now() : new Date().getTime(); const time = Math.min(1, (now - startTime) / duration); const timeFunction = easings[easing](time); window.scroll( 0, Math.ceil(timeFunction * (destinationOffsetToScroll - start) + start) ); if (window.pageYOffset === destinationOffsetToScroll) { if (callback) { callback(); } return; } requestAnimationFrame(scroll); } scroll(); } /** * * source: https://stackoverflow.com/a/16270434 */ export function isElementInViewport (el) { const rect = el.getBoundingClientRect(); return ( rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.top < (window.innerHeight || document.documentElement.clientHeight) ); } let cacheMainList = null; let cacheOp = null; function fillCache () { if (cacheMainList == null) { cacheMainList = []; const mainList = [ "RK_PROGRAM", "RK_REAL", "RK_VOID", "RK_BOOLEAN", "RK_STRING", "RK_INTEGER", "RK_CHARACTER", "RK_SWITCH", "RK_CASE", "RK_DEFAULT", "RK_CONST", "RK_FUNCTION", "RK_RETURN", "RK_FOR", "RK_BREAK", "RK_DO", "RK_WHILE", "RK_IF", "RK_ELSE", "RK_FALSE", "RK_TRUE", ]; const lexer = LanguageService.getCurrentLexer(); const names = lexer.getReservedKeys(); for (let key = 0; key < mainList.length; ++key) { const word = mainList[key]; const keyword = names[word]; cacheMainList.push(keyword.substring(1, keyword.length - 1)); } } if (cacheOp == null) { cacheOp = []; const logicOpList = [ Operators.AND.value, Operators.OR.value, Operators.NOT.value, ]; for (let op = 0; op < logicOpList.length; ++op) { const lOp = `logic_operator_${logicOpList[op]}`; cacheOp.push(LocalizedStrings.getUI(lOp)); } } } export function isKeyword (text) { fillCache(); for (let key = 0; key < cacheMainList.length; ++key) { const keyword = cacheMainList[key]; if (keyword == text) { return true; } } // not in main list, check op for (let op = 0; op < cacheOp.length; op++) { const lOp = cacheOp[op]; if (lOp == text) { return true; } } return false; } export function isValidIdentifier (identifier_str) { const validRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier_str); if (!validRegex) { return false; } return !isKeyword(identifier_str); } export function getCodeEditorModeConfig () { const blockList = [ "RK_SWITCH", "RK_PROGRAM", "RK_CASE", "RK_DEFAULT", "RK_FOR", "RK_FOR_ALT", "RK_FUNCTION", "RK_DO", "RK_WHILE", "RK_WHILE_ALT", "RK_IF", "RK_ELSE", ]; const keywordsList = [ "RK_CONST", "RK_RETURN", "RK_BREAK", "RK_FOR_FROM", "RK_FOR_TO", "RK_FOR_PASS", "RK_DO_UNTIL", ]; const typeList = [ "RK_REAL", "RK_VOID", "RK_BOOLEAN", "RK_STRING", "RK_INTEGER", "RK_CHARACTER", ]; const atomList = ["RK_FALSE", "RK_TRUE"]; const case_default = []; const blocks = []; const keywords = []; const types = []; const atoms = []; let switchString = ""; cacheMainList = []; const lexer = LanguageService.getCurrentLexer(); const names = lexer.getReservedKeys(); //console.debug(names); blockList.forEach((v) => { const keyword = names[v]; const value = keyword.substring(1, keyword.length - 1); cacheMainList.push(value); keywords.push(value); blocks.push(value); if (v == "RK_SWITCH") { switchString = value; } else if (v == "RK_CASE" || v == "RK_DEFAULT") { case_default.push(value); } }); keywordsList.forEach((v) => { const keyword = names[v]; const value = keyword.substring(1, keyword.length - 1); cacheMainList.push(value); keywords.push(value); }); typeList.forEach((v) => { const keyword = names[v]; const value = keyword.substring(1, keyword.length - 1); cacheMainList.push(value); types.push(value); }); atomList.forEach((v) => { const keyword = names[v]; const value = keyword.substring(1, keyword.length - 1); cacheMainList.push(value); atoms.push(value); }); cacheOp = []; const logicOpList = ["NOT_OPERATOR", "OR_OPERATOR", "AND_OPERATOR"]; logicOpList.forEach((v) => { const keyword = names[v]; const value = keyword.substring(1, keyword.length - 1); cacheOp.push(value); keywords.push(value); }); return { case_default: case_default, atoms: atoms, keywords: keywords, switchString: switchString, types: types, blocks: blocks, }; } /** * Source: https://gist.github.com/andrei-m/982927 * @param {string} a * @param {string} b */ export function levenshteinDistance (a, b) { if (a.length == 0) return b.length; if (b.length == 0) return a.length; const matrix = []; // increment along the first column of each row let i; for (i = 0; i <= b.length; i++) { matrix[i] = [i]; } // increment each column in the first row let j; for (j = 0; j <= a.length; j++) { matrix[0][j] = j; } // Fill in the rest of the matrix for (i = 1; i <= b.length; i++) { for (j = 1; j <= a.length; j++) { if (b.charCodeAt(i - 1) == a.charCodeAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, // substitution Math.min( matrix[i][j - 1] + 1, // insertion matrix[i - 1][j] + 1 ) ); // deletion } } } return matrix[b.length][a.length]; } let win = null; export function openAssessmentDetail (event) { event.preventDefault(); const page_code = event.currentTarget.dataset.page; if (win != null) { win.close(); } win = window.open("", "DetailWindow", "width=550,height=600"); win.document.open(); win.document.write(page_code); win.document.close(); } export function range (size, startAt = 0) { return [...Array(size).keys()].map((i) => i + startAt); } /** * * @param {number} ms */ export async function sleep (ms) { return new Promise((res, _) => setTimeout(res, ms)); }