Subversion Repositories oidplus

Rev

Rev 506 | Rev 512 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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