blob: c99f69500fb3f609c633a1555f02488f8554a11a [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
Akronda32e7a2021-11-16 17:28:57 +010016 // State manager undefined
17 const states = KorAP.States ? KorAP.States :
18
19 // Not serialized state manager
20 stateManagerClass.create(document.createElement('input'));
21
Akron22598cd2019-12-09 14:59:03 +010022 // Contains all servicess to address with
Akrona6c32b92018-07-02 18:39:42 +020023 // messages to them
Akron22598cd2019-12-09 14:59:03 +010024 var services = {};
25 var plugins = {};
Akron10a47962018-07-12 21:17:10 +020026
27 // TODO:
28 // These should better be panels and every panel
29 // has a buttonGroup
Akron2d0d96d2019-11-18 19:49:50 +010030
31 // List of panels with dynamic buttons, i.e.
32 // panels that may occur multiple times.
Akron7c6e05f2018-07-12 19:08:13 +020033 var buttons = {
34 match : []
35 };
Akron2d0d96d2019-11-18 19:49:50 +010036
37 // List of panels with static buttons, i.e.
38 // panels that occur only once.
39 var buttonsSingle = {
hebasta043e96f2019-11-28 12:33:00 +010040 query : [],
41 result : []
Akron22598cd2019-12-09 14:59:03 +010042 }
Akron10a47962018-07-12 21:17:10 +020043
Akrone8e2c952018-07-04 13:43:12 +020044 // This is a counter to limit acceptable incoming messages
45 // to a certain amount. For every message, this counter will
46 // be decreased (down to 0), for every second this will be
47 // increased (up to 100).
48 // Once a widget surpasses the limit, it will be killed
49 // and called suspicious.
50 var maxMessages = 100;
51 var limits = {};
52
53 // TODO:
54 // It may be useful to establish a watcher that pings
Akrone1c27f62018-07-20 11:42:59 +020055 // all widgets every second to see if it is still alive,
56 // otherwise kill
Akrone8e2c952018-07-04 13:43:12 +020057
Akrone1c27f62018-07-20 11:42:59 +020058 // Load Plugin server
Akron479994e2018-07-02 13:21:44 +020059 return {
60
61 /**
62 * Create new plugin management system
63 */
64 create : function () {
65 return Object.create(this)._init();
66 },
67
68 /*
Akron76dd8d32018-07-06 09:30:22 +020069 * Initialize the plugin manager
Akron479994e2018-07-02 13:21:44 +020070 */
71 _init : function () {
Akron479994e2018-07-02 13:21:44 +020072 return this;
73 },
74
75 /**
Akron7c6e05f2018-07-12 19:08:13 +020076 * Register a plugin described as a JSON object.
77 *
78 * This is work in progress.
Akron10a47962018-07-12 21:17:10 +020079 *
80 * Example:
81 *
82 * KorAP.Plugin.register({
83 * 'name' : 'CatContent',
84 * 'desc' : 'Some content about cats',
85 * 'about' : 'https://localhost:5678/',
86 * 'embed' : [{
87 * 'panel' : 'match',
88 * 'title' : loc.TRANSLATE,
89 * 'classes' : ['translate']
90 * 'onClick' : {
91 * 'action' : 'addWidget',
92 * 'template' : 'https://localhost:5678/?match={matchid}',
93 * }
Akron22598cd2019-12-09 14:59:03 +010094 * },{
95 * 'title' : 'glemm',
96 * 'panel' : 'query',
97 * 'onClick' : {
98 * 'action' : 'toggle',
99 * 'state' : 'glemm',
100 * 'service' : {
101 * 'id' : 'glemm',
102 * 'template' : 'https://localhost:5678/'
103 * }
104 * }
Akron10a47962018-07-12 21:17:10 +0200105 * }]
106 * });
107 *
Akron7c6e05f2018-07-12 19:08:13 +0200108 */
109 register : function (obj) {
Akron7c6e05f2018-07-12 19:08:13 +0200110 // TODO:
Akron10a47962018-07-12 21:17:10 +0200111 // These fields need to be localized for display by a structure like
112 // { de : { name : '..' }, en : { .. } }
Akronda32e7a2021-11-16 17:28:57 +0100113 const name = obj["name"];
Akron7c6e05f2018-07-12 19:08:13 +0200114
Akron10a47962018-07-12 21:17:10 +0200115 if (!name)
116 throw new Error("Missing name of plugin");
117
Akron3d013802020-10-07 15:03:38 +0200118 var desc = obj["desc"];
119
Akron7c6e05f2018-07-12 19:08:13 +0200120 // Register plugin by name
121 var plugin = plugins[name] = {
122 name : name,
Akron3d013802020-10-07 15:03:38 +0200123 desc : desc,
Akron7c6e05f2018-07-12 19:08:13 +0200124 about : obj["about"],
Akron22598cd2019-12-09 14:59:03 +0100125 widgets : [],
126 services : []
Akron7c6e05f2018-07-12 19:08:13 +0200127 };
Akron10a47962018-07-12 21:17:10 +0200128
129 if (typeof obj["embed"] !== 'object')
130 throw new Error("Embedding of plugin is no list");
Akron7c6e05f2018-07-12 19:08:13 +0200131
132 // Embed all embeddings of the plugin
Akrone1c27f62018-07-20 11:42:59 +0200133 var that = this;
Akron678c26f2020-10-09 08:52:50 +0200134 obj["embed"].forEach(function(embed) {
Akron10a47962018-07-12 21:17:10 +0200135
136 if (typeof embed !== 'object')
137 throw new Error("Embedding of plugin is no object");
138
Akron7c6e05f2018-07-12 19:08:13 +0200139 // Needs to be localized as well
Akron22598cd2019-12-09 14:59:03 +0100140 let title = embed["title"];
141 let panel = embed["panel"];
142 let onClick = embed["onClick"];
hebasta40a85cf2020-07-15 18:10:08 +0200143 let icon = embed["icon"];
144
Akron22598cd2019-12-09 14:59:03 +0100145 if (!panel || !(buttons[panel] || buttonsSingle[panel]))
Akronba09ed22020-10-01 16:01:45 +0200146 throw new Error("Panel for plugin is invalid");
Akron7c6e05f2018-07-12 19:08:13 +0200147
148 // The embedding will open a widget
Akronba09ed22020-10-01 16:01:45 +0200149 if (!onClick["action"] ||
150 onClick["action"] == "addWidget" ||
151 onClick["action"] == "setWidget") {
Akron22598cd2019-12-09 14:59:03 +0100152
153 let cb = function (e) {
Akron7c6e05f2018-07-12 19:08:13 +0200154
Akrone1c27f62018-07-20 11:42:59 +0200155 // "this" is bind to the panel
Akronba09ed22020-10-01 16:01:45 +0200156 // "this".button is the button
157 // "that" is the server object
Akrone1c27f62018-07-20 11:42:59 +0200158
Akronba09ed22020-10-01 16:01:45 +0200159 // The button has a state and the state is associated to the
160 // a intermediate object to toggle the view
161 if ('state' in this.button && this.button.state.associates() > 0) {
162
Akronda32e7a2021-11-16 17:28:57 +0100163 const s = this.button.state;
Akronfcf89db2020-10-01 17:40:20 +0200164
165 // The associated service is existent
166 if (services[this.button['widgetID']]) {
Akron237abc42020-10-07 14:14:52 +0200167 s.roll();
Akronfcf89db2020-10-01 17:40:20 +0200168 return;
169 }
170
171 // The service is not existent
172 else {
173
174 // Remove broken state associations
175 s.clear();
Akronba09ed22020-10-01 16:01:45 +0200176 s.set(true);
Akronfcf89db2020-10-01 17:40:20 +0200177 }
Akronba09ed22020-10-01 16:01:45 +0200178 };
179
Akron7c6e05f2018-07-12 19:08:13 +0200180 // Add the widget to the panel
Akronbb891982020-10-05 16:07:18 +0200181 let id = that.addWidget(this, {
182 "name": name,
183 "src": onClick["template"], // that._interpolateURI(onClick["template"], this.match);
Akron3d013802020-10-07 15:03:38 +0200184 "permissions": onClick["permissions"],
185 "desc":desc
Akronbb891982020-10-05 16:07:18 +0200186 });
Akron7c6e05f2018-07-12 19:08:13 +0200187 plugin["widgets"].push(id);
Akronba09ed22020-10-01 16:01:45 +0200188
189 // If a state exists, associate with a mediator object
190 if ('state' in this.button) {
Akronfcf89db2020-10-01 17:40:20 +0200191 this.button['widgetID'] = id;
Akronba09ed22020-10-01 16:01:45 +0200192 this.button.state.associate({
193 setState : function (value) {
194 // Minimize the widget
195 if (value == false) {
196 services[id].minimize();
197 }
198 else {
199 services[id].show();
200 };
201 }
202 });
203 }
Akron7c6e05f2018-07-12 19:08:13 +0200204 };
205
Akron22598cd2019-12-09 14:59:03 +0100206
Akronba09ed22020-10-01 16:01:45 +0200207 // Button object
Akron83a58bc2024-11-08 09:55:19 +0100208 let obj = {'cls':embed["classes"], 'icon': icon };
209
210 if (embed['desc'] != undefined)
211 obj['desc'] = embed['desc'];
Akronba09ed22020-10-01 16:01:45 +0200212
213 if (onClick["action"] && onClick["action"] == "setWidget") {
214
Akronda32e7a2021-11-16 17:28:57 +0100215 // Create a boolean state value,
216 // that initializes to true == opened
Akron237abc42020-10-07 14:14:52 +0200217 obj['state'] = stateClass.create([true, false]);
Akronda32e7a2021-11-16 17:28:57 +0100218 obj['state'].setIfNotYet(true);
Akronba09ed22020-10-01 16:01:45 +0200219 };
220
Akron2d0d96d2019-11-18 19:49:50 +0100221 // Add to dynamic button list (e.g. for matches)
222 if (buttons[panel]) {
Akronba09ed22020-10-01 16:01:45 +0200223 buttons[panel].push([title, obj, cb]);
Akron2d0d96d2019-11-18 19:49:50 +0100224 }
225
226 // Add to static button list (e.g. for query) already loaded
227 else if (KorAP.Panel[panel]) {
Akron37ea1192021-07-28 10:40:14 +0200228 KorAP.Panel[panel].actions().add(title, obj, cb);
Akron2d0d96d2019-11-18 19:49:50 +0100229 }
230
231 // Add to static button list (e.g. for query) not yet loaded
232 else {
Akronba09ed22020-10-01 16:01:45 +0200233 buttonsSingle[panel].push([title, obj, cb]);
Akron2d0d96d2019-11-18 19:49:50 +0100234 }
Akron22598cd2019-12-09 14:59:03 +0100235 }
Akronba09ed22020-10-01 16:01:45 +0200236
Akron22598cd2019-12-09 14:59:03 +0100237 else if (onClick["action"] == "toggle") {
238
Akron237abc42020-10-07 14:14:52 +0200239 // TODO:
240 // Accept a "value" list here for toggling, which should
241 // also allow for "rolling" through states via CSS classes
242 // as 'toggle-true', 'toggle-false' etc.
Akronda32e7a2021-11-16 17:28:57 +0100243 let state = states.newState(
244 (onClick["state"] ? onClick["state"] : name),
245 [true, false],
246 onClick["default"]
247 );
Akrona70b6892021-11-04 14:23:24 +0100248
Akron83a58bc2024-11-08 09:55:19 +0100249 let obj = {'cls':["title"], 'icon': icon};
250
251 if (embed['desc'] != undefined)
252 obj['desc'] = embed['desc'];
253
Akron22598cd2019-12-09 14:59:03 +0100254 // TODO:
255 // Lazy registration (see above!)
Akron83a58bc2024-11-08 09:55:19 +0100256 KorAP.Panel[panel].actions().addToggle(title, obj, state);
Akron22598cd2019-12-09 14:59:03 +0100257
Akron22598cd2019-12-09 14:59:03 +0100258 // Add the service
Akronbb891982020-10-05 16:07:18 +0200259 let id = this.addService({
260 "name" : name,
261 // TODO:
262 // Use the "service" keyword
263 "src" : onClick["template"],
264 "permissions" : onClick["permissions"]
265 });
Akronfb11a962020-10-05 12:12:55 +0200266
Akron22598cd2019-12-09 14:59:03 +0100267 // TODO:
268 // This is a bit stupid to get the service window
Akronc3003642020-03-30 10:19:14 +0200269 let service = services[id];
270 let iframe = service.load();
Akron22598cd2019-12-09 14:59:03 +0100271
272 // Create object to communicate the toggle state
273 // once the iframe is loaded.
274 iframe.onload = function () {
275 let sendToggle = {
276 setState : function (val) {
Akronc3003642020-03-30 10:19:14 +0200277 service.sendMsg({
Akron22598cd2019-12-09 14:59:03 +0100278 action: 'state',
279 key : onClick['state'],
280 value : val
Akronc3003642020-03-30 10:19:14 +0200281 });
Akron22598cd2019-12-09 14:59:03 +0100282 }
283 };
284
285 // Associate object with the state
286 state.associate(sendToggle);
287 };
288
289 plugin["services"].push(id);
Akron7c6e05f2018-07-12 19:08:13 +0200290 };
Akron678c26f2020-10-09 08:52:50 +0200291 }, this);
Akron7c6e05f2018-07-12 19:08:13 +0200292 },
293
Akron7c6e05f2018-07-12 19:08:13 +0200294 // TODO:
295 // Interpolate URIs similar to https://tools.ietf.org/html/rfc6570
296 // but as simple as possible
297 _interpolateURI : function (uri, obj) {
298 // ...
299 },
300
301
302 /**
Akron4a703872018-07-26 10:59:41 +0200303 * Get named button group - better rename to "action"
Akron7c6e05f2018-07-12 19:08:13 +0200304 */
305 buttonGroup : function (name) {
Akron2d0d96d2019-11-18 19:49:50 +0100306 if (buttons[name] != undefined) {
307 return buttons[name];
308 } else if (buttonsSingle[name] != undefined) {
309 return buttonsSingle[name];
310 };
311 return [];
312 },
313
314 /**
315 * Clear named button group - better rename to "action"
316 */
317 clearButtonGroup : function (name) {
318 if (buttons[name] != undefined) {
319 buttons[name] = [];
320 } else if (buttonsSingle[name] != undefined) {
321 buttonsSingle[name] = [];
322 }
Akron7c6e05f2018-07-12 19:08:13 +0200323 },
Akron479994e2018-07-02 13:21:44 +0200324
Akron22598cd2019-12-09 14:59:03 +0100325 // Optionally initialize the service mechanism and get an ID
326 _getServiceID : function () {
Akron4a703872018-07-26 10:59:41 +0200327
Akron22598cd2019-12-09 14:59:03 +0100328 // Is it the first service?
Akron76dd8d32018-07-06 09:30:22 +0200329 if (!this._listener) {
330
331 /*
332 * Establish the global 'message' hook.
333 */
334 this._listener = this._receiveMsg.bind(this);
335 window.addEventListener("message", this._listener);
336
Akron22598cd2019-12-09 14:59:03 +0100337 // Every second increase the limits of all registered services
Akron76dd8d32018-07-06 09:30:22 +0200338 this._timer = window.setInterval(function () {
Akron678c26f2020-10-09 08:52:50 +0200339 for (let i = 0; i < limits.length; i++) {
Akron76dd8d32018-07-06 09:30:22 +0200340 if (limits[i]++ >= maxMessages) {
341 limits[i] = maxMessages;
342 }
343 }
344 }, 1000);
345 };
346
Akron22598cd2019-12-09 14:59:03 +0100347 // Create a unique random ID per service
348 return 'id-' + this._randomID();
349 },
350
351 /**
352 * Add a service in a certain panel and return the id.
353 */
Akronbb891982020-10-05 16:07:18 +0200354 addService : function (data) {
355 if (!data["src"])
Akron22598cd2019-12-09 14:59:03 +0100356 return;
357
358 let id = this._getServiceID();
359
Akronbb891982020-10-05 16:07:18 +0200360 data["id"] = id;
361
Akron22598cd2019-12-09 14:59:03 +0100362 // Create a new service
Akronbb891982020-10-05 16:07:18 +0200363 let service = serviceClass.create(data);
Akron22598cd2019-12-09 14:59:03 +0100364
Akron22598cd2019-12-09 14:59:03 +0100365 services[id] = service;
366 limits[id] = maxMessages;
367
Akron22598cd2019-12-09 14:59:03 +0100368 // Add service to panel
369 this.element().appendChild(
370 service.load()
371 );
372
373 return id;
374 },
375
376
377 /**
378 * Open a new widget view in a certain panel and return
379 * the id.
380 */
Akronbb891982020-10-05 16:07:18 +0200381 addWidget : function (panel, data) {
382 // panel, name, src, permissions
Akron22598cd2019-12-09 14:59:03 +0100383
384 let id = this._getServiceID();
Akrona6c32b92018-07-02 18:39:42 +0200385
Akronbb891982020-10-05 16:07:18 +0200386 data["id"] = id;
387
Akrona6c32b92018-07-02 18:39:42 +0200388 // Create a new widget
Akronbb891982020-10-05 16:07:18 +0200389 var widget = widgetClass.create(data);
Akrona6c32b92018-07-02 18:39:42 +0200390
391 // Store the widget based on the identifier
Akron22598cd2019-12-09 14:59:03 +0100392 services[id] = widget;
Akrona99315e2018-07-03 22:56:45 +0200393 limits[id] = maxMessages;
Akrona6c32b92018-07-02 18:39:42 +0200394
Akron4a703872018-07-26 10:59:41 +0200395 widget._mgr = this;
396
Akrone1c27f62018-07-20 11:42:59 +0200397 // Add widget to panel
398 panel.add(widget);
Akronb43c8c62018-07-04 18:27:28 +0200399
400 return id;
Akron479994e2018-07-02 13:21:44 +0200401 },
402
Akron4a703872018-07-26 10:59:41 +0200403
404 /**
Akron22598cd2019-12-09 14:59:03 +0100405 * Get service by identifier
Akron4a703872018-07-26 10:59:41 +0200406 */
Akron22598cd2019-12-09 14:59:03 +0100407 service : function (id) {
408 return services[id];
Akron4a703872018-07-26 10:59:41 +0200409 },
410
411
Akron22598cd2019-12-09 14:59:03 +0100412 // Receive a call from an embedded service.
Akrone8e2c952018-07-04 13:43:12 +0200413 // The handling needs to be very careful,
414 // as this can easily become a security nightmare.
Akron479994e2018-07-02 13:21:44 +0200415 _receiveMsg : function (e) {
Akronbc94a9c2021-04-15 00:07:35 +0200416
Akron479994e2018-07-02 13:21:44 +0200417 // Get event data
418 var d = e.data;
419
Akrona99315e2018-07-03 22:56:45 +0200420 // If no data given - fail
421 // (probably check that it's an assoc array)
422 if (!d)
423 return;
424
425 // e.origin is probably set and okay - CHECK!
Akronbc94a9c2021-04-15 00:07:35 +0200426 // TODO: Check e.origin is in the list of registered participants
427 // if (e.origin !== "http://example.com:8080")
428 // return;
Akron479994e2018-07-02 13:21:44 +0200429
Akronbc94a9c2021-04-15 00:07:35 +0200430
Akrona99315e2018-07-03 22:56:45 +0200431 // Get origin ID
432 var id = d["originID"];
433
434 // If no origin ID given - fail
435 if (!id)
436 return;
437
Akron22598cd2019-12-09 14:59:03 +0100438 // Get the service
439 let service = services[id];
Akrona6c32b92018-07-02 18:39:42 +0200440
Akron22598cd2019-12-09 14:59:03 +0100441 // If the addressed service does not exist - fail
442 if (!service)
Akrona6c32b92018-07-02 18:39:42 +0200443 return;
444
Akrona99315e2018-07-03 22:56:45 +0200445 // Check for message limits
446 if (limits[id]-- < 0) {
Akrone8e2c952018-07-04 13:43:12 +0200447
Akron22598cd2019-12-09 14:59:03 +0100448 // Kill service
449 KorAP.log(0, 'Suspicious action by service', service.src);
Akron7c6e05f2018-07-12 19:08:13 +0200450
451 // TODO:
452 // Potentially kill the whole plugin!
Akron4a703872018-07-26 10:59:41 +0200453
Akron22598cd2019-12-09 14:59:03 +0100454 // This removes all connections before closing the service
455 this._closeService(service.id);
456
457 // if (service.isWidget)
458 service.close();
459
Akrona99315e2018-07-03 22:56:45 +0200460 return;
461 };
Akrona6c32b92018-07-02 18:39:42 +0200462
Akron479994e2018-07-02 13:21:44 +0200463 // Resize the iframe
Akron22598cd2019-12-09 14:59:03 +0100464 switch (d.action) {
465 case 'resize':
466 if (service.isWidget)
467 service.resize(d);
468 break;
Akron479994e2018-07-02 13:21:44 +0200469
470 // Log message from iframe
Akron22598cd2019-12-09 14:59:03 +0100471 case 'log':
472 KorAP.log(d.code, d.msg, service.src);
473 break;
Akron51ee6232019-12-17 21:00:05 +0100474
475 // Modify pipes
476 case 'pipe':
477 if (KorAP.Pipe != undefined) {
478 if (d.job == 'del') {
479 KorAP.Pipe.remove(d.service);
480 } else {
481 KorAP.Pipe.append(d.service);
482 };
483 };
484 break;
Akronc3003642020-03-30 10:19:14 +0200485
486 // Get information from the embedding platform
487 case 'get':
Akron45308ce2020-08-28 14:10:23 +0200488
489 // Get KoralQuery
Akronc3003642020-03-30 10:19:14 +0200490 if (d.key == 'KQ') {
491 if (KorAP.koralQuery !== undefined) {
492 d["value"] = KorAP.koralQuery;
493 };
Akron45308ce2020-08-28 14:10:23 +0200494 }
495
Akron26d57f22021-09-10 16:48:57 +0200496 // Get Query information from form
Akron45308ce2020-08-28 14:10:23 +0200497 else if (d.key == 'QueryForm') {
498 let doc = document;
499 let v = d["value"] = {};
500
501 var el;
502 if (el = doc.getElementById('q-field')) {
503 v["q"] = el.value;
504 };
505 if (el = doc.getElementById('ql-field')) {
506 v["ql"] = el.value;
507 };
508 if (el = KorAP.vc) {
509 v["cq"] = el.toQuery();
510 };
Akron432972b2020-09-18 17:05:53 +0200511 }
512
Akron338b4d42022-12-20 13:59:22 +0100513 // Get text sigle from match
514 else if (d.key == 'textSigle') {
515 if (service.panel.type != "match") {
516 KorAP.log(0, "Service can only be called on matches", service.src);
517 return;
518 };
519 let v = d["value"] = {};
520 v["value"] = service.panel._match.textSigle;
521 }
522
Akron432972b2020-09-18 17:05:53 +0200523 // Get Query information from parameters
524 else if (d.key == 'QueryParam') {
525
526 // Supported in all modern browsers
527 var p = new URLSearchParams(window.location.search);
528 let v = d["value"] = {};
Akron4de759f2021-10-13 10:46:45 +0200529 v["search"] = window.location.search; // readonly
Akron432972b2020-09-18 17:05:53 +0200530 v["q"] = p.get('q');
531 v["ql"] = p.get('ql');
532 v["cq"] = p.get('cq');
Akron26d57f22021-09-10 16:48:57 +0200533 }
534
535 // Get pagination information
536 else if (d.key == 'Pagination') {
537 const pi = pageInfoClass.create();
538 let v = d["value"] = {};
539 v["page"] = pi.page();
540 v["total"] = pi.total();
541 v["count"] = pi.count();
Akronec4bbfa2021-09-15 15:00:59 +0200542 };
Akronc3003642020-03-30 10:19:14 +0200543
Akronec4bbfa2021-09-15 15:00:59 +0200544 // data needs to be mirrored
545 if (d._id) {
546 service.sendMsg(d);
547 };
548
549 break;
550
551 // Redirect to a different page relative to the current
552 case 'redirect':
553 const url = new URL(window.location);
554
555 // Currently this only accepts search parameters
556 if (d["queryParam"]) {
557 url.search = new URLSearchParams(d["queryParam"]);
558 };
559
560 window.location = url.toString();
561 break;
Akrona6c32b92018-07-02 18:39:42 +0200562 };
563
564 // TODO:
565 // Close
Akron479994e2018-07-02 13:21:44 +0200566 },
567
Akron22598cd2019-12-09 14:59:03 +0100568 // Close the service
569 _closeService : function (id) {
Akron4a703872018-07-26 10:59:41 +0200570 delete limits[id];
Akronda32e7a2021-11-16 17:28:57 +0100571
Akron22598cd2019-12-09 14:59:03 +0100572 // Close the iframe
573 if (services[id] && services[id]._closeIframe) {
Akronda32e7a2021-11-16 17:28:57 +0100574
Akron22598cd2019-12-09 14:59:03 +0100575 services[id]._closeIframe();
576
577 // Remove from list
578 delete services[id];
579 };
580
Akron76dd8d32018-07-06 09:30:22 +0200581
582 // Remove listeners in case no widget
583 // is available any longer
584 if (Object.keys(limits).length == 0)
585 this._removeListener();
586 },
587
Akrona6c32b92018-07-02 18:39:42 +0200588 // Get a random identifier
589 _randomID : function () {
590 return randomID(20);
Akronb43c8c62018-07-04 18:27:28 +0200591 },
592
Akron76dd8d32018-07-06 09:30:22 +0200593 // Remove the listener
594 _removeListener : function () {
595 window.clearInterval(this._timer);
596 this._timer = undefined;
597 window.removeEventListener("message", this._listener);
598 this._listener = undefined;
599 },
600
Akron22598cd2019-12-09 14:59:03 +0100601 /**
602 * Return the service element.
603 */
604 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100605 if (!this._el) {
606 this._el = document.createElement('div');
607 this._el.setAttribute("id", "services");
Akron22598cd2019-12-09 14:59:03 +0100608 }
Akron24aa0052020-11-10 11:00:34 +0100609 return this._el;
Akron22598cd2019-12-09 14:59:03 +0100610 },
Akronda32e7a2021-11-16 17:28:57 +0100611
612
613 // Return states object
614 states : function () {
615 return states;
616 },
617
Akronb43c8c62018-07-04 18:27:28 +0200618 // Destructor, just for testing scenarios
619 destroy : function () {
620 limits = {};
Akron678c26f2020-10-09 08:52:50 +0200621 Object.keys(services).forEach(
622 s => services[s].close()
623 );
Akron22598cd2019-12-09 14:59:03 +0100624 services = {};
Akron678c26f2020-10-09 08:52:50 +0200625 Object.keys(buttons).forEach(
626 b => buttons[b] = []
627 );
628 Object.keys(buttonsSingle).forEach(
629 b => buttonsSingle[b] = []
630 );
Akron22598cd2019-12-09 14:59:03 +0100631
Akron24aa0052020-11-10 11:00:34 +0100632 if (this._el) {
633 let e = this._el;
Akron22598cd2019-12-09 14:59:03 +0100634 if (e.parentNode) {
635 e.parentNode.removeChild(e);
636 };
Akron24aa0052020-11-10 11:00:34 +0100637 this._el = null;
Akron22598cd2019-12-09 14:59:03 +0100638 };
Akron76dd8d32018-07-06 09:30:22 +0200639 this._removeListener();
Akron479994e2018-07-02 13:21:44 +0200640 }
Akrone1c27f62018-07-20 11:42:59 +0200641 };
Akron479994e2018-07-02 13:21:44 +0200642});