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.
- package/README.md +5 -7
- package/dist-node/jclic-node.js +14157 -0
- package/dist-node/jclic-node.umd.cjs +530 -0
- package/package.json +38 -26
- package/.vscode/launch.json +0 -33
- package/.vscode/settings.json +0 -13
- package/CHANGELOG.md +0 -672
- package/TRANSLATIONS.md +0 -11
- package/build-locales.mjs +0 -82
- package/dist/jclic-node.js +0 -31680
- package/dist/jclic-node.js.map +0 -1
- package/dist/jclic.components.LICENSE +0 -2254
- package/dist/jclic.min.js +0 -27
- package/dist/jclic.min.js.map +0 -1
- package/eslint.config.mjs +0 -31
- package/jsdoc.config.js +0 -71
- package/locales/ar.po +0 -244
- package/locales/ast.po +0 -246
- package/locales/bs.po +0 -247
- package/locales/ca.po +0 -248
- package/locales/ca_ES@valencia.po +0 -248
- package/locales/cs.po +0 -244
- package/locales/da.po +0 -244
- package/locales/de.po +0 -246
- package/locales/el.po +0 -244
- package/locales/es.po +0 -248
- package/locales/eu.po +0 -244
- package/locales/fr.po +0 -244
- package/locales/gl.po +0 -244
- package/locales/he.po +0 -244
- package/locales/hr.po +0 -245
- package/locales/it.po +0 -246
- package/locales/ja.po +0 -242
- package/locales/jclic.js.pot +0 -241
- package/locales/nb_NO.po +0 -244
- package/locales/nl.po +0 -244
- package/locales/pl.po +0 -244
- package/locales/pt.po +0 -244
- package/locales/pt_BR.po +0 -248
- package/locales/ro.po +0 -248
- package/locales/ru.po +0 -245
- package/locales/ta.po +0 -244
- package/locales/tr.po +0 -246
- package/locales/uk.po +0 -247
- package/locales/vec.po +0 -244
- package/locales/zh_TW.po +0 -246
- package/patches/po2json+1.0.0-beta-3.patch +0 -12
- package/src/AWT.js +0 -2067
- package/src/Activity.js +0 -1311
- package/src/Deps.js +0 -232
- package/src/GlobalData.js +0 -5
- package/src/JClic.js +0 -196
- package/src/JClicPlayer.js +0 -1308
- package/src/PlayerHistory.js +0 -305
- package/src/Utils.js +0 -1355
- package/src/activities/associations/ComplexAssociation.js +0 -321
- package/src/activities/associations/SimpleAssociation.js +0 -519
- package/src/activities/memory/MemoryGame.js +0 -423
- package/src/activities/panels/Explore.js +0 -349
- package/src/activities/panels/Identify.js +0 -356
- package/src/activities/panels/InformationScreen.js +0 -262
- package/src/activities/panels/Menu.js +0 -209
- package/src/activities/panels/icons/ico00.png +0 -0
- package/src/activities/panels/icons/ico01.png +0 -0
- package/src/activities/panels/icons/ico02.png +0 -0
- package/src/activities/panels/icons/ico03.png +0 -0
- package/src/activities/panels/icons/icofolder.png +0 -0
- package/src/activities/puzzles/DoublePuzzle.js +0 -424
- package/src/activities/puzzles/ExchangePuzzle.js +0 -374
- package/src/activities/puzzles/HolePuzzle.js +0 -360
- package/src/activities/text/Complete.js +0 -127
- package/src/activities/text/Evaluator.js +0 -534
- package/src/activities/text/FillInBlanks.js +0 -426
- package/src/activities/text/IdentifyText.js +0 -253
- package/src/activities/text/OrderText.js +0 -421
- package/src/activities/text/TextActivityBase.js +0 -557
- package/src/activities/text/TextActivityDocument.js +0 -660
- package/src/activities/text/WrittenAnswer.js +0 -557
- package/src/activities/textGrid/CrossWord.js +0 -565
- package/src/activities/textGrid/WordSearch.js +0 -458
- package/src/activities/textGrid/icons/hIcon.svg +0 -3
- package/src/activities/textGrid/icons/vIcon.svg +0 -3
- package/src/automation/AutoContentProvider.js +0 -182
- package/src/automation/arith/Arith.js +0 -864
- package/src/bags/ActivitySequence.js +0 -318
- package/src/bags/ActivitySequenceElement.js +0 -161
- package/src/bags/ActivitySequenceJump.js +0 -140
- package/src/bags/ConditionalJumpInfo.js +0 -113
- package/src/bags/JumpInfo.js +0 -136
- package/src/bags/MediaBag.js +0 -215
- package/src/bags/MediaBagElement.js +0 -516
- package/src/boxes/AbstractBox.js +0 -699
- package/src/boxes/ActiveBagContent.js +0 -494
- package/src/boxes/ActiveBox.js +0 -810
- package/src/boxes/ActiveBoxBag.js +0 -357
- package/src/boxes/ActiveBoxContent.js +0 -484
- package/src/boxes/ActiveBoxGrid.js +0 -179
- package/src/boxes/BoxBag.js +0 -500
- package/src/boxes/BoxBase.js +0 -398
- package/src/boxes/BoxConnector.js +0 -325
- package/src/boxes/TextGrid.js +0 -887
- package/src/boxes/TextGridContent.js +0 -215
- package/src/init-jsdom.js +0 -65
- package/src/jclic-node.js +0 -219
- package/src/media/ActiveMediaBag.js +0 -145
- package/src/media/ActiveMediaPlayer.js +0 -297
- package/src/media/AudioBuffer.js +0 -219
- package/src/media/EventSounds.js +0 -169
- package/src/media/EventSoundsElement.js +0 -155
- package/src/media/MediaContent.js +0 -328
- package/src/media/MidiAudioPlayer.js +0 -254
- package/src/media/icons/audio.svg +0 -3
- package/src/media/icons/generic.svg +0 -3
- package/src/media/icons/mic.svg +0 -3
- package/src/media/icons/movie.svg +0 -3
- package/src/media/icons/music.svg +0 -3
- package/src/media/icons/url.svg +0 -3
- package/src/media/sounds/actionError.mp3 +0 -0
- package/src/media/sounds/actionOk.mp3 +0 -0
- package/src/media/sounds/click.mp3 +0 -0
- package/src/media/sounds/finishedError.mp3 +0 -0
- package/src/media/sounds/finishedOk.mp3 +0 -0
- package/src/media/sounds/start.mp3 +0 -0
- package/src/project/JClicProject.js +0 -282
- package/src/project/ProjectSettings.js +0 -273
- package/src/report/ActionReg.js +0 -123
- package/src/report/ActivityReg.js +0 -271
- package/src/report/EncryptMin.js +0 -210
- package/src/report/Reporter.js +0 -727
- package/src/report/SCORM.js +0 -272
- package/src/report/SequenceReg.js +0 -275
- package/src/report/SessionReg.js +0 -340
- package/src/report/SessionStorageReporter.js +0 -131
- package/src/report/TCPReporter.js +0 -628
- package/src/shapers/ClassicJigSaw.js +0 -138
- package/src/shapers/Holes.js +0 -77
- package/src/shapers/JigSaw.js +0 -161
- package/src/shapers/Rectangular.js +0 -78
- package/src/shapers/Shaper.js +0 -386
- package/src/shapers/TriangularJigSaw.js +0 -121
- package/src/skins/BlueSkin.js +0 -80
- package/src/skins/Counter.js +0 -152
- package/src/skins/CustomSkin.js +0 -412
- package/src/skins/DefaultSkin.js +0 -376
- package/src/skins/EmptySkin.js +0 -82
- package/src/skins/GreenSkin.js +0 -94
- package/src/skins/MiniSkin.js +0 -130
- package/src/skins/OrangeSkin.js +0 -78
- package/src/skins/SimpleSkin.js +0 -92
- package/src/skins/Skin.js +0 -1021
- package/src/skins/assets/actionsIcon.svg +0 -3
- package/src/skins/assets/appLogo.svg +0 -8
- package/src/skins/assets/basic.css +0 -41
- package/src/skins/assets/closeDialogIcon.svg +0 -3
- package/src/skins/assets/closeIcon.svg +0 -3
- package/src/skins/assets/copyIcon.svg +0 -3
- package/src/skins/assets/fullScreenExitIcon.svg +0 -3
- package/src/skins/assets/fullScreenIcon.svg +0 -3
- package/src/skins/assets/infoIcon.svg +0 -3
- package/src/skins/assets/main.css +0 -43
- package/src/skins/assets/mainHalf.css +0 -23
- package/src/skins/assets/mainTwoThirds.css +0 -23
- package/src/skins/assets/mini.css +0 -15
- package/src/skins/assets/nextIcon.svg +0 -3
- package/src/skins/assets/okDialogIcon.svg +0 -3
- package/src/skins/assets/prevIcon.svg +0 -3
- package/src/skins/assets/reports.css +0 -156
- package/src/skins/assets/reportsIcon.svg +0 -3
- package/src/skins/assets/scoreIcon.svg +0 -3
- package/src/skins/assets/simple.css +0 -16
- package/src/skins/assets/simpleHalf.css +0 -11
- package/src/skins/assets/simpleTwoThirds.css +0 -11
- package/src/skins/assets/timeIcon.svg +0 -4
- package/src/skins/assets/waitAnim.css +0 -54
- package/src/skins/assets/waitImgBig.svg +0 -3
- package/src/skins/assets/waitImgSmall.svg +0 -3
- package/webpack.config.mjs +0 -169
package/src/report/Reporter.js
DELETED
|
@@ -1,727 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File : report/Reporter.js
|
|
3
|
-
* Created : 17/05/2016
|
|
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 Promise, window */
|
|
33
|
-
|
|
34
|
-
import $ from 'jquery';
|
|
35
|
-
import SessionReg from './SessionReg.js';
|
|
36
|
-
import Encryption from './EncryptMin.js';
|
|
37
|
-
import Scorm from './SCORM.js';
|
|
38
|
-
import { log, getMsg, getVal } from '../Utils.js';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* This class implements the basic operations related with the processing of times and scores
|
|
42
|
-
* done by users playing JClic activities. These operations include: identification of users,
|
|
43
|
-
* compilation of data coming from the activities, storage of this data for later use, and
|
|
44
|
-
* presentation of summarized results.
|
|
45
|
-
*/
|
|
46
|
-
export class Reporter {
|
|
47
|
-
/**
|
|
48
|
-
* Reporter constructor
|
|
49
|
-
* @param {module:JClicPlayer.JClicPlayer} ps - The {@link module:JClicPlayer.JClicPlayer JClicPlayer} used to retrieve localized messages
|
|
50
|
-
*/
|
|
51
|
-
constructor(ps) {
|
|
52
|
-
this.ps = ps;
|
|
53
|
-
this.sessions = [];
|
|
54
|
-
this.started = new Date();
|
|
55
|
-
this.initiated = false;
|
|
56
|
-
this.info = new ReporterInfo(this);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Registers a new type of reporter
|
|
61
|
-
* @param {string} reporterName - The name used to identify this reporter
|
|
62
|
-
* @param {function} reporterClass - The reporter class, usually extending Reporter
|
|
63
|
-
* @returns {module:report/Reporter.Reporter} - The provided reporter class
|
|
64
|
-
*/
|
|
65
|
-
static registerClass(reporterName, reporterClass) {
|
|
66
|
-
Reporter.CLASSES[reporterName] = reporterClass;
|
|
67
|
-
return reporterClass;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Creates a new Reporter of the requested class
|
|
72
|
-
* The resulting object must be prepared to operate with a call to its `init` method.
|
|
73
|
-
* @param {string} className - Class name of the requested reporter. When `null`, a basic Reporter is created.
|
|
74
|
-
* @param {module:JClicPlayer.JClicPlayer} ps - The {@link module:JClicPlayer.JClicPlayer JClicPlayer} used to retrieve localized messages
|
|
75
|
-
* @returns {module:report/Reporter.Reporter}
|
|
76
|
-
*/
|
|
77
|
-
static getReporter(className, ps) {
|
|
78
|
-
let result = null;
|
|
79
|
-
if (className === null) {
|
|
80
|
-
className = 'Reporter';
|
|
81
|
-
if (ps.options.hasOwnProperty('reporter'))
|
|
82
|
-
className = ps.options.reporter;
|
|
83
|
-
}
|
|
84
|
-
if (Reporter.CLASSES.hasOwnProperty(className))
|
|
85
|
-
result = new Reporter.CLASSES[className](ps);
|
|
86
|
-
else
|
|
87
|
-
log('error', 'Unknown reporter class: %s', className);
|
|
88
|
-
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Returns the `info` element associated to this Reporter.
|
|
94
|
-
* @returns {module:report/Reporter.ReporterInfo}
|
|
95
|
-
*/
|
|
96
|
-
getInfo() {
|
|
97
|
-
return this.info.recalc();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Gets a specific property from this reporting system
|
|
102
|
-
* @param {string} key - Requested property
|
|
103
|
-
* @param {string}+ defaultValue - Default return value when requested property does not exist
|
|
104
|
-
* @returns {string}
|
|
105
|
-
*/
|
|
106
|
-
getProperty(key, defaultValue) {
|
|
107
|
-
return defaultValue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Gets a specific boolean property from this reporting system
|
|
112
|
-
* @param {string} key - Requested property
|
|
113
|
-
* @param {boolean}+ defaultValue - Default return when requested property does not exist
|
|
114
|
-
* @returns {boolean}
|
|
115
|
-
*/
|
|
116
|
-
getBooleanProperty(key, defaultValue) {
|
|
117
|
-
const s = this.getProperty(key, defaultValue === true ? 'true' : 'false');
|
|
118
|
-
return key === null ? defaultValue : s === 'true' ? true : false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Gets the list of groups or organizations currently registered in the system. This
|
|
123
|
-
* method should be implemented by classes derived of `Reporter`.
|
|
124
|
-
* @returns {external:Promise} - When fulfilled, an array of group data is returned as a result
|
|
125
|
-
*/
|
|
126
|
-
getGroups() {
|
|
127
|
-
return Promise.reject('No groups defined!');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Gets the list of users currently registered in the system, optionally filtered by
|
|
132
|
-
* a specific group ID. This method should be implemented by classes derived of `Reporter`.
|
|
133
|
-
* @param {string}+ groupId - Optional group ID to be used as a filter criteria
|
|
134
|
-
* @returns {external:Promise} - When fulfilled, an object with a collection of user data records
|
|
135
|
-
* is returned
|
|
136
|
-
*/
|
|
137
|
-
getUsers(groupId) {
|
|
138
|
-
return Promise.reject('No users defined in ' + groupId);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Gets extended data associated with a specific user. This is a method intended to be
|
|
143
|
-
* implemented in subclasses.
|
|
144
|
-
* @param {string} _userId - The requested user ID
|
|
145
|
-
* @returns {external:Promise} - When fulfilled, an object with user data is returned.
|
|
146
|
-
*/
|
|
147
|
-
getUserData(_userId) {
|
|
148
|
-
return Promise.reject('Unknown user!');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Gets extended data associated with a specific group or organization. This
|
|
153
|
-
* is a method intended to be implemented in subclasses.
|
|
154
|
-
* @param {string} _groupId - The requested group ID
|
|
155
|
-
* @returns {external:Promise} - When fulfilled, an object with group data is returned.
|
|
156
|
-
*/
|
|
157
|
-
getGroupData(_groupId) {
|
|
158
|
-
return Promise.reject('Unknown group!');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Checks if this reporting system manages its own database of users and groups. Defaults to `false`
|
|
163
|
-
* @returns {boolean}
|
|
164
|
-
*/
|
|
165
|
-
userBased() {
|
|
166
|
-
if (this.bUserBased === null)
|
|
167
|
-
this.bUserBased = this.getBooleanProperty('USER_TABLES', false);
|
|
168
|
-
return this.bUserBased;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Allows the current user to create a new group, and asks his name
|
|
173
|
-
* @returns {external:Promise} - When fulfilled, the chosen name for the new group is returned.
|
|
174
|
-
*/
|
|
175
|
-
promptForNewGroup() {
|
|
176
|
-
// TODO: Implement promptForNewGroup
|
|
177
|
-
return Promise.reject('Remote creation of groups not yet implemented!');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Allows the current user to create a new user ID, and asks his ID and password
|
|
182
|
-
* @returns {external:Promise} - When fulfilled, an object with the new user ID and password
|
|
183
|
-
* is returned.
|
|
184
|
-
*/
|
|
185
|
-
promptForNewUser() {
|
|
186
|
-
// TODO: Implement promptForNewUser
|
|
187
|
-
return Promise.reject('Remote creation of users not yet implemented!');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Allows the current user to select its group or organization from the current groups list
|
|
192
|
-
* @returns {external:Promise}
|
|
193
|
-
*/
|
|
194
|
-
promptGroupId() {
|
|
195
|
-
return new Promise((resolve, reject) => {
|
|
196
|
-
if (!this.userBased())
|
|
197
|
-
reject('This system does not manage users!');
|
|
198
|
-
else {
|
|
199
|
-
this.getGroups().then((groupList) => {
|
|
200
|
-
// Creation of new groups not yet implemented!
|
|
201
|
-
if (!groupList || groupList.length < 1)
|
|
202
|
-
reject('No groups defined!');
|
|
203
|
-
else {
|
|
204
|
-
let sel = 0;
|
|
205
|
-
const $groupSelect = $('<select/>').attr({ size: Math.max(3, Math.min(15, groupList.length)) });
|
|
206
|
-
groupList.forEach(g => $groupSelect.append($('<option/>').attr({ value: g.id }).text(g.name)));
|
|
207
|
-
$groupSelect.change(ev => { sel = ev.target.selectedIndex; });
|
|
208
|
-
this.ps.skin.showDlg(true, {
|
|
209
|
-
main: [
|
|
210
|
-
$('<h2/>', { class: 'subtitle' }).html(getMsg('Select group:')),
|
|
211
|
-
$groupSelect],
|
|
212
|
-
bottom: [
|
|
213
|
-
this.ps.skin.$okDlgBtn,
|
|
214
|
-
this.ps.skin.$cancelDlgBtn]
|
|
215
|
-
}).then(() => {
|
|
216
|
-
resolve(groupList[sel].id);
|
|
217
|
-
}).catch(reject);
|
|
218
|
-
}
|
|
219
|
-
}).catch(reject);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Asks for a valid user ID fulfilling the promise if found, rejecting it otherwise
|
|
226
|
-
* @param {boolean}+ forcePrompt - Prompt also if `userId` is already defined (default is `false`)
|
|
227
|
-
* @returns {external:Promise}
|
|
228
|
-
*/
|
|
229
|
-
promptUserId(forcePrompt) {
|
|
230
|
-
return new Promise((resolve, reject) => {
|
|
231
|
-
if (this.userId !== null && !forcePrompt)
|
|
232
|
-
resolve(this.userId);
|
|
233
|
-
else if (!this.userBased())
|
|
234
|
-
reject('This system does not manage users!');
|
|
235
|
-
else {
|
|
236
|
-
const $pwdInput = $('<input/>', { type: 'password', size: 8, maxlength: 64 });
|
|
237
|
-
if (this.getBooleanProperty('SHOW_USER_LIST', true)) {
|
|
238
|
-
this.promptGroupId().then(groupId => {
|
|
239
|
-
this.getUsers(groupId).then(userList => {
|
|
240
|
-
// Creation of new users not yet implemented
|
|
241
|
-
// let userCreationAllowed = this.getBooleanProperty('ALLOW_CREATE_USERS', false)
|
|
242
|
-
if (!userList || userList.length < 1)
|
|
243
|
-
reject('Group ' + groupId + ' has no users!');
|
|
244
|
-
else {
|
|
245
|
-
let sel = -1;
|
|
246
|
-
const $userSelect = $('<select/>').attr({ size: Math.max(3, Math.min(15, userList.length)) });
|
|
247
|
-
userList.forEach(u => $userSelect.append($('<option/>').attr({ value: u.id }).text(u.name)));
|
|
248
|
-
$userSelect.change(ev => { sel = ev.target.selectedIndex; });
|
|
249
|
-
this.ps.skin.showDlg(true, {
|
|
250
|
-
main: [
|
|
251
|
-
$('<h2/>', { class: 'subtitle' }).html(getMsg('Select user:')),
|
|
252
|
-
$userSelect,
|
|
253
|
-
$('<h2/>', { class: 'subtitle' }).html(getMsg('Password:')).append($pwdInput)],
|
|
254
|
-
bottom: [
|
|
255
|
-
this.ps.skin.$okDlgBtn,
|
|
256
|
-
this.ps.skin.$cancelDlgBtn]
|
|
257
|
-
}).then(() => {
|
|
258
|
-
if (sel >= 0) {
|
|
259
|
-
if (userList[sel].pwd && Encryption.Decrypt(userList[sel].pwd) !== $pwdInput.val()) {
|
|
260
|
-
window.alert(getMsg('Incorrect password'));
|
|
261
|
-
reject('Incorrect password');
|
|
262
|
-
} else {
|
|
263
|
-
this.userId = userList[sel].id;
|
|
264
|
-
resolve(this.userId);
|
|
265
|
-
}
|
|
266
|
-
} else
|
|
267
|
-
reject('No user has been selected');
|
|
268
|
-
}).catch(reject);
|
|
269
|
-
}
|
|
270
|
-
}).catch(reject);
|
|
271
|
-
}).catch(reject);
|
|
272
|
-
} else {
|
|
273
|
-
const $userInput = $('<input/>', { type: 'text', size: 8, maxlength: 64 });
|
|
274
|
-
this.ps.skin.showDlg(true, {
|
|
275
|
-
main: [
|
|
276
|
-
$('<div/>').css({ 'text-align': 'right' })
|
|
277
|
-
.append($('<h2/>', { class: 'subtitle' }).html(getMsg('User:'))
|
|
278
|
-
.append($userInput))
|
|
279
|
-
.append($('<h2/>', { class: 'subtitle' }).html(getMsg('Password:'))
|
|
280
|
-
.append($pwdInput))],
|
|
281
|
-
bottom: [
|
|
282
|
-
this.ps.skin.$okDlgBtn,
|
|
283
|
-
this.ps.skin.$cancelDlgBtn]
|
|
284
|
-
}).then(() => {
|
|
285
|
-
this.getUserData($userInput.val()).then(user => {
|
|
286
|
-
if (user.pwd && Encryption.Decrypt(user.pwd) !== $pwdInput.val()) {
|
|
287
|
-
window.alert(getMsg('Incorrect password'));
|
|
288
|
-
reject('Incorrect password');
|
|
289
|
-
} else {
|
|
290
|
-
this.userId = user.id;
|
|
291
|
-
resolve(this.userId);
|
|
292
|
-
}
|
|
293
|
-
}).catch(reject);
|
|
294
|
-
}).catch(reject);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Builds a complex object containing all the results reported while playing activities
|
|
302
|
-
* @returns {object} - The current results
|
|
303
|
-
*/
|
|
304
|
-
getData() {
|
|
305
|
-
|
|
306
|
-
// Force the re-calculation of all scores
|
|
307
|
-
this.info.recalc();
|
|
308
|
-
|
|
309
|
-
const result = {
|
|
310
|
-
started: this.started.toISOString(),
|
|
311
|
-
descriptionKey: this.descriptionKey,
|
|
312
|
-
descriptionDetail: this.descriptionDetail,
|
|
313
|
-
projects: this.info.numSessions,
|
|
314
|
-
sequences: this.info.numSequences,
|
|
315
|
-
activitiesDone: this.info.nActivities,
|
|
316
|
-
playedOnce: this.info.nActPlayed,
|
|
317
|
-
reportable: this.info.reportableActs,
|
|
318
|
-
ratioPlayed: Math.round(this.info.ratioPlayed * 100),
|
|
319
|
-
activitiesSolved: this.info.nActSolved,
|
|
320
|
-
ratioSolved: Math.round(this.info.ratioSolved * 100),
|
|
321
|
-
actScore: this.info.nActScore,
|
|
322
|
-
partialScore: Math.round(this.info.partialScore * 100),
|
|
323
|
-
globalScore: Math.round(this.info.globalScore * 100),
|
|
324
|
-
time: Math.round(this.info.tTime / 10) / 100,
|
|
325
|
-
actions: this.info.nActions,
|
|
326
|
-
sessions: []
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
if (this.userId)
|
|
330
|
-
result.userId = this.userId;
|
|
331
|
-
else if (this.SCORM)
|
|
332
|
-
result.user = this.SCORM.studentName + (this.SCORM.studentId === '' ? '' : ` (${this.SCORM.studentId})`);
|
|
333
|
-
|
|
334
|
-
this.sessions.forEach(sr => {
|
|
335
|
-
if (sr.getInfo().numSequences > 0)
|
|
336
|
-
result.sessions.push(sr.getData(false, false));
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
return result;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Initializes this report system with an optional set of parameters.
|
|
344
|
-
* Returns a Promise, fulfilled when the reporter is fully initialized.
|
|
345
|
-
* @param {object} [options] - Initial settings passed to the reporting system
|
|
346
|
-
* @returns {external:Promise}
|
|
347
|
-
*/
|
|
348
|
-
init(options) {
|
|
349
|
-
if (!options)
|
|
350
|
-
options = this.ps.options;
|
|
351
|
-
this.userId = getVal(options.user);
|
|
352
|
-
this.sessionKey = getVal(options.key);
|
|
353
|
-
this.sessionContext = getVal(options.context);
|
|
354
|
-
this.groupCodeFilter = getVal(options.groupCodeFilter);
|
|
355
|
-
this.userCodeFilter = getVal(options.userCodeFilter);
|
|
356
|
-
if (options.SCORM !== false) {
|
|
357
|
-
this.SCORM = Scorm.getSCORM(this);
|
|
358
|
-
if (this.SCORM !== null && this.descriptionKey === Reporter.prototype.descriptionKey)
|
|
359
|
-
this.descriptionKey = this.SCORM.getScormType();
|
|
360
|
-
}
|
|
361
|
-
this.initiated = true;
|
|
362
|
-
log('debug', 'Basic Reporter initialized');
|
|
363
|
-
return Promise.resolve(true);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Closes this reporting system
|
|
368
|
-
* @returns {external:Promise} - A Promise object to be fullfilled when all pending tasks are finished.
|
|
369
|
-
*/
|
|
370
|
-
end() {
|
|
371
|
-
log('debug', 'Basic Reporter ending');
|
|
372
|
-
this.endSession();
|
|
373
|
-
return Promise.resolve(true);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Finalizes the current sequence
|
|
378
|
-
*/
|
|
379
|
-
endSequence() {
|
|
380
|
-
if (this.currentSession) {
|
|
381
|
-
this.currentSession.endSequence();
|
|
382
|
-
this.info.valid = false;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Finalizes the current session
|
|
388
|
-
*/
|
|
389
|
-
endSession() {
|
|
390
|
-
this.endSequence();
|
|
391
|
-
this.currentSession = null;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Creates a new group (method to be implemented in subclasses)
|
|
396
|
-
* @param {object} _gd
|
|
397
|
-
*/
|
|
398
|
-
newGroup(_gd) {
|
|
399
|
-
throw "No database!";
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Creates a new user (method to be implemented in subclasses)
|
|
404
|
-
* @param {object} _ud
|
|
405
|
-
*/
|
|
406
|
-
newUser(_ud) {
|
|
407
|
-
throw "No database!";
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* This method should be invoked when a new session starts.
|
|
412
|
-
* @param {module:project/JClicProject.JClicProject} jcp - The {@link module:project/JClicProject.JClicProject JClicProject} this session refers to.
|
|
413
|
-
*/
|
|
414
|
-
newSession(jcp) {
|
|
415
|
-
this.endSession();
|
|
416
|
-
this.currentSession = new SessionReg(jcp);
|
|
417
|
-
this.sessions.push(this.currentSession);
|
|
418
|
-
this.info.valid = false;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* This method should be invoked when a new sequence starts
|
|
423
|
-
* @param {module:bags/ActivitySequenceElement.ActivitySequenceElement} ase - The {@link module:bags/ActivitySequenceElement.ActivitySequenceElement ActivitySequenceElement} referenced by this sequence.
|
|
424
|
-
*/
|
|
425
|
-
newSequence(ase) {
|
|
426
|
-
if (this.currentSession) {
|
|
427
|
-
this.currentSession.newSequence(ase);
|
|
428
|
-
this.info.valid = false;
|
|
429
|
-
if (this.SCORM)
|
|
430
|
-
this.SCORM.commitInfo();
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* This method should be invoked when the user starts a new activity
|
|
436
|
-
* @param {module:Activity.Activity} act - The {@link module:Activity.Activity Activity} reporter has just started
|
|
437
|
-
*/
|
|
438
|
-
newActivity(act) {
|
|
439
|
-
if (this.currentSession) {
|
|
440
|
-
this.currentSession.newActivity(act);
|
|
441
|
-
this.info.valid = false;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* This method should be called when the current activity finishes. Data about user's final results
|
|
447
|
-
* on the activity will then be saved.
|
|
448
|
-
* @param {number} score - The final score, usually in a 0-100 scale.
|
|
449
|
-
* @param {number} numActions - The total number of actions done by the user to solve the activity
|
|
450
|
-
* @param {boolean} solved - `true` if the activity was finally solved, `false` otherwise.
|
|
451
|
-
*/
|
|
452
|
-
endActivity(score, numActions, solved) {
|
|
453
|
-
if (this.currentSession) {
|
|
454
|
-
this.currentSession.endActivity(score, numActions, solved);
|
|
455
|
-
this.info.valid = false;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Reports a new action done by the user while playing the current activity
|
|
461
|
-
* @param {string} type - Type of action (`click`, `write`, `move`, `select`...)
|
|
462
|
-
* @param {string}+ source - Description of the object on which the action is done.
|
|
463
|
-
* @param {string}+ dest - Description of the object reporter acts as a target of the action (usually in pairings)
|
|
464
|
-
* @param {boolean} ok - `true` if the action was OK, `false`, `null` or `undefined` otherwhise
|
|
465
|
-
*/
|
|
466
|
-
newAction(type, source, dest, ok) {
|
|
467
|
-
if (this.currentSession) {
|
|
468
|
-
this.currentSession.newAction(type, source, dest, ok);
|
|
469
|
-
this.info.valid = false;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Gets information about the current sequence
|
|
475
|
-
* @returns {module:report/SequenceReg.SequenceRegInfo}
|
|
476
|
-
*/
|
|
477
|
-
getCurrentSequenceInfo() {
|
|
478
|
-
return this.currentSession === null ? null : this.currentSession.getCurrentSequenceInfo();
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Gets the name of the current sequence
|
|
483
|
-
* @returns {string}
|
|
484
|
-
*/
|
|
485
|
-
getCurrentSequenceTag() {
|
|
486
|
-
return this.currentSession === null ? null : this.currentSession.getCurrentSequenceTag();
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
Object.assign(Reporter.prototype, {
|
|
491
|
-
/**
|
|
492
|
-
* The {@link module:report/Reporter.ReporterInfo ReporterInfo} used to calculate and store global results.
|
|
493
|
-
* @name module:report/Reporter.Reporter#info
|
|
494
|
-
* @type {module:report/Reporter.ReporterInfo} */
|
|
495
|
-
info: null,
|
|
496
|
-
/**
|
|
497
|
-
* The {@link module:JClicPlayer.JClicPlayer JClicPlayer} used to retrieve messages
|
|
498
|
-
* @name module:report/Reporter.Reporter#ps
|
|
499
|
-
* @type {module:JClicPlayer.JClicPlayer} */
|
|
500
|
-
ps: null,
|
|
501
|
-
/**
|
|
502
|
-
* A valid SCORM bridge, or `null` if no SCORM API detected.
|
|
503
|
-
* @name module:report/Reporter.Reporter#SCORM */
|
|
504
|
-
SCORM: null,
|
|
505
|
-
/**
|
|
506
|
-
* User ID currently associated with this reporting system
|
|
507
|
-
* @name module:report/Reporter.Reporter#userId
|
|
508
|
-
* @type {string} */
|
|
509
|
-
userId: null,
|
|
510
|
-
/**
|
|
511
|
-
* Optional key to be added as a field in session records
|
|
512
|
-
* @name module:report/Reporter.Reporter#sessionKey
|
|
513
|
-
* @type {string} */
|
|
514
|
-
sessionKey: null,
|
|
515
|
-
/**
|
|
516
|
-
* A second optional key to be reported as a field in session records
|
|
517
|
-
* @name module:report/Reporter.Reporter#sessionContext
|
|
518
|
-
* @type {string} */
|
|
519
|
-
sessionContext: null,
|
|
520
|
-
/**
|
|
521
|
-
* Optional filter key to be used in the group selection dialog
|
|
522
|
-
* @name module:report/Reporter.Reporter#groupCodeFilter
|
|
523
|
-
* @type {string} */
|
|
524
|
-
groupCodeFilter: null,
|
|
525
|
-
/**
|
|
526
|
-
* Another optional filter key to be used in the user selection dialog
|
|
527
|
-
* @name module:report/Reporter.Reporter#userCodeFilter
|
|
528
|
-
* @type {string} */
|
|
529
|
-
userCodeFilter: null,
|
|
530
|
-
/**
|
|
531
|
-
* Description of this reporting system
|
|
532
|
-
* @name module:report/Reporter.Reporter#descriptionKey
|
|
533
|
-
* @type {string} */
|
|
534
|
-
descriptionKey: 'Results are not currently being saved',
|
|
535
|
-
/**
|
|
536
|
-
* Additional info to display after the reporter's `description`
|
|
537
|
-
* @name module:report/Reporter.Reporter#descriptionDetail
|
|
538
|
-
* @type {string} */
|
|
539
|
-
descriptionDetail: '',
|
|
540
|
-
/**
|
|
541
|
-
* Starting date and time of this report
|
|
542
|
-
* @name module:report/Reporter.Reporter#started
|
|
543
|
-
* @type {external:Date} */
|
|
544
|
-
started: null,
|
|
545
|
-
/**
|
|
546
|
-
* Array of sessions included in this report
|
|
547
|
-
* @name module:report/Reporter.Reporter#sessions
|
|
548
|
-
* @type {module:report/SessionReg.SessionReg[]} */
|
|
549
|
-
sessions: [],
|
|
550
|
-
/**
|
|
551
|
-
* Currently active session
|
|
552
|
-
* @name module:report/Reporter.Reporter#currentSession
|
|
553
|
-
* @type {module:report/SessionReg.SessionReg} */
|
|
554
|
-
currentSession: null,
|
|
555
|
-
/**
|
|
556
|
-
* `true` if the system was successfully initiated, `false` otherwise
|
|
557
|
-
* @name module:report/Reporter.Reporter#initiated
|
|
558
|
-
* @type {boolean} */
|
|
559
|
-
initiated: false,
|
|
560
|
-
/**
|
|
561
|
-
* `true` if the system is connected to a database with user's data.
|
|
562
|
-
* When `false`, a generic ID will be used.
|
|
563
|
-
* @name module:report/Reporter.Reporter#bUserBased
|
|
564
|
-
* @type {boolean} */
|
|
565
|
-
bUserBased: null,
|
|
566
|
-
/**
|
|
567
|
-
* Maximum number of incorrect UserID attempts
|
|
568
|
-
* @name module:report/Reporter.Reporter#MAX_USERID_PROMPT_ATTEMPTS
|
|
569
|
-
* @type {number} */
|
|
570
|
-
MAX_USERID_PROMPT_ATTEMPTS: 3,
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* This object stores the global results of a {@link module:Reporter.Reporter Reporter}
|
|
575
|
-
*/
|
|
576
|
-
export class ReporterInfo {
|
|
577
|
-
/**
|
|
578
|
-
* ReporterInfo constructor
|
|
579
|
-
* @param {module:report/Reporter.Reporter} rep - The {@link module:Reporter.Reporter Reporter} associated tho this `Info` object.
|
|
580
|
-
*/
|
|
581
|
-
constructor(rep) {
|
|
582
|
-
this.rep = rep;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* Clears all data associated with this ReporterInfo
|
|
587
|
-
*/
|
|
588
|
-
clear() {
|
|
589
|
-
this.numSessions = this.numSequences = this.nActivities = this.reportableActs = this.nActSolved =
|
|
590
|
-
this.nActPlayed = this.nActScore = this.nActions = this.ratioSolved = this.ratioPlayed =
|
|
591
|
-
this.tScore = this.tTime = this.partialScore = this.globalScore = 0;
|
|
592
|
-
this.valid = false;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Computes the value of all global variables based on the data stored in `sessions`
|
|
597
|
-
* @returns {module:report/Reporter.ReporterInfo} - This "info" object
|
|
598
|
-
*/
|
|
599
|
-
recalc() {
|
|
600
|
-
if (!this.valid) {
|
|
601
|
-
this.clear();
|
|
602
|
-
this.rep.sessions.forEach(ses => {
|
|
603
|
-
const inf = ses.getInfo();
|
|
604
|
-
this.reportableActs += inf.sReg.reportableActs;
|
|
605
|
-
if (inf.numSequences > 0) {
|
|
606
|
-
this.numSessions++;
|
|
607
|
-
this.numSequences += inf.numSequences;
|
|
608
|
-
if (inf.nActivities > 0) {
|
|
609
|
-
this.nActivities += inf.nActivities;
|
|
610
|
-
this.nActPlayed += inf.sReg.actNames.length;
|
|
611
|
-
this.nActSolved += inf.nActSolved;
|
|
612
|
-
this.nActions += inf.nActions;
|
|
613
|
-
if (inf.nActScore > 0) {
|
|
614
|
-
this.tScore += inf.tScore * inf.nActScore;
|
|
615
|
-
this.nActScore += inf.nActScore;
|
|
616
|
-
}
|
|
617
|
-
this.tTime += inf.tTime;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
if (this.nActivities > 0) {
|
|
622
|
-
this.ratioSolved = this.nActSolved / this.nActivities;
|
|
623
|
-
if (this.reportableActs > 0)
|
|
624
|
-
this.ratioPlayed = this.nActPlayed / this.reportableActs;
|
|
625
|
-
this.partialScore = this.tScore / (this.nActScore * 100);
|
|
626
|
-
this.globalScore = this.partialScore * this.ratioPlayed;
|
|
627
|
-
}
|
|
628
|
-
this.valid = true;
|
|
629
|
-
}
|
|
630
|
-
return this;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
Object.assign(ReporterInfo.prototype, {
|
|
635
|
-
/**
|
|
636
|
-
* The Reporter linked to this Info object
|
|
637
|
-
* @name module:report/Reporter.ReporterInfo#rep
|
|
638
|
-
* @type {module:report/Reporter.Reporter}
|
|
639
|
-
*/
|
|
640
|
-
rep: null,
|
|
641
|
-
/**
|
|
642
|
-
* When `false`, data must be recalculated
|
|
643
|
-
* @name module:report/Reporter.ReporterInfo#valid
|
|
644
|
-
* @type {boolean} */
|
|
645
|
-
valid: false,
|
|
646
|
-
/**
|
|
647
|
-
* Number of sessions registered
|
|
648
|
-
* @name module:report/Reporter.ReporterInfo#numSessions
|
|
649
|
-
* @type {number} */
|
|
650
|
-
numSessions: 0,
|
|
651
|
-
/**
|
|
652
|
-
* Number of sequences played
|
|
653
|
-
* @name module:report/Reporter.ReporterInfo#numSequences
|
|
654
|
-
* @type {number} */
|
|
655
|
-
numSequences: 0,
|
|
656
|
-
/**
|
|
657
|
-
* Number of activities played
|
|
658
|
-
* @name module:report/Reporter.ReporterInfo#nActivities
|
|
659
|
-
* @type {number} */
|
|
660
|
-
nActivities: 0,
|
|
661
|
-
/**
|
|
662
|
-
* Number of activities in existing in the played projects suitable to be reported
|
|
663
|
-
* @name module:report/Reporter.ReporterInfo#reportableActs
|
|
664
|
-
* @type {number} */
|
|
665
|
-
reportableActs: 0,
|
|
666
|
-
/**
|
|
667
|
-
* Number of activities solved
|
|
668
|
-
* @name module:report/Reporter.ReporterInfo#nActSolved
|
|
669
|
-
* @type {number} */
|
|
670
|
-
nActSolved: 0,
|
|
671
|
-
/**
|
|
672
|
-
* Number of different activities played
|
|
673
|
-
* @name module:report/Reporter.ReporterInfo#nActPlayed
|
|
674
|
-
* @type {number} */
|
|
675
|
-
nActPlayed: 0,
|
|
676
|
-
/**
|
|
677
|
-
* Global score obtained in all sessions registered by this reporter
|
|
678
|
-
* @name module:report/Reporter.ReporterInfo#nActScore
|
|
679
|
-
* @type {number} */
|
|
680
|
-
nActScore: 0,
|
|
681
|
-
/**
|
|
682
|
-
* Number of actions done by the user while in this working session
|
|
683
|
-
* @name module:report/Reporter.ReporterInfo#nActions
|
|
684
|
-
* @type {number} */
|
|
685
|
-
nActions: 0,
|
|
686
|
-
/**
|
|
687
|
-
* Percentage of solved activities
|
|
688
|
-
* @name module:report/Reporter.ReporterInfo#ratioSolved
|
|
689
|
-
* @type {number} */
|
|
690
|
-
ratioSolved: 0,
|
|
691
|
-
/**
|
|
692
|
-
* Percentage of reportable activities played
|
|
693
|
-
* @name module:report/Reporter.ReporterInfo#ratioPlayed
|
|
694
|
-
* @type {number} */
|
|
695
|
-
ratioPlayed: 0,
|
|
696
|
-
/**
|
|
697
|
-
* Sum of the scores of all the activities played
|
|
698
|
-
* @name module:report/Reporter.ReporterInfo#tScore
|
|
699
|
-
* @type {number} */
|
|
700
|
-
tScore: 0,
|
|
701
|
-
/**
|
|
702
|
-
* Global score obtained
|
|
703
|
-
* @name module:report/Reporter.ReporterInfo#partialScore
|
|
704
|
-
* @type {number} */
|
|
705
|
-
partialScore: 0,
|
|
706
|
-
/**
|
|
707
|
-
* Sum of the playing time reported by each activity (not always equals to the sum of all session's time)
|
|
708
|
-
* @name module:report/Reporter.ReporterInfo#tTime
|
|
709
|
-
* @type {number} */
|
|
710
|
-
tTime: 0,
|
|
711
|
-
/**
|
|
712
|
-
* Final score based on the percent of reportable activities played. If the user plays all the
|
|
713
|
-
* activities, this result equals to `partialScore`.
|
|
714
|
-
* @name module:report/Reporter.ReporterInfo#globalScore
|
|
715
|
-
* @type {number} */
|
|
716
|
-
globalScore: 0,
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
Reporter.Info = ReporterInfo;
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* Static list of classes derived from Reporter. It should be filled by Reporter classes at declaration time.
|
|
723
|
-
* @type {object}
|
|
724
|
-
*/
|
|
725
|
-
Reporter.CLASSES = { 'Reporter': Reporter };
|
|
726
|
-
|
|
727
|
-
export default Reporter;
|