jclic 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +5 -7
  2. package/dist-node/jclic-node.js +14157 -0
  3. package/dist-node/jclic-node.umd.cjs +530 -0
  4. package/package.json +38 -26
  5. package/.vscode/launch.json +0 -33
  6. package/.vscode/settings.json +0 -13
  7. package/CHANGELOG.md +0 -672
  8. package/TRANSLATIONS.md +0 -11
  9. package/build-locales.mjs +0 -82
  10. package/dist/jclic-node.js +0 -31680
  11. package/dist/jclic-node.js.map +0 -1
  12. package/dist/jclic.components.LICENSE +0 -2254
  13. package/dist/jclic.min.js +0 -27
  14. package/dist/jclic.min.js.map +0 -1
  15. package/eslint.config.mjs +0 -31
  16. package/jsdoc.config.js +0 -71
  17. package/locales/ar.po +0 -244
  18. package/locales/ast.po +0 -246
  19. package/locales/bs.po +0 -247
  20. package/locales/ca.po +0 -248
  21. package/locales/ca_ES@valencia.po +0 -248
  22. package/locales/cs.po +0 -244
  23. package/locales/da.po +0 -244
  24. package/locales/de.po +0 -246
  25. package/locales/el.po +0 -244
  26. package/locales/es.po +0 -248
  27. package/locales/eu.po +0 -244
  28. package/locales/fr.po +0 -244
  29. package/locales/gl.po +0 -244
  30. package/locales/he.po +0 -244
  31. package/locales/hr.po +0 -245
  32. package/locales/it.po +0 -246
  33. package/locales/ja.po +0 -242
  34. package/locales/jclic.js.pot +0 -241
  35. package/locales/nb_NO.po +0 -244
  36. package/locales/nl.po +0 -244
  37. package/locales/pl.po +0 -244
  38. package/locales/pt.po +0 -244
  39. package/locales/pt_BR.po +0 -248
  40. package/locales/ro.po +0 -248
  41. package/locales/ru.po +0 -245
  42. package/locales/ta.po +0 -244
  43. package/locales/tr.po +0 -246
  44. package/locales/uk.po +0 -247
  45. package/locales/vec.po +0 -244
  46. package/locales/zh_TW.po +0 -246
  47. package/patches/po2json+1.0.0-beta-3.patch +0 -12
  48. package/src/AWT.js +0 -2067
  49. package/src/Activity.js +0 -1311
  50. package/src/Deps.js +0 -232
  51. package/src/GlobalData.js +0 -5
  52. package/src/JClic.js +0 -196
  53. package/src/JClicPlayer.js +0 -1308
  54. package/src/PlayerHistory.js +0 -305
  55. package/src/Utils.js +0 -1355
  56. package/src/activities/associations/ComplexAssociation.js +0 -321
  57. package/src/activities/associations/SimpleAssociation.js +0 -519
  58. package/src/activities/memory/MemoryGame.js +0 -423
  59. package/src/activities/panels/Explore.js +0 -349
  60. package/src/activities/panels/Identify.js +0 -356
  61. package/src/activities/panels/InformationScreen.js +0 -262
  62. package/src/activities/panels/Menu.js +0 -209
  63. package/src/activities/panels/icons/ico00.png +0 -0
  64. package/src/activities/panels/icons/ico01.png +0 -0
  65. package/src/activities/panels/icons/ico02.png +0 -0
  66. package/src/activities/panels/icons/ico03.png +0 -0
  67. package/src/activities/panels/icons/icofolder.png +0 -0
  68. package/src/activities/puzzles/DoublePuzzle.js +0 -424
  69. package/src/activities/puzzles/ExchangePuzzle.js +0 -374
  70. package/src/activities/puzzles/HolePuzzle.js +0 -360
  71. package/src/activities/text/Complete.js +0 -127
  72. package/src/activities/text/Evaluator.js +0 -534
  73. package/src/activities/text/FillInBlanks.js +0 -426
  74. package/src/activities/text/IdentifyText.js +0 -253
  75. package/src/activities/text/OrderText.js +0 -421
  76. package/src/activities/text/TextActivityBase.js +0 -557
  77. package/src/activities/text/TextActivityDocument.js +0 -660
  78. package/src/activities/text/WrittenAnswer.js +0 -557
  79. package/src/activities/textGrid/CrossWord.js +0 -565
  80. package/src/activities/textGrid/WordSearch.js +0 -458
  81. package/src/activities/textGrid/icons/hIcon.svg +0 -3
  82. package/src/activities/textGrid/icons/vIcon.svg +0 -3
  83. package/src/automation/AutoContentProvider.js +0 -182
  84. package/src/automation/arith/Arith.js +0 -864
  85. package/src/bags/ActivitySequence.js +0 -318
  86. package/src/bags/ActivitySequenceElement.js +0 -161
  87. package/src/bags/ActivitySequenceJump.js +0 -140
  88. package/src/bags/ConditionalJumpInfo.js +0 -113
  89. package/src/bags/JumpInfo.js +0 -136
  90. package/src/bags/MediaBag.js +0 -215
  91. package/src/bags/MediaBagElement.js +0 -516
  92. package/src/boxes/AbstractBox.js +0 -699
  93. package/src/boxes/ActiveBagContent.js +0 -494
  94. package/src/boxes/ActiveBox.js +0 -810
  95. package/src/boxes/ActiveBoxBag.js +0 -357
  96. package/src/boxes/ActiveBoxContent.js +0 -484
  97. package/src/boxes/ActiveBoxGrid.js +0 -179
  98. package/src/boxes/BoxBag.js +0 -500
  99. package/src/boxes/BoxBase.js +0 -398
  100. package/src/boxes/BoxConnector.js +0 -325
  101. package/src/boxes/TextGrid.js +0 -887
  102. package/src/boxes/TextGridContent.js +0 -215
  103. package/src/init-jsdom.js +0 -65
  104. package/src/jclic-node.js +0 -219
  105. package/src/media/ActiveMediaBag.js +0 -145
  106. package/src/media/ActiveMediaPlayer.js +0 -297
  107. package/src/media/AudioBuffer.js +0 -219
  108. package/src/media/EventSounds.js +0 -169
  109. package/src/media/EventSoundsElement.js +0 -155
  110. package/src/media/MediaContent.js +0 -328
  111. package/src/media/MidiAudioPlayer.js +0 -254
  112. package/src/media/icons/audio.svg +0 -3
  113. package/src/media/icons/generic.svg +0 -3
  114. package/src/media/icons/mic.svg +0 -3
  115. package/src/media/icons/movie.svg +0 -3
  116. package/src/media/icons/music.svg +0 -3
  117. package/src/media/icons/url.svg +0 -3
  118. package/src/media/sounds/actionError.mp3 +0 -0
  119. package/src/media/sounds/actionOk.mp3 +0 -0
  120. package/src/media/sounds/click.mp3 +0 -0
  121. package/src/media/sounds/finishedError.mp3 +0 -0
  122. package/src/media/sounds/finishedOk.mp3 +0 -0
  123. package/src/media/sounds/start.mp3 +0 -0
  124. package/src/project/JClicProject.js +0 -282
  125. package/src/project/ProjectSettings.js +0 -273
  126. package/src/report/ActionReg.js +0 -123
  127. package/src/report/ActivityReg.js +0 -271
  128. package/src/report/EncryptMin.js +0 -210
  129. package/src/report/Reporter.js +0 -727
  130. package/src/report/SCORM.js +0 -272
  131. package/src/report/SequenceReg.js +0 -275
  132. package/src/report/SessionReg.js +0 -340
  133. package/src/report/SessionStorageReporter.js +0 -131
  134. package/src/report/TCPReporter.js +0 -628
  135. package/src/shapers/ClassicJigSaw.js +0 -138
  136. package/src/shapers/Holes.js +0 -77
  137. package/src/shapers/JigSaw.js +0 -161
  138. package/src/shapers/Rectangular.js +0 -78
  139. package/src/shapers/Shaper.js +0 -386
  140. package/src/shapers/TriangularJigSaw.js +0 -121
  141. package/src/skins/BlueSkin.js +0 -80
  142. package/src/skins/Counter.js +0 -152
  143. package/src/skins/CustomSkin.js +0 -412
  144. package/src/skins/DefaultSkin.js +0 -376
  145. package/src/skins/EmptySkin.js +0 -82
  146. package/src/skins/GreenSkin.js +0 -94
  147. package/src/skins/MiniSkin.js +0 -130
  148. package/src/skins/OrangeSkin.js +0 -78
  149. package/src/skins/SimpleSkin.js +0 -92
  150. package/src/skins/Skin.js +0 -1021
  151. package/src/skins/assets/actionsIcon.svg +0 -3
  152. package/src/skins/assets/appLogo.svg +0 -8
  153. package/src/skins/assets/basic.css +0 -41
  154. package/src/skins/assets/closeDialogIcon.svg +0 -3
  155. package/src/skins/assets/closeIcon.svg +0 -3
  156. package/src/skins/assets/copyIcon.svg +0 -3
  157. package/src/skins/assets/fullScreenExitIcon.svg +0 -3
  158. package/src/skins/assets/fullScreenIcon.svg +0 -3
  159. package/src/skins/assets/infoIcon.svg +0 -3
  160. package/src/skins/assets/main.css +0 -43
  161. package/src/skins/assets/mainHalf.css +0 -23
  162. package/src/skins/assets/mainTwoThirds.css +0 -23
  163. package/src/skins/assets/mini.css +0 -15
  164. package/src/skins/assets/nextIcon.svg +0 -3
  165. package/src/skins/assets/okDialogIcon.svg +0 -3
  166. package/src/skins/assets/prevIcon.svg +0 -3
  167. package/src/skins/assets/reports.css +0 -156
  168. package/src/skins/assets/reportsIcon.svg +0 -3
  169. package/src/skins/assets/scoreIcon.svg +0 -3
  170. package/src/skins/assets/simple.css +0 -16
  171. package/src/skins/assets/simpleHalf.css +0 -11
  172. package/src/skins/assets/simpleTwoThirds.css +0 -11
  173. package/src/skins/assets/timeIcon.svg +0 -4
  174. package/src/skins/assets/waitAnim.css +0 -54
  175. package/src/skins/assets/waitImgBig.svg +0 -3
  176. package/src/skins/assets/waitImgSmall.svg +0 -3
  177. package/webpack.config.mjs +0 -169
