Subversion Repositories oidplus

Rev

Rev 695 | Rev 704 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 695 Rev 699
1
/*
1
/*
2
 * OIDplus 2.0
2
 * OIDplus 2.0
3
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
3
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
4
 *
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
7
 * You may obtain a copy of the License at
8
 *
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
15
 * limitations under the License.
16
 */
16
 */
17
 
17
 
18
/*jshint esversion: 6 */
18
/*jshint esversion: 6 */
19
 
19
 
20
// $('#html').jstree();
20
// $('#html').jstree();
21
 
21
 
22
var current_node = "";
22
var current_node = "";
23
var popstate_running = false;
23
var popstate_running = false;
24
// DEFAULT_LANGUAGE will be set by oidplus.min.js.php
24
// DEFAULT_LANGUAGE will be set by oidplus.min.js.php
25
// language_messages will be set by oidplus.min.js.php
25
// language_messages will be set by oidplus.min.js.php
26
// language_tblprefix will be set by oidplus.min.js.php
26
// language_tblprefix will be set by oidplus.min.js.php
27
// csrf_token will be set by oidplus.min.js.php
27
// csrf_token will be set by oidplus.min.js.php
28
// samesite_policy will bet set by oidplus.min.js.php
28
// samesite_policy will bet set by oidplus.min.js.php
29
 
29
 
30
var pageChangeCallbacks = [];
30
var pageChangeCallbacks = [];
31
var pageChangeRequestCallbacks = [];
31
var pageChangeRequestCallbacks = [];
32
 
32
 
33
function isInternetExplorer() {
33
function isInternetExplorer() {
34
        // see also includes/functions.inc.php
34
        // see also includes/functions.inc.php
35
        var ua = window.navigator.userAgent;
35
        var ua = window.navigator.userAgent;
36
        return ((ua.indexOf("MSIE ") > 0) || (ua.indexOf("Trident/") > 0));
36
        return ((ua.indexOf("MSIE ") > 0) || (ua.indexOf("Trident/") > 0));
37
}
37
}
38
 
38
 
39
String.prototype.explode = function (separator, limit) {
39
String.prototype.explode = function (separator, limit) {
40
        // https://stackoverflow.com/questions/4514323/javascript-equivalent-to-php-explode
40
        // https://stackoverflow.com/questions/4514323/javascript-equivalent-to-php-explode
41
        const array = this.split(separator);
41
        const array = this.split(separator);
42
        if (limit !== undefined && array.length >= limit) {
42
        if (limit !== undefined && array.length >= limit) {
43
                array.push(array.splice(limit - 1).join(separator));
43
                array.push(array.splice(limit - 1).join(separator));
44
        }
44
        }
45
        return array;
45
        return array;
46
};
46
};
47
 
47
 
48
String.prototype.htmlentities = function () {
48
String.prototype.htmlentities = function () {
49
        return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');//"
49
        return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');//"
50
};
50
};
51
 
51
 
52
String.prototype.html_entity_decode = function () {
52
String.prototype.html_entity_decode = function () {
53
        return $('<textarea />').html(this).text();
53
        return $('<textarea />').html(this).text();
54
};
54
};
55
 
55
 
56
function getMeta(metaName) {
56
function getMeta(metaName) {
57
        const metas = $('meta[name='+metaName+']');
57
        const metas = $('meta[name='+metaName+']');
58
        return (metas.length == 0) ? '' : metas[0].content;
58
        return (metas.length == 0) ? '' : metas[0].content;
59
}
59
}
60
 
60
 
61
function getOidPlusSystemTitle() {
61
function getOidPlusSystemTitle() {
62
        return getMeta('OIDplus-SystemTitle'); // do not translate
62
        return getMeta('OIDplus-SystemTitle'); // do not translate
63
}
63
}
64
 
64
 
65
function combine_systemtitle_and_pagetitle(systemtitle, pagetitle) {
65
function combine_systemtitle_and_pagetitle(systemtitle, pagetitle) {
66
        // Please also change the function in index.php
66
        // Please also change the function in index.php
67
        if (systemtitle == pagetitle) {
67
        if (systemtitle == pagetitle) {
68
                return systemtitle;
68
                return systemtitle;
69
        } else {
69
        } else {
70
                return pagetitle + ' - ' + systemtitle;
70
                return pagetitle + ' - ' + systemtitle;
71
        }
71
        }
72
}
72
}
73
 
73
 
74
function getSystemUrl(relative) {
74
function getSystemUrl(relative) {
75
        var url = new URL(window.location.href);
75
        var url = new URL(window.location.href);
76
        if (relative) {
76
        if (relative) {
77
                return url.pathname;
77
                return url.pathname;
78
        } else {
78
        } else {
79
                return url.href.substr(0, url.href.length-url.search.length);
79
                return url.href.substr(0, url.href.length-url.search.length);
80
        }
80
        }
81
}
81
}
82
 
82
 
83
function getTreeLoadURL() {
83
function getTreeLoadURL() {
84
        var url = new URL(window.location.href);
84
        var url = new URL(window.location.href);
85
        var goto = url.searchParams.get("goto");
85
        var goto = url.searchParams.get("goto");
86
        return (goto != null) ? "ajax.php?csrf_token="+encodeURIComponent(csrf_token)+"&action=tree_load&anticache="+Date.now()+"&goto="+encodeURIComponent(goto)
86
        return (goto != null) ? "ajax.php?csrf_token="+encodeURIComponent(csrf_token)+"&action=tree_load&anticache="+Date.now()+"&goto="+encodeURIComponent(goto)
87
                              : "ajax.php?csrf_token="+encodeURIComponent(csrf_token)+"&action=tree_load&anticache="+Date.now();
87
                              : "ajax.php?csrf_token="+encodeURIComponent(csrf_token)+"&action=tree_load&anticache="+Date.now();
88
}
88
}
89
 
