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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');//" |
49 | return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');//" |
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(" "); |
198 | $('#real_title').html(" "); |
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 |