@@ -1,887 +0,0 @@
1
- /**
2
- * File : boxes/TextGrid.js
3
- * Created : 12/06/2015
4
- * By : Francesc Busquets <francesc@gmail.com>
5
- *
6
- * JClic.js
7
- * An HTML5 player of JClic activities
8
- * https://projectestac.github.io/jclic.js
9
- *
10
- * @source https://github.com/projectestac/jclic.js
11
- *
12
- * @license EUPL-1.2
13
- * @licstart
14
- * (c) 2000-2020 Educational Telematic Network of Catalonia (XTEC)
15
- *
16
- * Licensed under the EUPL, Version 1.1 or -as soon they will be approved by
17
- * the European Commission- subsequent versions of the EUPL (the "Licence");
18
- * You may not use this work except in compliance with the Licence.
19
- *
20
- * You may obtain a copy of the Licence at:
21
- * https://joinup.ec.europa.eu/software/page/eupl
22
- *
23
- * Unless required by applicable law or agreed to in writing, software
24
- * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
25
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26
- * Licence for the specific language governing permissions and limitations
27
- * under the Licence.
28
- * @licend
29
- * @module
30
- */
31
-
32
- import { Rectangle, Timer, Point, Dimension, Stroke } from '../AWT.js';
33
- import { roundTo } from '../Utils.js';
34
- import AbstractBox from './AbstractBox.js';
35
- import TextGridContent from './TextGridContent.js';
36
-
37
- /**
38
- * Default values
39
- * @type {object}
40
- */
41
- export const defaults = {
42
- MIN_CELL_SIZE: 12,
43
- DEFAULT_CELL_SIZE: 20,
44
- MIN_INTERNAL_MARGIN: 2
45
- };
46
-
47
- /**
48
- * Binary flags used to mark status
49
- * @type {object}
50
- */
51
- export const flags = {
52
- NORMAL: 0,
53
- INVERTED: 1,
54
- HIDDEN: 2,
55
- LOCKED: 4,
56
- MARKED: 8,
57
- TRANSPARENT: 16
58
- };
59
-
60
- /**
61
- * This class is a special type of {@link module:boxes/AbstractBox.AbstractBox AbstractBox} that displays a grid of single
62
- * characters.
63
- *
64
- * It's used {@link module:activities/textGrid/CrossWord.CrossWord CrossWord} and {@link module:activities/textGrid/WordSearch.WordSearch WordSearch} activities.
65
- * @extends module:boxes/AbstractBox.AbstractBox
66
- */
67
- export class TextGrid extends AbstractBox {
68
- /**
69
- * TextGrid constructor
70
- * @param {module:boxes/AbstractBox.AbstractBox} parent - The AbstractBox to which this text grid belongs
71
- * @param {module:AWT.Container} container - The container where this text grid is placed.
72
- * @param {module:boxes/BoxBase.BoxBase} boxBase - The object where colors, fonts, border and other graphic properties
73
- * @param {number} x - `X` coordinate of the upper left corner of this grid
74
- * @param {number} y - `Y` coordinate of the upper left corner of this grid
75
- * @param {number} ncw - Number of columns of the grid
76
- * @param {number} nch - Nomber of rows of the grid
77
- * @param {number} cellW - Width of the cells
78
- * @param {number} cellH - Height of the cells
79
- * @param {boolean} border - When `true`, a border must be drawn between the cells
80
- */
81
- constructor(parent, container, boxBase, x, y, ncw, nch, cellW, cellH, border) {
82
- // *TextGrid* extends [AbstractBox](AbstractBox.html)
83
- super(parent, container, boxBase);
84
- this.pos.x = x;
85
- this.pos.y = y;
86
- this.nCols = Math.max(1, ncw);
87
- this.nRows = Math.max(1, nch);
88
- this.cellWidth = Math.max(cellW, defaults.MIN_CELL_SIZE);
89
- this.cellHeight = Math.max(cellH, defaults.MIN_CELL_SIZE);
90
- this.dim.width = cellW * this.nCols;
91
- this.dim.height = cellH * this.nRows;
92
- this.setChars(' ');
93
- this.preferredBounds = new Rectangle(this.pos, this.dim);
94
- this.setBorder(border);
95
- this.cursorTimer = new Timer(() => this.blink(0), 500, false);
96
- this.cursorEnabled = false;
97
- this.useCursor = false;
98
- this.wildTransparent = false;
99
- this.cursor = new Point();
100
- }
101
-
102
- /**
103
- * Factory constructor that creates an empty grid based on a {@link module:boxes/TextGridContent.TextGridContent TextGridContent}
104
- * @param {module:boxes/AbstractBox.AbstractBox} parent - The AbstractBox to which the text grid belongs
105
- * @param {module:AWT.Container} container - The container where the text grid will be placed.
106
- * @param {number} x - `X` coordinate of the upper left corner of the grid
107
- * @param {number} y - `Y` coordinate of the upper left corner of the grid
108
- * @param {module:boxes/TextGridContent.TextGridContent} tgc - Object with the content and other settings of the grid
109
- * @param {boolean} wildTransparent - When `true`, the wildcard character will be transparent
110
- * @returns {module:boxes/TextGrid.TextGrid}
111
- */
112
- static createEmptyGrid(parent, container, x, y, tgc, wildTransparent) {
113
- const result = new TextGrid(parent, container, tgc.style,
114
- x, y, tgc.ncw, tgc.nch, tgc.w, tgc.h, tgc.border);
115
- result.wild = tgc.wild;
116
- result.randomChars = tgc.randomChars;
117
- result.wildTransparent = wildTransparent;
118
- return result;
119
- }
120
-
121
- /**
122
- * Sets the characters to be placed in the cells of this TextGrid
123
- * @param {string} text
124
- */
125
- setChars(text) {
126
- this.chars = [];
127
- this.answers = [];
128
- this.attributes = [];
129
- for (let py = 0; py < this.nRows; py++) {
130
- const line = py < text.length ? text[py] : '';
131
- this.chars[py] = line.split('');
132
- this.answers[py] = [];
133
- this.attributes[py] = [];
134
- for (let px = 0; px < this.nCols; px++) {
135
- if (px >= line.length)
136
- this.chars[py][px] = ' ';
137
- this.answers[py][px] = this.chars[py][px];
138
- this.attributes[py][px] = flags.NORMAL;
139
- }
140
- }
141
- }
142
-
143
- /**
144
- * Substitutes the current content of all cells with wildcards with a randomly generated char.
145
- * @see TextGridContent#randomChars
146
- */
147
- randomize() {
148
- for (let py = 0; py < this.nRows; py++)
149
- for (let px = 0; px < this.nCols; px++)
150
- if (this.chars[py][px] === this.wild)
151
- this.chars[py][px] = this.randomChars.charAt(
152
- Math.floor(Math.random() * this.randomChars.length));
153
- }
154
-
155
- /**
156
- * Clears or sets global attributes to all cells
157
- * @param {boolean} lockWild - When `true`, the wildcard cells will be marked with special
158
- * attributes (used in CrossWords to mark black cells)
159
- * @param {boolean} clearChars - When `true`, the current content of cells will be erased.
160
- */
161
- setCellAttributes(lockWild, clearChars) {
162
- let atr = flags.LOCKED;
163
- if (this.wildTransparent)
164
- atr |= flags.TRANSPARENT;
165
- else
166
- atr |= flags.INVERTED | flags.HIDDEN;
167
- for (let py = 0; py < this.nRows; py++) {
168
- for (let px = 0; px < this.nCols; px++) {
169
- if (lockWild && this.chars[py][px] === this.wild)
170
- this.attributes[py][px] = atr;
171
- else {
172
- this.attributes[py][px] = flags.NORMAL;
173
- if (clearChars)
174
- this.chars[py][px] = ' ';
175
- }
176
- }
177
- }
178
- }
179
-
180
- /**
181
- * Sets or unsets the `locked` properties (black cell) to a specific cell.
182
- * @param {number} px - The logical 'X' coordinate of the cell
183
- * @param {number} py - The logical 'Y' coordinate of the cell
184
- * @param {boolean} locked - When true, the `locked` attribute will be on.
185
- */
186
- setCellLocked(px, py, locked) {
187
- if (px >= 0 && px < this.nCols && py >= 0 && py < this.nRows) {
188
- this.attributes[py][px] = locked ?
189
- flags.LOCKED |
190
- (this.wildTransparent ?
191
- flags.TRANSPARENT :
192
- flags.INVERTED |
193
- flags.HIDDEN) :
194
- flags.NORMAL;
195
- }
196
- }
197
-
198
- /**
199
- * For a specific cell located at column `rx` and row `ry`, finds the number of words delimited
200
- * by wildchars located behind its current position and in the same row and column. Used in
201
- * {@link module:activities/textGrid/CrossWord.CrossWord CrossWord} activities to find the definition for a specific cell.
202
- *
203
- * The result is returned as 'x' and 'y' properties of a logical point.
204
- * @param {number} rx - The 'X' position of the cell
205
- * @param {number} ry - The 'Y' position of the cell
206
- * @returns {module:AWT.Point} - The logical positions of the definition for this cell inside the list
207
- * of current definitions of its row and column. '0' means first definition of its row/column,
208
- * '1' the second one, etc.
209
- */
210
- getItemFor(rx, ry) {
211
- if (!this.isValidCell(rx, ry))
212
- return null;
213
-
214
- const point = new Point();
215
- let
216
- inBlack = false,
217
- startCount = false;
218
-
219
- for (let px = 0; px < rx; px++) {
220
- if ((this.attributes[ry][px] & flags.LOCKED) !== 0) {
221
- if (!inBlack) {
222
- if (startCount)
223
- point.x++;
224
- inBlack = true;
225
- }
226
- } else {
227
- startCount = true;
228
- inBlack = false;
229
- }
230
- }
231
- inBlack = false;
232
- startCount = false;
233
- for (let py = 0; py < ry; py++) {
234
- if ((this.attributes[py][rx] & flags.LOCKED) !== 0) {
235
- if (!inBlack) {
236
- if (startCount)
237
- point.y++;
238
- inBlack = true;
239
- }
240
- } else {
241
- startCount = true;
242
- inBlack = false;
243
- }
244
- }
245
- return point;
246
- }
247
-
248
- /**
249
- * Whether the blinking cursor must be enabled or disabled.
250
- * @param {boolean} status
251
- */
252
- setCursorEnabled(status) {
253
- this.cursorEnabled = status;
254
- if (status === true)
255
- this.startCursorBlink();
256
- else
257
- this.stopCursorBlink();
258
- }
259
-
260
- /**
261
- * Starts the {@link module:AWT.Timer} that makes the cursor blink.
262
- */
263
- startCursorBlink() {
264
- if (this.useCursor && this.cursorEnabled && this.cursorTimer && !this.cursorTimer.isRunning()) {
265
- this.blink(1);
266
- this.cursorTimer.start();
267
- }
268
- }
269
-
270
- /**
271
- * Stops the {@link module:AWT.Timer} that makes the cursor blink.
272
- */
273
- stopCursorBlink() {
274
- if (this.cursorTimer && this.cursorTimer.isRunning()) {
275
- this.cursorTimer.stop();
276
- this.blink(-1);
277
- }
278
- }
279
-
280
- /**
281
- * Moves the cursor in the specified x and y directions.
282
- * @param {number} dx - Amount to move in the 'X' axis
283
- * @param {number} dy - Amount to move in the 'Y' axis
284
- * @param {boolean} skipLocked - Skip locked cells (wildcards in {@link module:activities/textGrid/CrossWord.CrossWord CrossWord})
285
- */
286
- moveCursor(dx, dy, skipLocked) {
287
- if (this.useCursor) {
288
- const point = this.findNextCellWithAttr(this.cursor.x, this.cursor.y,
289
- skipLocked ? flags.LOCKED : flags.NORMAL,
290
- dx, dy, false);
291
-
292
- if (!this.cursor.equals(point))
293
- this.setCursorAt(point.x, point.y, skipLocked);
294
- }
295
- }
296
-
297
- /**
298
- * Finds the coordinates of the nearest non-locked cell (non-wildcard) moving on the indicated
299
- * 'X' and 'Y' directions.
300
- * @param {module:AWT.Point} - Logical coordinates of the starting point
301
- * @param {number} dx - 0 means no movement, 1 go right, -1 go left.
302
- * @param {number} dy - 0 means no movement, 1 go down, -1 go up.
303
- * @returns {module:AWT.Point}
304
- */
305
- findFreeCell(from, dx, dy) {
306
- let result = null;
307
- if (from && (dx !== 0 || dy !== 0)) {
308
- const scan = new Point(from);
309
- while (result === null) {
310
- scan.x += dx;
311
- scan.y += dy;
312
- if (scan.x < 0 || scan.x >= this.nCols || scan.y < 0 || scan.y >= this.nRows)
313
- break;
314
- if (!this.getCellAttribute(scan.x, scan.y, flags.LOCKED))
315
- result = scan;
316
- }
317
- }
318
- return result;
319
- }
320
-
321
- /**
322
- * Finds the first cell with the specified attributes at the specified state, starting
323
- * at specified point.
324
- * @param {number} startX - Starting X coordinate
325
- * @param {number} startY - Starting Y coordinate
326
- * @param {number} attr - Attribute to check. See {@link module:boxes/TextGrid.TextGrid.flags}.
327
- * @param {number} dx - 0 means no movement, 1 go right, -1 go left.
328
- * @param {number} dy - 0 means no movement, 1 go down, -1 go up.
329
- * @param {boolean} attrState - Desired state (enabled or disabled) of `attr`
330
- * @returns {module:AWT.Point}
331
- */
332
- findNextCellWithAttr(startX, startY, attr, dx, dy, attrState) {
333
- const point = new Point(startX + dx, startY + dy);
334
- while (true) {
335
- if (point.x < 0) {
336
- point.x = this.nCols - 1;
337
- if (point.y > 0)
338
- point.y--;
339
- else
340
- point.y = this.nRows - 1;
341
- } else if (point.x >= this.nCols) {
342
- point.x = 0;
343
- if (point.y < this.nRows - 1)
344
- point.y++;
345
- else
346
- point.y = 0;
347
- }
348
- if (point.y < 0) {
349
- point.y = this.nRows - 1;
350
- if (point.x > 0)
351
- point.x--;
352
- else
353
- point.x = this.nCols - 1;
354
- } else if (point.y >= this.nRows) {
355
- point.y = 0;
356
- if (point.x < this.nCols - 1)
357
- point.x++;
358
- else
359
- point.x = 0;
360
- }
361
- if (point.x === startX && point.y === startY ||
362
- this.getCellAttribute(point.x, point.y, attr) === attrState)
363
- break;
364
- point.x += dx;
365
- point.y += dy;
366
- }
367
- return point;
368
- }
369
-
370
- /**
371
- * Sets the blinking cursor at a specific point
372
- * @param {number} px - X coordinate
373
- * @param {number} py - Y coordinate
374
- * @param {boolean} skipLocked - Skip locked (wildcard) cells
375
- */
376
- setCursorAt(px, py, skipLocked) {
377
- this.stopCursorBlink();
378
- if (this.isValidCell(px, py)) {
379
- this.cursor.x = px;
380
- this.cursor.y = py;
381
- this.useCursor = true;
382
- if (skipLocked && this.getCellAttribute(px, py, flags.LOCKED)) {
383
- this.moveCursor(1, 0, skipLocked);
384
- } else {
385
- if (this.cursorEnabled)
386
- this.startCursorBlink();
387
- }
388
- }
389
- }
390
-
391
- /**
392
- * Sets the `useCursor` property of this text grid
393
- * @param {boolean} value
394
- */
395
- setUseCursor(value) {
396
- this.useCursor = value;
397
- }
398
-
399
- /**
400
- * Gets the current position of the blinking cursor
401
- * @returns {module:AWT.Point}
402
- */
403
- getCursor() {
404
- return this.cursor;
405
- }
406
-
407
- /**
408
- * Counts the number of cells of this grid with the specified character
409
- * @param {string} ch
410
- * @returns {number}
411
- */
412
- countCharsLike(ch) {
413
- let result = 0;
414
- for (let py = 0; py < this.nRows; py++)
415
- for (let px = 0; px < this.nCols; px++)
416
- if (this.chars[py][px] === ch)
417
- result++;
418
- return result;
419
- }
420
-
421
- /**
422
- * Gets the number of cells of this grid
423
- * @returns {number}
424
- */
425
- getNumCells() {
426
- return this.nRows * this.nCols;
427
- }
428
-
429
- /**
430
- * Counts the number of coincidences between the `answers` array and the current content of this grid
431
- * @param {boolean} checkCase - Make comparisions case-sensitive
432
- * @returns {number}
433
- */
434
- countCoincidences(checkCase) {
435
- let result = 0;
436
- if (this.answers)
437
- for (let py = 0; py < this.nRows; py++)
438
- for (let px = 0; px < this.nCols; px++)
439
- if (this.isCellOk(px, py, checkCase))
440
- result++;
441
- return result;
442
- }
443
-
444
- /**
445
- * Checks if a specific cell is equivalent to the content of `answers` at its position
446
- * @param {number} px - X coordinate
447
- * @param {number} py - Y coordinate
448
- * @param {boolean} checkCase - Make comparisions case-sensitive
449
- * @returns {boolean}
450
- */
451
- isCellOk(px, py, checkCase) {
452
- let result = false;
453
- if (this.isValidCell(px, py)) {
454
- const ch = this.chars[py][px];
455
- if (ch !== this.wild) {
456
- const ch2 = this.answers[py][px];
457
- if (ch === ch2 ||
458
- !checkCase && ch.toUpperCase() === ch2.toUpperCase())
459
- result = true;
460
- }
461
- }
462
- return result;
463
- }
464
-
465
- /**
466
- * Gets the logical coordinates (in 'cell' units) of a device point into the grid
467
- * @param {module:AWT.Point} devicePoint
468
- * @returns {module:AWT.Point}
469
- */
470
- getLogicalCoords(devicePoint) {
471
- if (!this.contains(devicePoint))
472
- return null;
473
- const
474
- px = Math.floor((devicePoint.x - this.pos.x) / this.cellWidth),
475
- py = Math.floor((devicePoint.y - this.pos.y) / this.cellHeight);
476
-
477
- return this.isValidCell(px, py) ? new Point(px, py) : null;
478
- }
479
-
480
- /**
481
- * Checks if the specified logical coordinates are inside the valid bounds of the grid.
482
- * @param {number} px - 'X' coordinate
483
- * @param {number} py - 'Y' coordinate
484
- * @returns {boolean}
485
- */
486
- isValidCell(px, py) {
487
- return px < this.nCols && py < this.nRows && px >= 0 && py >= 0;
488
- }
489
-
490
- /**
491
- * Sets the specified character as a content of the cell at specified coordinates
492
- * @param {number} px - 'X' coordinate
493
- * @param {number} py - 'Y' coordinate
494
- * @param {string} ch - The character to set.
495
- */
496
- setCharAt(px, py, ch) {
497
- if (this.isValidCell(px, py)) {
498
- this.chars[py][px] = ch;
499
- this.repaintCell(px, py);
500
- }
501
- }
502
-
503
- /**
504
- * Gets the character of the cell at the specified coordinates
505
- * @param {number} px - 'X' coordinate
506
- * @param {number} py - 'Y' coordinate
507
- * @returns {string}
508
- */
509
- getCharAt(px, py) {
510
- return this.isValidCell(px, py) ? this.chars[py][px] : ' ';
511
- }
512
-
513
- /**
514
- * Gets the text formed by the letters between two cells that share a straight line on the grid.
515
- * The text can be formed horizontally, vertically and diagonal, both in left-to-right or
516
- * right-to-left direction.
517
- * @param {number} x0 - 'X' coordinate of the first cell
518
- * @param {number} y0 - 'Y' coordinate of the first cell
519
- * @param {number} x1 - 'X' coordinate of the second cell
520
- * @param {number} y1 - 'Y' coordinate of the second cell
521
- * @returns {string}
522
- */
523
- getStringBetween(x0, y0, x1, y1) {
524
- let sb = '';
525
- if (this.isValidCell(x0, y0) && this.isValidCell(x1, y1)) {
526
- let
527
- dx = x1 - x0,
528
- dy = y1 - y0;
529
- if (dx === 0 || dy === 0 || Math.abs(dx) === Math.abs(dy)) {
530
- const steps = Math.max(Math.abs(dx), Math.abs(dy));
531
- if (steps > 0) {
532
- dx /= steps;
533
- dy /= steps;
534
- }
535
- for (let i = 0; i <= steps; i++)
536
- sb += this.getCharAt(x0 + dx * i, y0 + dy * i);
537
- }
538
- }
539
- return sb;
540
- }
541
-
542
- /**
543
- * Sets a specific attribute to all cells forming a straight line between two cells on the grid.
544
- * @param {number} x0 - 'X' coordinate of the first cell
545
- * @param {number} y0 - 'Y' coordinate of the first cell
546
- * @param {number} x1 - 'X' coordinate of the second cell
547
- * @param {number} y1 - 'Y' coordinate of the second cell
548
- * @param {number} attribute - The binary flag representing this attribute. See {@link module:boxes/TextGrid.TextGrid.flags}.
549
- * @param {boolean} value - Whether to set or unset the attribute.
550
- */
551
- setAttributeBetween(x0, y0, x1, y1, attribute, value) {
552
- if (this.isValidCell(x0, y0) && this.isValidCell(x1, y1)) {
553
- let
554
- dx = x1 - x0,
555
- dy = y1 - y0;
556
-
557
- if (dx === 0 || dy === 0 || Math.abs(dx) === Math.abs(dy)) {
558
- const steps = Math.max(Math.abs(dx), Math.abs(dy));
559
- if (steps > 0) {
560
- dx /= steps;
561
- dy /= steps;
562
- }
563
- for (let i = 0; i <= steps; i++)
564
- this.setAttribute(x0 + dx * i, y0 + dy * i, attribute, value);
565
- }
566
- }
567
- }
568
-
569
- /**
570
- * Sets or unsets a specifi attrobut to a cell.
571
- * @param {number} px - The 'X' coordinate of the cell
572
- * @param {number} py - The 'Y' coordinate of the cell
573
- * @param {number} attribute - The binary flag representing this attribute. See {@link module:boxes/TextGrid.TextGrid.flags}.
574
- * @param {boolean} state - Whether to set or unset the attribute.
575
- */
576
- setAttribute(px, py, attribute, state) {
577
- if (this.isValidCell(px, py)) {
578
- if (this.attribute === flags.MARKED && !state)
579
- this.repaintCell(px, py);
580
- this.attributes[py][px] &= ~attribute;
581
- this.attributes[py][px] |= state ? attribute : 0;
582
- if (attribute !== flags.MARKED || state)
583
- this.repaintCell(px, py);
584
- }
585
- }
586
-
587
- /**
588
- * Sets the specified attribute to all cells.
589
- * @param {number} attribute - The binary flag representing this attribute. See {@link module:boxes/TextGrid.TextGrid.flags}.
590
- * @param {boolean} state - Whether to set or unset the attribute.
591
- */
592
- setAllCellsAttribute(attribute, state) {
593
- for (let py = 0; py < this.nRows; py++)
594
- for (let px = 0; px < this.nCols; px++)
595
- this.setAttribute(px, py, attribute, state);
596
- }
597
-
598
- /**
599
- * Gets the specified attribute of a cell
600
- * @param {number} px - The 'X' coordinate of the cell
601
- * @param {number} py - The 'Y' coordinate of the cell
602
- * @param {number} attribute - The binary flag representing this attribute. See {@link module:boxes/TextGrid.TextGrid.flags}.
603
- * @returns {boolean} - `true` if the cell has this attribute, `false` otherwise.
604
- */
605
- getCellAttribute(px, py, attribute) {
606
- return this.isValidCell(px, py) ? (this.attributes[py][px] & attribute) !== 0 : false;
607
- }
608
-
609
- /**
610
- * Gets the rectangle enclosing a specific cell
611
- * @param {number} px - The 'X' coordinate of the cell
612
- * @param {number} py - The 'Y' coordinate of the cell
613
- * @returns {module:AWT.Rectangle}
614
- */
615
- getCellRect(px, py) {
616
- return new Rectangle(this.pos.x + px * this.cellWidth, this.pos.y + py * this.cellHeight, this.cellWidth, this.cellHeight);
617
- }
618
-
619
- /**
620
- * Gets the rectangle enclosing a specific cell, including the border thick.
621
- * @param {number} px - The 'X' coordinate of the cell
622
- * @param {number} py - The 'Y' coordinate of the cell
623
- * @returns {module:AWT.Rectangle}
624
- */
625
- getCellBorderBounds(px, py) {
626
- const isMarked = this.getCellAttribute(px, py, flags.MARKED);
627
- if (!this.border && !isMarked)
628
- return this.getCellRect(px, py);
629
-
630
- const
631
- style = this.getBoxBaseResolve(),
632
- strk = isMarked ? style.markerStroke : style.borderStroke;
633
-
634
- return this.getCellRect(px, py).grow(strk.lineWidth, strk.lineWidth);
635
- }
636
-
637
- /**
638
- * Repaints a cell
639
- * @param {number} px - The 'X' coordinate of the cell
640
- * @param {number} py - The 'Y' coordinate of the cell
641
- */
642
- repaintCell(px, py) {
643
- if (this.container)
644
- this.container.invalidate(this.getCellBorderBounds(px, py)).update();
645
- }
646
-
647
- /**
648
- * Gets the preferred size of this grid
649
- * @returns {module:AWT.Dimension}
650
- */
651
- getPreferredSize() {
652
- return this.preferredBounds.dim;
653
- }
654
-
655
- /**
656
- * Gets the minimum size of this grid
657
- * @returns {module:AWT.Dimension}
658
- */
659
- getMinimumSize() {
660
- return new Dimension(defaults.MIN_CELL_SIZE * this.nCols, defaults.MIN_CELL_SIZE * this.nRows);
661
- }
662
-
663
- /**
664
- * Scales the grid to a new size
665
- * @param {number} scale - The factor used to multiply all coordinates and sizes
666
- * @returns {module:AWT.Dimension}
667
- */
668
- getScaledSize(scale) {
669
- return new Dimension(
670
- roundTo(scale * this.preferredBounds.dim.width, this.nCols),
671
- roundTo(scale * this.preferredBounds.dim.height, this.nRows));
672
- }
673
-
674
- /**
675
- * Overrides {@link module:boxes/AbstractBox.AbstractBox#setBounds}
676
- * @override
677
- * @param {AWT.Rectangle|number} rect - An AWT.Rectangle object, or the `x` coordinate of the
678
- * upper-left corner of a new rectangle.
679
- * @param {number} [y] - `y` coordinate of the upper-left corner of the new rectangle.
680
- * @param {number} [w] - Width of the new rectangle.
681
- * @param {number} [h] - Height of the new rectangle.
682
- */
683
- setBounds(rect, y, w, h) {
684
- super.setBounds(rect, y, w, h);
685
- this.cellWidth = this.dim.width / this.nCols;
686
- this.cellHeight = this.dim.height / this.nRows;
687
- }
688
-
689
- /**
690
- * Overrides {@link module:boxes/AbstractBox.AbstractBox#updateContent}
691
- * @override
692
- * @param {external:CanvasRenderingContext2D} ctx - The canvas rendering context used to draw the
693
- * grid.
694
- * @param {module:AWT.Rectangle} [dirtyRegion] - The area that must be repainted. `null` refers to the whole box.
695
- */
696
- updateContent(ctx, dirtyRegion) {
697
- const style = this.getBoxBaseResolve();
698
-
699
- // test font size
700
- ctx.font = style.font.cssFont();
701
- ctx.textBaseline = 'alphabetic';
702
- style.prepareText(ctx, 'W',
703
- this.cellWidth - 2 * defaults.MIN_INTERNAL_MARGIN,
704
- this.cellHeight - 2 * defaults.MIN_INTERNAL_MARGIN);
705
-
706
- const ch = [];
707
- const ry = (this.cellHeight - style.font.getHeight()) / 2 + style.font.getMetrics().ascent;
708
-
709
- for (let py = 0; py < this.nRows; py++) {
710
- for (let px = 0; px < this.nCols; px++) {
711
- const bxr = this.getCellBorderBounds(px, py);
712
- if (bxr.intersects(dirtyRegion)) {
713
- const attr = this.attributes[py][px];
714
- if ((attr & flags.TRANSPARENT) === 0) {
715
- const isInverted = (attr & flags.INVERTED) !== 0;
716
- const isMarked = (attr & flags.MARKED) !== 0;
717
- const isCursor = this.useCursor && this.cursor.x === px && this.cursor.y === py;
718
- const boxBounds = this.getCellRect(px, py);
719
- ctx.fillStyle = isCursor && this.cursorBlink ?
720
- style.inactiveColor :
721
- isInverted ? style.textColor : style.backColor;
722
- boxBounds.fill(ctx);
723
- ctx.strokeStyle = 'black';
724
- if ((attr & flags.HIDDEN) === 0) {
725
- ch[0] = this.chars[py][px];
726
- if (ch[0]) {
727
- const dx = boxBounds.pos.x + (this.cellWidth - ctx.measureText(ch[0]).width) / 2;
728
- const dy = boxBounds.pos.y + ry;
729
-
730
- if (style.shadow) {
731
- // Render text shadow
732
- const d = Math.max(1, style.font.size / 10);
733
- ctx.fillStyle = style.shadowColor;
734
- ctx.fillText(ch[0], dx + d, dy + d);
735
- }
736
- // Render text
737
- ctx.fillStyle = isInverted ? style.backColor
738
- : this.isAlternative() ? style.alternativeColor : style.textColor;
739
- ctx.fillText(ch[0], dx, dy);
740
- }
741
- }
742
- if (this.border || isMarked) {
743
- ctx.strokeStyle = style.borderColor;
744
- style[isMarked ? 'markerStroke' : 'borderStroke'].setStroke(ctx);
745
- if (isMarked)
746
- ctx.globalCompositeOperation = 'xor';
747
-
748
- // Draw border
749
- boxBounds.stroke(ctx);
750
-
751
- // Reset ctx default values
752
- if (isMarked)
753
- ctx.globalCompositeOperation = 'source-over';
754
- }
755
- ctx.strokeStyle = 'black';
756
- Stroke.prototype.setStroke(ctx);
757
- }
758
- }
759
- }
760
- }
761
- return true;
762
- }
763
-
764
- /**
765
- * Makes the cursor blink, alternating between two states. This method should be called only by
766
- * {@link module:boxes/TextGrid.TextGrid#cursorTimer}
767
- * @param {boolean} status
768
- */
769
- blink(status) {
770
- // TODO: Move blink and timer to ActivityPanel
771
- if (this.useCursor) {
772
- this.cursorBlink = status === 1 ? true : status === -1 ? false : !this.cursorBlink;
773
- this.repaintCell(this.cursor.x, this.cursor.y);
774
- }
775
- }
776
-
777
- /**
778
- * Stops the cursor timer if not `null` and active
779
- */
780
- end() {
781
- if (this.cursorTimer) {
782
- this.cursorTimer.stop();
783
- this.cursorTimer = null;
784
- }
785
- }
786
- }
787
-
788
- Object.assign(TextGrid.prototype, {
789
- /**
790
- * Number of rows
791
- * @name module:boxes/TextGrid.TextGrid#nRows
792
- * @type {number} */
793
- nRows: 1,
794
- /**
795
- * Number of columns
796
- * @name module:boxes/TextGrid.TextGrid#nCols
797
- * @type {number} */
798
- nCols: 1,
799
- /**
800
- * Two-dimension array of characters
801
- * @name module:boxes/TextGrid.TextGrid#chars
802
- * @type {string[][]} */
803
- chars: null,
804
- /**
805
- * Two-dimension array with the expected characters, used to check user's answers.
806
- * @name module:boxes/TextGrid.TextGrid#answers
807
- * @type {string[][]} */
808
- answers: null,
809
- /**
810
- * Two-dimension array of bytes used as containers of boolean attributes
811
- * @name module:boxes/TextGrid.TextGrid#attributes
812
- * @see TextGrid.flags
813
- * @type {number[][]} */
814
- attributes: null,
815
- /**
816
- * The cell width, in pixels
817
- * @name module:boxes/TextGrid.TextGrid#cellWidth
818
- * @type {number} */
819
- cellWidth: 20,
820
- /**
821
- * The cell height, in pixels
822
- * @name module:boxes/TextGrid.TextGrid#cellHeight
823
- * @type {number} */
824
- cellHeight: 20,
825
- /**
826
- * The preferred bounds of this grid
827
- * @name module:boxes/TextGrid.TextGrid#preferredBounds
828
- * @type {module:AWT.Rectangle} */
829
- preferredBounds: null,
830
- /**
831
- * The character to be used as wildcard
832
- * @name module:boxes/TextGrid.TextGrid#wild
833
- * @type {string} */
834
- wild: TextGridContent.prototype.wild,
835
- /**
836
- * Characters that can be used when randomizing the content of this grid
837
- * @name module:boxes/TextGrid.TextGrid#randomChars
838
- * @see TextGridContent#randomChars
839
- * @type {string} */
840
- randomChars: TextGridContent.prototype.randomChars,
841
- /**
842
- * Whether the blinking cursor is enabled or disabled
843
- * @name module:boxes/TextGrid.TextGrid#cursorEnabled
844
- * @type {boolean} */
845
- cursorEnabled: false,
846
- /**
847
- * Whether this grid uses a blinking cursor or not
848
- * @name module:boxes/TextGrid.TextGrid#useCursor
849
- * @type {boolean} */
850
- useCursor: false,
851
- /**
852
- * The current position of the cursor
853
- * @name module:boxes/TextGrid.TextGrid#cursor
854
- * @type {module:AWT.Point} */
855
- cursor: null,
856
- /**
857
- * `true` when the cursor is "blinking" (cell drawn with {@link module:boxes/BoxBase.BoxBase BoxBase} `inverse` attributes)
858
- * @name module:boxes/TextGrid.TextGrid#cursorBlink
859
- * @type {boolean} */
860
- cursorBlink: false,
861
- /**
862
- * Controls the blinking of the cursor
863
- * @name module:boxes/TextGrid.TextGrid#cursorTimer
864
- * @type {module:AWT.Timer} */
865
- cursorTimer: null,
866
- /**
867
- * Whether the wildcard character is transparent or opaque
868
- * @name module:boxes/TextGrid.TextGrid#wildTransparent
869
- * @type {boolean} */
870
- wildTransparent: false,
871
- });
872
-
873
- /**
874
- * TextGrid default values
875
- * @name module:boxes/TextGrid.TextGrid.defaults
876
- * @constant
877
- * @type {object} */
878
- TextGrid.defaults = defaults;
879
-
880
- /**
881
- * Binary flags used to mark status
882
- * @name module:boxes/TextGrid.TextGrid.flags
883
- * @constant
884
- * @type {object} */
885
- TextGrid.flags = flags;
886
-
887
- export default TextGrid;