utils.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 (destination, duration = 200, easing = 'linear', callback = null) {
  9. const easings = {
  10. linear(t) {
  11. return t;
  12. },
  13. easeInQuad(t) {
  14. return t * t;
  15. },
  16. easeOutQuad(t) {
  17. return t * (2 - t);
  18. },
  19. easeInOutQuad(t) {
  20. return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
  21. },
  22. easeInCubic(t) {
  23. return t * t * t;
  24. },
  25. easeOutCubic(t) {
  26. return (--t) * t * t + 1;
  27. },
  28. easeInOutCubic(t) {
  29. return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
  30. },
  31. easeInQuart(t) {
  32. return t * t * t * t;
  33. },
  34. easeOutQuart(t) {
  35. return 1 - (--t) * t * t * t;
  36. },
  37. easeInOutQuart(t) {
  38. return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
  39. },
  40. easeInQuint(t) {
  41. return t * t * t * t * t;
  42. },
  43. easeOutQuint(t) {
  44. return 1 + (--t) * t * t * t * t;
  45. },
  46. easeInOutQuint(t) {
  47. return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
  48. }
  49. };
  50. const start = window.pageYOffset;
  51. const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
  52. const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
  53. const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;
  54. const destinationOffset = typeof destination === 'number' ? destination : destination.offsetTop;
  55. const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);
  56. if ('requestAnimationFrame' in window === false) {
  57. window.scroll(0, destinationOffsetToScroll);
  58. if (callback) {
  59. callback();
  60. }
  61. return;
  62. }
  63. function scroll() {
  64. const now = 'now' in window.performance ? performance.now() : new Date().getTime();
  65. const time = Math.min(1, ((now - startTime) / duration));
  66. const timeFunction = easings[easing](time);
  67. window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));
  68. if (window.pageYOffset === destinationOffsetToScroll) {
  69. if (callback) {
  70. callback();
  71. }
  72. return;
  73. }
  74. requestAnimationFrame(scroll);
  75. }
  76. scroll();
  77. }
  78. /**
  79. *
  80. * source: https://stackoverflow.com/a/16270434
  81. */
  82. export function isElementInViewport (el) {
  83. const rect = el.getBoundingClientRect();
  84. return rect.bottom > 0 &&
  85. rect.right > 0 &&
  86. rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
  87. rect.top < (window.innerHeight || document.documentElement.clientHeight);
  88. }
  89. let cacheMainList = null;
  90. let cacheOp = null;
  91. export function isKeyword (text) {
  92. fillCache();
  93. for (let key = 0; key < cacheMainList.length; ++key) {
  94. const keyword = cacheMainList[key];
  95. if(keyword == text) {
  96. return true;
  97. }
  98. }
  99. // not in main list, check op
  100. for (let op = 0; op < cacheOp.length; op++) {
  101. const lOp = cacheOp[op];
  102. if(lOp == text) {
  103. return true;
  104. }
  105. }
  106. return false;
  107. }
  108. export function isValidIdentifier (identifier_str) {
  109. const validRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier_str);
  110. if(!validRegex) {
  111. return false;
  112. }
  113. return !isKeyword(identifier_str);
  114. }
  115. function fillCache () {
  116. if(cacheMainList == null) {
  117. cacheMainList = [];
  118. const mainList = ["RK_PROGRAM","RK_REAL","RK_VOID","RK_BOOLEAN","RK_STRING",
  119. "RK_INTEGER","RK_CHARACTER","RK_SWITCH","RK_CASE","RK_DEFAULT","RK_CONST",
  120. "RK_FUNCTION","RK_RETURN","RK_FOR","RK_BREAK","RK_DO","RK_WHILE","RK_IF",
  121. "RK_ELSE","RK_FALSE","RK_TRUE"];
  122. const lexerClass = LanguageService.getCurrentLexer();
  123. const nullLexer = new lexerClass();
  124. for (let key = 0; key < mainList.length; ++key) {
  125. const word = mainList[key];
  126. const keyword = nullLexer.literalNames[lexerClass[word]];
  127. cacheMainList.push(keyword.substring(1, keyword.length-1));
  128. }
  129. }
  130. if(cacheOp == null) {
  131. cacheOp = []
  132. const logicOpList = [Operators.AND.value, Operators.OR.value, Operators.NOT.value];
  133. for (let op = 0; op < logicOpList.length; ++op) {
  134. const lOp = `logic_operator_${logicOpList[op]}`;
  135. cacheOp.push(LocalizedStrings.getUI(lOp))
  136. }
  137. }
  138. }
  139. export function getCodeEditorModeConfig () {
  140. const blockList = ["RK_SWITCH", "RK_PROGRAM","RK_CASE","RK_DEFAULT","RK_FOR",
  141. "RK_FUNCTION","RK_DO","RK_WHILE","RK_IF","RK_ELSE"]
  142. const keywordsList = ["RK_CONST","RK_RETURN","RK_BREAK"];
  143. const typeList = ["RK_REAL","RK_VOID","RK_BOOLEAN","RK_STRING","RK_INTEGER"];
  144. const atomList = ["RK_FALSE", "RK_TRUE"];
  145. const case_default = [];
  146. const blocks = [];
  147. const keywords = [];
  148. const types = [];
  149. const atoms = []
  150. let switchString = "";
  151. cacheMainList = [];
  152. const lexerClass = LanguageService.getCurrentLexer();
  153. const nullLexer = new lexerClass();
  154. blockList.forEach( v => {
  155. const keyword = nullLexer.literalNames[lexerClass[v]];
  156. const value = keyword.substring(1, keyword.length-1);
  157. cacheMainList.push(value);
  158. keywords.push(value);
  159. blocks.push(value);
  160. if(v == "RK_SWITCH") {
  161. switchString = value;
  162. } else if (v == "RK_CASE" || v == "RK_DEFAULT") {
  163. case_default.push(value);
  164. }
  165. });
  166. keywordsList.forEach( v => {
  167. const keyword = nullLexer.literalNames[lexerClass[v]];
  168. const value = keyword.substring(1, keyword.length-1);
  169. cacheMainList.push(value);
  170. keywords.push(value);
  171. });
  172. typeList.forEach(v => {
  173. const keyword = nullLexer.literalNames[lexerClass[v]];
  174. const value = keyword.substring(1, keyword.length-1);
  175. cacheMainList.push(value);
  176. types.push(value);
  177. })
  178. atomList.forEach( v => {
  179. const keyword = nullLexer.literalNames[lexerClass[v]];
  180. const value = keyword.substring(1, keyword.length-1);
  181. cacheMainList.push(value);
  182. atoms.push(value);
  183. })
  184. cacheOp = []
  185. const logicOpList = [Operators.AND.value, Operators.OR.value, Operators.NOT.value];
  186. for (let op = 0; op < logicOpList.length; ++op) {
  187. const lOp = `logic_operator_${logicOpList[op]}`;
  188. const value = LocalizedStrings.getUI(lOp);
  189. cacheOp.push(value)
  190. keywords.push(value);
  191. }
  192. return {
  193. case_default: case_default,
  194. atoms: atoms,
  195. keywords: keywords,
  196. switchString: switchString,
  197. types: types,
  198. blocks: blocks
  199. }
  200. }
  201. /**
  202. * Source: https://gist.github.com/andrei-m/982927
  203. * @param {string} a
  204. * @param {string} b
  205. */
  206. export function levenshteinDistance (a, b) {
  207. if(a.length == 0) return b.length;
  208. if(b.length == 0) return a.length;
  209. const matrix = [];
  210. // increment along the first column of each row
  211. let i;
  212. for(i = 0; i <= b.length; i++){
  213. matrix[i] = [i];
  214. }
  215. // increment each column in the first row
  216. let j;
  217. for(j = 0; j <= a.length; j++){
  218. matrix[0][j] = j;
  219. }
  220. // Fill in the rest of the matrix
  221. for(i = 1; i <= b.length; i++){
  222. for(j = 1; j <= a.length; j++){
  223. if(b.charCodeAt(i-1) == a.charCodeAt(j-1)){
  224. matrix[i][j] = matrix[i-1][j-1];
  225. } else {
  226. matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
  227. Math.min(matrix[i][j-1] + 1, // insertion
  228. matrix[i-1][j] + 1)); // deletion
  229. }
  230. }
  231. }
  232. return matrix[b.length][a.length];
  233. }
  234. let win = null
  235. export function openAssessmentDetail (event) {
  236. event.preventDefault();
  237. const page_code = event.currentTarget.dataset.page;
  238. if(win != null) {
  239. win.close()
  240. }
  241. win = window.open("", "DetailWindow", "width=550,height=600");
  242. win.document.open();
  243. win.document.write(page_code);
  244. win.document.close();
  245. }
  246. export function range (size, startAt = 0) {
  247. return [...Array(size).keys()].map(i => i + startAt);
  248. }