blob: b47e75cca35ba92ff0aa43df915f4358eea3b4f1 [file] [log] [blame]
Akron479994e2018-07-02 13:21:44 +02001/**
2 * The plugin system is based
Akron22598cd2019-12-09 14:59:03 +01003 * on registered services (iframes) from
Akron479994e2018-07-02 13:21:44 +02004 * foreign services.
5 * The server component spawns new iframes and
6 * listens to them.
7 *
8 * @author Nils Diewald
9 */
Akrone51eaa32020-11-10 09:35:53 +010010"use strict";
Akron479994e2018-07-02 13:21:44 +020011
Akronda32e7a2021-11-16 17:28:57 +010012define(['plugin/widget', 'plugin/service', 'state', 'state/manager', 'pageInfo', 'util'], function (widgetClass, serviceClass, stateClass, stateManagerClass, pageInfoClass) {
Akron479994e2018-07-02 13:21:44 +020013
Akron2d0d96d2019-11-18 19:49:50 +010014 KorAP.Panel = KorAP.Panel || {};
15
Akron7d18d8e2024-11-08 11:08:48 +010016 const d = document;
17
Akronda32e7a2021-11-16 17:28:57 +010018 // State manager undefined
19 const states = KorAP.States ? KorAP.States :
20
21 // Not serialized state manager
22 stateManagerClass.create(document.createElement('input'));
23
Akron22598cd2019-12-09 14:59:03 +010024 // Contains all servicess to address with
Akrona6c32b92018-07-02 18:39:42 +020025 // messages to them
Akron22598cd2019-12-09 14:59:03 +010026 var services = {};
27 var plugins = {};
Akron10a47962018-07-12 21:17:10 +020028
29 // TODO:
30 // These should better be panels and every panel
31 // has a buttonGroup
Akron2d0d96d2019-11-18 19:49:50 +010032
33 // List of panels with dynamic buttons, i.e.
34 // panels that may occur multiple times.
Akron7c6e05f2018-07-12 19:08:13 +020035 var buttons = {
36 match : []
37 };
Akron2d0d96d2019-11-18 19:49:50 +010038
39 // List of panels with static buttons, i.e.
40 // panels that occur only once.
41 var buttonsSingle = {
hebasta043e96f2019-11-28 12:33:00 +010042 query : [],
43 result : []
Akron22598cd2019-12-09 14:59:03 +010044 }
Akron10a47962018-07-12 21:17:10 +020045
Akrone8e2c952018-07-04 13:43:12 +020046 // This is a counter to limit acceptable incoming messages
47 // to a certain amount. For every message, this counter will
48 // be decreased (down to 0), for every second this will be
49 // increased (up to 100).
50 // Once a widget surpasses the limit, it will be killed
51 // and called suspicious.
52 var maxMessages = 100;
53 var limits = {};
54
55 // TODO:
56 // It may be useful to establish a watcher that pings
Akrone1c27f62018-07-20 11:42:59 +020057 // all widgets every second to see if it is still alive,
58 // otherwise kill
Akrone8e2c952018-07-04 13:43:12 +020059
Akrone1c27f62018-07-20 11:42:59 +020060 // Load Plugin server
Akron479994e2018-07-02 13:21:44 +020061 return {
62
63 /**
64 * Create new plugin management system
65 */
66 create : function () {
67 return Object.create(this)._init();
68 },
69
70 /*
Akron76dd8d32018-07-06 09:30:22 +020071 * Initialize the plugin manager
Akron479994e2018-07-02 13:21:44 +020072 */
73 _init : function () {
Akron7d18d8e2024-11-08 11:08:48 +010074 this._ql = d.getElementById("ql-field");
75 this._q = d.getElementById("q-field")
76 this._cutoff = d.getElementById("q-cutoff-field");
77
Akron479994e2018-07-02 13:21:44 +020078 return this;
79 },
80
81 /**
Akron7c6e05f2018-07-12 19:08:13 +020082 * Register a plugin described as a JSON object.
83 *
84 * This is work in progress.
Akron10a47962018-07-12 21:17:10 +020085 *
86 * Example:
87 *
88 * KorAP.Plugin.register({
89 * 'name' : 'CatContent',
90 * 'desc' : 'Some content about cats',
91 * 'about' : 'https://localhost:5678/',
92 * 'embed' : [{
93 * 'panel' : 'match',
94 * 'title' : loc.TRANSLATE,
95 * 'classes' : ['translate']
96 * 'onClick' : {
97 * 'action' : 'addWidget',
98 * 'template' : 'https://localhost:5678/?match={matchid}',
99 * }
Akron22598cd2019-12-09 14:59:03 +0100100 * },{
101 * 'title' : 'glemm',
102 * 'panel' : 'query',
103 * 'onClick' : {
104 * 'action' : 'toggle',
105 * 'state' : 'glemm',
106 * 'service' : {
107 * 'id' : 'glemm',
108 * 'template' : 'https://localhost:5678/'
109 * }
110 * }
Akron10a47962018-07-12 21:17:10 +0200111 * }]
112 * });
113 *
Akron7c6e05f2018-07-12 19:08:13 +0200114 */
115 register : function (obj) {
Akron7c6e05f2018-07-12 19:08:13 +0200116 // TODO:
Akron10a47962018-07-12 21:17:10 +0200117 // These fields need to be localized for display by a structure like
118 // { de : { name : '..' }, en : { .. } }
Akronda32e7a2021-11-16 17:28:57 +0100119 const name = obj["name"];
Akron7c6e05f2018-07-12 19:08:13 +0200120
Akron10a47962018-07-12 21:17:10 +0200121 if (!name)
122 throw new Error("Missing name of plugin");
123
Akron3d013802020-10-07 15:03:38 +0200124 var desc = obj["desc"];
125
Akron7c6e05f2018-07-12 19:08:13 +0200126 // Register plugin by name
127 var plugin = plugins[name] = {
128 name : name,
Akron3d013802020-10-07 15:03:38 +0200129 desc : desc,
Akron7c6e05f2018-07-12 19:08:13 +0200130 about : obj["about"],
Akron22598cd2019-12-09 14:59:03 +0100131 widgets : [],
132 services : []
Akron7c6e05f2018-07-12 19:08:13 +0200133 };
Akron10a47962018-07-12 21:17:10 +0200134
135 if (typeof obj["embed"] !== 'object')
136 throw new Error("Embedding of plugin is no list");
Akron7c6e05f2018-07-12 19:08:13 +0200137
138 // Embed all embeddings of the plugin
Akrone1c27f62018-07-20 11:42:59 +0200139 var that = this;
Akron678c26f2020-10-09 08:52:50 +0200140 obj["embed"].forEach(function(embed) {
Akron10a47962018-07-12 21:17:10 +0200141
142 if (typeof embed !== 'object')
143 throw new Error("Embedding of plugin is no object");
144
Akron7c6e05f2018-07-12 19:08:13 +0200145 // Needs to be localized as well
Akron22598cd2019-12-09 14:59:03 +0100146 let title = embed["title"];
147 let panel = embed["panel"];
148 let onClick = embed["onClick"];
hebasta40a85cf2020-07-15 18:10:08 +0200149 let icon = embed["icon"];
150
Akron22598cd2019-12-09 14:59:03 +0100151 if (!panel || !(buttons[panel] || buttonsSingle[panel]))
Akronba09ed22020-10-01 16:01:45 +0200152 throw new Error("Panel for plugin is invalid");
Akron7c6e05f2018-07-12 19:08:13 +0200153
154 // The embedding will open a widget
Akronba09ed22020-10-01 16:01:45 +0200155 if (!onClick["action"] ||
156 onClick["action"] == "addWidget" ||
157 onClick["action"] == "setWidget") {
Akron22598cd2019-12-09 14:59:03 +0100158
159 let cb = function (e) {
Akron7c6e05f2018-07-12 19:08:13 +0200160
Akrone1c27f62018-07-20 11:42:59 +0200161 // "this" is bind to the panel
Akronba09ed22020-10-01 16:01:45 +0200162 // "this".button is the button
163 // "that" is the server object
Akrone1c27f62018-07-20 11:42:59 +0200164
Akronba09ed22020-10-01 16:01:45 +0200165 // The button has a state and the state is associated to the
166 // a intermediate object to toggle the view
167 if ('state' in this.button && this.button.state.associates() > 0) {
168
Akronda32e7a2021-11-16 17:28:57 +0100169 const s = this.button.state;
Akronfcf89db2020-10-01 17:40:20 +0200170
171 // The associated service is existent
172 if (services[this.button['widgetID']]) {
Akron237abc42020-10-07 14:14:52 +0200173 s.roll();
Akronfcf89db2020-10-01 17:40:20 +0200174 return;
175 }
176
177 // The service is not existent
178 else {
179
180 // Remove broken state associations
181 s.clear();
Akronba09ed22020-10-01 16:01:45 +0200182 s.set(true);
Akronfcf89db2020-10-01 17:40:20 +0200183 }
Akronba09ed22020-10-01 16:01:45 +0200184 };
185
Akron7c6e05f2018-07-12 19:08:13 +0200186 // Add the widget to the panel
Akronbb891982020-10-05 16:07:18 +0200187 let id = that.addWidget(this, {
188 "name": name,
189 "src": onClick["template"], // that._interpolateURI(onClick["template"], this.match);
Akron3d013802020-10-07 15:03:38 +0200190 "permissions": onClick["permissions"],
191 "desc":desc
Akronbb891982020-10-05 16:07:18 +0200192 });
Akron7c6e05f2018-07-12 19:08:13 +0200193 plugin["widgets"].push(id);
Akronba09ed22020-10-01 16:01:45 +0200194
195 // If a state exists, associate with a mediator object
196 if ('state' in this.button) {
Akronfcf89db2020-10-01 17:40:20 +0200197 this.button['widgetID'] = id;
Akronba09ed22020-10-01 16:01:45 +0200198 this.button.state.associate({
199 setState : function (value) {
200 // Minimize the widget
201 if (value == false) {
202 services[id].minimize();
203 }
204 else {
205 services[id].show();
206 };
207 }
208 });
209 }
Akron7c6e05f2018-07-12 19:08:13 +0200210 };
211
Akron22598cd2019-12-09 14:59:03 +0100212
Akronba09ed22020-10-01 16:01:45 +0200213 // Button object
Akron83a58bc2024-11-08 09:55:19 +0100214 let obj = {'cls':embed["classes"], 'icon': icon };
215
216 if (embed['desc'] != undefined)
217 obj['desc'] = embed['desc'];
Akronba09ed22020-10-01 16:01:45 +0200218
219 if (onClick["action"] && onClick["action"] == "setWidget") {
220
Akronda32e7a2021-11-16 17:28:57 +0100221 // Create a boolean state value,
222 // that initializes to true == opened
Akron237abc42020-10-07 14:14:52 +0200223 obj['state'] = stateClass.create([true, false]);
Akronda32e7a2021-11-16 17:28:57 +0100224 obj['state'].setIfNotYet(true);
Akronba09ed22020-10-01 16:01:45 +0200225 };
226
Akron2d0d96d2019-11-18 19:49:50 +0100227 // Add to dynamic button list (e.g. for matches)
228 if (buttons[panel]) {
Akronba09ed22020-10-01 16:01:45 +0200229 buttons[panel].push([title, obj, cb]);
Akron2d0d96d2019-11-18 19:49:50 +0100230 }
231
232 // Add to static button list (e.g. for query) already loaded
233 else if (KorAP.Panel[panel]) {
Akron37ea1192021-07-28 10:40:14 +0200234 KorAP.Panel[panel].actions().add(title, obj, cb);
Akron2d0d96d2019-11-18 19:49:50 +0100235 }
236
237 // Add to static button list (e.g. for query) not yet loaded
238 else {
Akronba09ed22020-10-01 16:01:45 +0200239 buttonsSingle[panel].push([title, obj, cb]);
Akron2d0d96d2019-11-18 19:49:50 +0100240 }
Akron22598cd2019-12-09 14:59:03 +0100241 }
Akronba09ed22020-10-01 16:01:45 +0200242
Akron22598cd2019-12-09 14:59:03 +0100243 else if (onClick["action"] == "toggle") {
244
Akron237abc42020-10-07 14:14:52 +0200245 // TODO:
246 // Accept a "value" list here for toggling, which should
247 // also allow for "rolling" through states via CSS classes
248 // as 'toggle-true', 'toggle-false' etc.
Akronda32e7a2021-11-16 17:28:57 +0100249 let state = states.newState(
250 (onClick["state"] ? onClick["state"] : name),
251 [true, false],
252 onClick["default"]
253 );
Akrona70b6892021-11-04 14:23:24 +0100254
Akron83a58bc2024-11-08 09:55:19 +0100255 let obj = {'cls':["title"], 'icon': icon};
256
257 if (embed['desc'] != undefined)
258 obj['desc'] = embed['desc'];
259
Akron22598cd2019-12-09 14:59:03 +0100260 // TODO:
261 // Lazy registration (see above!)
Akron83a58bc2024-11-08 09:55:19 +0100262 KorAP.Panel[panel].actions().addToggle(title, obj, state);
Akron22598cd2019-12-09 14:59:03 +0100263
Akron22598cd2019-12-09 14:59:03 +0100264 // Add the service
Akronbb891982020-10-05 16:07:18 +0200265 let id = this.addService({
266 "name" : name,
267 // TODO:
268 // Use the "service" keyword
269 "src" : onClick["template"],
270 "permissions" : onClick["permissions"]
271 });
Akronfb11a962020-10-05 12:12:55 +0200272
Akron22598cd2019-12-09 14:59:03 +0100273 // TODO:
274 // This is a bit stupid to get the service window
Akronc3003642020-03-30 10:19:14 +0200275 let service = services[id];
276 let iframe = service.load();
Akron22598cd2019-12-09 14:59:03 +0100277
278 // Create object to communicate the toggle state
279 // once the iframe is loaded.
280 iframe.onload = function () {
281 let sendToggle = {
282 setState : function (val) {
Akronc3003642020-03-30 10:19:14 +0200283 service.sendMsg({
Akron22598cd2019-12-09 14:59:03 +0100284 action: 'state',
285 key : onClick['state'],
286 value : val
Akronc3003642020-03-30 10:19:14 +0200287 });
Akron22598cd2019-12-09 14:59:03 +0100288 }
289 };
290
291 // Associate object with the state
292 state.associate(sendToggle);
293 };
294
295 plugin["services"].push(id);
Akron7c6e05f2018-07-12 19:08:13 +0200296 };
Akron678c26f2020-10-09 08:52:50 +0200297 }, this);
Akron7c6e05f2018-07-12 19:08:13 +0200298 },
299
Akron7c6e05f2018-07-12 19:08:13 +0200300 // TODO:
301 // Interpolate URIs similar to https://tools.ietf.org/html/rfc6570
302 // but as simple as possible
303 _interpolateURI : function (uri, obj) {
304 // ...
305 },
306
307
308 /**
Akron4a703872018-07-26 10:59:41 +0200309 * Get named button group - better rename to "action"
Akron7c6e05f2018-07-12 19:08:13 +0200310 */
311 buttonGroup : function (name) {
Akron2d0d96d2019-11-18 19:49:50 +0100312 if (buttons[name] != undefined) {
313 return buttons[name];
314 } else if (buttonsSingle[name] != undefined) {
315 return buttonsSingle[name];
316 };
317 return [];
318 },
319
320 /**
321 * Clear named button group - better rename to "action"
322 */
323 clearButtonGroup : function (name) {
324 if (buttons[name] != undefined) {
325 buttons[name] = [];
326 } else if (buttonsSingle[name] != undefined) {
327 buttonsSingle[name] = [];
328 }
Akron7c6e05f2018-07-12 19:08:13 +0200329 },
Akron479994e2018-07-02 13:21:44 +0200330
Akron22598cd2019-12-09 14:59:03 +0100331 // Optionally initialize the service mechanism and get an ID
332 _getServiceID : function () {
Akron4a703872018-07-26 10:59:41 +0200333
Akron22598cd2019-12-09 14:59:03 +0100334 // Is it the first service?
Akron76dd8d32018-07-06 09:30:22 +0200335 if (!this._listener) {
336
337 /*
338 * Establish the global 'message' hook.
339 */
340 this._listener = this._receiveMsg.bind(this);
341 window.addEventListener("message", this._listener);
342
Akron22598cd2019-12-09 14:59:03 +0100343 // Every second increase the limits of all registered services
Akron76dd8d32018-07-06 09:30:22 +0200344 this._timer = window.setInterval(function () {
Akron678c26f2020-10-09 08:52:50 +0200345 for (let i = 0; i < limits.length; i++) {
Akron76dd8d32018-07-06 09:30:22 +0200346 if (limits[i]++ >= maxMessages) {
347 limits[i] = maxMessages;
348 }
349 }
350 }, 1000);
351 };
352
Akron22598cd2019-12-09 14:59:03 +0100353 // Create a unique random ID per service
354 return 'id-' + this._randomID();
355 },
356
357 /**
358 * Add a service in a certain panel and return the id.
359 */
Akronbb891982020-10-05 16:07:18 +0200360 addService : function (data) {
361 if (!data["src"])
Akron22598cd2019-12-09 14:59:03 +0100362 return;
363
364 let id = this._getServiceID();
365
Akronbb891982020-10-05 16:07:18 +0200366 data["id"] = id;
367
Akron22598cd2019-12-09 14:59:03 +0100368 // Create a new service
Akronbb891982020-10-05 16:07:18 +0200369 let service = serviceClass.create(data);
Akron22598cd2019-12-09 14:59:03 +0100370
Akron22598cd2019-12-09 14:59:03 +0100371 services[id] = service;
372 limits[id] = maxMessages;
373
Akron22598cd2019-12-09 14:59:03 +0100374 // Add service to panel
375 this.element().appendChild(
376 service.load()
377 );
378
379 return id;
380 },
381
382
383 /**
384 * Open a new widget view in a certain panel and return
385 * the id.
386 */
Akronbb891982020-10-05 16:07:18 +0200387 addWidget : function (panel, data) {
388 // panel, name, src, permissions
Akron22598cd2019-12-09 14:59:03 +0100389
390 let id = this._getServiceID();
Akrona6c32b92018-07-02 18:39:42 +0200391
Akronbb891982020-10-05 16:07:18 +0200392 data["id"] = id;
393
Akrona6c32b92018-07-02 18:39:42 +0200394 // Create a new widget
Akronbb891982020-10-05 16:07:18 +0200395 var widget = widgetClass.create(data);
Akrona6c32b92018-07-02 18:39:42 +0200396
397 // Store the widget based on the identifier
Akron22598cd2019-12-09 14:59:03 +0100398 services[id] = widget;
Akrona99315e2018-07-03 22:56:45 +0200399 limits[id] = maxMessages;
Akrona6c32b92018-07-02 18:39:42 +0200400
Akron4a703872018-07-26 10:59:41 +0200401 widget._mgr = this;
402
Akrone1c27f62018-07-20 11:42:59 +0200403 // Add widget to panel
404 panel.add(widget);
Akronb43c8c62018-07-04 18:27:28 +0200405
406 return id;
Akron479994e2018-07-02 13:21:44 +0200407 },
408
Akron4a703872018-07-26 10:59:41 +0200409
410 /**
Akron22598cd2019-12-09 14:59:03 +0100411 * Get service by identifier
Akron4a703872018-07-26 10:59:41 +0200412 */
Akron22598cd2019-12-09 14:59:03 +0100413 service : function (id) {
414 return services[id];
Akron4a703872018-07-26 10:59:41 +0200415 },
416
417
Akron22598cd2019-12-09 14:59:03 +0100418 // Receive a call from an embedded service.
Akrone8e2c952018-07-04 13:43:12 +0200419 // The handling needs to be very careful,
420 // as this can easily become a security nightmare.
Akron479994e2018-07-02 13:21:44 +0200421 _receiveMsg : function (e) {
Akronbc94a9c2021-04-15 00:07:35 +0200422
Akron479994e2018-07-02 13:21:44 +0200423 // Get event data
424 var d = e.data;
425
Akrona99315e2018-07-03 22:56:45 +0200426 // If no data given - fail
427 // (probably check that it's an assoc array)
428 if (!d)
429 return;
430
431 // e.origin is probably set and okay - CHECK!
Akronbc94a9c2021-04-15 00:07:35 +0200432 // TODO: Check e.origin is in the list of registered participants
433 // if (e.origin !== "http://example.com:8080")
434 // return;
Akron479994e2018-07-02 13:21:44 +0200435
Akronbc94a9c2021-04-15 00:07:35 +0200436
Akrona99315e2018-07-03 22:56:45 +0200437 // Get origin ID
438 var id = d["originID"];
439
440 // If no origin ID given - fail
441 if (!id)
442 return;
443
Akron22598cd2019-12-09 14:59:03 +0100444 // Get the service
445 let service = services[id];
Akrona6c32b92018-07-02 18:39:42 +0200446
Akron22598cd2019-12-09 14:59:03 +0100447 // If the addressed service does not exist - fail
448 if (!service)
Akrona6c32b92018-07-02 18:39:42 +0200449 return;
450
Akrona99315e2018-07-03 22:56:45 +0200451 // Check for message limits
452 if (limits[id]-- < 0) {
Akrone8e2c952018-07-04 13:43:12 +0200453
Akron22598cd2019-12-09 14:59:03 +0100454 // Kill service
455 KorAP.log(0, 'Suspicious action by service', service.src);
Akron7c6e05f2018-07-12 19:08:13 +0200456
457 // TODO:
458 // Potentially kill the whole plugin!
Akron4a703872018-07-26 10:59:41 +0200459
Akron22598cd2019-12-09 14:59:03 +0100460 // This removes all connections before closing the service
461 this._closeService(service.id);
462
463 // if (service.isWidget)
464 service.close();
465
Akrona99315e2018-07-03 22:56:45 +0200466 return;
467 };
Akrona6c32b92018-07-02 18:39:42 +0200468
Akron479994e2018-07-02 13:21:44 +0200469 // Resize the iframe
Akron22598cd2019-12-09 14:59:03 +0100470 switch (d.action) {
471 case 'resize':
472 if (service.isWidget)
473 service.resize(d);
474 break;
Akron479994e2018-07-02 13:21:44 +0200475
476 // Log message from iframe
Akron22598cd2019-12-09 14:59:03 +0100477 case 'log':
478 KorAP.log(d.code, d.msg, service.src);
479 break;
Akron51ee6232019-12-17 21:00:05 +0100480
481 // Modify pipes
482 case 'pipe':
483 if (KorAP.Pipe != undefined) {
484 if (d.job == 'del') {
485 KorAP.Pipe.remove(d.service);
486 } else {
487 KorAP.Pipe.append(d.service);
488 };
489 };
490 break;
Akronc3003642020-03-30 10:19:14 +0200491
492 // Get information from the embedding platform
493 case 'get':
Akron45308ce2020-08-28 14:10:23 +0200494
495 // Get KoralQuery
Akronc3003642020-03-30 10:19:14 +0200496 if (d.key == 'KQ') {
497 if (KorAP.koralQuery !== undefined) {
498 d["value"] = KorAP.koralQuery;
499 };
Akron45308ce2020-08-28 14:10:23 +0200500 }
501
Akron26d57f22021-09-10 16:48:57 +0200502 // Get Query information from form
Akron45308ce2020-08-28 14:10:23 +0200503 else if (d.key == 'QueryForm') {
504 let doc = document;
505 let v = d["value"] = {};
506
507 var el;
508 if (el = doc.getElementById('q-field')) {
509 v["q"] = el.value;
510 };
511 if (el = doc.getElementById('ql-field')) {
512 v["ql"] = el.value;
513 };
514 if (el = KorAP.vc) {
515 v["cq"] = el.toQuery();
516 };
Akron432972b2020-09-18 17:05:53 +0200517 }
518
Akron338b4d42022-12-20 13:59:22 +0100519 // Get text sigle from match
520 else if (d.key == 'textSigle') {
521 if (service.panel.type != "match") {
522 KorAP.log(0, "Service can only be called on matches", service.src);
523 return;
524 };
525 let v = d["value"] = {};
526 v["value"] = service.panel._match.textSigle;
527 }
528
Akron432972b2020-09-18 17:05:53 +0200529 // Get Query information from parameters
530 else if (d.key == 'QueryParam') {
531
532 // Supported in all modern browsers
533 var p = new URLSearchParams(window.location.search);
534 let v = d["value"] = {};
Akron4de759f2021-10-13 10:46:45 +0200535 v["search"] = window.location.search; // readonly
Akron432972b2020-09-18 17:05:53 +0200536 v["q"] = p.get('q');
537 v["ql"] = p.get('ql');
538 v["cq"] = p.get('cq');
Akron26d57f22021-09-10 16:48:57 +0200539 }
540
541 // Get pagination information
542 else if (d.key == 'Pagination') {
543 const pi = pageInfoClass.create();
544 let v = d["value"] = {};
545 v["page"] = pi.page();
546 v["total"] = pi.total();
547 v["count"] = pi.count();
Akronec4bbfa2021-09-15 15:00:59 +0200548 };
Akronc3003642020-03-30 10:19:14 +0200549
Akronec4bbfa2021-09-15 15:00:59 +0200550 // data needs to be mirrored
551 if (d._id) {
552 service.sendMsg(d);
553 };
554
555 break;
556
Akron7d18d8e2024-11-08 11:08:48 +0100557 case 'set':
558
559 // Get Query information from form
560 if (d.key == 'QueryForm') {
561 let v = d["value"];
562 if (v["q"] != undefined && this._q) {
563 this._q.value = v["q"];
564 };
565
566 // Set query language field
567 // Identical to tutorial.js
568 if (v[ql] != undefined && KorAP.QLmenu) {
569 KorAP.QLmenu.selectValue(ql);
570 }
571
572 else if (this._ql) {
573 let found = Array.from(this._ql.options).find(o => o.value === ql);
574 if (found)
575 found.selected = true;
576 };
577
578 window.scrollTo(0, 0);
579 // if (v["cq"] != undefined) {};
580 }
581
582 break;
583
Akronec4bbfa2021-09-15 15:00:59 +0200584 // Redirect to a different page relative to the current
585 case 'redirect':
586 const url = new URL(window.location);
587
588 // Currently this only accepts search parameters
589 if (d["queryParam"]) {
590 url.search = new URLSearchParams(d["queryParam"]);
591 };
592
593 window.location = url.toString();
594 break;
Akrona6c32b92018-07-02 18:39:42 +0200595 };
596
597 // TODO:
598 // Close
Akron479994e2018-07-02 13:21:44 +0200599 },
600
Akron22598cd2019-12-09 14:59:03 +0100601 // Close the service
602 _closeService : function (id) {
Akron4a703872018-07-26 10:59:41 +0200603 delete limits[id];
Akronda32e7a2021-11-16 17:28:57 +0100604
Akron22598cd2019-12-09 14:59:03 +0100605 // Close the iframe
606 if (services[id] && services[id]._closeIframe) {
Akronda32e7a2021-11-16 17:28:57 +0100607
Akron22598cd2019-12-09 14:59:03 +0100608 services[id]._closeIframe();
609
610 // Remove from list
611 delete services[id];
612 };
613
Akron76dd8d32018-07-06 09:30:22 +0200614
615 // Remove listeners in case no widget
616 // is available any longer
617 if (Object.keys(limits).length == 0)
618 this._removeListener();
619 },
620
Akrona6c32b92018-07-02 18:39:42 +0200621 // Get a random identifier
622 _randomID : function () {
623 return randomID(20);
Akronb43c8c62018-07-04 18:27:28 +0200624 },
625
Akron76dd8d32018-07-06 09:30:22 +0200626 // Remove the listener
627 _removeListener : function () {
628 window.clearInterval(this._timer);
629 this._timer = undefined;
630 window.removeEventListener("message", this._listener);
631 this._listener = undefined;
632 },
633
Akron22598cd2019-12-09 14:59:03 +0100634 /**
635 * Return the service element.
636 */
637 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100638 if (!this._el) {
639 this._el = document.createElement('div');
640 this._el.setAttribute("id", "services");
Akron22598cd2019-12-09 14:59:03 +0100641 }
Akron24aa0052020-11-10 11:00:34 +0100642 return this._el;
Akron22598cd2019-12-09 14:59:03 +0100643 },
Akronda32e7a2021-11-16 17:28:57 +0100644
645
646 // Return states object
647 states : function () {
648 return states;
649 },
650
Akronb43c8c62018-07-04 18:27:28 +0200651 // Destructor, just for testing scenarios
652 destroy : function () {
653 limits = {};
Akron678c26f2020-10-09 08:52:50 +0200654 Object.keys(services).forEach(
655 s => services[s].close()
656 );
Akron22598cd2019-12-09 14:59:03 +0100657 services = {};
Akron678c26f2020-10-09 08:52:50 +0200658 Object.keys(buttons).forEach(
659 b => buttons[b] = []
660 );
661 Object.keys(buttonsSingle).forEach(
662 b => buttonsSingle[b] = []
663 );
Akron22598cd2019-12-09 14:59:03 +0100664
Akron24aa0052020-11-10 11:00:34 +0100665 if (this._el) {
666 let e = this._el;
Akron22598cd2019-12-09 14:59:03 +0100667 if (e.parentNode) {
668 e.parentNode.removeChild(e);
669 };
Akron24aa0052020-11-10 11:00:34 +0100670 this._el = null;
Akron22598cd2019-12-09 14:59:03 +0100671 };
Akron76dd8d32018-07-06 09:30:22 +0200672 this._removeListener();
Akron479994e2018-07-02 13:21:44 +0200673 }
Akrone1c27f62018-07-20 11:42:59 +0200674 };
Akron479994e2018-07-02 13:21:44 +0200675});