89
 
90
function reloadContent() {
90
function reloadContent() {
91
        // window.location.href = "?goto="+encodeURIComponent(current_node);
91
        // window.location.href = "?goto="+encodeURIComponent(current_node);
92
        if (openOidInPanel(current_node, false)) {
92
        if (openOidInPanel(current_node, false)) {
93
                if(!$('#oidtree').jstree(true).get_node(current_node)) {
93
                if(!$('#oidtree').jstree(true).get_node(current_node)) {
94
                        // Avoid that a language change at "oidplus:srvreg_status" won't redirect the user to "oidplus:srv_registration" because of the reselection during refresh
94
                        // Avoid that a language change at "oidplus:srvreg_status" won't redirect the user to "oidplus:srv_registration" because of the reselection during refresh
95
                        $('#oidtree').jstree("deselect_all");
95
                        $('#oidtree').jstree("deselect_all");
96
                }
96
                }
97
 
97
 
98
                $('#oidtree').jstree("refresh");
98
                $('#oidtree').jstree("refresh");
99
        }
99
        }
100
}
100
}
101
 
101
 
102
function x_rec(x_data, i) {
102
function x_rec(x_data, i) {
103
        $('#oidtree').jstree('open_node', x_data[i], function(e, data) {
103
        $('#oidtree').jstree('open_node', x_data[i], function(e, data) {
104
                if (i+1 < x_data.length) {
104
                if (i+1 < x_data.length) {
105
                        x_rec(x_data, i+1);
105
                        x_rec(x_data, i+1);
106
                } else {
106
                } else {
107
                        popstate_running = true; // don't call openOidInPanel again
107
                        popstate_running = true; // don't call openOidInPanel again
108
                        try {
108
                        try {
109
                                $('#oidtree').jstree('select_node', x_data[i]);
109
                                $('#oidtree').jstree('select_node', x_data[i]);
110
                        } catch (err) {
110
                        } catch (err) {
111
                                popstate_running = false;
111
                                popstate_running = false;
112
                        } finally {
112
                        } finally {
113
                                popstate_running = false;
113
                                popstate_running = false;
114
                        }
114
                        }
115
                }
115
                }
116
        });
116
        });
117
}
117
}
118
 
118
 
119
function performCloseQueryCB() {
119
function performCloseQueryCB() {
120
        for (var i=0; i<pageChangeRequestCallbacks.length; i++) {
120
        for (var i=0; i<pageChangeRequestCallbacks.length; i++) {
121
                if (!pageChangeRequestCallbacks[i][0](pageChangeRequestCallbacks[i][1])) return false;
121
                if (!pageChangeRequestCallbacks[i][0](pageChangeRequestCallbacks[i][1])) return false;
122
        }
122
        }
123
        pageChangeRequestCallbacks = [];
123
        pageChangeRequestCallbacks = [];
124
        return true; // may close
124
        return true; // may close
125
}
125
}
126
 
126
 
127
function performCloseCB() {
127
function performCloseCB() {
128
        for (var i=0; i<pageChangeCallbacks.length; i++) {
128
        for (var i=0; i<pageChangeCallbacks.length; i++) {
129
                pageChangeCallbacks[i][0](pageChangeCallbacks[i][1]);
129
                pageChangeCallbacks[i][0](pageChangeCallbacks[i][1]);
130
        }
130
        }
131
        pageChangeCallbacks = [];
131
        pageChangeCallbacks = [];
132
}
132
}
133
 
133
 
