utils.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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);
  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. return validRegex && !isKeyword(identifier_str)
  184. }
  185. export function getCodeEditorModeConfig () {
  186. const blockList = [
  187. "RK_SWITCH",
  188. "RK_PROGRAM",
  189. "RK_CASE",
  190. "RK_DEFAULT",
  191. "RK_FOR",
  192. "RK_FOR_ALT",
  193. "RK_FUNCTION",
  194. "RK_DO",
  195. "RK_WHILE",
  196. "RK_WHILE_ALT",
  197. "RK_IF",
  198. "RK_ELSE",
  199. ];
  200. const keywordsList = [
  201. "RK_CONST",
  202. "RK_RETURN",
  203. "RK_BREAK",
  204. "RK_FOR_FROM",
  205. "RK_FOR_TO",
  206. "RK_FOR_PASS",
  207. "RK_DO_UNTIL",
  208. ];
  209. const typeList = [
  210. "RK_REAL",
  211. "RK_VOID",
  212. "RK_BOOLEAN",
  213. "RK_STRING",
  214. "RK_INTEGER",
  215. "RK_CHARACTER",
  216. ];
  217. const atomList = ["RK_FALSE", "RK_TRUE"];
  218. const case_default = [];
  219. const blocks = [];
  220. const keywords = [];
  221. const types = [];
  222. const atoms = [];
  223. let switchString = "";
  224. cacheMainList = [];
  225. const lexer = LanguageService.getCurrentLexer();
  226. const names = lexer.getReservedKeys();
  227. //console.debug(names);
  228. blockList.forEach((v) => {
  229. const keyword = names[v];
  230. const value = keyword;
  231. cacheMainList.push(value);
  232. keywords.push(value);
  233. blocks.push(value);
  234. if (v == "RK_SWITCH") {
  235. switchString = value;
  236. } else if (v == "RK_CASE" || v == "RK_DEFAULT") {
  237. case_default.push(value);
  238. }
  239. });
  240. keywordsList.forEach((v) => {
  241. const keyword = names[v];
  242. const value = keyword;
  243. cacheMainList.push(value);
  244. keywords.push(value);
  245. });
  246. typeList.forEach((v) => {
  247. const keyword = names[v];
  248. const value = keyword;
  249. cacheMainList.push(value);
  250. types.push(value);
  251. });
  252. atomList.forEach((v) => {
  253. const keyword = names[v];
  254. const value = keyword;
  255. cacheMainList.push(value);
  256. atoms.push(value);
  257. });
  258. cacheOp = [];
  259. const logicOpList = ["NOT_OPERATOR", "OR_OPERATOR", "AND_OPERATOR"];
  260. logicOpList.forEach((v) => {
  261. const keyword = names[v];
  262. const value = keyword;
  263. cacheOp.push(value);
  264. keywords.push(value);
  265. });
  266. cacheMainList.push(lexer.getLangFuncs().main_function);
  267. return {
  268. case_default: case_default,
  269. atoms: atoms,
  270. keywords: keywords,
  271. switchString: switchString,
  272. types: types,
  273. blocks: blocks,
  274. };
  275. }
  276. /**
  277. * Source: https://gist.github.com/andrei-m/982927
  278. * @param {string} a
  279. * @param {string} b
  280. */
  281. export function levenshteinDistance (a, b) {
  282. if (a.length == 0) return b.length;
  283. if (b.length == 0) return a.length;
  284. const matrix = [];
  285. // increment along the first column of each row
  286. let i;
  287. for (i = 0; i <= b.length; i++) {
  288. matrix[i] = [i];
  289. }
  290. // increment each column in the first row
  291. let j;
  292. for (j = 0; j <= a.length; j++) {
  293. matrix[0][j] = j;
  294. }
  295. // Fill in the rest of the matrix
  296. for (i = 1; i <= b.length; i++) {
  297. for (j = 1; j <= a.length; j++) {
  298. if (b.charCodeAt(i - 1) == a.charCodeAt(j - 1)) {
  299. matrix[i][j] = matrix[i - 1][j - 1];
  300. } else {
  301. matrix[i][j] = Math.min(
  302. matrix[i - 1][j - 1] + 1, // substitution
  303. Math.min(
  304. matrix[i][j - 1] + 1, // insertion
  305. matrix[i - 1][j] + 1
  306. )
  307. ); // deletion
  308. }
  309. }
  310. }
  311. return matrix[b.length][a.length];
  312. }
  313. let win = null;
  314. export function openAssessmentDetail (event) {
  315. event.preventDefault();
  316. const page_code = event.currentTarget.dataset.page;
  317. if (win != null) {
  318. win.close();
  319. }
  320. win = window.open("", "DetailWindow", "width=550,height=600");
  321. win.document.open();
  322. win.document.write(page_code);
  323. win.document.close();
  324. }
  325. export function range (size, startAt = 0) {
  326. return [...Array(size).keys()].map((i) => i + startAt);
  327. }
  328. /**
  329. *
  330. * @param {number} ms
  331. */
  332. export async function sleep (ms) {
  333. return new Promise((res, _) => setTimeout(res, ms));
  334. }