utils.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import { LanguageService } from "./../services/languageService";
  2. import { LocalizedStrings } from "./../services/localizedStringsService";
  3. import { Operators } from "./../ast/operators";
  4. /**
  5. * source: https://pawelgrzybek.com/page-scroll-in-vanilla-javascript/
  6. *
  7. */
  8. export function scrollIt (
  9. destination,
  10. duration = 200,
  11. easing = "linear",
  12. callback = null
  13. ) {
  14. const easings = {
  15. linear (t) {
  16. return t;
  17. },
  18. easeInQuad (t) {
  19. return t * t;
  20. },
  21. easeOutQuad (t) {
  22. return t * (2 - t);
  23. },
  24. easeInOutQuad (t) {
  25. return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
  26. },
  27. easeInCubic (t) {
  28. return t * t * t;
  29. },
  30. easeOutCubic (t) {
  31. return --t * t * t + 1;
  32. },
  33. easeInOutCubic (t) {
  34. return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
  35. },
  36. easeInQuart (t) {
  37. return t * t * t * t;
  38. },
  39. easeOutQuart (t) {
  40. return 1 - --t * t * t * t;
  41. },
  42. easeInOutQuart (t) {
  43. return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
  44. },
  45. easeInQuint (t) {
  46. return t * t * t * t * t;
  47. },
  48. easeOutQuint (t) {
  49. return 1 + --t * t * t * t * t;
  50. },
  51. easeInOutQuint (t) {
  52. return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
  53. },
  54. };
  55. const start = window.pageYOffset;
  56. const startTime =
  57. "now" in window.performance ? performance.now() : new Date().getTime();
  58. const documentHeight = Math.max(
  59. document.body.scrollHeight,
  60. document.body.offsetHeight,
  61. document.documentElement.clientHeight,
  62. document.documentElement.scrollHeight,
  63. document.documentElement.offsetHeight
  64. );
  65. const windowHeight =
  66. window.innerHeight ||
  67. document.documentElement.clientHeight ||
  68. document.getElementsByTagName("body")[0].clientHeight;
  69. const destinationOffset =
  70. typeof destination === "number" ? destination : destination.offsetTop;
  71. const destinationOffsetToScroll = Math.round(
  72. documentHeight - destinationOffset < windowHeight
  73. ? documentHeight - windowHeight
  74. : destinationOffset
  75. );
  76. if ("requestAnimationFrame" in window === false) {
  77. window.scroll(0, destinationOffsetToScroll);
  78. if (callback) {
  79. callback();
  80. }
  81. return;
  82. }
  83. function scroll () {
  84. const now =
  85. "now" in window.performance ? performance.now() : new Date().getTime();
  86. const time = Math.min(1, (now - startTime) / duration);
  87. const timeFunction = easings[easing](time);
  88. window.scroll(
  89. 0,
  90. Math.ceil(timeFunction * (destinationOffsetToScroll - start) + start)
  91. );
  92. if (window.pageYOffset === destinationOffsetToScroll) {
  93. if (callback) {
  94. callback();
  95. }
  96. return;
  97. }
  98. requestAnimationFrame(scroll);
  99. }
  100. scroll();
  101. }
  102. /**
  103. *
  104. * source: https://stackoverflow.com/a/16270434
  105. */
  106. export function isElementInViewport (el) {
  107. const rect = el.getBoundingClientRect();
  108. return (
  109. rect.bottom > 0 &&
  110. rect.right > 0 &&
  111. rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
  112. rect.top < (window.innerHeight || document.documentElement.clientHeight)
  113. );
  114. }
  115. let cacheMainList = null;
  116. let cacheOp = null;
  117. function fillCache () {
  118. if (cacheMainList == null) {
  119. cacheMainList = [];
  120. const mainList = [
  121. "RK_PROGRAM",
  122. "RK_REAL",
  123. "RK_VOID",
  124. "RK_BOOLEAN",
  125. "RK_STRING",
  126. "RK_INTEGER",
  127. "RK_CHARACTER",
  128. "RK_SWITCH",
  129. "RK_CASE",
  130. "RK_DEFAULT",
  131. "RK_CONST",
  132. "RK_FUNCTION",
  133. "RK_RETURN",
  134. "RK_FOR",
  135. "RK_BREAK",
  136. "RK_DO",
  137. "RK_WHILE",
  138. "RK_IF",
  139. "RK_ELSE",
  140. "RK_FALSE",
  141. "RK_TRUE",
  142. ];
  143. const lexer = LanguageService.getCurrentLexer();
  144. const names = lexer.getReservedKeys();
  145. for (let key = 0; key < mainList.length; ++key) {
  146. const word = mainList[key];
  147. const keyword = names[word];
  148. cacheMainList.push(keyword.substring(1, keyword.length - 1));
  149. }
  150. }
  151. if (cacheOp == null) {
  152. cacheOp = [];
  153. const logicOpList = [
  154. Operators.AND.value,
  155. Operators.OR.value,
  156. Operators.NOT.value,
  157. ];
  158. for (let op = 0; op < logicOpList.length; ++op) {
  159. const lOp = `logic_operator_${logicOpList[op]}`;
  160. cacheOp.push(LocalizedStrings.getUI(lOp));
  161. }
  162. }
  163. }
  164. export function isKeyword (text) {
  165. fillCache();
  166. for (let key = 0; key < cacheMainList.length; ++key) {
  167. const keyword = cacheMainList[key];
  168. if (keyword == text) {
  169. return true;
  170. }
  171. }
  172. // not in main list, check op
  173. for (let op = 0; op < cacheOp.length; op++) {
  174. const lOp = cacheOp[op];
  175. if (lOp == text) {
  176. return true;
  177. }
  178. }
  179. return false;
  180. }
  181. export function isValidIdentifier (identifier_str) {
  182. const validRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier_str);
  183. if (!validRegex) {
  184. return false;
  185. }
  186. return !isKeyword(identifier_str);
  187. }
  188. export function getCodeEditorModeConfig () {
  189. const blockList = [
  190. "RK_SWITCH",
  191. "RK_PROGRAM",
  192. "RK_CASE",
  193. "RK_DEFAULT",
  194. "RK_FOR",
  195. "RK_FOR_ALT",
  196. "RK_FUNCTION",
  197. "RK_DO",
  198. "RK_WHILE",
  199. "RK_WHILE_ALT",
  200. "RK_IF",
  201. "RK_ELSE",
  202. ];
  203. const keywordsList = [
  204. "RK_CONST",
  205. "RK_RETURN",
  206. "RK_BREAK",
  207. "RK_FOR_FROM",
  208. "RK_FOR_TO",
  209. "RK_FOR_PASS",
  210. "RK_DO_UNTIL",
  211. ];
  212. const typeList = [
  213. "RK_REAL",
  214. "RK_VOID",
  215. "RK_BOOLEAN",
  216. "RK_STRING",
  217. "RK_INTEGER",
  218. "RK_CHARACTER",
  219. ];
  220. const atomList = ["RK_FALSE", "RK_TRUE"];
  221. const case_default = [];
  222. const blocks = [];
  223. const keywords = [];
  224. const types = [];
  225. const atoms = [];
  226. let switchString = "";
  227. cacheMainList = [];
  228. const lexer = LanguageService.getCurrentLexer();
  229. const names = lexer.getReservedKeys();
  230. //console.debug(names);
  231. blockList.forEach((v) => {
  232. const keyword = names[v];
  233. const value = keyword.substring(1, keyword.length - 1);
  234. cacheMainList.push(value);
  235. keywords.push(value);
  236. blocks.push(value);
  237. if (v == "RK_SWITCH") {
  238. switchString = value;
  239. } else if (v == "RK_CASE" || v == "RK_DEFAULT") {
  240. case_default.push(value);
  241. }
  242. });
  243. keywordsList.forEach((v) => {
  244. const keyword = names[v];
  245. const value = keyword.substring(1, keyword.length - 1);
  246. cacheMainList.push(value);
  247. keywords.push(value);
  248. });
  249. typeList.forEach((v) => {
  250. const keyword = names[v];
  251. const value = keyword.substring(1, keyword.length - 1);
  252. cacheMainList.push(value);
  253. types.push(value);
  254. });
  255. atomList.forEach((v) => {
  256. const keyword = names[v];
  257. const value = keyword.substring(1, keyword.length - 1);
  258. cacheMainList.push(value);
  259. atoms.push(value);
  260. });
  261. cacheOp = [];
  262. const logicOpList = ["NOT_OPERATOR", "OR_OPERATOR", "AND_OPERATOR"];
  263. logicOpList.forEach((v) => {
  264. const keyword = names[v];
  265. const value = keyword.substring(1, keyword.length - 1);
  266. cacheOp.push(value);
  267. keywords.push(value);
  268. });
  269. return {
  270. case_default: case_default,
  271. atoms: atoms,
  272. keywords: keywords,
  273. switchString: switchString,
  274. types: types,
  275. blocks: blocks,
  276. };
  277. }
  278. /**
  279. * Source: https://gist.github.com/andrei-m/982927
  280. * @param {string} a
  281. * @param {string} b
  282. */
  283. export function levenshteinDistance (a, b) {
  284. if (a.length == 0) return b.length;
  285. if (b.length == 0) return a.length;
  286. const matrix = [];
  287. // increment along the first column of each row
  288. let i;
  289. for (i = 0; i <= b.length; i++) {
  290. matrix[i] = [i];
  291. }
  292. // increment each column in the first row
  293. let j;
  294. for (j = 0; j <= a.length; j++) {
  295. matrix[0][j] = j;
  296. }
  297. // Fill in the rest of the matrix
  298. for (i = 1; i <= b.length; i++) {
  299. for (j = 1; j <= a.length; j++) {
  300. if (b.charCodeAt(i - 1) == a.charCodeAt(j - 1)) {
  301. matrix[i][j] = matrix[i - 1][j - 1];
  302. } else {
  303. matrix[i][j] = Math.min(
  304. matrix[i - 1][j - 1] + 1, // substitution
  305. Math.min(
  306. matrix[i][j - 1] + 1, // insertion
  307. matrix[i - 1][j] + 1
  308. )
  309. ); // deletion
  310. }
  311. }
  312. }
  313. return matrix[b.length][a.length];
  314. }
  315. let win = null;
  316. export function openAssessmentDetail (event) {
  317. event.preventDefault();
  318. const page_code = event.currentTarget.dataset.page;
  319. if (win != null) {
  320. win.close();
  321. }
  322. win = window.open("", "DetailWindow", "width=550,height=600");
  323. win.document.open();
  324. win.document.write(page_code);
  325. win.document.close();
  326. }
  327. export function range (size, startAt = 0) {
  328. return [...Array(size).keys()].map((i) => i + startAt);
  329. }
  330. /**
  331. *
  332. * @param {number} ms
  333. */
  334. export async function sleep (ms) {
  335. return new Promise((res, _) => setTimeout(res, ms));
  336. }