134
function openOidInPanel(id, reselect/*=false*/, anchor/*=''*/, force/*=false*/) {
134
function openOidInPanel(id, reselect/*=false*/, anchor/*=''*/, force/*=false*/) {
135
        reselect = (typeof reselect === 'undefined') ? false : reselect;
135
        reselect = (typeof reselect === 'undefined') ? false : reselect; // do not translate
136
        anchor = (typeof anchor === 'undefined') ? '' : anchor;
136
        anchor = (typeof anchor === 'undefined') ? '' : anchor; // do not translate
137
        force = (typeof force === 'undefined') ? false : force;
137
        force = (typeof force === 'undefined') ? false : force; // do not translate
138
 
138
 
139
        var mayClose = performCloseQueryCB();
139
        var mayClose = performCloseQueryCB();
140
        if (!force && !mayClose) return false;
140
        if (!force && !mayClose) return false;
141
 
141
 
142
        performCloseCB();
142
        performCloseCB();
143
 
143
 
144
        if (reselect) {
144
        if (reselect) {
145
                $('#oidtree').jstree('deselect_all');
145
                $('#oidtree').jstree('deselect_all');
146
 
146
 
147
                popstate_running = true; // don't call openOidInPanel during tree selection
147
                popstate_running = true; // don't call openOidInPanel during tree selection
148
                try {
148
                try {
149
                        // If the node is already loaded in the tree, select it
149
                        // If the node is already loaded in the tree, select it
150
                        if (!$('#oidtree').jstree('select_node', id)) {
150
                        if (!$('#oidtree').jstree('select_node', id)) {
151
                                // If the node is not loaded, then we try to search it.
151
                                // If the node is not loaded, then we try to search it.
152
                                // If it can be found, then open all parent nodes and select the node
152
                                // If it can be found, then open all parent nodes and select the node
153
                                $.ajax({
153
                                $.ajax({
154
                                        url:"ajax.php",
154
                                        url:"ajax.php",
155
                                        method:"POST",
155
                                        method:"POST",
156
                                        beforeSend: function(jqXHR, settings) {
156
                                        beforeSend: function(jqXHR, settings) {
157
                                                $.xhrPool.abortAll();
157
                                                $.xhrPool.abortAll();
158
                                                $.xhrPool.add(jqXHR);
158
                                                $.xhrPool.add(jqXHR);
159
                                        },
159
                                        },
160
                                        complete: function(jqXHR, text) {
160
                                        complete: function(jqXHR, text) {
161
                                                $.xhrPool.remove(jqXHR);
161
                                                $.xhrPool.remove(jqXHR);
162
                                        },
162
                                        },
163
                                        data:{
163
                                        data:{
164
                                                csrf_token:csrf_token,
164
                                                csrf_token:csrf_token,
165
                                                action:"tree_search",
165
                                                action:"tree_search",
166
                                                search:id,
166
                                                search:id,
167
                                                anticache:Date.now()
167
                                                anticache:Date.now()
168
                                        },
168
                                        },
169
                                        error:function(jqXHR, textStatus, errorThrown) {
169
                                        error:function(jqXHR, textStatus, errorThrown) {
170
                                                if (errorThrown == "abort") return;
170
                                                if (errorThrown == "abort") return;
171
                                                console.error(_L("Error: %1",errorThrown));
171
                                                console.error(_L("Error: %1",errorThrown));
172
                                        },
172
                                        },
173
                                        success:function(data) {
173
                                        success:function(data) {
174
                                                if ("error" in data) {
174
                                                if ("error" in data) {
175
                                                        console.error(data);
175
                                                        console.error(data);
176
                                                } else if ((data instanceof Array) && (data.length > 0)) {
176
                                                } else if ((data instanceof Array) && (data.length > 0)) {
177
                                                        x_rec(data, 0);
177
                                                        x_rec(data, 0);
178
                                                } else {
178
                                                } else {
179
                                                        console.error(data);
179
                                                        console.error(data);
180
                                                }
180
                                                }
181
                                        }
181
                                        }
182
                                });
182
                                });
183
                        }
183
                        }
184
                } catch (err) {
184
                } catch (err) {
185
                        popstate_running = false;
185
                        popstate_running = false;
186
                } finally {
186
                } finally {
187
                        popstate_running = false;
187
                        popstate_running = false;
188
                }
188
                }
189
        }
189
        }
190
 
190
 
191
        // This loads the actual content
191
        // This loads the actual content
192
 
192
 
193
        // document.title = ""; // <-- we may not do this, otherwise Firefox won't
193
        // document.title = ""; // <-- we may not do this, otherwise Firefox won't
194
        //                            show titles in the browser history (right-click
194
        //                            show titles in the browser history (right-click
195
        //                            on back-button), although document.title() is
195
        //                            on back-button), although document.title() is
196
        //                            set inside the AJAX-callback [Firefox bug?!]
196
        //                            set inside the AJAX-callback [Firefox bug?!]
197
 
197
 
198
        $('#real_title').html("&nbsp;");
198
        $('#real_title').html("&nbsp;");
199
        $('#real_content').html(_L("Loading..."));
199
        $('#real_content').html(_L("Loading..."));
200
        $('#static_link').attr("href", "index.php?goto="+encodeURIComponent(id));
200
        $('#static_link').attr("href", "index.php?goto="+encodeURIComponent(id));
201
        $("#gotoedit").val(id);
201
        $("#gotoedit").val(id);
202
 
202
 
203
        // Normal opening of a description
203
        // Normal opening of a description
204
        $.ajax({
204
        $.ajax({
205
                url:"ajax.php",
205
                url:"ajax.php",
206
                method:"GET",
206
                method:"GET",
207
                beforeSend: function(jqXHR, settings) {
207
                beforeSend: function(jqXHR, settings) {
208
                        $.xhrPool.abortAll();
208
                        $.xhrPool.abortAll();
209
                        $.xhrPool.add(jqXHR);
209
                        $.xhrPool.add(jqXHR);
210
                },
210
                },
211
                complete: function(jqXHR, text) {
211
                complete: function(jqXHR, text) {
212
                        $.xhrPool.remove(jqXHR);
212
                        $.xhrPool.remove(jqXHR);
213
                },
213
                },
214
                data:{
214
                data:{
215
                        csrf_token:csrf_token,
215
                        csrf_token:csrf_token,
216
                        action:"get_description",
216
                        action:"get_description",
217
                        id:id,
217
                        id:id,
218
                        anticache:Date.now()
218
                        anticache:Date.now()
219
                },
219
                },
220
                error:function(jqXHR, textStatus, errorThrown) {
220
                error:function(jqXHR, textStatus, errorThrown) {
221
                        if (errorThrown == "abort") return;
221
                        if (errorThrown == "abort") return;
222
                        alert(_L("Failed to load content: %1",errorThrown));
222
                        alert(_L("Failed to load content: %1",errorThrown));
223
                        console.error(_L("Error: %1",errorThrown));
223
                        console.error(_L("Error: %1",errorThrown));
224
                },
224
                },
225
                success:function(data) {
225
                success:function(data) {
226
                        if ("error" in data) {
226
                        if ("error" in data) {
227
                                alert(_L("Failed to load content: %1",data.error));
227
                                alert(_L("Failed to load content: %1",data.error));
228
                                console.error(data.error);
228
                                console.error(data.error);
229
                        } else if (data.status >= 0) {
229
                        } else if (data.status >= 0) {
230
                                data.id = id;
230
                                data.id = id;
231
 
231
 
232
                                var state = {
232
                                var state = {
233
                                        "node_id":id,
233
                                        "node_id":id,
234
                                        "titleHTML":(data.icon ? '<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' : '') + data.title.htmlentities(),
234
                                        "titleHTML":(data.icon ? '<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' : '') + data.title.htmlentities(),
235
                                        "textHTML":data.text,
235
                                        "textHTML":data.text,
236
                                        "staticlinkHREF":"index.php?goto="+encodeURIComponent(id),
236
                                        "staticlinkHREF":"index.php?goto="+encodeURIComponent(id),
237
                                };
237
                                };
238
                                if (current_node != id) {
238
                                if (current_node != id) {
239
                                        window.history.pushState(state, data.title, "?goto="+encodeURIComponent(id));
239
                                        window.history.pushState(state, data.title, "?goto="+encodeURIComponent(id));
240
                                } else {
240
                                } else {
241
                                        window.history.replaceState(state, data.title, "?goto="+encodeURIComponent(id));
241
                                        window.history.replaceState(state, data.title, "?goto="+encodeURIComponent(id));
242
                                }
242
                                }
243
 
243
 
244
                                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
244
                                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
245
 
245
 
246
                                if (data.icon) {
246
                                if (data.icon) {
247
                                        $('#real_title').html('<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' + data.title.htmlentities());
247
                                        $('#real_title').html('<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' + data.title.htmlentities());
248
                                } else {
248
                                } else {
249
                                        $('#real_title').html(data.title.htmlentities());
249
                                        $('#real_title').html(data.title.htmlentities());
250
                                }
250
                                }
251
                                $('#real_content').html(data.text);
251
                                $('#real_content').html(data.text);
252
                                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
252
                                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
253
                                current_node = id;
253
                                current_node = id;
254
 
254
 
255
                                if (anchor != '') {
255
                                if (anchor != '') {
256
                                        jumpToAnchor(anchor);
256
                                        jumpToAnchor(anchor);
257
                                }
257
                                }
258
                        } else {
258
                        } else {
259
                                alert(_L("Failed to load content: %1",data.status));
259
                                alert(_L("Failed to load content: %1",data.status));
260
                                console.error(data);
260
                                console.error(data);
261
                        }
261
                        }
262
                }
262
                }
263
        });
263
        });
264
 
264
 
265
        return true;
265
        return true;
266
}
266
}
267
 
