jclic 2.2.0 → 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 -664
  8. package/TRANSLATIONS.md +0 -11
  9. package/build-locales.mjs +0 -82
  10. package/dist/jclic-node.js +0 -31678
  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 -658
  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,810 +0,0 @@
1
- /**
2
- * File : boxes/ActiveBox.js
3
- * Created : 18/04/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
- /* global document */
33
-
34
- import $ from 'jquery';
35
- import AbstractBox from './AbstractBox.js';
36
- import ActiveBoxContent from './ActiveBoxContent.js';
37
- import ActiveBagContent from './ActiveBagContent.js';
38
- import { Rectangle, Point, Container } from '../AWT.js';
39
- import { settings, log, getMsg } from '../Utils.js';
40
-
41
- /**
42
- * Objects of this class are widely used in JClic activities: cells in puzzles and associations,
43
- * messages and other objects are active boxes.
44
- *
45
- * The specific content, size and location of `ActiveBox` objects is determined by its
46
- * {@link module:boxes/ActiveBoxContent.ActiveBoxContent ActiveBoxContent} member. Most ActiveBoxes have only one content, but some of them can
47
- * have a secondary or "alternative" content stored in the `altContent` field. This content is
48
- * used only when the `alternative` flag of the ActiveBox is `on`.
49
- *
50
- * Active boxes can host video and interactive media content (specified in the `mediaContent`
51
- * member of the {@link module:boxes/ActiveBoxContent.ActiveBoxContent ActiveBoxContent} through its `hostedMediaPlayer` member.
52
- * @extends module:boxes/AbstractBox.AbstractBox
53
- */
54
- export class ActiveBox extends AbstractBox {
55
- /**
56
- * ActiveBox constructor
57
- * @param {module:boxes/AbstractBox.AbstractBox} [parent] - The AbstractBox to which this ActiveBox belongs
58
- * @param {module:AWT.Container} [container] - The container where this box is placed.
59
- * @param {module:boxes/BoxBase.BoxBase} [boxBase] - The object where colors, fonts, border and other graphic properties
60
- * of this box are defined.
61
- * @param {number} [setIdLoc] - A numeric identifier, used to locate this box in a set of sibling objects.
62
- * @param {module:AWT.Rectangle} [rect] - The initial bounds of the box.
63
- */
64
- constructor(parent, container, boxBase, setIdLoc, rect) {
65
- // ActiveBox extends AbstractBox
66
- super(parent, container, boxBase);
67
- this.clear();
68
- if (typeof setIdLoc === 'number') {
69
- this.idLoc = setIdLoc;
70
- this.idAss = 0;
71
- this.idOrder = 0;
72
- }
73
- if (rect)
74
- this.setBounds(rect);
75
- }
76
-
77
- /**
78
- * Factory constructor that creates a new cell inside a JQuery DOM element.
79
- * @param {external:jQuery} $dom - The DOM element that will act as a container
80
- * @param {module:boxes/ActiveBoxContent.ActiveBoxContent} abc - The cell's content. Must not be null and have the `dimension`
81
- * member initialized.
82
- * @returns {module:boxes/ActiveBox.ActiveBox}
83
- */
84
- static createCell($dom, abc) {
85
- if (abc && abc.dimension) {
86
- const
87
- box = new ActiveBox(),
88
- $canvas = $('<canvas width="' + abc.dimension.width + '" height="' + abc.dimension.height + '"/>'),
89
- rect = new Rectangle(0, 0, abc.dimension.width, abc.dimension.height);
90
- box.container = new Container();
91
- box.container.$div = $dom;
92
- box.setContent(abc);
93
- box.setBounds(rect);
94
- $dom.append($canvas);
95
- // Create accessible, focusable elements only for cells with media content
96
- // TODO: remove focus mark on blur in cells placed on fillInBlanks activities
97
- if (abc.mediaContent)
98
- box.buildAccessibleElement($canvas, $dom);
99
- box.update($canvas.get(-1).getContext('2d'), rect);
100
- return box;
101
- }
102
- }
103
-
104
- /**
105
- * Returns the current content used by the box
106
- * @returns {module:boxes/ActiveBoxContent.ActiveBoxContent}
107
- */
108
- getCurrentContent() {
109
- return this.isAlternative() ? this.altContent : this.content;
110
- }
111
-
112
- /**
113
- * Returns the current content, creating an empty one if needed.
114
- * @returns {module:boxes/ActiveBoxContent.ActiveBoxContent}
115
- */
116
- getContent() {
117
- if (!this.content)
118
- this.setContent(new ActiveBoxContent());
119
- return this.content;
120
- }
121
-
122
- /**
123
- * Clears the current content
124
- */
125
- clear() {
126
- this.content = null;
127
- this.altContent = null;
128
- this.idOrder = -1;
129
- this.setInactive(true);
130
- if (!this.hasHostedComponent)
131
- this.setHostedComponent(null);
132
- this.setHostedMediaPlayer(null);
133
- if (this.$accessibleElement)
134
- this.$accessibleElement.html('');
135
- if (this.tmpTrans)
136
- this.tmpTrans = false;
137
- this.invalidate();
138
- }
139
-
140
- /**
141
- * Checks if two ActiveBox objects have equivalent content
142
- * @param {module:boxes/ActiveBox.ActiveBox} bx - The ActiveBox to check against this.
143
- * @param {boolean} [checkCase] - When `true`, the comparing will be case-sensitive.
144
- * @returns {boolean} - `true` if both cells are equivalent.
145
- */
146
- isEquivalent(bx, checkCase) {
147
- return bx !== null &&
148
- this.content !== null &&
149
- this.content.isEquivalent(bx.content, checkCase);
150
- }
151
-
152
- /**
153
- * Same functionality as {@link module:boxes/ActiveBox.ActiveBox#isEquivalent isEquivalent}, but comparing the current content.
154
- * @param {module:boxes/ActiveBox.ActiveBox} bx - The ActiveBox to check against this.
155
- * @param {boolean} [checkCase] - When `true`, the comparing will be case-sensitive.
156
- * @returns {boolean}
157
- */
158
- isCurrentContentEquivalent(bx, checkCase) {
159
- return bx !== null &&
160
- this.getCurrentContent() !== null &&
161
- this.getCurrentContent().isEquivalent(bx.getCurrentContent(), checkCase);
162
- }
163
-
164
- /**
165
- * Swaps the location of two active boxes
166
- * @param {module:boxes/ActiveBox.ActiveBox} bx - The ActiveBox to swap with this one.
167
- */
168
- exchangeLocation(bx) {
169
- const
170
- pt = new Point(this.pos),
171
- idLoc0 = this.idLoc;
172
- this.moveTo(bx.pos);
173
- bx.moveTo(pt);
174
- this.idLoc = bx.idLoc;
175
- bx.idLoc = idLoc0;
176
- }
177
-
178
- /**
179
- * Copy the content of another ActiveBox into this one
180
- * @param {module:boxes/ActiveBox.ActiveBox} bx - The ActiveBox from which to take the content
181
- */
182
- copyContent(bx) {
183
- this.idOrder = bx.idOrder;
184
- this.idAss = bx.idAss;
185
- this.content = bx.content;
186
- this.altContent = bx.altContent;
187
- if (this.content) {
188
- if (this.content.style)
189
- this.setBoxBase(this.content.style);
190
- if (this.content.border !== null && bx.hasBorder() !== this.content.border)
191
- this.setBorder(this.content.border);
192
- }
193
- this.setInactive(bx.isInactive());
194
- this.setInverted(bx.isInverted());
195
- this.setAlternative(bx.isAlternative());
196
- this.setHostedComponent(bx.getHostedComponent());
197
- this.hasHostedComponent = bx.hasHostedComponent;
198
- this.setHostedMediaPlayer(bx.hostedMediaPlayer);
199
- if (this.hostedMediaPlayer)
200
- this.hostedMediaPlayer.setVisualComponentVisible(!this.isInactive() && this.isVisible());
201
- if (this.$accessibleElement)
202
- this.$accessibleElement.html(this.toString());
203
- }
204
-
205
- /**
206
- *
207
- * Exhanges the content of this ActiveBox with another one
208
- * @param {module:boxes/ActiveBox.ActiveBox} bx - The ActiveBox with which to exchange the content.
209
- */
210
- exchangeContent(bx) {
211
- const bx0 = new ActiveBox(this.getParent(), this.getContainerX(), this.boxBase);
212
- bx0.copyContent(this);
213
- this.copyContent(bx);
214
- bx.copyContent(bx0);
215
- }
216
-
217
- /**
218
- *
219
- * Sets the text content of this ActiveBox.
220
- * @param {string} tx - The text to set.
221
- */
222
- setTextContent(tx) {
223
- // only plain text!
224
- if (!tx)
225
- tx = '';
226
- if (!this.content)
227
- this.content = new ActiveBoxContent();
228
- this.content.text = tx;
229
- this.content.mediaContent = null;
230
- this.content.img = null;
231
-
232
- this.setHostedComponent(null);
233
- this.setInactive(false);
234
- this.checkHostedComponent();
235
- this.setHostedMediaPlayer(null);
236
-
237
- if (this.$accessibleElement)
238
- this.$accessibleElement.html(this.toString());
239
- }
240
-
241
- /**
242
- * Sets the default value to `idAss`
243
- */
244
- setDefaultIdAss() {
245
- this.idAss = this.content === null ? -1 : this.content.id;
246
- }
247
-
248
- /**
249
- * Checks if this ActiveBox is at its original place.
250
- * @returns {boolean}
251
- */
252
- isAtPlace() {
253
- return this.idOrder === this.idLoc;
254
- }
255
-
256
- /**
257
- * Sets the {@link module:boxes/ActiveBoxContent.ActiveBoxContent ActiveBoxContent} of this ActiveBox
258
- * @param {ActiveBoxContent|ActiveBagContent} abc - Object containing the content to set.
259
- * @param {number} i - When `abc` is an {@link module:boxes/ActiveBagContent.ActiveBagContent ActiveBagContent}, this field indicates an
260
- * index in the content array.
261
- */
262
- setContent(abc, i) {
263
- if (abc instanceof ActiveBagContent) {
264
- if (i < 0)
265
- i = this.idOrder;
266
- if (i >= abc.getNumCells())
267
- return;
268
- if (abc.style !== this.boxBase)
269
- this.setBoxBase(abc.style);
270
-
271
- // `abc` is now an [ActiveBoxContent](ActiveBoxContent.html)
272
- abc = abc.getActiveBoxContent(i);
273
- }
274
- this.setHostedComponent(null);
275
- this.setHostedMediaPlayer(null);
276
- this.content = abc;
277
- if (abc) {
278
- if (abc.animatedGifFile && !this.specialShape) {
279
- const url = `url(${abc.animatedGifFile})`;
280
- const $hc = $('<span/>').css({
281
- 'background-image': url,
282
- 'background-position': 'center',
283
- 'background-repeat': 'no-repeat'
284
- });
285
- // Save background image for later use
286
- $hc.data('background-image', url);
287
-
288
- if (abc.imgClip !== null) {
289
- $hc.css({
290
- 'background-origin': 'border-box',
291
- 'background-position': `${-abc.imgClip.pos.x}px ${-abc.imgClip.pos.y}px`
292
- // TODO: Use background-size only when the original image must be compressed
293
- //,'background-size': abc.imgClip.dim.width + 'px ' + abc.imgClip.dim.height + 'px'
294
- });
295
- }
296
- this.setHostedComponent($hc);
297
- }
298
-
299
- if (abc.style !== this.boxBase)
300
- this.setBoxBase(abc.style);
301
-
302
- if (abc.innerHtmlText)
303
- this.setHostedComponent($('<div/>').html(abc.innerHtmlText));
304
-
305
- if (abc.hasOwnProperty('border') && this.hasBorder() !== abc.border)
306
- this.setBorder(abc.border);
307
- this.setInactive(false);
308
- if (abc.amp)
309
- this.setHostedMediaPlayer(abc.amp);
310
- this.checkHostedComponent();
311
- this.checkAutoStartMedia();
312
- } else
313
- this.clear();
314
-
315
- this.invalidate();
316
- if (this.$accessibleElement)
317
- this.$accessibleElement.html(this.toString());
318
- }
319
-
320
- /**
321
- * Sets the {@link module:boxes/ActiveBoxContent.ActiveBoxContent ActiveBoxContent} that will act as an alternative content (`altContent` field)
322
- * of this ActiveBox,
323
- * @param {ActiveBoxContent|ActiveBagContent} abc - Object containing the content to set.
324
- * @param {number} i - When `abc` is an {@link module:boxes/ActiveBagContent.ActiveBagContent ActiveBagContent}, this field indicates an
325
- * index in the content array.
326
- */
327
- setAltContent(abc, i) {
328
- if (abc instanceof ActiveBagContent) {
329
- if (i < 0)
330
- i = this.idOrder;
331
- // `abc` is now an [ActiveBoxContent](ActiveBoxContent.html)
332
- abc = abc.getActiveBoxContent(i);
333
- }
334
- this.altContent = abc;
335
- this.checkHostedComponent();
336
- if (this.isAlternative() && this.hostedMediaPlayer)
337
- this.setHostedMediaPlayer(null);
338
-
339
- if (this.$accessibleElement) {
340
- this.$accessibleElement.html(this.toString());
341
- this.$accessibleElement.prop('disabled', true);
342
- }
343
- }
344
-
345
- /**
346
- * Sets the current content of this ActiveBox
347
- * @param {module:boxes/ActiveBoxContent.ActiveBoxContent} abc - The content to set.
348
- */
349
- setCurrentContent(abc) {
350
- if (this.isAlternative())
351
- this.setAltContent(abc);
352
- else
353
- this.setContent(abc);
354
- this.invalidate();
355
- }
356
-
357
- /**
358
- * Puts this ActiveBox in "alternative" mode, meaning that `altContent` will be used instead of `content`
359
- */
360
- switchToAlt() {
361
- if (this.isAlternative() || !this.altContent || this.altContent.isEmpty())
362
- return false;
363
- this.setHostedComponent(null);
364
- this.setHostedMediaPlayer(null);
365
- this.setAlternative(true);
366
- this.tmpTrans = false;
367
- this.checkHostedComponent();
368
- this.checkAutoStartMedia();
369
-
370
- if (this.$accessibleElement)
371
- this.$accessibleElement.html(this.toString());
372
-
373
- return true;
374
- }
375
-
376
- /**
377
- * Checks the presence of content susceptible to be treated as HTML DOM embedded in this ActiveBox.
378
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas}
379
- */
380
- checkHostedComponent() {
381
- if (this.hasHostedComponent)
382
- return;
383
- const
384
- abc = this.getCurrentContent(),
385
- style = this.getBoxBaseResolve();
386
- if (!this.isInactive() && abc && abc.innerHtmlText)
387
- style.getCSS()['text-align'] = abc.txtAlign.h.replace('middle', 'center');
388
- }
389
-
390
- /**
391
- * Checks if the call has a {@link module:media/MediaContent.MediaContent} set to `autostart`, and launches it when found.
392
- */
393
- checkAutoStartMedia() {
394
- const cnt = this.getContent();
395
- if (cnt && cnt.mediaContent && cnt.mediaContent.autoStart && cnt.amp) {
396
- cnt.amp.playNow(this);
397
- }
398
- }
399
-
400
- /**
401
- * Draws the content of this Activebox on the specified canvas context.
402
- * @override
403
- * @param {external:CanvasRenderingContext2D} ctx - The canvas rendering context used to draw the
404
- * box content.
405
- * @param {module:AWT.Rectangle} [dirtyRegion] - The area that must be repainted. `null` refers to the whole box.
406
- */
407
- updateContent(ctx, dirtyRegion) {
408
-
409
- const
410
- abc = this.getCurrentContent(),
411
- style = this.getBoxBaseResolve();
412
-
413
- if (this.isInactive() || !abc || this.dim.width < 2 || this.dim.height < 2) {
414
- this._focusAccessibleElement(ctx);
415
- return true;
416
- }
417
-
418
- if (dirtyRegion && !this.intersects(dirtyRegion))
419
- return false;
420
-
421
- let imgRect = null;
422
-
423
- if (abc.img && !this.tmpTrans) {
424
- try {
425
- if (abc.imgClip) {
426
- const r = abc.imgClip.getBounds();
427
- let img = abc.img;
428
- if (!abc.imgClip.isRect()) {
429
- // Prepare a temporary `canvas` object that will contain the clipped image
430
- const tmpCanvas = document.createElement('canvas');
431
- tmpCanvas.width = r.pos.x + r.dim.width;
432
- tmpCanvas.height = r.pos.y + r.dim.height;
433
- const tmpCtx = tmpCanvas.getContext('2d');
434
- // Set the clipping region
435
- abc.imgClip.clip(tmpCtx);
436
- // Draw the original image
437
- tmpCtx.drawImage(abc.img, 0, 0);
438
- // Use the temporary canvas as a source image
439
- // (as seen on: [http://stackoverflow.com/questions/7242006/html5-copy-a-canvas-to-image-and-back])
440
- img = tmpCanvas;
441
- }
442
- ctx.drawImage(img,
443
- Math.max(0, r.pos.x), Math.max(0, r.pos.y), Math.min(img.width, r.dim.width), Math.min(img.height, r.dim.height),
444
- this.pos.x, this.pos.y, this.dim.width, this.dim.height);
445
- } else {
446
- let
447
- imgw = abc.img.naturalWidth || this.dim.width,
448
- imgh = abc.img.naturalHeight || this.dim.height,
449
- compress = false,
450
- scale = 1.0;
451
- if (settings.COMPRESS_IMAGES &&
452
- (this.dim.width > 0 && this.dim.height > 0) &&
453
- (imgw > this.dim.width || imgh > this.dim.height)) {
454
-
455
- scale = Math.min(this.dim.width / imgw, this.dim.height / imgh);
456
- imgw *= scale;
457
- imgh *= scale;
458
- compress = true;
459
- }
460
- const xs = abc.imgAlign.h === 'left' ? 0
461
- : abc.imgAlign.h === 'right' ? this.dim.width - imgw
462
- : (this.dim.width - imgw) / 2;
463
- const ys = abc.imgAlign.v === 'top' ? 0
464
- : abc.imgAlign.v === 'bottom' ? this.dim.height - imgh
465
- : (this.dim.height - imgh) / 2;
466
- if (compress) {
467
- ctx.drawImage(abc.img, this.pos.x + xs, this.pos.y + ys, imgw, imgh);
468
- } else
469
- ctx.drawImage(abc.img, this.pos.x + xs, this.pos.y + ys);
470
-
471
- if (abc.avoidOverlapping && abc.text)
472
- imgRect = new Rectangle(
473
- Math.max(0, xs), Math.max(0, ys),
474
- Math.min(this.dim.width, imgw), Math.min(this.dim.height, imgh));
475
- }
476
- } catch (ex) {
477
- log('warn', `Unable to draw image "${abc.image}": ${ex.message}`);
478
- }
479
- }
480
- if (abc.text && abc.text.length > 0) {
481
- let
482
- px = this.pos.x,
483
- py = this.pos.y,
484
- pWidth = this.dim.width,
485
- pHeight = this.dim.height;
486
-
487
- if (imgRect) {
488
- // There is an image in the ActiveBox
489
- // Try to compute the current space available for text
490
- const
491
- prx = [0, imgRect.pos.x, imgRect.pos.x + imgRect.dim.width, pWidth],
492
- pry = [0, imgRect.pos.y, imgRect.pos.y + imgRect.dim.height, pHeight],
493
- rr = [
494
- // Calc four rectangles inside BoxBag, sourronding imgRect
495
- // Top rectangle:
496
- new Rectangle(prx[0], pry[0], prx[3], pry[1]),
497
- // Bottom rectangle:
498
- new Rectangle(prx[0], pry[2], prx[3], pry[3] - pry[2]),
499
- // Left rectangle:
500
- new Rectangle(prx[0], pry[0], prx[1], pry[3]),
501
- // Right rectangle:
502
- new Rectangle(prx[2], pry[0], prx[3] - prx[2], pry[3])
503
- ];
504
- //
505
- // Find the rectangle with highest surface, and in accordance
506
- // with the `txtAlign` values of the current
507
- // [ActiveBoxContent](ActiveBoxContent)
508
- let rmax = rr[0];
509
- let maxSurface = rmax.dim.width * rmax.dim.height;
510
- for (let i = 1; i < rr.length; i++) {
511
- let s = rr[i].dim.width * rr[i].dim.height;
512
- if (s > maxSurface - 1) {
513
- if (Math.abs(s - maxSurface) <= 1) {
514
- let b = false;
515
- switch (i) {
516
- case 1:
517
- b = abc.txtAlign.v === 'bottom';
518
- break;
519
- case 2:
520
- b = abc.txtAlign.h === 'left';
521
- break;
522
- case 3:
523
- b = abc.txtAlign.h === 'right';
524
- break;
525
- }
526
- if (!b)
527
- continue;
528
- }
529
- maxSurface = s;
530
- rmax = rr[i];
531
- }
532
- }
533
- // Finally, this is the surface available to draw text:
534
- px += rmax.pos.x;
535
- py += rmax.pos.y;
536
- pWidth = rmax.dim.width;
537
- pHeight = rmax.dim.height;
538
- }
539
-
540
- // Calc available width and height, discounting margins
541
- const
542
- availWidth = Math.max(5, pWidth - 2 * style.textMargin),
543
- availHeight = Math.max(5, pHeight - 2 * style.textMargin);
544
-
545
- // Calc the size of each line
546
- const lines = style.prepareText(ctx, abc.text, availWidth, availHeight);
547
-
548
- ctx.font = style.font.cssFont();
549
- ctx.textBaseline = 'alphabetic';
550
- const
551
- lineHeight = style.font.getHeight(),
552
- totalHeight = lineHeight * lines.length;
553
-
554
- // Calc the vertical co-ordinate of the first line
555
- // Default is 'middle'
556
- let y = py + style.textMargin + (abc.txtAlign.v === 'top' ? 0
557
- : abc.txtAlign.v === 'bottom' ? availHeight - totalHeight
558
- : (availHeight - totalHeight) / 2) + style.font.getMetrics().ascent;
559
-
560
- for (let l = 0; l < lines.length; l++, y += lineHeight) {
561
- // Calc the horizontal position of each line
562
- // Default is 'middle'
563
- const x = px + style.textMargin + (abc.txtAlign.h === 'left' ? 0
564
- : abc.txtAlign.h === 'right' ?
565
- availWidth - lines[l].size.width
566
- : (availWidth - lines[l].size.width) / 2);
567
-
568
- if (style.shadow) {
569
- // Render text shadow
570
- const d = Math.max(1, style.font.size / 10);
571
- ctx.fillStyle = style.shadowColor;
572
- ctx.fillText(lines[l].text, x + d, y + d);
573
- }
574
- // Render text
575
- ctx.fillStyle = this.isInverted() ? style.backColor
576
- : this.isAlternative() ? style.alternativeColor : style.textColor;
577
- ctx.fillText(lines[l].text, x, y);
578
- }
579
-
580
- this._focusAccessibleElement(ctx);
581
-
582
- }
583
- return true;
584
- }
585
-
586
- /**
587
- * Draw focus on accessible element if needed
588
- * @param {external:CanvasRenderingContext2D} ctx - The canvas rendering context used to draw the
589
- * box content.
590
- */
591
- _focusAccessibleElement(ctx) {
592
- if (settings.CANVAS_DRAW_FOCUS && this.$accessibleElement) {
593
- this.shape.preparePath(ctx);
594
- ctx.drawFocusIfNeeded(this.$accessibleElement.get(-1));
595
- }
596
- }
597
-
598
-
599
- /**
600
- * Gets the `description` field of the current {@link module:boxes/ActiveBoxContent.ActiveBoxContent ActiveBoxContent}
601
- * @returns {string}
602
- */
603
- getDescription() {
604
- return this.content ? this.content.getDescription() : '';
605
- }
606
-
607
- /**
608
- * Gets a descriptive text for this ActiveBox
609
- * @returns {string}
610
- */
611
- toString() {
612
- return (this.role !== 'cell' ? getMsg(this.role) : '') + (this.getCurrentContent() || '-').toString();
613
- }
614
-
615
- /**
616
- * Plays the action or media associated with this ActiveBox
617
- * @param {module:JClicPlayer.JClicPlayer} ps - Usually, a {@link module:JClicPlayer.JClicPlayer JClicPlayer}
618
- * @param {function[]} delayedActions - If set, store the the action in this array for future execution
619
- */
620
- playMedia(ps, delayedActions = null) {
621
- const abc = this.getCurrentContent();
622
- if (abc && abc.mediaContent) {
623
- log('debug', `Playing: ${abc.mediaContent.toString()}`);
624
- ps.playMedia(abc.mediaContent, this, delayedActions);
625
- return true;
626
- }
627
- return false;
628
- }
629
-
630
- /**
631
- * Sets the hosted media player of this ActiveBox
632
- * @param {module:media/ActiveMediaPlayer.ActiveMediaPlayer} amp - The media player.
633
- */
634
- setHostedMediaPlayer(amp) {
635
- const old = this.hostedMediaPlayer;
636
- this.hostedMediaPlayer = amp;
637
- if (old && old !== amp)
638
- old.linkTo(null);
639
- if (amp)
640
- amp.linkTo(this);
641
- }
642
-
643
- /**
644
- * Sets a new size and/or dimension to this box.
645
- * @override
646
- * @param {AWT.Rectangle|number} rect - An AWT.Rectangle object, or the `x` coordinate of the
647
- * upper-left corner of a new rectangle.
648
- * @param {number} [y] - `y` coordinate of the upper-left corner of the new rectangle.
649
- * @param {number} [w] - Width of the new rectangle.
650
- * @param {number} [h] - Height of the new rectangle.
651
- */
652
- setBounds(rect, y, w, h) {
653
- if (typeof rect === 'number')
654
- // arguments are co-ordinates and size
655
- rect = new Rectangle(rect, y, w, h);
656
- // Rectangle comparision
657
- if (this.equals(rect))
658
- return;
659
- super.setBounds(rect);
660
- if (this.hostedMediaPlayer)
661
- this.hostedMediaPlayer.checkVisualComponentBounds(this);
662
- }
663
-
664
- /**
665
- * Places and resizes {@link module:boxes/AbstractBox.AbstractBox#$hostedComponent $hostedComponent}, based on the size
666
- * and position of this box.
667
- * @override
668
- * @param {boolean} sizeChanged - `true` when this {@link module:boxes/ActiveBox.ActiveBox ActiveBox} has changed its size
669
- */
670
- setHostedComponentBounds(sizeChanged) {
671
- if (this.$hostedComponent) {
672
- super.setHostedComponentBounds(sizeChanged);
673
- const abc = this.getCurrentContent();
674
- if (sizeChanged && abc && abc.animatedGifFile && abc.img) {
675
- const
676
- img = abc.img,
677
- w = Math.max(img.naturalWidth, this.dim.width),
678
- h = Math.max(img.naturalHeight, this.dim.height);
679
- let scale = 1, bgSize = '';
680
- if (abc.imgClip) {
681
- const r = abc.imgClip.getBounds();
682
- if (this.dim.width < r.dim.width || this.dim.height < r.dim.height) {
683
- scale = Math.min(this.dim.width / r.dim.width, this.dim.height / r.dim.height);
684
- bgSize = `${w * scale}px ${h * scale}px`;
685
- }
686
- this.$hostedComponent.css({
687
- 'background-position': `${-abc.imgClip.pos.x * scale}px ${-abc.imgClip.pos.y * scale}px`,
688
- 'background-size': bgSize
689
- });
690
- } else {
691
- if (this.dim.width < w || this.dim.height < h) {
692
- scale = Math.min(this.dim.width / w, this.dim.height / h);
693
- bgSize = `${w * scale}px ${h * scale}px`;
694
- }
695
- this.$hostedComponent.css({
696
- 'background-size': bgSize
697
- });
698
- }
699
- }
700
- }
701
- }
702
-
703
- /**
704
- * Builds a hidden `buton` that will act as a accessible element associated to the canvas area
705
- * of this ActiveBox.
706
- * The button will be created only when `CanvasRenderingContext2D` has a method named `addHitRegion`.
707
- * See https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility
708
- * for more information and supported browsers.
709
- * @param {external:jQuery} $canvas - The `canvas` where this `ActiveBox` will deploy, wrapped up in a jQuery object
710
- * @param {external:jQuery} $clickReceiver - The DOM element that will be notified when `$accessibleElement` is activated.
711
- * @param {external:jQuery} [$canvasGroup] - Optional DOM element containing the accessible element. Useful to group cells in associations. When `null`, the element belongs to $canvas.
712
- * @param {string} [eventType] - Type of event sent to $clickReceiver. Default is `click`.
713
- * @returns {external:jQuery} - The accessible element associated to this ActiveBox.
714
- */
715
- buildAccessibleElement($canvas, $clickReceiver, $canvasGroup, eventType) {
716
- if (this.$accessibleElement)
717
- this.$accessibleElement.remove();
718
-
719
- const canvas = $canvas.get(-1);
720
- if (canvas.width > 0 && canvas.height > 0) {
721
- const
722
- id = Math.round(Math.random() * 100000),
723
- disabled = this.isInactive() && !this.accessibleAlwaysActive;
724
- this.$accessibleElement = $('<button/>', {
725
- tabindex: disabled ? -1 : 0,
726
- id: `AE${id}`
727
- })
728
- .prop('disabled', disabled ? true : null)
729
- .html(this.toString())
730
- .on('click', ev => {
731
- // Check if event was produced by a mouse click
732
- if (ev.originalEvent && (ev.originalEvent.pageX !== 0 || ev.originalEvent.pageY !== 0)) {
733
- // Mouse clicks should be processed odirectly by the canvas, so ignore this accessible event
734
- return true;
735
- }
736
- log('debug', `Click on accessible element: ${this.toString()}`);
737
- const
738
- $event = $.Event(eventType || 'click'),
739
- bounds = this.getBounds(),
740
- offset = $canvas.offset();
741
- $event.pageX = offset.left + bounds.pos.x + bounds.dim.width / 2;
742
- $event.pageY = offset.top + bounds.pos.y + bounds.dim.height / 2;
743
- $clickReceiver.trigger($event);
744
- return false;
745
- });
746
- const $dest = $canvasGroup || $canvas;
747
- $dest.append(this.$accessibleElement);
748
- if (settings.CANVAS_DRAW_FOCUS) {
749
- this.$accessibleElement.on('focus blur', ev => {
750
- log('debug', `${ev.type} accessible element: ${this.toString()}`);
751
- if (this.container)
752
- this.container.update();
753
- this.updateContent(canvas.getContext('2d'), null);
754
- });
755
- }
756
- }
757
- return this.$accessibleElement;
758
- }
759
- }
760
-
761
- Object.assign(ActiveBox.prototype, {
762
- /**
763
- * Identifier used to set the relative position of this box in a set.
764
- * @name module:boxes/ActiveBox.ActiveBox#idOrder
765
- * @type {number} */
766
- idOrder: -1,
767
- /**
768
- * Identifier used to set a relative position in the space.
769
- * @name module:boxes/ActiveBox.ActiveBox#idLoc
770
- * @type {number} */
771
- idLoc: -1,
772
- /**
773
- * Identifier used to establish relationships between cells of different sets (in associations)
774
- * @name module:boxes/ActiveBox.ActiveBox#idAss
775
- * @type {number} */
776
- idAss: -1,
777
- /**
778
- * Backup of the original position of the cell, useful when the real position must be restored after a temporary change.
779
- * @name module:boxes/ActiveBox.ActiveBox#pos0
780
- * @type {module:AWT.Point} */
781
- pos0: null,
782
- /**
783
- * Main content of this box
784
- * @name module:boxes/ActiveBox.ActiveBox#content
785
- * @type {module:boxes/ActiveBoxContent.ActiveBoxContent} */
786
- content: null,
787
- /**
788
- * Alternative content of this box
789
- * @name module:boxes/ActiveBox.ActiveBox#altContent
790
- * @type {module:boxes/ActiveBoxContent.ActiveBoxContent} */
791
- altContent: null,
792
- /**
793
- * Flag to check if this box has a 'hosted component'
794
- * @name module:boxes/ActiveBox.ActiveBox#hostedComponent
795
- * @type {boolean} */
796
- hasHostedComponent: false,
797
- /**
798
- * The media player associated to this box
799
- * @name module:boxes/ActiveBox.ActiveBox#hostedMediaPlayer
800
- * @type {module:media/ActiveMediaPlayer.ActiveMediaPlayer} */
801
- hostedMediaPlayer: null,
802
- /**
803
- * Indicates that this box is used as a background. When drawing, the clipping region must be respected.
804
- * @name module:boxes/ActiveBox.ActiveBox#isBackground
805
- * @type {boolean} */
806
- isBackground: false,
807
- });
808
-
809
- export default ActiveBox;
810
-