267
 
268
// This function opens the "parentID" node, and then selects the "childID" node (which should be beneath the parent node)
268
// This function opens the "parentID" node, and then selects the "childID" node (which should be beneath the parent node)
269
function openAndSelectNode(childID, parentID) {
269
function openAndSelectNode(childID, parentID) {
270
        if ($('#oidtree').jstree(true).get_node(parentID)) {
270
        if ($('#oidtree').jstree(true).get_node(parentID)) {
271
                $('#oidtree').jstree('open_node', parentID, function(e, data) { // open parent node
271
                $('#oidtree').jstree('open_node', parentID, function(e, data) { // open parent node
272
                        if ($('#oidtree').jstree(true).get_node(childID)) { // is the child there?
272
                        if ($('#oidtree').jstree(true).get_node(childID)) { // is the child there?
273
                                $('#oidtree').jstree('deselect_all').jstree('select_node', childID); // select it
273
                                $('#oidtree').jstree('deselect_all').jstree('select_node', childID); // select it
274
                        } else {
274
                        } else {
275
                                // This can happen if the content page contains brand new items which are not in the treeview yet
275
                                // This can happen if the content page contains brand new items which are not in the treeview yet
276
                                $("#gotoedit").val(childID);
276
                                $("#gotoedit").val(childID);
277
                                window.location.href = "?goto="+encodeURIComponent(childID);
277
                                window.location.href = "?goto="+encodeURIComponent(childID);
278
                        }
278
                        }
279
                }, true);
279
                }, true);
280
        } else {
280
        } else {
281
                // This should usually not happen
281
                // This should usually not happen
282
                $("#gotoedit").val(childID);
282
                $("#gotoedit").val(childID);
283
                window.location.href = "?goto="+encodeURIComponent(childID);
283
                window.location.href = "?goto="+encodeURIComponent(childID);
284
        }
284
        }
285
}
285
}
286
 
286
 
287
$(window).on("popstate", function(e) {
287
$(window).on("popstate", function(e) {
288
        if (!performCloseQueryCB()) {
288
        if (!performCloseQueryCB()) {
289
                // TODO: does not work!!! The "back/forward" action will be cancelled, but the browser still thinks it was successful,
289
                // TODO: does not work!!! The "back/forward" action will be cancelled, but the browser still thinks it was successful,
290
                // so if you do it again, you will then jump 2 pages back, etc!
290
                // so if you do it again, you will then jump 2 pages back, etc!
291
                // This does also not help:
291
                // This does also not help:
292
                //window.history.pushState(e.originalEvent.state, e.originalEvent.title, e.originalEvent.url);
292
                //window.history.pushState(e.originalEvent.state, e.originalEvent.title, e.originalEvent.url);
293
                //window.history.forward();
293
                //window.history.forward();
294
                return;
294
                return;
295
        }
295
        }
296
 
296
 
297
        popstate_running = true;
297
        popstate_running = true;
298
        try {
298
        try {
299
                var data = e.originalEvent.state;
299
                var data = e.originalEvent.state;
300
 
300
 
301
                current_node = data.node_id;
301
                current_node = data.node_id;
302
                $("#gotoedit").val(current_node);
302
                $("#gotoedit").val(current_node);
303
                $('#oidtree').jstree('deselect_all').jstree('select_node', data.node_id);
303
                $('#oidtree').jstree('deselect_all').jstree('select_node', data.node_id);
304
                $('#real_title').html(data.titleHTML);
304
                $('#real_title').html(data.titleHTML);
305
                $('#real_content').html(data.textHTML);
305
                $('#real_content').html(data.textHTML);
306
                $('#static_link').attr("href", data.staticlinkHREF);
306
                $('#static_link').attr("href", data.staticlinkHREF);
307
                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.titleHTML.html_entity_decode());
307
                document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.titleHTML.html_entity_decode());
308
        } catch (err) {
308
        } catch (err) {
309
                popstate_running = false;
309
                popstate_running = false;
310
        } finally {
310
        } finally {
311
                popstate_running = false;
311
                popstate_running = false;
312
        }
312
        }
313
});
313
});
314
 
314
 
315
$(document).ready(function () {
315
$(document).ready(function () {
316
        /*
316
        /*
317
        window.onbeforeunload = function(e) {
317
        window.onbeforeunload = function(e) {
318
                // TODO: This won't be called because TinyMCE overrides it??
318
                // TODO: This won't be called because TinyMCE overrides it??
319
                // TODO: when the user accepted the query in performCloseQueryCB(), then the message will be shown again by the browser!
319
                // TODO: when the user accepted the query in performCloseQueryCB(), then the message will be shown again by the browser!
320
                if (!performCloseQueryCB()) {
320
                if (!performCloseQueryCB()) {
321
                        // Cancel the event
321
                        // Cancel the event
322
                        e.preventDefault(); // If you prevent default behavior in Mozilla Firefox prompt will always be shown
322
                        e.preventDefault(); // If you prevent default behavior in Mozilla Firefox prompt will always be shown
323
                        // Chrome requires returnValue to be set
323
                        // Chrome requires returnValue to be set
324
                        e.returnValue = '';
324
                        e.returnValue = '';
325
                } else {
325
                } else {
326
                        // the absence of a returnValue property on the event will guarantee the browser unload happens
326
                        // the absence of a returnValue property on the event will guarantee the browser unload happens
327
                        delete e['returnValue'];
327
                        delete e['returnValue'];
328
                }
328
                }
329
        };
329
        };
330
        */
330
        */
331
 
331
 
332
        // --- JsTree
332
        // --- JsTree
333
 
333
 
334
        $('#oidtree')
334
        $('#oidtree')
335
        .jstree({
335
        .jstree({
336
                plugins: ['massload','search','conditionalselect'],
336
                plugins: ['massload','search','conditionalselect'],
337
                'core' : {
337
                'core' : {
338
                        'data' : {
338
                        'data' : {
339
                                "url" : getTreeLoadURL(),
339
                                "url" : getTreeLoadURL(),
340
                                "data" : function (node) {
340
                                "data" : function (node) {
341
                                        return { "id" : node.id };
341
                                        return { "id" : node.id };
342
                                }
342
                                }
343
                        },
343
                        },
344
                        "multiple": false
344
                        "multiple": false
345
                },
345
                },
346
                'conditionalselect' : function (node) {
346
                'conditionalselect' : function (node) {
347
                        if (node.original.conditionalselect !== undefined) {
347
                        if (node.original.conditionalselect !== undefined) {
348
                                return eval(node.original.conditionalselect);
348
                                return eval(node.original.conditionalselect);
349
                        } else {
349
                        } else {
350
                                return performCloseQueryCB();
350
                                return performCloseQueryCB();
351
                        }
351
                        }
352
                },
352
                },
353
        })
353
        })
354
        .on('ready.jstree', function (e, data) {
354
        .on('ready.jstree', function (e, data) {
355
                var url = new URL(window.location.href);
355
                var url = new URL(window.location.href);
356
                var goto = url.searchParams.get("goto");
356
                var goto = url.searchParams.get("goto");
357
                if (goto == null) goto = "oidplus:system"; // the page was not called with ?goto=...
357
                if (goto == null) goto = "oidplus:system"; // the page was not called with ?goto=...
358
                $("#gotoedit").val(goto);
358
                $("#gotoedit").val(goto);
359
 
359
 
360
                // By setting current_node, select_node() will not cause ajax.php?action=get_description to load (since we already loaded the first static content via PHP, for search engines mainly)
360
                // By setting current_node, select_node() will not cause ajax.php?action=get_description to load (since we already loaded the first static content via PHP, for search engines mainly)
361
                // But then we need to set the history state manually
361
                // But then we need to set the history state manually
362
                current_node = goto;
362
                current_node = goto;
363
                window.history.replaceState({
363
                window.history.replaceState({
364
                        "node_id":goto,
364
                        "node_id":goto,
365
                        "titleHTML":$('#real_title').html(),
365
                        "titleHTML":$('#real_title').html(),
366
                        "textHTML":$('#real_content').html(),
366
                        "textHTML":$('#real_content').html(),
367
                        "staticlinkHREF":"index.php?goto="+encodeURIComponent(goto),
367
                        "staticlinkHREF":"index.php?goto="+encodeURIComponent(goto),
368
                }, $('#real_title').html(), "?goto="+encodeURIComponent(goto));
368
                }, $('#real_title').html(), "?goto="+encodeURIComponent(goto));
369
 
369
 
370
                if (goto != null) data.instance.select_node([goto]);
370
                if (goto != null) data.instance.select_node([goto]);
371
 
371
 
372
                setTimeout(glayoutWorkaroundAC, 100);
372
                setTimeout(glayoutWorkaroundAC, 100);
373
                setTimeout(glayoutWorkaroundB, 100);
373
                setTimeout(glayoutWorkaroundB, 100);
374
        })
374
        })
375
        .on('select_node.jstree', function (node, selected, event) {
375
        .on('select_node.jstree', function (node, selected, event) {
376
                mobileNavClose();
376
                mobileNavClose();
377
 
377
 
378
                var id = selected.node.id;
378
                var id = selected.node.id;
379
                if ((!popstate_running) && (current_node != id)) {
379
                if ((!popstate_running) && (current_node != id)) {
380
                        // 4th argument: we force the reload (because in the
380
                        // 4th argument: we force the reload (because in the
381
                        // conditional select above, we already asked if
381
                        // conditional select above, we already asked if
382
                        // tinyMCE needs to be saved)
382
                        // tinyMCE needs to be saved)
383
                        openOidInPanel(id, false, '', true);
383
                        openOidInPanel(id, false, '', true);
384
                }
384
                }
385
        });
385
        });
386
 
386
 
387
        // --- Layout
387
        // --- Layout
388
 
388
 
389
        $("#system_title_menu")[0].style.display = "block";
389
        $("#system_title_menu")[0].style.display = "block";
390
 
390
 
391
        var tmpObjectTree = _L("OBJECT TREE").replace(/(.{1})/g,"$1<br>");
391
        var tmpObjectTree = _L("OBJECT TREE").replace(/(.{1})/g,"$1<br>");
392
        tmpObjectTree = tmpObjectTree.substring(0, tmpObjectTree.length-"<br>".length);
392
        tmpObjectTree = tmpObjectTree.substring(0, tmpObjectTree.length-"<br>".length);
393
 
393
 
394
        $('#oidtree').addClass('ui-layout-west');
394
        $('#oidtree').addClass('ui-layout-west');
395
        $('#content_window').addClass('ui-layout-center');
395
        $('#content_window').addClass('ui-layout-center');
396
        $('#system_title_bar').addClass('ui-layout-north');
396
        $('#system_title_bar').addClass('ui-layout-north');
397
        glayout = $('#frames').layout({
397
        glayout = $('#frames').layout({
398
                north__size:                  40,
398
                north__size:                  40,
399
                north__slidable:              false,
399
                north__slidable:              false,
400
                north__closable:              false,
400
                north__closable:              false,
401
                north__resizable:             false,
401
                north__resizable:             false,
402
                west__size:                   450,
402
                west__size:                   450,
403
                west__spacing_closed:         20,
403
                west__spacing_closed:         20,
404
                west__togglerLength_closed:   230,
404
                west__togglerLength_closed:   230,
405
                west__togglerAlign_closed:    "center", // TODO: does not work! The text "OBJECT TREE" is still at the top and nearly cut off by the title bar!!!
405
                west__togglerAlign_closed:    "center", // TODO: does not work! The text "OBJECT TREE" is still at the top and nearly cut off by the title bar!!!
406
                west__togglerContent_closed:  tmpObjectTree,
406
                west__togglerContent_closed:  tmpObjectTree,
407
                west__togglerTip_closed:      _L("Open & Pin Menu"),
407
                west__togglerTip_closed:      _L("Open & Pin Menu"),
408
                west__sliderTip:              _L("Slide Open Menu"),
408
                west__sliderTip:              _L("Slide Open Menu"),
409
                west__slideTrigger_open:      "mouseover",
409
                west__slideTrigger_open:      "mouseover",
410
                center__maskContents:         true // IMPORTANT - enable iframe masking
410
                center__maskContents:         true // IMPORTANT - enable iframe masking
411
        });
411
        });
412
 
412
 
413
        $("#gotobox").addClass("mobilehidden");
413
        $("#gotobox").addClass("mobilehidden");
414
        $("#languageBox").addClass("mobilehidden");
414
        $("#languageBox").addClass("mobilehidden");
415
        $("#gotobox")[0].style.display = "block";
415
        $("#gotobox")[0].style.display = "block";
416
        $('#gotoedit').keypress(function(event) {
416
        $('#gotoedit').keypress(function(event) {
417
                var keycode = (event.keyCode ? event.keyCode : event.which);
417
                var keycode = (event.keyCode ? event.keyCode : event.which);
418
                if (keycode == '13') {
418
                if (keycode == '13') {
419
                        gotoButtonClicked();
419
                        gotoButtonClicked();
420
                }
420
                }
421
        });
421
        });
422
});
422
});
423
 
423
 
424
function glayoutWorkaroundAC() {
424
function glayoutWorkaroundAC() {
425
        // "Bug A": Sometimes, the design is completely destroyed after reloading the page. It does not help when glayout.resizeAll()
425
        // "Bug A": Sometimes, the design is completely destroyed after reloading the page. It does not help when glayout.resizeAll()
426
        //          is called at the beginning (e.g. during the ready function), and it does not help if we wait 500ms.
426
        //          is called at the beginning (e.g. during the ready function), and it does not help if we wait 500ms.
427
        //          So we do it all the time. It has probably something to do with slow loading times, since the error
427
        //          So we do it all the time. It has probably something to do with slow loading times, since the error
428
        //          does only appear when the page is "blank" for a short while while it is loading.
428
        //          does only appear when the page is "blank" for a short while while it is loading.
429
        glayout.resizeAll();
429
        glayout.resizeAll();
430
 
430
 
431
        // "Bug C": With Firefox (And sometimes with Chrome), there is a gap between the content-window (including scroll bars)
431
        // "Bug C": With Firefox (And sometimes with Chrome), there is a gap between the content-window (including scroll bars)
432
        //          and the right corner of the screen. Removing the explicit width solves this problem.
432
        //          and the right corner of the screen. Removing the explicit width solves this problem.
433
        $("#content_window")[0].style.removeProperty("width");
433
        $("#content_window")[0].style.removeProperty("width");
434
 
434
 
435
        setTimeout(glayoutWorkaroundAC, 100);
435
        setTimeout(glayoutWorkaroundAC, 100);
436
}
436
}
437
 
437
 
438
function glayoutWorkaroundB() {
438
function glayoutWorkaroundB() {
439
        // "Bug B": Sometimes, after reload, weird space between oidtree and content window, because oidtree has size of 438px
439
        // "Bug B": Sometimes, after reload, weird space between oidtree and content window, because oidtree has size of 438px
440
        $("#oidtree")[0].style.width = "450px";
440
        $("#oidtree")[0].style.width = "450px";
441
}
441
}
442
 
442
 
443
function mobileNavClose() {
443
function mobileNavClose() {
444
        if ($("#system_title_menu").is(":hidden")) {
444
        if ($("#system_title_menu").is(":hidden")) {
445
                return;
445
                return;
446
        }
446
        }
447
 
447
 
448
        $("#oidtree").slideUp("medium").promise().done(function() {
448
        $("#oidtree").slideUp("medium").promise().done(function() {
449
                $("#oidtree").addClass("ui-layout-west");
449
                $("#oidtree").addClass("ui-layout-west");
450
                $("#oidtree").show();
450
                $("#oidtree").show();
451
//              $("#gotobox").hide();
451
//              $("#gotobox").hide();
452
//              $("#languageBox").hide();
452
//              $("#languageBox").hide();
453
                $("#gotobox").addClass("mobilehidden");
453
                $("#gotobox").addClass("mobilehidden");
454
                $("#languageBox").addClass("mobilehidden");
454
                $("#languageBox").addClass("mobilehidden");
455
        });
455
        });
456
        $("#system_title_menu").removeClass("active");
456
        $("#system_title_menu").removeClass("active");
457
}
457
}
458
 
458
 
459
function mobileNavOpen() {
459
function mobileNavOpen() {
460
        $("#oidtree").hide();
460
        $("#oidtree").hide();
461
        $("#oidtree").removeClass("ui-layout-west");
461
        $("#oidtree").removeClass("ui-layout-west");
462
        $("#oidtree").slideDown("medium");
462
        $("#oidtree").slideDown("medium");
463
//      $("#gotobox").show();
463
//      $("#gotobox").show();
464
//      $("#languageBox").show();
464
//      $("#languageBox").show();
465
        $("#gotobox").removeClass("mobilehidden");
465
        $("#gotobox").removeClass("mobilehidden");
466
        $("#languageBox").removeClass("mobilehidden");
466
        $("#languageBox").removeClass("mobilehidden");
467
        $("#system_title_menu").addClass("active");
467
        $("#system_title_menu").addClass("active");
468
}
468
}
469
 
469
 
470
function mobileNavButtonClick(sender) {
470
function mobileNavButtonClick(sender) {
471
        if ($("#oidtree").hasClass("ui-layout-west")) {
471
        if ($("#oidtree").hasClass("ui-layout-west")) {
472
                mobileNavOpen();
472
                mobileNavOpen();
473
        } else {
473
        } else {
474
                mobileNavClose();
474
                mobileNavClose();
475
        }
475
        }
476
}
476
}
477
 
477
 
478
function mobileNavButtonHover(sender) {
478
function mobileNavButtonHover(sender) {
479
        sender.classList.toggle("hover");
479
        sender.classList.toggle("hover");
480
}
480
}
481
 
481
 
482
function gotoButtonClicked() {
482
function gotoButtonClicked() {
483
        openOidInPanel($("#gotoedit").val(), 1);
483
        openOidInPanel($("#gotoedit").val(), 1);
484
}
484
}
485
 
485
 
486
function jumpToAnchor(anchor) {
486
function jumpToAnchor(anchor) {
487
        window.location.href = "#" + anchor;
487
        window.location.href = "#" + anchor;
488
}
488
}
489
 
489
 
490
function getCookie(cname) {
490
function getCookie(cname) {
491
        // Source: https://www.w3schools.com/js/js_cookies.asp
491
        // Source: https://www.w3schools.com/js/js_cookies.asp
492
        var name = cname + "=";
492
        var name = cname + "=";
493
        var decodedCookie = decodeURIComponent(document.cookie);
493
        var decodedCookie = decodeURIComponent(document.cookie);
494
        var ca = decodedCookie.split(';');
494
        var ca = decodedCookie.split(';');
495
        for(var i = 0; i <ca.length; i++) {
495
        for(var i = 0; i <ca.length; i++) {
496
                var c = ca[i];
496
                var c = ca[i];
497
                while (c.charAt(0) == ' ') {
497
                while (c.charAt(0) == ' ') {
498
                        c = c.substring(1);
498
                        c = c.substring(1);
499
                }
499
                }
500
                if (c.indexOf(name) == 0) {
500
                if (c.indexOf(name) == 0) {
501
                        return c.substring(name.length, c.length);
501
                        return c.substring(name.length, c.length);
502
                }
502
                }
503
        }
503
        }
504
        return undefined;
504
        return undefined;
505
}
505
}
506
 
506
 
507
function setCookie(cname, cvalue, exdays, path) {
507
function setCookie(cname, cvalue, exdays, path) {
508
        var d = new Date();
508
        var d = new Date();
509
        d.setTime(d.getTime() + (exdays*24*60*60*1000));
509
        d.setTime(d.getTime() + (exdays*24*60*60*1000));
510
        var expires = exdays == 0 ? "" : "; expires="+d.toUTCString();
510
        var expires = exdays == 0 ? "" : "; expires="+d.toUTCString();
511
        document.cookie = cname + "=" + cvalue + expires + ";path=" + path + ";SameSite=" + samesite_policy;
511
        document.cookie = cname + "=" + cvalue + expires + ";path=" + path + ";SameSite=" + samesite_policy;
512
}
512
}
513
 
513
 
514
function setLanguage(lngid) {
514
function setLanguage(lngid) {
515
        setCookie('LANGUAGE', lngid, 0/*Until browser closes*/, location.pathname);
515
        setCookie('LANGUAGE', lngid, 0/*Until browser closes*/, location.pathname);
516
 
516
 
517
        $(".lng_flag").each(function(){
517
        $(".lng_flag").each(function(){
518
                $(this).addClass("picture_ghost");
518
                $(this).addClass("picture_ghost");
519
        });
519
        });
520
        $("#lng_flag_"+$.escapeSelector(lngid)).removeClass("picture_ghost");
520
        $("#lng_flag_"+$.escapeSelector(lngid)).removeClass("picture_ghost");
521
 
521
 
522
        if (isInternetExplorer()) {
522
        if (isInternetExplorer()) {
523
                // Internet Explorer has problems with sending new cookies to new AJAX requests, so we reload the page completely
523
                // Internet Explorer has problems with sending new cookies to new AJAX requests, so we reload the page completely
524
                window.location.reload();
524
                window.location.reload();
525
        } else {
525
        } else {
526
                // TODO: Small detail: The "Go" button also needs to be re-translated
526
                // TODO: Small detail: The "Go" button also needs to be re-translated
527
                reloadContent();
527
                reloadContent();
528
                mobileNavClose();
528
                mobileNavClose();
529
        }
529
        }
530
}
530
}
531
 
531
 
532
function getCurrentLang() {
532
function getCurrentLang() {
533
        // Note: If the argument "?lang=" is used, PHP will automatically set a Cookie, so it is OK when we only check for the cookie
533
        // Note: If the argument "?lang=" is used, PHP will automatically set a Cookie, so it is OK when we only check for the cookie
534
        var lang = getCookie('LANGUAGE');
534
        var lang = getCookie('LANGUAGE'); // do not translate
535
        return (typeof lang != "undefined") ? lang : DEFAULT_LANGUAGE;
535
        return (typeof lang != 'undefined') ? lang : DEFAULT_LANGUAGE; // do not translate
536
}
536
}
537
 
537
 
538
function _L() {
538
function _L() {
539
        var args = Array.prototype.slice.call(arguments);
539
        var args = Array.prototype.slice.call(arguments);
540
        var str = args.shift().trim();
540
        var str = args.shift().trim();
541
 
541
 
542
        var tmp = "";
542
        var tmp = "";
543
        if (typeof language_messages[getCurrentLang()] == "undefined") {
543
        if (typeof language_messages[getCurrentLang()] == 'undefined') { // do not translate
544
                tmp = str;
544
                tmp = str;
545
        } else {
545
        } else {
546
                var msg = language_messages[getCurrentLang()][str];
546
                var msg = language_messages[getCurrentLang()][str];
547
                if (typeof msg != "undefined") {
547
                if (typeof msg != 'undefined') { // do not translate
548
                        tmp = msg;
548
                        tmp = msg;
549
                } else {
549
                } else {
550
                        tmp = str;
550
                        tmp = str;
551
                }
551
                }
552
        }
552
        }
553
 
553
 
554
        tmp = tmp.replace('###', language_tblprefix);
554
        tmp = tmp.replace('###', language_tblprefix);
555
 
555
 
556
        var n = 1;
556
        var n = 1;
557
        while (args.length > 0) {
557
        while (args.length > 0) {
558
                var val = args.shift();
558
                var val = args.shift();
559
                tmp = tmp.replace("%"+n, val);
559
                tmp = tmp.replace("%"+n, val);
560
                n++;
560
                n++;
561
        }
561
        }
562
 
562
 
563
        tmp = tmp.replace("%%", "%");
563
        tmp = tmp.replace("%%", "%");
564
 
564
 
565
        return tmp;
565
        return tmp;
566
}
566
}
567
 
567
 
568
function show_waiting_anim() {
568
function show_waiting_anim() {
569
        $("#loading").show();
569
        $("#loading").show();
570
}
570
}
571
 
571
 
572
function hide_waiting_anim() {
572
function hide_waiting_anim() {
573
        $("#loading").hide();
573
        $("#loading").hide();
574
}
574
}
575
 
575
 
576
/* Mini-framework to abort all AJAX requests if a new request is made */
576
/* Mini-framework to abort all AJAX requests if a new request is made */
577
 
577
 
578
$.xhrPool = [];
578
$.xhrPool = [];
579
$.xhrPool.add = function(jqXHR) {
579
$.xhrPool.add = function(jqXHR) {
580
        $.xhrPool.push(jqXHR);
580
        $.xhrPool.push(jqXHR);
581
}
581
}
582
$.xhrPool.remove = function(jqXHR) {
582
$.xhrPool.remove = function(jqXHR) {
583
        var index = $.xhrPool.indexOf(jqXHR);
583
        var index = $.xhrPool.indexOf(jqXHR);
584
        if (index > -1) {
584
        if (index > -1) {
585
                $.xhrPool.splice(index, 1);
585
                $.xhrPool.splice(index, 1);
586
        }
586
        }
587
};
587
};
588
$.xhrPool.abortAll = function() {
588
$.xhrPool.abortAll = function() {
589
        var calls = Array.from($.xhrPool);
589
        var calls = Array.from($.xhrPool);
590
        $.each(calls, function(key, value) {
590
        $.each(calls, function(key, value) {
591
                value.abort();
591
                value.abort();
592
        });
592
        });
593
}
593
}
594
 
594