PageSourceSearch

https://nets4u.de/tmp/assets/12764250/em_javascript.js

js nets4u.de collected 2026-10-07 14:28:21 UTC 141,000 bytes, 3,759 lines download raw bytes

1/*
2 * This file is part of LimeSurvey
3 * Copyright (C) 2007-2018 The LimeSurvey Project Team / Carsten Schmitz
4 * All rights reserved.
5 * License: GNU/GPL License v2 or later, see LICENSE.php
6 * LimeSurvey is free software. This version may have been modified pursuant
7 * to the GNU General Public License, and as distributed it includes or
8 * is derivative of works licensed under the GNU General Public License or
9 * other free or open source software licenses.
10 * See COPYRIGHT.php for copyright notices and details.
11 *
12 * Core JavaScript functions needed by ExpressionManager
13 * @author Thomas M. White (TMSWhite)
14 * @author Denis Chenu (Shnoulle)
15 *
16 * Portion from php.js licensed under the MIT licenses.
17 * Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io)
18 * and Contributors (http://phpjs.org/authors)
19 */
20/**
21 * checkconditions : javascript function attach to some element
22 * Launch ExprMgr_process_relevance_and_tailoring with good value
23 * @todo : move this directly to event
24 */
25function checkconditions(value, name, type, evt_type)
26{
27    if (typeof evt_type === 'undefined')
28    {
29        evt_type = 'onchange';
30    }
31    if (type == 'radio' || type == 'select-one')
32    {
33        $('#java'+name).val(value);
34    }
35    else if (type == 'checkbox')
36    {
37        if ($('#answer'+name).is(':checked'))
38        {
39            $('#java'+name).val('Y');
40        } else
41        {
42            $('#java'+name).val('');
43        }
44    }
45    else if (type == 'text' && name.match(/other$/))
46    {
47        $('#java'+name).val(value);
48    }
49
50    aQuestionsWithDependencies = $('#aQuestionsWithDependencies').data('qids');
51
52    var questionCode;
53    if(typeof name !== 'undefined') {
54        var parts = name.split('X');
55        questionCode = parts[2];
56        var LEMvarNameAttr = LEMvarNameAttr || {};
57        if (LEMvarNameAttr['java' + name] != undefined) {
58            questionCode = '' + LEMvarNameAttr['java' + name].qid;
59        }
60    }
61
62    /*
63    // STILL NOT WORKING !!!!!
64    // But we're getting closer... 
65    var $isRelevant = $.inArray(questionCode, aQuestionsWithDependencies);// NEED TO ADD THE QUESTIONS WITH CONDITIONS BEFORE WE CAN USE IT !!!!
66    if($.isFunction(window.ExprMgr_process_relevance_and_tailoring ) && $isRelevant!=-1) {
67        ExprMgr_process_relevance_and_tailoring(evt_type,name,type);
68    }*/
69    try{
70        ExprMgr_process_relevance_and_tailoring(evt_type,name,type);
71    } catch(e) {
72        console.ls.error(e);
73    }
74}
75
76/**
77 * fixnum_checkconditions : javascript function attach to some element
78 * Update the answer of the user to be numeric and launch checkconditions
79 *
80 * Also checks if any of the arrow keys is pressed to avoid unecessary hassle.
81 * @todo : move this directly to event
82 */
83function fixnum_checkconditions(value, name, type, evt_type, intonly)
84{
85    /* Do we have to check if LSvar is set and is valid ? */
86    if(window.event){
87        var keyPressed =  window.event.keyCode || 0;
88        if(
89                keyPressed == 37 //left arrow
90            ||  keyPressed == 39 //right arrow
91        ){return false; }
92    }
93
94    var decimalValue;
95    var newval = new String(value);
96    var checkNumericRegex = new RegExp(/^(-)?[0-9]*(,|\.|)[0-9]*$/);
97    var cleansedValue = newval.replace(numRegex,'');
98    /**
99    * If have to use parsed value.
100    */
101    if(!LSvar.bNumRealValue)
102    {
103        if(checkNumericRegex.test(value)) {
104            try{
105                decimalValue = new Decimal(cleansedValue);
106            } catch(e){
107                try{
108                    decimalValue = new Decimal(cleansedValue.replace(',','.'));
109                } catch(e){
110                    decimalValue = new Decimal(NaN);
111                }
112            }
113
114            if (typeof intonly !=='undefined' && intonly==1) {
115                newval = decimalValue.trunc();
116            }
117        } else {
118            newval = cleansedValue;
119        }
120
121    }
122
123    /**
124     * If have to fix numbers automatically.
125     */
126    if(LSvar.bFixNumAuto)
127    {
128        // If the field value needs to be auto-corrected, we will do it with a timeout in order to avoid
129        // interfering with the user's typing. We will use window.correctNumberField to track one timer
130        // per field, and clear it when the user starts typing again.
131
132        if (typeof window.correctNumberField === 'undefined') {
133            window.correctNumberField = {};
134        }
135
136        if (window.correctNumberField[name] != null) {
137            clearTimeout(window.correctNumberField[name]);
138            window.correctNumberField[name] = null;
139        }
140
141        var addition = "";
142
143        var matchFollowingZeroes =  cleansedValue.match(/^-?([0-9])*(,|\.)(0+)$/); /* 1.0 : keep .0 */
144        var matchMustGetZeroes =  cleansedValue.match(/^-?([0-9])*(,|\.)([0-9]*)$/); /* Maybe have 0 */
145        if(matchFollowingZeroes){
146            addition = LEMradix+matchFollowingZeroes[3];
147        } else if(matchMustGetZeroes) {
148            /* Don‘t find good regexp … */
149            while (cleansedValue.substr(-1) === "0") {
150                addition += "0";
151                cleansedValue = cleansedValue.slice(0, -1);
152            }
153        }
154
155        if(decimalValue == undefined){
156            try{
157                decimalValue = new Decimal(cleansedValue);
158            } catch(e){
159                try{
160                    decimalValue = new Decimal(cleansedValue.replace(',','.'));
161                } catch(e){
162                    decimalValue = new Decimal(NaN);
163
164                }
165            }
166        }
167
168        /**
169         * Work on length of the number
170         * Avoid numbers longer than 20 characters before the decimal separator and 10 after the decimal separator.
171         */
172        // Treat decimal part, if there is one.
173        // Trim after 10th decimal if larger than 10 decimals.
174        if(decimalValue.dp()>10){
175            decimalValue.toDecimalPlaces(10);
176        }
177
178        /**
179         * Set display value
180         */
181        displayVal = decimalValue.toString();
182        if (displayVal=='NaN')
183        {
184            newval=displayVal;
185            if(cleansedValue == '' && value != cleansedValue) {
186                window.correctNumberField[name] = setTimeout(function(){$('#answer'+name).val(cleansedValue).trigger("keyup");}, 500);
187            }
188        }
189        else{
190            if(LEMradix==",")
191                displayVal = displayVal.replace(/\./,',');
192
193            newval = displayVal+addition
194
195            if (name.match(/other$/)) {
196                if($('#answer'+name+'text').val() != newval){
197                    $('#answer'+name+'text').val(newval);
198                }
199            }
200
201            if(value != newval){
202                window.correctNumberField[name] = setTimeout(function(){$('#answer'+name).val(newval).trigger("keyup");}, 1500);
203            }
204        }
205    }
206
207    /**
208     * Check conditions
209     */
210    if (typeof evt_type === 'undefined')
211    {
212        evt_type = 'onchange';
213    }
214    checkconditions(newval, name, type, evt_type);
215}
216/**
217 * Default event to trigger on answer part
218 * Launch function according to anser-item type
219 * @todo : checkconditions/fixnum_checkconditions in this function
220 **/
221/* text/number item */
222$(document).on("keyup change",".answer-item textarea:not([onkeyup]),.answer-item :text:not([onkeyup])",function(event){
223    if($(this).data("number"))// data-type ?
224    {
225        fixnum_checkconditions($(this).val(), $(this).attr('name'), 'text', 'keyup', $(this).data("integer"))
226    }
227    else
228    {
229        checkconditions($(this).val(), $(this).attr('name'), 'text', 'keyup')
230    }
231});
232/* select/dropdown item */
233$(document).on("change",".select-item select:not([onchange]),.ls-dropdown-item select:not([onchange]), select.list-question-select:not([onchange])",function(event){
234    checkconditions($(this).val(), $(this).attr('name'), 'select-one', 'change')
235});
236/* radio/button item */
237$(document).on("change",".radio-item :radio:not([onclick]), .button-item :radio:not([onclick]), .ls-button-radio",function(event){
238    checkconditions($(this).val(), $(this).attr('name'), 'radio', 'click')
239});
240/* checkbox item */
241$(document).on("change",".checkbox-item :checkbox:not([onclick]),.button-item :checkbox:not([onclick]), .ls-button-checkbox",function(event){
242    checkconditions($(this).val(), $(this).attr('name'), 'checkbox', 'click')
243});
244/* upload item */
245$(document).on("updated",".upload-item :hidden",function(event){
246    checkconditions($(this).val(), $(this).attr('name'), 'upload', 'updated')
247});
248/* equation item */
249$(document).on("updated",".hidden-item :hidden",function(event){
250    /* equation item must have a name */
251    if(!$(this).attr('name')) {
252        return;
253    }
254    checkconditions($(this).val(), $(this).attr('name'), 'equation', 'updated')
255});
256/* new multiple choice bootstrap buttons */
257$(document).on("change","input:checkbox.button-item.btn-check",function(event){
258    checkconditions($(this).val(), $(this).attr('name'), 'checkbox', 'click')
259});
260/* new singlechoice radio bootstrap buttons */
261$(document).on("change","input:radio.button-item.btn-check",function(event){
262    checkconditions($(this).val(), $(this).attr('name'), 'radio', 'click')
263});
264/**
265 * For number
266 */
267var pad = function(num,places) {
268  var zero = places - num.toString().length + 1;
269  return Array(+(zero > 0 && zero)).join("0") + num;
270}
271/**
272 * All EM function (see em_core_helper.php)
273 */
274function LEMcount()
275{
276    // takes variable number of arguments - returns count of those arguments that are not null/empty
277    var result=0;
278    for (i=0;i<arguments.length;++i) {
279        var arg = arguments[i];
280        if (arg !== '') {
281            ++result;
282        }
283    }
284    return result;
285}
286
287function LEMunique()
288{
289    var uniqs = new Array();
290    for (i=0;i<arguments.length;++i) {
291        var arg = arguments[i];
292        if (trim(arg)=='')
293            continue;
294        if (typeof uniqs[arg] !== 'undefined')
295            return false;
296        uniqs[arg] = 1;
297    }
298    return true;
299}
300
301function LEMcountif()
302{
303    // takes variable number of arguments - returns count of those arguments that match the first parameter
304    var result=0;
305    var match=arguments[0];
306    for (i=1;i<arguments.length;++i) {
307        var arg = arguments[i];
308        if (arg == match) {
309            ++result;
310        }
311    }
312    return result;
313}
314
315function LEMcountifop()
316{
317    // takes variable number of arguments - returns count of answered questions which meet the criteria (arg op value)
318    var result=0;
319    var op=arguments[0];
320    var value=arguments[1];
321    if (op == 'RX') {
322        var reg = new RegExp(value.substr(1,value.length-2));
323    }
324    for (i=2;i<arguments.length;++i) {
325        var arg = arguments[i];
326        switch(op)
327        {
328            case '==': case 'eq': if (arg == value) { ++result; } break;
329            case '>=': case 'ge': if (arg >= value) { ++result; } break;
330            case '>':  case 'gt': if (arg > value) { ++result; } break;
331            case '<=': case 'le': if (arg <= value) { ++result; } break;
332            case '<':  case 'lt': if (arg < value) { ++result; } break;
333            case '!=': case 'ne': if (arg != value) { ++result; } break;
334            case 'RX': {
335                try {
336                    if (reg.test(arg)) {
337                        ++result;
338                    }
339                }
340                catch (err) {
341                    return false;
342                }
343            }
344        }
345    }
346    return result;
347}
348
349function LEMsumifop()
350{
351    // takes variable number of arguments - returns sum of answered questions which meet the criteria (arg op value)
352    var result=0;
353    var op=arguments[0];
354    var value=arguments[1];
355    if (op == 'RX') {
356        var reg = new RegExp(value.substr(1,value.length-2));
357    }
358    for (i=2;i<arguments.length;++i) {
359        var arg = arguments[i] || 0;
360        if (LEMis_numeric(arg)){
361            try{
362                arg = new Decimal(arg);
363            } catch(e){
364                arg = new Decimal(arg.toString().replace(/,/,'.'));
365            }
366        }
367        else if(arg === true){
368            arg=1;
369        }
370
371        switch(op) {
372            case '==': case 'eq': if (arg == value) { result += arg; } break;
373            case '>=': case 'ge': if (arg >= value) { result += arg; } break;
374            case '>':  case 'gt': if (arg > value) { result += arg; } break;
375            case '<=': case 'le': if (arg <= value) { result += arg; } break;
376            case '<':  case 'lt': if (arg < value) { result += arg; } break;
377            case '!=': case 'ne': if (arg != value) { result += arg; } break;
378            case 'RX': {
379                try {
380                    if (reg.test(arg)) {
381                        result += arg;
382                    }
383                }
384                catch (err) {
385                    return false;
386                }
387            }
388        }
389    }
390    return result;
391}
392
393function LEMpi()
394{
395    return Math.PI;
396}
397
398function LEMsum()
399{
400    // takes variable number of arguments, returns their sum
401    var result= new Decimal(0);
402    for (i=0;i<arguments.length;++i) {
403        var arg = arguments[i] || 0;
404        if (LEMis_numeric(arg)){
405            try{
406                arg = new Decimal(arg);
407            } catch(e){
408                arg = new Decimal(arg.toString().replace(/,/,'.'));
409            }
410            //create decimal checks!
411            result = result.add(arg);
412        } else if(arg === true){
413            result = result.add(1);
414        }
415    }
416    return result.toString();
417}
418
419/**
420 * return interger value of mixedVar (near like PHP floatval)
421 * @param mixed mixedVar
422 * @return integer
423 * @see: https://locutus.io/php/var/intval
424 * original by: Kevin van Zonneveld (https://kvz.io)
425 * improved by: stensi
426 * adapated for LimeSurvey by Denis Chenu
427 *   example 1: intval('150.03LimeSurvey')
428 *   returns 1: 150
429 *   example 2: intval('LimeSurvey: 10')
430 *   returns 2: 0
431 *   example 3: intval('-50.1 + 8')
432 *   returns 3: -50
433 *   example 4: intval(1 > 0)
434 *   returns 4: 1
435 */
436function LEMintval(mixedVar)
437{
438    var type = typeof mixedVar
439    if (type === 'boolean') {
440        return +mixedVar;
441    } 
442    if (type === 'string') {
443        tmp = parseInt(mixedVar, 10)
444        return (isNaN(tmp) || !isFinite(tmp)) ? 0 : tmp;
445    }
446    if (type === 'number' && isFinite(mixedVar)) {
447        return mixedVar < 0 ? Math.ceil(mixedVar) : Math.floor(mixedVar);
448    }
449    return 0;
450}
451
452function LEMis_null(a)
453{
454    return (a == null);
455}
456
457function LEMis_float(a)
458{
459    if (!LEMis_numeric(a))
460    {
461        return false;
462    }
463    var num = new Number(a);
464    // should this only return true if there is a non-zero decimal part to the number?
465    return (Math.floor(num) != num);
466}
467
468/**
469 * Test if mixed_var is in integer value (no matter the real type)
470 */
471function LEMis_int(mixed_var)
472{
473    try {
474        var iCheckValue = new Decimal(mixed_var)
475    }
476    catch (err) {
477        return false;
478    }
479    return iCheckValue.isInteger();
480}
481/**
482 * Test if mixed_var is a PHP numeric value
483 * Do not use locutus because of LEMradix
484 */
485function LEMis_numeric(mixed_var)
486{
487    var isNumericRegex = new RegExp(/^(-)?\d*(,|\.)?\d*$/);
488    return ( ( ( typeof mixed_var === 'string' && isNumericRegex.test(mixed_var)) || typeof mixed_var === 'number') && mixed_var !== '' && !isNaN(mixed_var));
489}
490
491function LEMis_string(a)
492{
493    return isNaN(a);
494}
495
496/**
497 * Find the closest matching numerical input values in a list an replace it by the
498 * corresponding value within another list
499 *
500 * @author Johannes Weberhofer, 2013
501 *
502 * @param numeric fValueToReplace
503 * @param numeric iStrict - 1 for exact matches only otherwise interpolation the
504 * 		  closest value should be returned
505 * @param string sTranslateFromList - comma seperated list of values to translate from
506 * @param string sTranslateToList - comma seperated list of values to translate to
507 * @return numeric
508 */
509function LEMconvert_value( fValueToReplace, iStrict, sTranslateFromList, sTranslateToList)
510{
511    if ( isNaN(fValueToReplace) || (iStrict==null) || (sTranslateFromList==null) || (sTranslateToList==null) )
512    {
513        return null;
514    }
515    aFromValues = sTranslateFromList.split(",");
516    aToValues = sTranslateToList.split(",");
517    if ( (aFromValues.length > 0)  && (aFromValues.length == aToValues.length) )
518    {
519        fMinimumDiff = null;
520        iNearestIndex = 0;
521        for ( i = 0; i < aFromValues.length; i++) {
522            if ( isNaN(aFromValues[i]) ) {
523                // break processing when non-numeric variables are about to be processed
524                return null;
525            }
526            fCurrentDiff = Math.abs(aFromValues[i] - fValueToReplace);
527            if (fCurrentDiff === 0) {
528                return aToValues[i];
529            } else if (i === 0) {
530                fMinimumDiff = fCurrentDiff;
531            } else if ( fMinimumDiff > fCurrentDiff ) {
532                fMinimumDiff = fCurrentDiff;
533                iNearestIndex = i;
534            }
535        }
536        if ( iStrict != 1 ) {
537            return aToValues[iNearestIndex];
538        }
539    }
540    return null;
541}
542
543/**
544 * return float value of mixedVar (near like PHP floatval)
545 * @param mixed mixedVar
546 * @return float
547 * @see: https://locutus.io/php/floatval/
548 * original by: Michael White (https://getsprink.com)
549 * updated for LimeSurvey by Denis Chenu
550 *      note 1: The native parseFloat() method of JavaScript returns NaN
551 *      note 1: when it encounters a string before an int or float value.
552 *   example 1: floatval('150.03LimeSurvey')
553 *   returns 1: 150.03
554 *   example 2: floatval('LimeSurvey: 10')
555 *   returns 2: 0
556 *   example 3: floatval('-50.1 + 8')
557 *   returns 3: -50.1
558 *   example 4: floatval(1 > 0)
559 *   returns 4: 1
560 */
561function LEMfloatval(mixedVar)
562{
563    var type = typeof mixedVar;
564    if (type === 'boolean') {
565        return +mixedVar
566    }
567    return (parseFloat(mixedVar) || 0)
568}
569/**
570 * If $test is true, return $iftrue, else return $iffalse
571 * @param mixed testDone
572 * @param mixed ifTrue
573 * @param mixed ifFalse
574 * @return mixed
575 */
576function LEMif(testDone,ifTrue,ifFalse)
577{
578    if(testDone && testDone !== '0') { // so consistent with PHP
579        return ifTrue;
580    }
581    if (typeof ifFalse == "undefined") { // Pre ES6 (else, just add ifFalse = '' in function call
582        return '';
583    }
584    return ifFalse;
585}
586
587/**
588 *  Returns comma separated list of non-null values
589 */
590
591function LEMlist()
592{
593    // takes variable number of arguments
594    var result="";
595    var joiner = ', ';
596    j=1;    // keep track of how many non-null values seen
597    for (i=0;i<arguments.length;++i) {
598        var arg = arguments[i];
599        if (arg !== '') {
600            if (j > 1) {
601                result += joiner + arg;
602            }
603            else {
604                result += arg;
605            }
606            ++j;
607        }
608    }
609    return result;
610}
611
612/**
613 * Implementation of listifop( cmpAttr, op, value, retAttr, glue, sgqa1, ..., sgqaN )
614 * Returns a list of retAttr from sgqa1...sgqaN which pass the critiera (cmpAttr op value)
615 */
616function LEMlistifop()
617{
618    // takes variable number of arguments
619    var result = "";
620    var cmpAttr = arguments[0];
621    var op = arguments[1];
622    var value = arguments[2];
623    var retAttr = arguments[3];
624    var glue = arguments[4];
625
626    var validAttributes = "/code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value/";
627
628    if ( ! cmpAttr.match( validAttributes ) ) {
629        return cmpAttr +" not recognized ?!";
630    }
631    if ( ! retAttr.match( validAttributes ) ) {
632        return retAttr +" not recognized ?!";
633    }
634
635    if ( op == 'RX' ) {
636        var reg = new RegExp( value.substr( 1, value.length-2 ) );
637    }
638
639    for ( i=5; i < arguments.length; ++i ) {
640        var sgqa = arguments[i];
641        var cmpVal = LEMval( sgqa +"."+ cmpAttr ); // Equal to LimeExpressionManager::GetVarAttribute($sgqa,$cmpAttr,null,-1,-1) ?
642        var match = false;
643
644        switch(op)
645        {
646            case '==': case 'eq': match = (cmpVal == value); break;
647            case '>=': case 'ge': match = (cmpVal >= value); break;
648            case '>':  case 'gt': match = (cmpVal > value);  break;
649            case '<=': case 'le': match = (cmpVal <= value); break;
650            case '<':  case 'lt': match = (cmpVal < value);  break;
651            case '!=': case 'ne': match = (cmpVal != value); break;
652            case 'RX': try { match = reg.test( cmpVal ); }
653            catch (err) { return "Invalid RegEx"; } break;
654        }
655
656        if ( match ) {
657            retVal = LEMval( sgqa +"."+ retAttr ); // Equal to LimeExpressionManager::GetVarAttribute($sgqa,$retAttr,null,-1,-1) ?
658            if ( result != "" ) {
659                result += glue;
660            }
661            result += retVal;
662        }
663    }
664
665    return result;
666}
667
668/**
669 *  Returns Natural logarithm of a number
670 */
671
672function LEMlog()
673{
674    // takes variable number of arguments
675    if (arguments.length < 1) {
676        return NaN;
677    }
678    var base=Math.exp(1);
679    if(arguments.length>1){
680        base = arguments[1];
681        if (isNaN(base)) { return NaN;}
682        if (base<=0 ) { return NaN;}
683        base=Math.abs(parseFloat(arguments[1]));
684    }
685    if(base==Math.exp(1)){// Not needed
686        return Math.log(arguments[0]);
687    }else{
688        return Math.log(arguments[0])/Math.log(base);
689    }
690}
691
692/**
693 * max like php in LimeSurvey, start by https://github.com/kvz/locutus/blob/master/src/php/math/max.js
694 * @see https://bugs.limesurvey.org/view.php?id=14337
695 * Review for ExpressionManager
696 **/
697function LEMmax () {
698  // original at: http://locutus.io/php/max/
699  // original by: Onno Marsman (https://twitter.com/onnomarsman)
700  //  revised by: Denis Chenu for LimeSurvey specific
701  //      note 1: Long code cause we're aiming for maximum PHP compatibility
702  //   example 1: max(1, 3, 5, 6, 7,'')
703  //   returns 1: 7
704  //   example 2: max(1, 'hello','')
705  //   returns 2: 'hello'
706  //   example 3: max('hello', 1,'')
707  //   returns 3: 'hello'
708  //   example 4: max('2hello', 1,'')
709  //   returns 4: '2hello'
710  //   example 5: max('1hello', 2,'')
711  //   returns 5: 2
712  //   example 6: max(-1, -2,'')
713  //   returns 6: -1
714
715  var ar
716  var retVal
717  var i = 0
718  var n = 0
719  var argv = arguments
720  var argc = argv.length
721
722  var _compare = function (current, next) {
723    if(next === '') {
724      return -1;
725    }
726    if(current === '') {
727      return 1;
728    }
729    if (current === next) {
730      return 0
731    }
732    if (isNaN(next) && !isNaN(current)) {
733      return (next.toString() > current.toString() ? 1 : -1)
734    }
735    if (isNaN(current) && !isNaN(next)) {
736      return (next.toString() > current.toString() ? 1 : -1)
737    }
738
739    return (next > current ? 1 : -1)
740  }
741
742  if (argc === 0) {
743    return '';
744  }
745  if (argc === 1) {
746    return argv[0];
747  }
748
749  ar = argv
750  retVal = ar[0]
751  for (i = 1, n = ar.length; i < n; ++i) {
752    if (_compare(retVal, ar[i]) === 1) {
753      retVal = ar[i]
754    }
755  }
756
757  return retVal
758}
759/**
760 * min like php in LimeSurvey : https://github.com/kvz/locutus/blob/master/src/php/math/min.js
761 * @see https://bugs.limesurvey.org/view.php?id=14337
762 * Review for ExpressionManager 
763 **/
764function LEMmin () {
765  // original at: http://locutus.io/php/max/
766  // original by: Onno Marsman (https://twitter.com/onnomarsman)
767  //  revised by: Denis Chenu for LimeSurvey specific
768  //      note 1: Long code cause we're aiming for maximum PHP compatibility
769  //   example 1: min(1, 3, 5, 6, 7)
770  //   returns 1: 1
771  //   example 2: max(1, 'hello')
772  //   returns 2: 1
773  //   example 3: max('hello', 1)
774  //   returns 3: 1
775  //   example 4: max('2hello', 1)
776  //   returns 4: 1
777  //   example 5: max('1hello', 2)
778  //   returns 5: '1hello'
779  //   example 6: min(-1, -2)
780  //   returns 6: -2
781  //   example 7: min(-1, '')
782  //   returns 7: ''
783
784  var ar
785  var retVal
786  var i = 0
787  var n = 0
788  var argv = arguments
789  var argc = argv.length
790
791  var _compare = function (current, next) {
792    if(next === '') {
793      return -1;
794    }
795    if(current === '') {
796      return 1;
797    }
798    if (current === next) {
799      return 0;
800    }
801    if (isNaN(next) && !isNaN(current)) {
802      return 1;
803    }
804    if (isNaN(current) && !isNaN(next)) {
805      return -1
806    }
807
808    return (next > current ? 1 : -1)
809  }
810
811  if (argc === 0) {
812    return '';
813  }
814  if (argc === 1) {
815    return argv[0];
816  }
817
818  ar = argv
819  retVal = ar[0]
820
821  for (i = 1, n = ar.length; i < n; ++i) {
822    if (_compare(retVal, ar[i]) === -1) {
823      retVal = ar[i]
824    }
825  }
826
827  return retVal
828}
829
830 /**
831 * Returns concatenates list
832 */
833function LEMjoin()
834{
835    var result="";
836    for (i=0;i<arguments.length;++i) {
837        result += arguments[i];
838    }
839    return result;
840}
841
842/**
843 * Returns concatenates list with first argument as the separator
844 */
845function LEMimplode()
846{
847    // takes variable number of arguments
848    var result="";
849    if (arguments.length <= 1) {
850        return "";
851    }
852    var joiner = arguments[0];
853    for (i=1;i<arguments.length;++i) {
854        var arg = arguments[i];
855        if (i > 1) {
856            result += joiner + arg;
857        }
858        else {
859            result += arg;
860        }
861    }
862    return result;
863}
864
865/*
866 * Returns true if within matches the pattern.  Pattern must start and end with the '/' character
867 */
868function LEMregexMatch(sRegExp,within)
869{
870    try {
871        var flags = sRegExp.replace(/.*\/([gimy]*)$/, '$1');
872        var pattern = sRegExp.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1').trim();
873        var reg = new RegExp(pattern, flags); // Note that the /u flag crashes IE11
874        var decodedValue = html_entity_decode(within);
875        return reg.test(decodedValue);
876    }
877    catch (err) {
878        return false;
879    }
880}
881
882function LEMstrlen(a)
883{
884    var  str = new String(a);
885    return str.length;
886}
887
888function LEMstr_replace (search, replace, subject) {
889  // Copied From: http://phpjs.org/functions
890  var i = 0,
891    j = 0,
892    temp = '',
893    repl = '',
894    sl = 0,
895    fl = 0,
896    f = [].concat(search),
897    r = [].concat(replace),
898    s = subject,
899    ra = Object.prototype.toString.call(r) === '[object Array]',
900    sa = Object.prototype.toString.call(s) === '[object Array]';
901    s = [].concat(s);
902  for (i = 0, sl = s.length; i < sl; i++) {
903    if (s[i] === '') {
904      continue;
905    }
906    for (j = 0, fl = f.length; j < fl; j++) {
907      temp = s[i] + '';
908      repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0];
909      s[i] = (temp).split(f[j]).join(repl);
910    }
911  }
912  return sa ? s : s[0];
913}
914
915
916
917function LEMstrpos(haystack,needle,offset)
918{
919    var haystack = haystack + '';
920    var needle = needle + '';
921    var index = 0;
922    if ((index = haystack.indexOf(needle, offset)) !== -1) {
923        return index;
924    }
925    return false;
926}
927
928function LEMempty(v)
929{
930    if (v === "" || v === false) {
931        return true;
932    }
933    return false;
934}
935
936function LEMbool(v)
937{
938    bool = new Boolean(v);
939    if (v.valueOf() && v != 'false') {
940        return true;    // fix for JavaScript native processing that considers the value "false" to be true
941    }
942    return false;
943}
944
945/**
946 * Match even if different types - especially true vs. 1, and false vs. 0 or blank
947 */
948function LEMeq(a,b)
949{
950    if ((a==="true" && b==="1") || (a==="1" && b==="true")) {
951        return true;
952    }
953    if ((a==="false" && (b==="0" || b==="")) || ((a==="0" || a==="") && b ==="false")) {
954        return true;
955    }
956    return a==b;
957}
958
959/**
960 * Return the value for data element jsName, treating it as blank if its question is irrelevant on the current page.
961 * Also convert the string 'false' to '' to cope with a JavaScript type casting issue
962 */
963function LEMval(alias)
964{
965    // first find out whether it is using a suffix
966    var str = new String(alias);
967    var varName = alias;
968    var suffix = 'code';    // the default
969    var value = "";
970    if (str == '') return '';
971    newval = str;
972    if (LEMradix === ',') {
973        newval = str.split(',').join('.');
974    }
975    var checkNumericRegex = new RegExp(/^[-0-9,\.]*$/);
976    if(checkNumericRegex.test(newval)){
977        try{
978            newval = new Decimal(newval);
979        } catch(e){
980            // Nothing to do : already done before if needed (accordig to LEMradix)
981        }
982    }
983    if (newval == parseFloat(newval)) {
984        if (newval.length > 0 && newval[0]==0) {
985            return newval;   // so keep 0 prefixes on numbers
986        }
987        return +newval;
988    }
989
990    if (str.match(/^INSERTANS:/)) {
991        suffix = 'shown';
992        varName = varName.substr(10);
993    }
994    else if (str.match(/\.(code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value)$/)) {
995        varName = str.replace(/\.(code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value)$/,'')
996        suffix = str.replace(/^(.+)\./,'');
997    }
998
999    jsName = LEMalias2varName[varName];
1000    attr = LEMvarNameAttr[jsName];
1001    if ((suffix.match(/^code|NAOK|shown|valueNAOK|value$/)) && attr.qid!='') {
1002        if (!LEMval(varName + '.relevanceStatus')) {
1003            return '';
1004        }
1005    }
1006    var whichJsName; // correct name whether on- or off-page
1007    var onSamePage; // Tag if value is on same page or not (
1008    if (LEMmode=='survey' || (LEMmode=='group' && attr.gseq == LEMgseq) || (LEMmode=='question' && attr.qid == LEMqid))
1009    {
1010        whichJsName = (typeof attr.jsName_on === 'undefined') ? attr.jsName : attr.jsName_on;
1011        onSamePage=true;
1012    }
1013    else
1014    {
1015        whichJsName = attr.jsName;
1016        onSamePage=false;
1017    }
1018
1019    if (whichJsName === null || typeof document.getElementById(whichJsName) === 'undefined' || document.getElementById(whichJsName) === null) {
1020        an_error = true;    // this line is here to make debugging easier
1021        return '';
1022    }
1023
1024    // values should always be stored encoded with htmlspecialchars()
1025    switch (suffix) {
1026        case 'relevanceStatus': {
1027            grel = qrel = sgqarel = 1;
1028            if (!(typeof attr.gseq === 'undefined') && !(document.getElementById('relevanceG' + attr.gseq) === null)) {
1029                if(typeof attr.type === 'undefined' || attr.type!="*")// Equation question don't test visibility of group ( child of bug #08315).
1030                    grel = parseInt(document.getElementById('relevanceG' + attr.gseq).value);
1031            }
1032            if (!(typeof attr.qid === 'undefined') && !(document.getElementById('relevance' + attr.qid) === null)) {
1033                qrel = parseInt(document.getElementById('relevance' + attr.qid).value);
1034            }
1035            if (!(typeof attr.rowdivid === 'undefined') && !(document.getElementById('relevance' + attr.rowdivid) === null)) {
1036                sgqarel = parseInt(document.getElementById('relevance' + attr.rowdivid).value);
1037            }
1038            return (grel && qrel && sgqarel);
1039        }
1040        case 'shown': {
1041            value = htmlspecialchars_decode(document.getElementById(whichJsName).value);
1042            var shown="";
1043            switch(attr.type)
1044            {
1045                case 'G': //GENDER drop-down list
1046                case 'Y': //YES/NO radio-buttons
1047                case 'C': //ARRAY (YES/UNCERTAIN/NO) radio-buttons
1048                case 'E': //ARRAY (Increase/Same/Decrease) radio-buttons
1049                    shown = (typeof attr.answers[value] === 'undefined') ? '' : attr.answers[value];
1050                    break;
1051                case '!': //List - dropdown
1052                case 'L': //LIST drop-down/radio-button list
1053                case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea
1054                case 'H': //ARRAY (Flexible) - Column Format
1055                case 'F': //ARRAY (Flexible) - Row Format
1056                case 'R': //RANKING STYLE
1057                    if (attr.type == 'O' && varName.match(/comment$/)) {
1058                        answer = htmlentities(value);
1059                    }
1060                    else if ((attr.type == 'L' || attr.type == '!') && varName.match(/_other$/)) {
1061                        answer = htmlentities(value);
1062                    }
1063                    else {
1064                        which_ans = '0~' + value;
1065                        if (attr.answers === undefined || typeof attr.answers[which_ans] === 'undefined') {
1066                            answer = value;
1067                        }
1068                        else {
1069                            answerParts = attr.answers[which_ans].split('|');
1070                            answerParts.shift();    // remove the first element
1071                            answer = answerParts.join('|');
1072                        }
1073                    }
1074                    shown = answer;
1075                    break;
1076                case '1': //Array (Flexible Labels) dual scale  // need scale
1077                    prefix = (attr.jsName.match(/_1$/)) ? '1' : '0';
1078                    which_ans = prefix + '~' + value;
1079                    if (attr.answers === undefined || typeof attr.answers[which_ans] === 'undefined') {
1080                        answer = '';
1081                    }
1082                    else {
1083                        answerParts = attr.answers[which_ans].split('|');
1084                        answerParts.shift();    // remove the first element
1085                        answer = answerParts.join('|');
1086                    }
1087                    shown = answer;
1088                    break;
1089                case 'N': //NUMERICAL QUESTION TYPE
1090                case 'K': //MULTIPLE NUMERICAL QUESTION
1091                case 'A': //ARRAY (5 POINT CHOICE) radio-buttons
1092                case 'B': //ARRAY (10 POINT CHOICE) radio-buttons
1093                case ':': //ARRAY (Multi Flexi) 1 to 10
1094                case '5': //5 POINT CHOICE radio-buttons
1095                case 'Q': //MULTIPLE SHORT TEXT
1096                case ';': //ARRAY (Multi Flexi) Text
1097                case 'S': //SHORT FREE TEXT
1098                case 'T': //LONG FREE TEXT
1099                case 'U': //HUGE FREE TEXT
1100                case 'D': //DATE
1101                case '*': //Equation
1102                case 'I': //Language Question
1103                case '|': //File Upload
1104                case 'X': //BOILERPLATE QUESTION
1105                        shown = htmlentities(value);
1106                    break;
1107                case 'M': //Multiple choice checkbox
1108                case 'P': //Multiple choice with comments checkbox + text
1109                    if (typeof attr.question === 'undefined' || value == '') {
1110                        shown = '';
1111                    }
1112                    else {
1113                        if (attr.type == 'P' && varName.match(/comment$/)) {
1114                            try {
1115                                var numtest = new Decimal(value);
1116                                shown = parseFloat(numtest.valueOf());
1117                            }
1118                            catch(e) {
1119                                shown = htmlentities(value);
1120                            }
1121                        }
1122                        else {
1123                            shown = htmlspecialchars_decode(attr.question);
1124                        }
1125                    }
1126                    break;
1127            }
1128            return shown;
1129        }
1130        case 'gid':
1131            return attr.gid;
1132        case 'grelevance':
1133            return attr.grelevance;
1134        case 'mandatory':
1135            return attr.mandatory;
1136        case 'qid':
1137            return attr.qid;
1138        case 'question':
1139            return htmlspecialchars_decode(attr.question);
1140        case 'readWrite':
1141            return attr.readWrite;
1142        case 'relevance':
1143            return htmlspecialchars_decode(attr.relevance);
1144        case 'sgqa':
1145            return attr.sgqa;
1146        case 'type':
1147            return attr.type;
1148        case 'gseq':
1149            return attr.gseq;
1150        case 'qseq':
1151            return attr.qseq;
1152        case 'jsName':
1153            return whichJsName;
1154        case 'code':
1155        case 'NAOK':
1156        case 'value':
1157        case 'valueNAOK':
1158        {
1159            value = htmlspecialchars_decode(document.getElementById(whichJsName).value);
1160            if (value === '') {
1161                return '';
1162            }
1163            // Always htmlentities user entered values, see #13928
1164            switch(attr.type)
1165            {
1166                case '!': //List - dropdown
1167                case 'L': //LIST drop-down/radio-button list
1168                case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea
1169                case 'H': //ARRAY (Flexible) - Column Format
1170                case 'F': //ARRAY (Flexible) - Row Format
1171                case 'R': //RANKING STYLE
1172                    if (attr.type == 'O' && varName.match(/comment$/)) {
1173                        value = htmlentities(value);
1174                    }
1175                    else if ((attr.type == 'L' || attr.type == '!') && varName.match(/_other$/)) {
1176                        value = htmlentities(value);
1177                    }
1178                    break;
1179                case 'N': //NUMERICAL QUESTION TYPE
1180                case 'K': //MULTIPLE NUMERICAL QUESTION
1181                case 'Q': //MULTIPLE SHORT TEXT
1182                case ';': //ARRAY (Multi Flexi) Text
1183                case 'S': //SHORT FREE TEXT
1184                case 'T': //LONG FREE TEXT
1185                case 'U': //HUGE FREE TEXT
1186                case 'D': //DATE
1187                case '*': //Equation
1188                case '|': //File Upload (unsure need to be htmlentities ?)
1189                        value = htmlentities(value);
1190                    break;
1191                case 'M': //Multiple choice checkbox
1192                case 'P': //Multiple choice with comments checkbox + text
1193                    if (attr.type == 'P' && varName.match(/comment$/)) {
1194                        value = htmlentities(value);
1195                    }
1196                    break;
1197                case 'A': //ARRAY (5 POINT CHOICE) radio-buttons
1198                case 'B': //ARRAY (10 POINT CHOICE) radio-buttons
1199                case ':': //ARRAY (Multi Flexi) 1 to 10
1200                case '5': //5 POINT CHOICE radio-buttons
1201                case 'I': //Language Question
1202                case 'X': //BOILERPLATE QUESTION
1203                default:
1204                    // Nothing to update
1205            }
1206
1207            if (suffix == 'value' || suffix == 'valueNAOK') {
1208                // if in assessment mode, this returns the assessment value
1209                // in non-assessment mode, this is identical to .code
1210                switch (attr.type) {
1211                    case '!': //List - dropdown
1212                    case 'L': //LIST drop-down/radio-button list
1213                    case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea
1214                    case 'H': //ARRAY (Flexible) - Column Format
1215                    case 'F': //ARRAY (Flexible) - Row Format
1216                    case 'R': //RANKING STYLE
1217                        if (attr.type == 'O' && varName.match(/comment$/)) {
1218//                            value = value;
1219                        }
1220                        else if ((attr.type == 'L' || attr.type == '!') && varName.match(/_other$/)) {
1221//                            value = value;
1222                        }
1223                        else {
1224                            which_ans = '0~' + value;
1225                            if (attr.answers === undefined || typeof attr.answers[which_ans] === 'undefined') {
1226                                value = '';
1227                            }
1228                            else {
1229                                answerParts = attr.answers[which_ans].split('|');
1230                                value = answerParts[0];
1231                            }
1232                        }
1233                        break;
1234                    case '1': //Array (Flexible Labels) dual scale  // need scale
1235                        prefix = (attr.jsName.match(/_1$/)) ? '1' : '0';
1236                        which_ans = prefix + '~' + value;
1237                        if (attr.answers === undefined || typeof attr.answers[which_ans] === 'undefined') {
1238                            value = '';
1239                        }
1240                        else {
1241                            answerParts = attr.answers[which_ans].split('|');
1242                            value = answerParts[0];
1243                        }
1244                        break;
1245                    case 'N': //NUMERICAL QUESTION TYPE
1246                    case 'K': //MULTIPLE NUMERICAL QUESTION
1247
1248                }
1249            }
1250
1251            if (typeof attr.onlynum !== 'undefined' && attr.onlynum==1) {
1252                if(value=="") {
1253                    return "";
1254                }
1255
1256                var checkNumericRegex = new RegExp(/^(-)?[0-9]*(,|\.)[0-9]*$/);
1257                /* Set as number if regexp is OK AND lenght is > 1 (then not fix [-.,] #14533 and no need to fix single number) */
1258                if( checkNumericRegex.test(value) && value.length > 1 )
1259                {
1260                    var length = value.length;
1261                    var firstLetterIsNull = value.split("").shift() === '0';
1262                    // @todo : use . or , according to LEMradix !
1263                    try{
1264                        var numtest = new Decimal(value);
1265                    } catch(e){
1266                        var numtest = new Decimal(value.toString().replace(/,/,'.'));
1267                        // Error can still happen maybe but don't cat
1267ch to know (and fix) it
1268                    }
1269                    value = numtest.valueOf();
1270                    if(value.length < length && firstLetterIsNull){
1271                        value = str_repeat('0', length).substr(0,(length - value.length))+''+value.toString();
1272                    }
1273                    value = Number(value); /* If it's a number : always return a number */
1274                }
1275                /* Always check if value are not updated, see #17963 */
1276                if(Number(value).toString() != value.toString() || LSvar.bNumRealValue) {
1277                    return value;
1278                }
1279                return Number(value);
1280            }
1281
1282            // convert content in date questions to standard format yy-mm-dd to facilitate use in EM (comparisons, min/max etc.)
1283            else if (attr.type=='D')  {
1284                // get date format pattern of referenced question
1285                var sdatetimePattern=$(jsName.replace(/java/g, '#dateformat')).val();
1286                if (sdatetimePattern == ''){
1287                    sdatetimePattern=$(jsName.replace(/java/g, '#dateformat')).text();
1288                }
1289                // if empty (eg., variable on a previous page), set default format yy-mm-dd HH:MM
1290                sdatetimePattern = sdatetimePattern == ''? 'YYYY-MM-DD HH:mm': sdatetimePattern;
1291
1292                if (sdatetimePattern==null) {
1293                    sdatetimePattern="";
1294                }
1295                if (value=='INVALID') {
1296                    value="";
1297                }
1298                else {
1299                    value= moment(value,sdatetimePattern).format('YYYY-MM-DD HH:mm');
1300                }
1301                return value;
1302            }
1303            else if (isNaN(value)) {
1304                if (value==='false') {
1305                    return '';  // so Boolean operations will treat it as false. In JavaScript, Boolean("false") is true since "false" is not a zero-length string
1306                }
1307                return value;
1308            }
1309            else if(!isNaN(parseFloat(value)) && isFinite(value))
1310            {
1311                var length = value.length;
1312                var firstLetterIsNull = value.split("").shift() === '0';
1313                try{
1314                    var numtest = new Decimal(value);
1315                } catch(e){
1316                    var numtest = new Decimal(value.toString().replace(/,/,'.'));
1317                }
1318                if(numtest.valueOf().length < length && firstLetterIsNull){
1319                    value = value.toString(); /* return string as it is */
1320                } else {
1321                    value = Number(numtest.valueOf()); /* If it's a number : always return a number */
1322                }
1323            }
1324
1325            return value;
1326        }
1327        case 'rowdivid':
1328            if (typeof attr.rowdivid === 'undefined' || attr.rowdivid == '') {
1329                return '';
1330            }
1331            return attr.rowdivid;
1332        default:
1333            return 'Unknown Attribute: ' . suffix;
1334    }
1335}
1336
1337
1338/** Display number with comma as radix separator, if needed
1339 */
1340function LEMfixnum(value)
1341{
1342    if (LEMradix===',') {
1343        var newval = String(value);
1344        if (!isNaN(parseFloat(newval)) && isFinite(newval)) {
1345            newval= newval.split('.').join(',');
1346            return newval;
1347        }
1348        return value;   // unchanged
1349    }
1350    return value;
1351}
1352
1353/*
1354 * Remove HTML and PHP tags from string
1355 */
1356function LEMstrip_tags(htmlString)
1357{
1358   var tmp = document.createElement("DIV");
1359   tmp.innerHTML = htmlString;
1360   return tmp.textContent||tmp.innerText;
1361}
1362
1363/**
1364 * Compute sample standard deviation of the input variables
1365 */
1366function LEMstddev()
1367{
1368    vals = new Array();
1369    j = 0;
1370    for (i=0;i<arguments.length;++i) {
1371        if (LEMis_numeric(arguments[i])) {
1372            vals[j++] = arguments[i];
1373        }
1374    }
1375    count = vals.length;
1376    if (count <= 1) {
1377        return 0;   // what should default value be?
1378    }
1379    sum = 0;
1380    for (i=0;i<vals.length;++i) {
1381        sum += vals[i];
1382    }
1383    mean = sum / count;
1384
1385    sumsqmeans = 0;
1386    for (i=0;i<vals.length;++i) {
1387        sumsqmeans += (vals[i] - mean) * (vals[i] - mean);
1388    }
1389    stddev = Math.sqrt(sumsqmeans / (count-1));
1390    return stddev;
1391}
1392
1393function LEMstrtoupper(s)
1394{
1395    return s.toUpperCase();
1396}
1397
1398function LEMstrtolower(s)
1399{
1400    return s.toLowerCase();
1401}
1402
1403/*
1404 * return true if any of the arguments are not relevant
1405 */
1406function LEManyNA()
1407{
1408    for (i=0;i<arguments.length;++i) {
1409        var arg = arguments[i];
1410        if (arg.match(/\.NAOK$/)) {
1411            /* Must be arg.match(/\.(gid|gseq|mandatory|NAOK|qid|qseq|question|sgqa|shown|type|valueNAOK|relevanceStatus)$/ ? */
1412            /* TODO : find a broken logic system (lss broken) */
1413            continue;
1414        }
1415        if (typeof LEMalias2varName[arg] === 'undefined') {
1416            continue;   // default is OK (e.g. for questions with dot notation suffix
1417        }
1418        jsName = LEMalias2varName[arg];
1419        if (typeof LEMvarNameAttr[jsName] === 'undefined') {
1420            continue;   // default is OK (e.g. for questions with dot notation suffix)
1421        }
1422        attr = LEMvarNameAttr[jsName];
1423        if (!LEMval(attr.sgqa + '.relevanceStatus')) {
1424            return true;
1425        }
1426    }
1427    return false;
1428}
1429
1430/* Set the tabIndex for all potentially visible form elements, and capture the TAB and SHIFT-TAB keys so can
1431 * control navigation when elements appear and disappear.
1432 */
1433function  LEMsetTabIndexes()
1434{
1435    if (typeof tabIndexesSet == 'undefined') {
1436        $(document).on('keydown',"#limesurvey :input[type!=hidden][id!=runonce]",function(event){
1437            var keyCode = event.keyCode || event.which;
1438            if (keyCode == 9) {
1439                // see bug #08590 : lauch checkcondition only for text input. Not needed for radio or checkbox
1440                // Not sure it's really needed actually, it's a blur event for text and change for select
1441                // Can use $(this)[0].type
1442                if($(this).attr('type')=="text")
1443                {
1444                    $(this).triggerHandler("keyup");
1445                }
1446                if($(this).is('select'))
1447                {
1448                    $(this).triggerHandler("change");
1449                }
1450                $(this).focus();
1451                return true;
1452            }
1453        });
1454        tabIndexesSet = true;
1455    }
1456}
1457
1458/** Function to set color coding for Other option in multiple choice questions so that it gets filled in if selected
1459 */
1460function LEMflagMandOther(sgqa,checked)
1461{
1462    if (checked) {
1463        if ($.trim($('#java'+sgqa).val()) == '') {
1464            $('#answer'+sgqa).addClass('em_sq_validation error').removeClass('good');
1465        }
1466        else {
1467            $('#answer'+sgqa).addClass('em_sq_validation good').removeClass('error');
1468        }
1469    }
1470    else {
1471        $('#answer'+sgqa).addClass('em_sq_validation good').removeClass('error');
1472    }
1473}
1474
1475/* The following functions are courtesy of phpjs.org */
1476/*
1477 * More info at: http://phpjs.org
1478 *
1479 * This is version: 3.26
1480 * php.js is copyright 2011 Kevin van Zonneveld.
1481 *
1482 * Portions copyright Brett Zamir (http://brett-zamir.me), Kevin van Zonneveld
1483 * (http://kevin.vanzonneveld.net), Onno Marsman, Theriault, Michael White
1484 * (http://getsprink.com), Waldo Malqui Silva, Paulo Freitas, Jack, Jonas
1485 * Raoni Soares Silva (http://www.jsfromhell.com), Philip Peterson, Legaev
1486 * Andrey, Ates Goral (http://magnetiq.com), Alex, Ratheous, Martijn Wieringa,
1487 * Rafal Kukawski (http://blog.kukawski.pl), lmeyrick
1488 * (https://sourceforge.net/projects/bcmath-js/), Nate, Philippe Baumann,
1489 * Enrique Gonzalez, Webtoolkit.info (http://www.webtoolkit.info/), Carlos R.
1490 * L. Rodrigues (http://www.jsfromhell.com), Ash Searle
1491 * (http://hexmen.com/blog/), Jani Hartikainen, travc, Ole Vrijenhoek,
1492 * Erkekjetter, Michael Grier, Rafal Kukawski (http://kukawski.pl), Johnny
1493 * Mast (http://www.phpvrouwen.nl), T.Wild, d3x,
1494 * http://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript,
1495 * Rafal Kukawski (http://blog.kukawski.pl/), stag019, pilus, WebDevHobo
1496 * (http://webdevhobo.blogspot.com/), marrtins, GeekFG
1497 * (http://geekfg.blogspot.com), Andrea Giammarchi
1498 * (http://webreflection.blogspot.com), Arpad Ray (mailto:[email protected]),
1499 * gorthaur, Paul Smith, Tim de Koning (http://www.kingsquare.nl), Joris, Oleg
1500 * Eremeev, Steve Hilder, majak, gettimeofday, KELAN, Josh Fraser
1501 * (http://onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/),
1502 * Marc Palau, Kevin van Zonneveld (http://kevin.vanzonneveld.net/), Martin
1503 * (http://www.erlenwiese.de/), Breaking Par Consulting Inc
1504 * (http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256CFB006C45F7),
1505 * Chris, Mirek Slugen, saulius, Alfonso Jimenez
1506 * (http://www.alfonsojimenez.com), Diplom@t (http://difane.com/), felix,
1507 * Mailfaker (http://www.weedem.fr/), Tyler Akins (http://rumkin.com), Caio
1508 * Ariede (http://caioariede.com), Robin, Kankrelune
1509 * (http://www.webfaktory.info/), Karol Kowalski, Imgen Tata
1510 * (http://www.myipdf.com/), mdsjack (http://www.mdsjack.bo.it), Dreamer,
1511 * Felix Geisendoerfer (http://www.debuggable.com/felix), Lars Fischer, AJ,
1512 * David, Aman Gupta, Michael White, Public Domain
1513 * (http://www.json.org/json2.js), Steven Levithan
1514 * (http://blog.stevenlevithan.com), Sakimori, Pellentesque Malesuada,
1515 * Thunder.m, Dj (http://phpjs.org/functions/htmlentities:425#comment_134018),
1516 * Steve Clay, David James, Francois, class_exists, nobbler, T. Wild, Itsacon
1517 * (http://www.itsacon.net/), date, Ole Vrijenhoek (http://www.nervous.nl/),
1518 * Fox, Raphael (Ao RUDLER), Marco, noname, Mateusz "loonquawl" Zalega, Frank
1519 * Forte, Arno, ger, mktime, john (http://www.jd-tech.net), Nick Kolosov
1520 * (http://sammy.ru), marc andreu, Scott Cariss, Douglas Crockford
1521 * (http://javascript.crockford.com), madipta, Slawomir Kaniecki,
1522 * ReverseSyntax, Nathan, Alex Wilson, kenneth, Bayron Guevara, Adam Wallner
1523 * (http://web2.bitbaro.hu/), paulo kuong, jmweb, Lincoln Ramsay, djmix,
1524 * Pyerre, Jon Hohle, Thiago Mata (http://thiagomata.blog.com), lmeyrick
1525 * (https://sourceforge.net/projects/bcmath-js/this.), Linuxworld, duncan,
1526 * Gilbert, Sanjoy Roy, Shingo, sankai, Oskar Larsson Högfeldt
1527 * (http://oskar-lh.name/), Denny Wardhana, 0m3r, Everlasto, Subhasis Deb,
1528 * josh, jd, Pier Paolo Ramon (http://www.mastersoup.com/), P, merabi, Soren
1529 * Hansen, Eugene Bulkin (http://doubleaw.com/), Der Simon
1530 * (http://innerdom.sourceforge.net/), echo is bad, Ozh, XoraX
1531 * (http://www.xorax.info), EdorFaus, JB, J A R, Marc Jansen, Francesco, LH,
1532 * Stoyan Kyosev (http://www.svest.org/), nord_ua, omid
1533 * (http://phpjs.org/functions/380:380#comment_137122), Brad Touesnard, MeEtc
1534 * (http://yass.meetcweb.com), Peter-Paul Koch
1535 * (http://www.quirksmode.org/js/beat.html), Olivier Louvignes
1536 * (http://mg-crea.com/), T0bsn, Tim Wiel, Bryan Elliott, Jalal Berrami,
1537 * Martin, JT, David Randall, Thomas Beaucourt (http://www.webapp.fr), taith,
1538 * vlado houba, Pierre-Luc Paour, Kristof Coomans (SCK-CEN Belgian Nucleair
1539 * Research Centre), Martin Pool, Kirk Strobeck, Rick Waldron, Brant Messenger
1540 * (http://www.brantmessenger.com/), Devan Penner-Woelk, Saulo Vallory, Wagner
1541 * B. Soares, Artur Tchernychev, Valentina De Rosa, Jason Wong
1542 * (http://carrot.org/), Christoph, Daniel Esteban, strftime, Mick@el, rezna,
1543 * Simon Willison (http://simonwillison.net), Anton Ongson, Gabriel Paderni,
1544 * Marco van Oort, penutbutterjelly, Philipp Lenssen, Bjorn Roesbeke
1545 * (http://www.bjornroesbeke.be/), Bug?, Eric Nagel, Tomasz Wesolowski,
1546 * Evertjan Garretsen, Bobby Drake, Blues (http://tech.bluesmoon.info/), Luke
1547 * Godfrey, Pul, uestla, Alan C, Ulrich, Rafal Kukawski, Yves Sucaet,
1548 * sowberry, Norman "zEh" Fuchs, hitwork, Zahlii, johnrembo, Nick Callen,
1549 * Steven Levithan (stevenlevithan.com), ejsanders, Scott Baker, Brian Tafoya
1550 * (http://www.premasolutions.com/), Philippe Jausions
1551 * (http://pear.php.net/user/jausions), Aidan Lister
1552 * (http://aidanlister.com/), Rob, e-mike, HKM, ChaosNo1, metjay, strcasecmp,
1553 * strcmp, Taras Bogach, jpfle, Alexander Ermolaev
1554 * (http://snippets.dzone.com/user/AlexanderErmolaev), DxGx, kilops, Orlando,
1555 * dptr1988, Le Torbi, James (http://www.james-bell.co.uk/), Pedro Tainha
1556 * (http://www.pedrotainha.com), James, Arnout Kazemier
1557 * (http://www.3rd-Eden.com), Chris McMacken, gabriel paderni, Yannoo,
1558 * FGFEmperor, baris ozdil, Tod Gentille, Greg Frazier, jakes, 3D-GRAF, Allan
1559 * Jensen (http://www.winternet.no), Howard Yeend, Benjamin Lupton, davook,
1560 * daniel airton wermann (http://wermann.com.br), Atli Þór, Maximusya, Ryan
1561 * W Tenney (http://ryan.10e.us), Alexander M Beedie, fearphage
1562 * (http://http/my.opera.com/fearphage/), Nathan Sepulveda, Victor, Matteo,
1563 * Billy, stensi, Cord, Manish, T.J. Leahy, Riddler
1564 * (http://www.frontierwebdev.com/), Rafal Kukawski, FremyCompany, Matt
1565 * Bradley, Tim de Koning, Luis Salazar (http://www.freaky-media.com/), Diogo
1566 * Resende, Rival, Andrej Pavlovic, Garagoth, Le Torbi
1567 * (http://www.letorbi.de/), Dino, Josep Sanz (http://www.ws3.es/), rem,
1568 * Russell Walker (http://www.nbill.co.uk/), Jamie Beck
1569 * (http://www.terabit.ca/), setcookie, Michael, YUI Library:
1570 * http://developer.yahoo.com/yui/docs/YAHOO.util.DateLocale.html, Blues at
1571 * http://hacks.bluesmoon.info/strftime/strftime.js, Ben
1572 * (http://benblume.co.uk/), DtTvB
1573 * (http://dt.in.th/2008-09-16.string-length-in-bytes.html), Andreas, William,
1574 * meo, incidence, Cagri Ekin, Amirouche, Amir Habibi
1575 * (http://www.residence-mixte.com/), Luke Smith (http://lucassmith.name),
1576 * Kheang Hok Chin (http://www.distantia.ca/), Jay Klehr, Lorenzo Pisani,
1577 * Tony, Yen-Wei Liu, Greenseed, mk.keck, Leslie Hoare, dude, booeyOH, Ben
1578 * Bryan
1579 *
1580 * Dual licensed under the MIT (MIT-LICENSE.txt)
1581 * and GPL (GPL-LICENSE.txt) licenses.
1582 *
1583 * Permission is hereby granted, free of charge, to any person obtaining a
1584 * copy of this software and associated documentation files (the
1585 * "Software"), to deal in the Software without restriction, including
1586 * without limitation the rights to use, copy, modify, merge, publish,
1587 * distribute, sublicense, and/or sell copies of the Software, and to
1588 * permit persons to whom the Software is furnished to do so, subject to
1589 * the following conditions:
1590 *
1591 * The above copyright notice and this permission notice shall be included
1592 * in all copies or substantial portions of the Software.
1593 *
1594 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1595 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1596 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1597 * IN NO EVENT SHALL KEVIN VAN ZONNEVELD BE LIABLE FOR ANY CLAIM, DAMAGES
1598 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1599 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1600 * OTHER DEALINGS IN THE SOFTWARE.
1601 */
1602
1603function is_bool (mixed_var) {
1604    // Returns true if variable is a boolean
1605    //
1606    // version: 1107.2516
1607    // discuss at: http://phpjs.org/functions/is_bool
1608    // +   original by: Onno Marsman
1609    // *     example 1: is_bool(false);
1610    // *     returns 1: true
1611    // *     example 2: is_bool(0);
1612    // *     returns 2: false
1613    return (typeof mixed_var === 'boolean');
1614}
1615
1616function addslashes (str) {
1617    // Escapes single quote, double quotes and backslash characters in a string with backslashes
1618    //
1619    // version: 1107.2516
1620    // discuss at: http://phpjs.org/functions/addslashes
1621    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1622    // +   improved by: Ates Goral (http://magnetiq.com)
1623    // +   improved by: marrtins
1624    // +   improved by: Nate
1625    // +   improved by: Onno Marsman
1626    // +   input by: Denny Wardhana
1627    // +   improved by: Brett Zamir (http://brett-zamir.me)
1628    // +   improved by: Oskar Larsson Högfeldt (http://oskar-lh.name/)
1629    // *     example 1: addslashes("kevin's birthday");
1630    // *     returns 1: 'kevin\'s birthday'
1631    return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
1632}
1633
1634function html_entity_decode (string, quote_style) {
1635    // Convert all HTML entities to their applicable characters
1636    //
1637    // version: 1107.2516
1638    // discuss at: http://phpjs.org/functions/html_entity_decode
1639    // +   original by: john (http://www.jd-tech.net)
1640    // +      input by: ger
1641    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1642    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1643    // +   bugfixed by: Onno Marsman
1644    // +   improved by: marc andreu
1645    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1646    // +      input by: Ratheous
1647    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
1648    // +      input by: Nick Kolosov (http://sammy.ru)
1649    // +   bugfixed by: Fox
1650    // -    depends on: get_html_translation_table
1651    // *     example 1: html_entity_decode('Kevin &amp; van Zonneveld');
1652    // *     returns 1: 'Kevin & van Zonneveld'
1653    // *     example 2: html_entity_decode('&amp;lt;');
1654    // *     returns 2: '&lt;'
1655    var hash_map = {},
1656        symbol = '',
1657        tmp_str = '',
1658        entity = '';
1659    tmp_str = string.toString();
1660
1661    if (false === (hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style))) {
1662        return false;
1663    }
1664
1665    // fix &amp; problem
1666    // http://phpjs.org/functions/get_html_translation_table:416#comment_97660
1667    delete(hash_map['&']);
1668    hash_map['&'] = '&amp;';
1669
1670    for (symbol in hash_map) {
1671        entity = hash_map[symbol];
1672        tmp_str = tmp_str.split(entity).join(symbol);
1673    }
1674    tmp_str = tmp_str.split('&#039;').join("'");
1675
1676    return tmp_str;
1677}
1678
1679function htmlentities (string, quote_style, charset, double_encode) {
1680    // Convert all applicable characters to HTML entities
1681    //
1682    // version: 1107.2516
1683    // discuss at: http://phpjs.org/functions/htmlentities
1684    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1685    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1686    // +   improved by: nobbler
1687    // +    tweaked by: Jack
1688    // +   bugfixed by: Onno Marsman
1689    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1690    // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1691    // +      input by: Ratheous
1692    // +   improved by: Rafał Kukawski (http://blog.kukawski.pl)
1693    // -    depends on: get_html_translation_table
1694    // *     example 1: htmlentities('Kevin & van Zonneveld');
1695    // *     returns 1: 'Kevin &amp; van Zonneveld'
1696    // *     example 2: htmlentities("foo'bar","ENT_QUOTES");
1697    // *     returns 2: 'foo&#039;bar'
1698    var hash_map = {},
1699        symbol = '',
1700        entity = '',
1701        self = this;
1702    string += '';
1703    double_encode = !!double_encode || double_encode == null;
1704
1705    if (false === (hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style))) {
1706        return false;
1707    }
1708    hash_map["'"] = '&#039;';
1709
1710    if (double_encode) {
1711        for (symbol in hash_map) {
1712            entity = hash_map[symbol];
1713            string = string.split(symbol).join(entity);
1714        }
1715    } else {
1716        string = string.replace(/([\s\S]*?)(&(?:#\d+|#x[\da-f]+|[a-z][\da-z]*);|$)/g, function (ignore, text, entity) {
1717            return self.htmlentities(text, quote_style, charset) + entity;
1718        });
1719    }
1720
1721    return string;
1722}
1723
1724function get_html_translation_table (table, quote_style) {
1725    // Returns the internal translation table used by htmlspecialchars and htmlentities
1726    //
1727    // version: 1107.2516
1728    // discuss at: http://phpjs.org/functions/get_html_translation_table
1729    // +   original by: Philip Peterson
1730    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1731    // +   bugfixed by: noname
1732    // +   bugfixed by: Alex
1733    // +   bugfixed by: Marco
1734    // +   bugfixed by: madipta
1735    // +   improved by: KELAN
1736    // +   improved by: Brett Zamir (http://brett-zamir.me)
1737    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
1738    // +      input by: Frank Forte
1739    // +   bugfixed by: T.Wild
1740    // +      input by: Ratheous
1741    // %          note: It has been decided that we're not going to add global
1742    // %          note: dependencies to php.js, meaning the constants are not
1743    // %          note: real constants, but strings instead. Integers are also supported if someone
1744    // %          note: chooses to create the constants themselves.
1745    // *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
1746    // *     returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
1747    var entities = {},
1748        hash_map = {},
1749        decimal = 0,
1750        symbol = '';
1751    var constMappingTable = {},
1752        constMappingQuoteStyle = {};
1753    var useTable = {},
1754        useQuoteStyle = {};
1755
1756    // Translate arguments
1757    constMappingTable[0] = 'HTML_SPECIALCHARS';
1758    constMappingTable[1] = 'HTML_ENTITIES';
1759    constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
1760    constMappingQuoteStyle[2] = 'ENT_COMPAT';
1761    constMappingQuoteStyle[3] = 'ENT_QUOTES';
1762
1763    useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
1764    useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';
1765
1766    if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
1767        throw new Error("Table: " + useTable + ' not supported');
1768        // return false;
1769    }
1770
1771    entities['38'] = '&amp;';
1772    if (useTable === 'HTML_ENTITIES') {
1773        entities['160'] = '&nbsp;';
1774        entities['161'] = '&iexcl;';
1775        entities['162'] = '&cent;';
1776        entities['163'] = '&pound;';
1777        entities['164'] = '&curren;';
1778        entities['165'] = '&yen;';
1779        entities['166'] = '&brvbar;';
1780        entities['167'] = '&sect;';
1781        entities['168'] = '&uml;';
1782        entities['169'] = '&copy;';
1783        entities['170'] = '&ordf;';
1784        entities['171'] = '&laquo;';
1785        entities['172'] = '&not;';
1786        entities['173'] = '&shy;';
1787        entities['174'] = '&reg;';
1788        entities['175'] = '&macr;';
1789        entities['176'] = '&deg;';
1790        entities['177'] = '&plusmn;';
1791        entities['178'] = '&sup2;';
1792        entities['179'] = '&sup3;';
1793        entities['180'] = '&acute;';
1794        entities['181'] = '&micro;';
1795        entities['182'] = '&para;';
1796        entities['183'] = '&middot;';
1797        entities['184'] = '&cedil;';
1798        entities['185'] = '&sup1;';
1799        entities['186'] = '&ordm;';
1800        entities['187'] = '&raquo;';
1801        entities['188'] = '&frac14;';
1802        entities['189'] = '&frac12;';
1803        entities['190'] = '&frac34;';
1804        entities['191'] = '&iquest;';
1805        entities['192'] = '&Agrave;';
1806        entities['193'] = '&Aacute;';
1807        entities['194'] = '&Acirc;';
1808        entities['195'] = '&Atilde;';
1809        entities['196'] = '&Auml;';
1810        entities['197'] = '&Aring;';
1811        entities['198'] = '&AElig;';
1812        entities['199'] = '&Ccedil;';
1813        entities['200'] = '&Egrave;';
1814        entities['201'] = '&Eacute;';
1815        entities['202'] = '&Ecirc;';
1816        entities['203'] = '&Euml;';
1817        entities['204'] = '&Igrave;';
1818        entities['205'] = '&Iacute;';
1819        entities['206'] = '&Icirc;';
1820        entities['207'] = '&Iuml;';
1821        entities['208'] = '&ETH;';
1822        entities['209'] = '&Ntilde;';
1823        entities['210'] = '&Ograve;';
1824        entities['211'] = '&Oacute;';
1825        entities['212'] = '&Ocirc;';
1826        entities['213'] = '&Otilde;';
1827        entities['214'] = '&Ouml;';
1828        entities['215'] = '&times;';
1829        entities['216'] = '&Oslash;';
1830        entities['217'] = '&Ugrave;';
1831        entities['218'] = '&Uacute;';
1832        entities['219'] = '&Ucirc;';
1833        entities['220'] = '&Uuml;';
1834        entities['221'] = '&Yacute;';
1835        entities['222'] = '&THORN;';
1836        entities['223'] = '&szlig;';
1837        entities['224'] = '&agrave;';
1838        entities['225'] = '&aacute;';
1839        entities['226'] = '&acirc;';
1840        entities['227'] = '&atilde;';
1841        entities['228'] = '&auml;';
1842        entities['229'] = '&aring;';
1843        entities['230'] = '&aelig;';
1844        entities['231'] = '&ccedil;';
1845        entities['232'] = '&egrave;';
1846        entities['233'] = '&eacute;';
1847        entities['234'] = '&ecirc;';
1848        entities['235'] = '&euml;';
1849        entities['236'] = '&igrave;';
1850        entities['237'] = '&iacute;';
1851        entities['238'] = '&icirc;';
1852        entities['239'] = '&iuml;';
1853        entities['240'] = '&eth;';
1854        entities['241'] = '&ntilde;';
1855        entities['242'] = '&ograve;';
1856        entities['243'] = '&oacute;';
1857        entities['244'] = '&ocirc;';
1858        entities['245'] = '&otilde;';
1859        entities['246'] = '&ouml;';
1860        entities['247'] = '&divide;';
1861        entities['248'] = '&oslash;';
1862        entities['249'] = '&ugrave;';
1863        entities['250'] = '&uacute;';
1864        entities['251'] = '&ucirc;';
1865        entities['252'] = '&uuml;';
1866        entities['253'] = '&yacute;';
1867        entities['254'] = '&thorn;';
1868        entities['255'] = '&yuml;';
1869    }
1870
1871    if (useQuoteStyle !== 'ENT_NOQUOTES') {
1872        entities['34'] = '&quot;';
1873    }
1874    if (useQuoteStyle === 'ENT_QUOTES') {
1875        entities['39'] = '&#39;';
1876    }
1877    entities['60'] = '&lt;';
1878    entities['62'] = '&gt;';
1879
1880
1881    // ascii decimals to real symbols
1882    for (decimal in entities) {
1883        symbol = String.fromCharCode(decimal);
1884        hash_map[symbol] = entities[decimal];
1885    }
1886
1887    return hash_map;
1888}
1889
1890function htmlspecialchars (string, quote_style, charset, double_encode) {
1891    // Convert special characters to HTML entities
1892    //
1893    // version: 1107.2516
1894    // discuss at: http://phpjs.org/functions/htmlspecialchars
1895    // +   original by: Mirek Slugen
1896    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1897    // +   bugfixed by: Nathan
1898    // +   bugfixed by: Arno
1899    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1900    // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1901    // +      input by: Ratheous
1902    // +      input by: Mailfaker (http://www.weedem.fr/)
1903    // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
1904    // +      input by: felix
1905    // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1906    // %        note 1: charset argument not supported
1907    // *     example 1: htmlspecialchars("<a href='test'>Test</a>", 'ENT_QUOTES');
1908    // *     returns 1: '&lt;a href=&#039;test&#039;&gt;Test&lt;/a&gt;'
1909    // *     example 2: htmlspecialchars("ab\"c'd", ['ENT_NOQUOTES', 'ENT_QUOTES']);
1910    // *     returns 2: 'ab"c&#039;d'
1911    // *     example 3: htmlspecialchars("my "&entity;" is still here", null, null, false);
1912    // *     returns 3: 'my &quot;&entity;&quot; is still here'
1913    var optTemp = 0,
1914        i = 0,
1915        noquotes = false;
vendor: 6,630 bytes, lines 1916-2077
1916    if (typeof quote_style === 'undefined' || quote_style === null) {
1917        quote_style = 3;    // default for PHP is 2 - using 3 as default for LimeSurvey
1918    }
1919    string = string.toString();
1920    if (double_encode !== false) { // Put this first to avoid double-encoding
1921        string = string.replace(/&/g, '&amp;');
1922    }
1923    string = string.replace(/</g, '&lt;').replace(/>/g, '&gt;');
1924
1925    var OPTS = {
1926        'ENT_NOQUOTES': 0,
1927        'ENT_HTML_QUOTE_SINGLE': 1,
1928        'ENT_HTML_QUOTE_DOUBLE': 2,
1929        'ENT_COMPAT': 2,
1930        'ENT_QUOTES': 3,
1931        'ENT_IGNORE': 4
1932    };
1933    if (quote_style === 0) {
1934        noquotes = true;
1935    }
1936    if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
1937        quote_style = [].concat(quote_style);
1938        for (i = 0; i < quote_style.length; i++) {
1939            // Resolve string input to bitwise e.g. 'ENT_IGNORE' becomes 4
1940            if (OPTS[quote_style[i]] === 0) {
1941                noquotes = true;
1942            }
1943            else if (OPTS[quote_style[i]]) {
1944                optTemp = optTemp | OPTS[quote_style[i]];
1945            }
1946        }
1947        quote_style = optTemp;
1948    }
1949    if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
1950        string = string.replace(/'/g, '&#039;');
1951    }
1952    if (!noquotes) {
1953        string = string.replace(/"/g, '&quot;');
1954    }
1955
1956    return string;
1957}
1958
1959function htmlspecialchars_decode (string, quote_style) {
1960    // Convert special HTML entities back to characters
1961    //
1962    // version: 1107.2516
1963    // discuss at: http://phpjs.org/functions/htmlspecialchars_decode
1964    // +   original by: Mirek Slugen
1965    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1966    // +   bugfixed by: Mateusz "loonquawl" Zalega
1967    // +      input by: ReverseSyntax
1968    // +      input by: Slawomir Kaniecki
1969    // +      input by: Scott Cariss
1970    // +      input by: Francois
1971    // +   bugfixed by: Onno Marsman
1972    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1973    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
1974    // +      input by: Ratheous
1975    // +      input by: Mailfaker (http://www.weedem.fr/)
1976    // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
1977    // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1978    // *     example 1: htmlspecialchars_decode("<p>this -&gt; &quot;</p>", 'ENT_NOQUOTES');
1979    // *     returns 1: '<p>this -> &quot;</p>'
1980    // *     example 2: htmlspecialchars_decode("&amp;quot;");
1981    // *     returns 2: '&quot;'
1982    var optTemp = 0,
1983        i = 0,
1984        noquotes = false;
1985    if (typeof quote_style === 'undefined') {
1986        quote_style = 3;    // default for PHP is 2 - using 3 as default for LimeSurvey
1987    }
1988    string = string.toString().replace(/&lt;/g, '<').replace(/&gt;/g, '>');
1989    var OPTS = {
1990        'ENT_NOQUOTES': 0,
1991        'ENT_HTML_QUOTE_SINGLE': 1,
1992        'ENT_HTML_QUOTE_DOUBLE': 2,
1993        'ENT_COMPAT': 2,
1994        'ENT_QUOTES': 3,
1995        'ENT_IGNORE': 4
1996    };
1997    if (quote_style === 0) {
1998        noquotes = true;
1999    }
2000    if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
2001        quote_style = [].concat(quote_style);
2002        for (i = 0; i < quote_style.length; i++) {
2003            // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
2004            if (OPTS[quote_style[i]] === 0) {
2005                noquotes = true;
2006            } else if (OPTS[quote_style[i]]) {
2007                optTemp = optTemp | OPTS[quote_style[i]];
2008            }
2009        }
2010        quote_style = optTemp;
2011    }
2012    if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
2013        string = string.replace(/&#0*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
2014        // string = string.replace(/&apos;|&#x0*27;/g, "'"); // This would also be useful here, but not a part of PHP
2015    }
2016    if (!noquotes) {
2017        string = string.replace(/&quot;/g, '"');
2018    }
2019    // Put this in last place to avoid escape being double-decoded
2020    string = string.replace(/&amp;/g, '&');
2021
2022    return string;
2023}
2024
2025function ltrim (str, charlist) {
2026    // Strips whitespace from the beginning of a string
2027    //
2028    // version: 1107.2516
2029    // discuss at: http://phpjs.org/functions/ltrim
2030    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2031    // +      input by: Erkekjetter
2032    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2033    // +   bugfixed by: Onno Marsman
2034    // *     example 1: ltrim('    Kevin van Zonneveld    ');
2035    // *     returns 1: 'Kevin van Zonneveld    '
2036    charlist = !charlist ? ' \\s\u00A0' : (charlist + '').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
2037    var re = new RegExp('^[' + charlist + ']+', 'g');
2038    return (str + '').replace(re, '');
2039}
2040
2041function nl2br(str, is_xhtml) {
2042  //  discuss at: http://phpjs.org/functions/nl2br/
2043  // original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2044  // improved by: Philip Peterson
2045  // improved by: Onno Marsman
2046  // improved by: Atli Þór
2047  // improved by: Brett Zamir (http://brett-zamir.me)
2048  // improved by: Maximusya
2049  // bugfixed by: Onno Marsman
2050  // bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2051  //    input by: Brett Zamir (http://brett-zamir.me)
2052  //   example 1: nl2br('Kevin\nvan\nZonneveld');
2053  //   returns 1: 'Kevin<br />\nvan<br />\nZonneveld'
2054  //   example 2: nl2br("\nOne\nTwo\n\nThree\n", false);
2055  //   returns 2: '<br>\nOne<br>\nTwo<br>\n<br>\nThree<br>\n'
2056  //   example 3: nl2br("\nOne\nTwo\n\nThree\n", true);
2057  //   returns 3: '<br />\nOne<br />\nTwo<br />\n<br />\nThree<br />\n'
2058
2059  var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br ' + '/>' : '<br>'; // Adjust comment to avoid issue on phpjs.org display
2060
2061  return (str + '')
2062    .replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
2063}
2064
2065function number_format (number, decimals, dec_point, thousands_sep) {
2066    // http://kevin.vanzonneveld.net
2067    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
2068    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2069    // +     bugfix by: Michael White (http://getsprink.com)
2070    // +     bugfix by: Benjamin Lupton
2071    // +     bugfix by: Allan Jensen (http://www.winternet.no)
2072    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
2073    // +     bugfix by: Howard Yeend
2074    // +    revised by: Luke Smith (http://lucassmith.name)
2075    // +     bugfix by: Diogo Resende
2076    // +     bugfix by: Rival
2077    // +      input by: Kheang Hok Chin (http://www.distantia.ca
2077/)
2078    // +   improved by: davook
2079    // +   improved by: Brett Zamir (http://brett-zamir.me)
2080    // +      input by: Jay Klehr
2081    // +   improved by: Brett Zamir (http://brett-zamir.me)
2082    // +      input by: Amir Habibi (http://www.residence-mixte.com/)
2083    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
2084    // +   improved by: Theriault
2085    // +      input by: Amirouche
2086    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2087    // *     example 1: number_format(1234.56);
2088    // *     returns 1: '1,235'
2089    // *     example 2: number_format(1234.56, 2, ',', ' ');
2090    // *     returns 2: '1 234,56'
2091    // *     example 3: number_format(1234.5678, 2, '.', '');
2092    // *     returns 3: '1234.57'
2093    // *     example 4: number_format(67, 2, ',', '.');
2094    // *     returns 4: '67,00'
2095    // *     example 5: number_format(1000);
2096    // *     returns 5: '1,000'
2097    // *     example 6: number_format(67.311, 2);
2098    // *     returns 6: '67.31'
2099    // *     example 7: number_format(1000.55, 1);
2100    // *     returns 7: '1,000.6'
2101    // *     example 8: number_format(67000, 5, ',', '.');
2102    // *     returns 8: '67.000,00000'
2103    // *     example 9: number_format(0.9, 0);
2104    // *     returns 9: '1'
2105    // *    example 10: number_format('1.20', 2);
2106    // *    returns 10: '1.20'
2107    // *    example 11: number_format('1.20', 4);
2108    // *    returns 11: '1.2000'
2109    // *    example 12: number_format('1.2000', 3);
2110    // *    returns 12: '1.200'
2111    // *    example 13: number_format('1 000,50', 2, '.', ' ');
2112    // *    returns 13: '100 050.00'
2113    // Strip all characters but numerical ones.
2114    number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
2115    var n = !isFinite(+number) ? 0 : +number,
2116        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
2117        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
2118        dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
2119        s = '',
2120        toFixedFix = function (n, prec) {
2121            var k = Math.pow(10, prec);
2122            return '' + Math.round(n * k) / k;
2123        };
2124    // Fix for IE parseFloat(0.55).toFixed(0) = 0;
2125    s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
2126    if (s[0].length > 3) {
2127        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
2128    }
2129    if ((s[1] || '').length < prec) {
2130        s[1] = s[1] || '';
2131        s[1] += new Array(prec - s[1].length + 1).join('0');
2132    }
2133    return s.join(dec);
2134}
2135
2136function quoted_printable_decode (str) {
2137    // Convert a quoted-printable string to an 8 bit string
2138    //
2139    // version: 1107.2516
2140    // discuss at: http://phpjs.org/functions/quoted_printable_decode
2141    // +   original by: Ole Vrijenhoek
2142    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
2143    // +   reimplemented by: Theriault
2144    // +   improved by: Brett Zamir (http://brett-zamir.me)
2145    // +   bugfixed by: Theriault
2146    // *     example 1: quoted_printable_decode('a=3Db=3Dc');
2147    // *     returns 1: 'a=b=c'
2148    // *     example 2: quoted_printable_decode('abc  =20\r\n123  =20\r\n');
2149    // *     returns 2: 'abc   \r\n123   \r\n'
2150    // *     example 3: quoted_printable_decode('012345678901234567890123456789012345678901234567890123456789012345678901234=\r\n56789');
2151    // *     returns 3: '01234567890123456789012345678901234567890123456789012345678901234567890123456789'
2152    // *    example 4: quoted_printable_decode("Lorem ipsum dolor sit amet=23, consectetur adipisicing elit");
2153    // *    returns 4: Lorem ipsum dolor sit amet#, consectetur adipisicing elit
2154    // Removes softline breaks
2155    var RFC2045Decode1 = /=\r\n/gm,
2156        // Decodes all equal signs followed by two hex digits
2157        RFC2045Decode2IN = /=([0-9A-F]{2})/gim,
2158        // the RFC states against decoding lower case encodings, but following apparent PHP behavior
2159        // RFC2045Decode2IN = /=([0-9A-F]{2})/gm,
2160        RFC2045Decode2OUT = function (sMatch, sHex) {
2161            return String.fromCharCode(parseInt(sHex, 16));
2162        };
2163    return str.replace(RFC2045Decode1, '').replace(RFC2045Decode2IN, RFC2045Decode2OUT);
2164}
2165
2166function quoted_printable_encode (str) {
2167    // +   original by: Theriault
2168    // +   improved by: Brett Zamir (http://brett-zamir.me)
2169    // +   improved by: Theriault
2170    // *     example 1: quoted_printable_encode('a=b=c');
2171    // *     returns 1: 'a=3Db=3Dc'
2172    // *     example 2: quoted_printable_encode('abc   \r\n123   \r\n');
2173    // *     returns 2: 'abc  =20\r\n123  =20\r\n'
2174    // *     example 3: quoted_printable_encode('0123456789012345678901234567890123456789012345678901234567890123456789012345');
2175    // *     returns 3: '012345678901234567890123456789012345678901234567890123456789012345678901234=\r\n5'
2176    // RFC 2045: 6.7.2: Octets with decimal values of 33 through 60 (bang to less-than) inclusive, and 62 through 126 (greater-than to tilde), inclusive, MAY be represented as the US-ASCII characters
2177    // PHP does not encode any of the above; as does this function.
2178    // RFC 2045: 6.7.3: Octets with values of 9 and 32 MAY be represented as US-ASCII TAB (HT) and 
2178SPACE characters, respectively, but MUST NOT be so represented at the end of an encoded line
2179    // PHP does not encode spaces (octet 32) except before a CRLF sequence as stated above. PHP always encodes tabs (octet 9). This function replicates PHP.
2180    // RFC 2045: 6.7.4: A line break in a text body, represented as a CRLF sequence in the text canonical form, must be represented by a (RFC 822) line break
2181    // PHP does not encode a CRLF sequence, as does this function.
2182    // RFC 2045: 6.7.5: The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, "soft" line breaks must be used.
2183    // PHP breaks lines greater than 76 characters; as does this function.
2184    var hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'],
2185        RFC2045Encode1IN = / \r\n|\r\n|[^!-<>-~ ]/gm,
2186        RFC2045Encode1OUT = function (sMatch) {
2187            // Encode space before CRLF sequence to prevent spaces from being stripped
2188            // Keep hard line breaks intact; CRLF sequences
2189            if (sMatch.length > 1) {
2190                return sMatch.replace(' ', '=20');
2191            }
2192            // Encode matching character
2193            var chr = sMatch.charCodeAt(0);
2194            return '=' + hexChars[((chr >>> 4) & 15)] + hexChars[(chr & 15)];
2195        },
2196        // Split lines to 75 characters; the reason it's 75 and not 76 is because softline breaks are preceeded by an equal sign; which would be the 76th character.
2197        // However, if the last line/string was exactly 76 characters, then a softline would not be needed. PHP currently softbreaks anyway; so this function replicates PHP.
2198        RFC2045Encode2IN = /.{1,72}(?!\r\n)[^=]{0,3}/g,
2199        RFC2045Encode2OUT = function (sMatch) {
2200            if (sMatch.substr(sMatch.length - 2) === '\r\n') {
2201                return sMatch;
2202            }
2203            return sMatch + '=\r\n';
2204        };
2205    str = str.replace(RFC2045Encode1IN, RFC2045Encode1OUT).replace(RFC2045Encode2IN, RFC2045Encode2OUT);
2206    // Strip last softline break
2207    return str.substr(0, str.length - 3);
2208}
2209
2210function quotemeta (str) {
2211    // Quotes meta characters
2212    //
2213    // version: 1107.2516
2214    // discuss at: http://phpjs.org/functions/quotemeta
2215    // +   original by: Paulo Freitas
2216    // *     example 1: quotemeta(". + * ? ^ ( $ )");
2217    // *     returns 1: '\. \+ \* \? \^ \( \$ \)'
2218    return (str + '').replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1');
2219}
2220
2221function round (value, precision, mode) {
2222    // http://kevin.vanzonneveld.net
2223    // +   original by: Philip Peterson
2224    // +    revised by: Onno Marsman
2225    // +      input by: Greenseed
2226    // +    revised by: T.Wild
2227    // +      input by: meo
2228    // +      input by: William
2229    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
2230    // +      input by: Josep Sanz (http://www.ws3.es/)
2231    // +    revised by: Rafał Kukawski (http://blog.kukawski.pl/)
2232    // %        note 1: Great work. Ideas for improvement:
2233    // %        note 1:  - code more compliant with developer guidelines
2234    // %        note 1:  - for implementing PHP constant arguments look at
2235    // %        note 1:  the pathinfo() function, it offers the greatest
2236    // %        note 1:  flexibility & compatibility possible
2237    // *     example 1: round(1241757, -3);
2238    // *     returns 1: 1242000
2239    // *     example 2: round(3.6);
2240    // *     returns 2: 4
2241    // *     example 3: round(2.835, 2);
2242    // *     returns 3: 2.84
2243    // *     example 4: round(1.1749999999999, 2);
2244    // *     returns 4: 1.17
2245    // *     example 5: round(58551.799999999996, 2);
2246    // *     returns 5: 58551.8
2247    var m, f, isHalf, sgn; // helper variables
2248    precision |= 0; // making sure precision is integer
2249    m = Math.pow(10, precision);
2250    value *= m;
2251    sgn = (value > 0) | -(value < 0); // sign of the number
2252    isHalf = value % 1 === 0.5 * sgn;
2253    f = Math.floor(value);
2254
2255    if (isHalf) {
2256        switch (mode) {
2257        case 'PHP_ROUND_HALF_DOWN':
2258            value = f + (sgn < 0); // rounds .5 toward zero
2259            break;
2260        case 'PHP_ROUND_HALF_EVEN':
2261            value = f + (f % 2 * sgn); // rouds .5 towards the next even integer
2262            break;
2263        case 'PHP_ROUND_HALF_ODD':
2264            value = f + !(f % 2); // rounds .5 towards the next odd integer
2265            break;
2266        default:
2267            value = f + (sgn > 0); // rounds .5 away from zero
2268        }
2269    }
2270
2271    return (isHalf ? value : Math.round(value)) / m;
2272}
2273
2274function rtrim (str, charlist) {
2275    // Removes trailing whitespace
2276    //
2277    // version: 1107.2516
2278    // discuss at: http://phpjs.org/functions/rtrim
2279    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2280    // +      input by: Erkekjetter
2281    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2282    // +   bugfixed by: Onno Marsman
2283    // +   input by: rem
2284    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
2285    // *     example 1: rtrim('    Kevin van Zonneveld    ');
2286    // *     returns 1: '    Kevin van Zonneveld'
2287    charlist = !charlist ? ' \\s\u00A0' : (charlist + '').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\\$1');
2288    var re = new RegExp('[' + charlist + ']+$', 'g');
2289    return (str + '').replace(re, '');
2290}
2291
2292function sprintf () {
2293    // Return a formatted string
2294    //
2295    // version: 1107.2516
2296    // discuss at: http://phpjs.org/functions/sprintf
2297    // +   original by: Ash Searle (http://hexmen.com/blog/)
2298    // + namespaced by: Michael White (http://getsprink.com)
2299    // +    tweaked by: Jack
2300    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2301    // +      input by: Paulo Freitas
2302    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2303    // +      input by: Brett Zamir (http://brett-zamir.me)
2304    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2305    // *     example 1: sprintf("%01.2f", 123.1);
2306    // *     returns 1: 123.10
2307    // *     example 2: sprintf("[%10s]", 'monkey');
2308    // *     returns 2: '[    monkey]'
2309    // *     example 3: sprintf("[%'#10s]", 'monkey');
2310    // *     returns 3: '[####monkey]'
2311    var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
2312    var a = arguments,
2313        i = 0,
2314        format = a[i++];
2315
2316    // pad()
2317    var pad = function (str, len, chr, leftJustify) {
2318        if (!chr) {
2319            chr = ' ';
2320        }
2321        var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
2322        return leftJustify ? str + padding : padding + str;
2323    };
2324
2325    // justify()
2326    var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
2327        var diff = minWidth - value.length;
2328        if (diff > 0) {
2329            if (leftJustify || !zeroPad) {
2330                value = pad(value, minWidth, customPadChar, leftJustify);
2331            } else {
2332                value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
2333            }
2334        }
2335        return value;
2336    };
2337
2338    // formatBaseX()
2339    var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
2340        // Note: casts negative numbers to positive ones
2341        var number = value >>> 0;
2342        prefix = prefix && number && {
2343            '2': '0b',
2344            '8': '0',
2345            '16': '0x'
2346        }[base] || '';
2347        value = prefix + pad(number.toString(base), precision || 0, '0', false);
2348        return justify(value, prefix, leftJustify, minWidth, zeroPad);
2349    };
2350
2351    // formatString()
2352    var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
2353        if (precision != null) {
2354            value = value.slice(0, precision);
2355        }
2356        return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
2357    };
2358
2359    // doFormat()
2360    var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {
2361        var number;
2362        var prefix;
2363        var method;
2364        var textTransform;
2365        var value;
2366
2367        if (substring == '%%') {
2368            return '%';
2369        }
2370
2371        // parse flags
2372        var leftJustify = false,
2373            positivePrefix = '',
2374            zeroPad = false,
2375            prefixBaseX = false,
2376            customPadChar = ' ';
2377        var flagsl = flags.length;
2378        for (var j = 0; flags && j < flagsl; j++) {
2379            switch (flags.charAt(j)) {
2380            case ' ':
2381                positivePrefix = ' ';
2382                break;
2383            case '+':
2384                positivePrefix = '+';
2385                break;
2386            case '-':
2387                leftJustify = true;
2388                break;
2389            case "'":
2390                customPadChar = flags.charAt(j + 1);
2391                break;
2392            case '0':
2393                zeroPad = true;
2394                break;
2395            case '#':
2396                prefixBaseX = true;
2397                break;
2398            }
2399        }
2400
2401        // parameters may be null, undefined, empty-string or real valued
2402        // we want to ignore null, undefined and empty-string values
2403        if (!minWidth) {
2404            minWidth = 0;
2405        } else if (minWidth == '*') {
2406            minWidth = +a[i++];
2407        } else if (minWidth.charAt(0) == '*') {
2408            minWidth = +a[minWidth.slice(1, -1)];
2409        } else {
2410            minWidth = +minWidth;
2411        }
2412
2413        // Note: undocumented perl feature:
2414        if (minWidth < 0) {
2415            minWidth = -minWidth;
2416            leftJustify = true;
2417        }
2418
2419        if (!isFinite(minWidth)) {
2420            throw new Error('sprintf: (minimum-)width must be finite');
2421        }
2422
2423        if (!precision) {
2424            precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
2425        } else if (precision == '*') {
2426            precision = +a[i++];
2427        } else if (precision.charAt(0) == '*') {
2428            precision = +a[precision.slice(1, -1)];
2429        } else {
2430            precision = +precision;
2431        }
2432
2433        // grab value using valueIndex if required?
2434        value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
2435
2436        switch (type) {
2437        case 's':
2438            return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
2439        case 'c':
2440            return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
2441        case 'b':
2442            return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
2443        case 'o':
2444            return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
2445        case 'x':
2446            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
2447        case 'X':
2448            return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
2449        case 'u':
2450            return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
2451        case 'i':
2452        case 'd':
2453            number = (+value) | 0;
2454            prefix = number < 0 ? '-' : positivePrefix;
2455            value = prefix + pad(String(Math.abs(number)), precision, '0', false);
2456            return justify(value, prefix, leftJustify, minWidth, zeroPad);
2457        case 'e':
2458        case 'E':
2459        case 'f':
2460        case 'F':
2461        case 'g':
2462        case 'G':
2463            number = +value;
2464            prefix = number < 0 ? '-' : positivePrefix;
2465            method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
2466            textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
2467            value = prefix + Math.abs(number)[method](precision);
2468            return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
2469        default:
2470            return substring;
2471        }
2472    };
2473
2474    return format.replace(regex, doFormat);
2475}
2476
2477function str_pad (input, pad_length, pad_string, pad_type) {
2478    // Returns input string padded on the left or right to specified length with pad_string
2479    //
2480    // version: 1107.2516
2481    // discuss at: http://phpjs.org/functions/str_pad
2482    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2483    // + namespaced by: Michael White (http://getsprink.com)
2484    // +      input by: Marco van Oort
2485    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
2486    // *     example 1: str_pad('Kevin van Zonneveld', 30, '-=', 'STR_PAD_LEFT');
2487    // *     returns 1: '-=-=-=-=-=-Kevin van Zonneveld'
2488    // *     example 2: str_pad('Kevin van Zonneveld', 30, '-', 'STR_PAD_BOTH');
2489    // *     returns 2: '------Kevin van Zonneveld-----'
2490    var half = '',
2491        pad_to_go;
2492
2493    var str_pad_repeater = function (s, len) {
2494        var collect = '',
2495            i;
2496
2497        while (collect.length < len) {
2498            collect += s;
2499        }
2500        collect = collect.substr(0, len);
2501
2502        return collect;
2503    };
2504
2505    input += '';
2506    pad_string = pad_string !== undefined ? pad_string : ' ';
2507
2508    if (pad_type != 'STR_PAD_LEFT' && pad_type != 'STR_PAD_RIGHT' && pad_type != 'STR_PAD_BOTH') {
2509        pad_type = 'STR_PAD_RIGHT';
2510    }
2511    if ((pad_to_go = pad_length - input.length) > 0) {
2512        if (pad_type == 'STR_PAD_LEFT') {
2513            input = str_pad_repeater(pad_string, pad_to_go) + input;
2514        } else if (pad_type == 'STR_PAD_RIGHT') {
2515            input = input + str_pad_repeater(pad_string, pad_to_go);
2516        } else if (pad_type == 'STR_PAD_BOTH') {
2517            half = str_pad_repeater(pad_string, Math.ceil(pad_to_go / 2));
2518            input = half + input + half;
2519            input = input.substr(0, pad_length);
2520        }
2521    }
2522
2523    return input;
2524}
2525
2526function str_repeat (input, multiplier) {
2527    // Returns the input string repeat mult times
2528    //
2529    // version: 1107.2516
2530    // discuss at: http://phpjs.org/functions/str_repeat
2531    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2532    // +   improved by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
2533    // *     example 1: str_repeat('-=', 10);
2534    // *     returns 1: '-=-=-=-=-=-=-=-=-=-='
2535    return new Array(multiplier + 1).join(input);
2536}
2537
2538function strcasecmp (f_string1, f_string2) {
2539    // Binary safe case-insensitive string comparison
2540    //
2541    // version: 1107.2516
2542    // discuss at: http://phpjs.org/functions/strcasecmp
2543    // +     original by: Martijn Wieringa
2544    // +     bugfixed by: Onno Marsman
2545    // *         example 1: strcasecmp('Hello', 'hello');
2546    // *         returns 1: 0
2547    var string1 = (f_string1 + '').toLowerCase();
2548    var string2 = (f_string2 + '').toLowerCase();
2549
2550    if (string1 > string2) {
2551        return 1;
2552    } else if (string1 == string2) {
2553        return 0;
2554    }
2555
2556    return -1;
2557}
2558
2559function strcmp (str1, str2) {
2560    // Binary safe string comparison
2561    //
2562    // version: 1107.2516
2563    // discuss at: http://phpjs.org/functions/strcmp
2564    // +   original by: Waldo Malqui Silva
2565    // +      input by: Steve Hilder
2566    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2567    // +    revised by: gorthaur
2568    // *     example 1: strcmp( 'waldo', 'owald' );
2569    // *     returns 1: 1
2570    // *     example 2: strcmp( 'owald', 'waldo' );
2571    // *     returns 2: -1
2572    return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
2573}
2574
2575function strip_tags (input, allowed) {
2576    // Strips HTML and PHP tags from a string
2577    //
2578    // version: 1107.2516
2579    // discuss at: http://phpjs.org/functions/strip_tags
2580    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2581    // +   improved by: Luke Godfrey
2582    // +      input by: Pul
2583    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2584    // +   bugfixed by: Onno Marsman
2585    // +      input by: Alex
2586    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2587    // +      input by: Marc Palau
2588    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2589    // +      input by: Brett Zamir (http://brett-zamir.me)
2590    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2591    // +   bugfixed by: Eric Nagel
2592    // +      input by: Bobby Drake
2593    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2594    // +   bugfixed by: Tomasz Wesolowski
2595    // +      input by: Evertjan Garretsen
2596    // +    revised by: Rafał Kukawski (http://blog.kukawski.pl/)
2597    // *     example 1: strip_tags('<p>Kevin</p> <b>van</b> <i>Zonneveld</i>', '<i><b>');
2598    // *     returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>'
2599    // *     example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>');
2600    // *     returns 2: '<p>Kevin van Zonneveld</p>'
2601    // *     example 3: strip_tags("<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>", "<a>");
2602    // *     returns 3: '<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>'
2603    // *     example 4: strip_tags('1 < 5 5 > 1');
2604    // *     returns 4: '1 < 5 5 > 1'
2605    // *     example 5: strip_tags('1 <br/> 1');
2606    // *     returns 5: '1  1'
2607    // *     example 6: strip_tags('1 <br/> 1', '<br>');
2608    // *     returns 6: '1  1'
2609    // *     example 7: strip_tags('1 <br/> 1', '<br><br/>');
2610    // *     returns 7: '1 <br/> 1'
2611    allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
2612    var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
2613        commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
2614    return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
2615        return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
2616    });
2617}
2618
2619function stripslashes (str) {
2620    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2621    // +   improved by: Ates Goral (http://magnetiq.com)
2622    // +      fixed by: Mick@el
2623    // +   improved by: marrtins
2624    // +   bugfixed by: Onno Marsman
2625    // +   improved by: rezna
2626    // +   input by: Rick Waldron
2627    // +   reimplemented by: Brett Zamir (http://brett-zamir.me)
2628    // +   input by: Brant Messenger (http://www.brantmessenger.com/)
2629    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
2630    // *     example 1: stripslashes('Kevin\'s code');
2631    // *     returns 1: "Kevin's code"
2632    // *     example 2: stripslashes('Kevin\\\'s code');
2633    // *     returns 2: "Kevin\'s code"
2634    return (str + '').replace(/\\(.?)/g, function (s, n1) {
2635        switch (n1) {
2636        case '\\':
2637            return '\\';
2638        case '0':
2639            return '\u0000';
2640        case '':
2641            return '';
2642        default:
2643            return n1;
2644        }
2645    });
2646}
2647
2648function stripos (f_haystack, f_needle, f_offset) {
2649    // Finds position of first occurrence of a string within another, case insensitive
2650    //
2651    // version: 1107.2516
2652    // discuss at: http://phpjs.org/functions/stripos
2653    // +     original by: Martijn Wieringa
2654    // +      revised by: Onno Marsman
2655    // *         example 1: stripos('ABC', 'a');
2656    // *         returns 1: 0
2657    var haystack = (f_haystack + '').toLowerCase();
2658    var needle = (f_needle + '').toLowerCase();
2659    var index = 0;
2660
2661    if ((index = haystack.indexOf(needle, f_offset)) !== -1) {
2662        return index;
2663    }
2664    return false;
2665}
2666
2667function stristr (haystack, needle, bool) {
2668    // Finds first occurrence of a string within another, case insensitive
2669    //
2670    // version: 1107.2516
2671    // discuss at: http://phpjs.org/functions/stristr
2672    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2673    // +   bugfxied by: Onno Marsman
2674    // *     example 1: stristr('Kevin van Zonneveld', 'Van');
2675    // *     returns 1: 'van Zonneveld'
2676    // *     example 2: stristr('Kevin van Zonneveld', 'VAN', true);
2677    // *     returns 2: 'Kevin '
2678    var pos = 0;
2679
2680    haystack += '';
2681    pos = haystack.toLowerCase().indexOf((needle + '').toLowerCase());
2682    if (pos == -1) {
2683        return false;
2684    } else {
2685        if (bool) {
2686            return haystack.substr(0, pos);
2687        } else {
2688            return haystack.slice(pos);
2689        }
2690    }
2691}
2692
2693function strrev (string) {
2694    // Reverse a string
2695    //
2696    // version: 1107.2516
2697    // discuss at: http://phpjs.org/functions/strrev
2698    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2699    // +   bugfixed by: Onno Marsman
2700    // +   reimplemented by: Brett Zamir (http://brett-zamir.me)
2701    // *     example 1: strrev('Kevin van Zonneveld');
2702    // *     returns 1: 'dlevennoZ nav niveK'
2703    // *     example 2: strrev('a\u0301haB') === 'Baha\u0301'; // combining
2704    // *     returns 2: true
2705    // *     example 3: strrev('A\uD87E\uDC04Z') === 'Z\uD87E\uDC04A'; // surrogates
2706    // *     returns 2: true
2707    string = string + '';
2708
2709    // Performance will be enhanced with the next two lines of code commented
2710    //      out if you don't care about combining characters
2711    // Keep Unicode combining characters together with the character preceding
2712    //      them and which they are modifying (as in PHP 6)
2713    // See http://unicode.org/reports/tr44/#Property_Table (Me+Mn)
2714    // We also add the low surrogate range at the beginning here so it will be
2715    //      maintained with its preceding high surrogate
2716    var grapheme_extend = /(.)([\uDC00-\uDFFF\u0300-\u036F\u0483-\u0489\u
27160591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065E\u0670\u06D6-\u06DC\u06DE-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0901-\u0903\u093C\u093E-\u094D\u0951-\u0954\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C82\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D02\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B6-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u19B0-\u19C0\u19C8\u19C9\u1A17-\u1A1B\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAA\u1C24-\u1C37\u1DC0-\u1DE6\u1DFE\u1DFF\u20D0-\u20F0\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA67C\uA67D\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA926-\uA92D\uA947-\uA953\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uFB1E\uFE00-\uFE0F\uFE20-\uFE26]+)/g;
2717    string = string.replace(grapheme_extend, '$2$1'); // Temporarily reverse
2718    return string.split('').reverse().join('');
2719}
2720
2721function strstr (haystack, needle, bool) {
2722    // Finds first occurrence of a string within another
2723    //
2724    // version: 1107.2516
2725    // discuss at: http://phpjs.org/functions/strstr
2726    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2727    // +   bugfixed by: Onno Marsman
2728    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
2729    // *     example 1: strstr('Kevin van Zonneveld', 'van');
2730    // *     returns 1: 'van Zonneveld'
2731    // *     example 2: strstr('Kevin van Zonneveld', 'van', true);
2732    // *     returns 2: 'Kevin '
2733    // *     example 3: strstr('[email protected]', '@');
2734    // *     returns 3: '@example.com'
2735    // *     example 4: strstr('[email protected]', '@', true);
2736    // *     returns 4: 'name'
2737    var pos = 0;
2738
2739    haystack += '';
2740    pos = haystack.indexOf(needle);
2741    if (pos == -1) {
2742        return false;
2743    } else {
2744        if (bool) {
2745            return haystack.substr(0, pos);
2746        } else {
2747            return haystack.slice(pos);
2748        }
2749    }
2750}
2751
2752function strtotime (text, now) {
2753 //  discuss at: http://locutus.io/php/strtotime/
2754  // original by: Caio Ariede (http://caioariede.com)
2755  // improved by: Kevin van Zonneveld (http://kvz.io)
2756  // improved by: Caio Ariede (http://caioariede.com)
2757  // improved by: A. Matías Quezada (http://amatiasq.com)
2758  // improved by: preuter
2759  // improved by: Brett Zamir (http://brett-zamir.me)
2760  // improved by: Mirko Faber
2761  //    input by: David
2762  // bugfixed by: Wagner B. Soares
2763  // bugfixed by: Artur Tchernychev
2764  // bugfixed by: Stephan Bösch-Plepelits (http://github.com/plepe)
2765  //      note 1: Examples all have a fixed timestamp to prevent
2766  //      note 1: tests to fail because of variable time(zones)
2767  //   example 1: strtotime('+1 day', 1129633200)
2768  //   returns 1: 1129719600
2769  //   example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200)
2770  //   returns 2: 1130425202
2771  //   example 3: strtotime('last month', 1129633200)
2772  //   returns 3: 1127041200
2773  //   example 4: strtotime('2009-05-04 08:30:00 GMT')
2774  //   returns 4: 1241425800
2775  //   example 5: strtotime('2009-05-04 08:30:00+00')
2776  //   returns 5: 1241425800
2777  //   example 6: strtotime('2009-05-04 08:30:00+02:00')
2778  //   returns 6: 1241418600
2779  //   example 7: strtotime('2009-05-04T08:30:00Z')
2780  //   returns 7: 1241425800
2781
2782  var parsed
2783  var match
2784  var today
2785  var year
2786  var date
2787  var days
2788  var ranges
2789  var len
2790  var times
2791  var regex
2792  var i
2793  var fail = false
2794
2795  if (!text) {
2796    return fail
2797  }
2798
2799  // Unecessary spaces
2800  text = text.replace(/^\s+|\s+$/g, '')
2801    .replace(/\s{2,}/g, ' ')
2802    .replace(/[\t\r\n]/g, '')
2803    .toLowerCase()
2804
2805  // in contrast to php, js Date.parse function interprets:
2806  // dates given as yyyy-mm-dd as in timezone: UTC,
2807  // dates with "." or "-" as MDY instead of DMY
2808  // dates with two-digit years differently
2809  // etc...etc...
2810  // ...therefore we manually parse lots of common date formats
2811  var pattern = new RegExp([
2812    '^(\\d{1,4})',
2813    '([\\-\\.\\/:])',
2814    '(\\d{1,2})',
2815    '([\\-\\.\\/:])',
2816    '(\\d{1,4})',
2817    '(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?',
2818    '(?:\\s([A-Z]+)?)?$'
2819  ].join(''))
2820  match = text.match(pattern)
2821
2822  if (match && match[2] === match[4]) {
2823    if (match[1] > 1901) {
2824      switch (match[2]) {
2825        case '-':
2826          // YYYY-M-D
2827          if (match[3] > 12 || match[5] > 31) {
2828            return fail
2829          }
2830
2831          return new Date(match[1], parseInt(match[3], 10) - 1, match[5],
2832          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2833        case '.':
2834          // YYYY.M.D is not parsed by strtotime()
2835          return fail
2836        case '/':
2837          // YYYY/M/D
2838          if (match[3] > 12 || match[5] > 31) {
2839            return fail
2840          }
2841
2842          return new Date(match[1], parseInt(match[3], 10) - 1, match[5],
2843          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2844      }
2845    } else if (match[5] > 1901) {
2846      switch (match[2]) {
2847        case '-':
2848          // D-M-YYYY
2849          if (match[3] > 12 || match[1] > 31) {
2850            return fail
2851          }
2852
2853          return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
2854          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2855        case '.':
2856          // D.M.YYYY
2857          if (match[3] > 12 || match[1] > 31) {
2858            return fail
2859          }
2860
2861          return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
2862          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2863        case '/':
2864          // M/D/YYYY
2865          if (match[1] > 12 || match[3] > 31) {
2866            return fail
2867          }
2868
2869          return new Date(match[5], parseInt(match[1], 10) - 1, match[3],
2870          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2871      }
2872    } else {
2873      switch (match[2]) {
2874        case '-':
2875          // YY-M-D
2876          if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) {
2877            return fail
2878          }
2879
2880          year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]
2881          return new Date(year, parseInt(match[3], 10) - 1, match[5],
2882          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2883        case '.':
2884          // D.M.YY or H.MM.SS
2885          if (match[5] >= 70) {
2886            // D.M.YY
2887            if (match[3] > 12 || match[1] > 31) {
2888              return fail
2889            }
2890
2891            return new Date(match[5], parseInt(match[3], 10) - 1, match[1],
2892            match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2893          }
2894          if (match[5] < 60 && !match[6]) {
2895            // H.MM.SS
2896            if (match[1] > 23 || match[3] > 59) {
2897              return fail
2898            }
2899
2900            today = new Date()
2901            return new Date(today.getFullYear(), today.getMonth(), today.getDate(),
2902            match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000
2903          }
2904
2905          // invalid format, cannot be parsed
2906          return fail
2907        case '/':
2908          // M/D/YY
2909          if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) {
2910            return fail
2911          }
2912
2913          year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]
2914          return new Date(year, parseInt(match[1], 10) - 1, match[3],
2915          match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000
2916        case ':':
2917          // HH:MM:SS
2918          if (match[1] > 23 || match[3] > 59 || match[5] > 59) {
2919            return fail
2920          }
2921
2922          today = new Date()
2923          return new Date(today.getFullYear(), today.getMonth(), today.getDate(),
2924          match[1] || 0, match[3] || 0, match[5] || 0) / 1000
2925      }
2926    }
2927  }
2928
2929  // other formats and "now" should be parsed by Date.parse()
2930  if (text === 'now') {
2931    return now === null || isNaN(now)
2932      ? new Date().getTime() / 1000 | 0
2933      : now | 0
2934  }
2935  if (!isNaN(parsed = Date.parse(text))) {
2936    return parsed / 1000 | 0
2937  }
2938  // Browsers !== Chrome have problems parsing ISO 8601 date strings, as they do
2939  // not accept lower case characters, space, or shortened time zones.
2940  // Therefore, fix these problems and try again.
2941  // Examples:
2942  //   2015-04-15 20:33:59+02
2943  //   2015-04-15 20:33:59z
2944  //   2015-04-15t20:33:59+02:00
2945  pattern = new RegExp([
2946    '^([0-9]{4}-[0-9]{2}-[0-9]{2})',
2947    '[ t]',
2948    '([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)',
2949    '([\\+-][0-9]{2}(:[0-9]{2})?|z)'
2950  ].join(''))
2951  match = text.match(pattern)
2952  if (match) {
2953    // @todo: time zone information
2954    if (match[4] === 'z') {
2955      match[4] = 'Z'
2956    } else if (match[4].match(/^([\+-][0-9]{2})$/)) {
2957      match[4] = match[4] + ':00'
2958    }
2959
2960    if (!isNaN(parsed = Date.parse(match[1] + 'T' + match[2] + match[4]))) {
2961      return parsed / 1000 | 0
2962    }
2963  }
2964
2965  date = now ? new Date(now * 1000) : new Date()
2966  days = {
2967    'sun': 0,
2968    'mon': 1,
2969    'tue': 2,
2970    'wed': 3,
2971    'thu': 4,
2972    'fri': 5,
2973    'sat': 6
2974  }
2975  ranges = {
2976    'yea': 'FullYear',
2977    'mon': 'Month',
2978    'day': 'Date',
2979    'hou': 'Hours',
2980    'min': 'Minutes',
2981    'sec': 'Seconds'
2982  }
2983
2984  function lastNext (type, range, modifier) {
2985    var diff
2986    var day = days[range]
2987
2988    if (typeof day !== 'undefined') {
2989      diff = day - date.getDay()
2990
2991      if (diff === 0) {
2992        diff = 7 * modifier
2993      } else if (diff > 0 && type === 'last') {
2994        diff -= 7
2995      } else if (diff < 0 && type === 'next') {
2996        diff += 7
2997      }
2998
2999      date.setDate(date.getDate() + diff)
3000    }
3001  }
3002
3003  function process (val) {
3004    // @todo: Reconcile this with regex using \s, taking into account
3005    // browser issues with split and regexes
3006    var splt = val.split(' ')
3007    var type = splt[0]
3008    var range = splt[1].substring(0, 3)
3009    var typeIsNumber = /\d+/.test(type)
3010    var ago = splt[2] === 'ago'
3011    var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1)
3012
3013    if (typeIsNumber) {
3014      num *= parseInt(type, 10)
3015    }
3016
3017    if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) {
3018      return date['set' + ranges[range]](date['get' + ranges[range]]() + num)
3019    }
3020
3021    if (range === 'wee') {
3022      return date.setDate(date.getDate() + (num * 7))
3023    }
3024
3025    if (type === 'next' || type === 'last') {
3026      lastNext(type, range, num)
3027    } else if (!typeIsNumber) {
3028      return false
3029    }
3030
3031    return true
3032  }
3033
3034  times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' +
3035    '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' +
3036    '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'
3037  regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'
3038
3039  match = text.match(new RegExp(regex, 'gi'))
3040  if (!match) {
3041    return fail
3042  }
3043
3044  for (i = 0, len = match.length; i < len; i++) {
3045    if (!process(match[i])) {
3046      return fail
3047    }
3048  }
3049
3050  return (date.getTime() / 1000)
3051}
3052
3053function substr (str, start, len) {
3054    // Returns part of a string
3055    //
3056    // version: 909.322
3057    // discuss at: http://phpjs.org/functions/substr
3058    // +     original by: Martijn Wieringa
3059    // +     bugfixed by: T.Wild
3060    // +      tweaked by: Onno Marsman
3061    // +      revised by: Theriault
3062    // +      improved by: Brett Zamir (http://brett-zamir.me)
3063    // %    note 1: Handles rare Unicode characters if 'unicode.semantics' ini (PHP6) is set to 'on'
3064    // *       example 1: substr('abcdef', 0, -1);
3065    // *       returns 1: 'abcde'
3066    // *       example 2: substr(2, 0, -6);
3067    // *       returns 2: false
3068    // *       example 3: ini_set('unicode.semantics',  'on');
3069    // *       example 3: substr('a\uD801\uDC00', 0, -1);
3070    // *       returns 3: 'a'
3071    // *       example 4: ini_set('unicode.semantics',  'on');
3072    // *       example 4: substr('a\uD801\uDC00', 0, 2);
3073    // *       returns 4: 'a\uD801\uDC00'
3074    // *       example 5: ini_set('unicode.semantics',  'on');
3075    // *       example 5: substr('a\uD801\uDC00', -1, 1);
3076    // *       returns 5: '\uD801\uDC00'
3077    // *       example 6: ini_set('unicode.semantics',  'on');
3078    // *       example 6: substr('a\uD801\uDC00z\uD801\uDC00', -3, 2);
3079    // *       returns 6: '\uD801\uDC00z'
3080    // *       example 7: ini_set('unicode.semantics',  'on');
3081    // *       example 7: substr('a\uD801\uDC00z\uD801\uDC00', -3, -1)
3082    // *       returns 7: '\uD801\uDC00z'
3083    // Add: (?) Use unicode.runtime_encoding (e.g., with string wrapped in "binary" or "Binary" class) to
3084    // allow access of binary (see file_get_contents()) by: charCodeAt(x) & 0xFF (see https://developer.mozilla.org/En/Using_XMLHttpRequest ) or require conversion first?
3085    var i = 0,
3086        allBMP = true,
3087        es = 0,
3088        el = 0,
3089        se = 0,
3090        ret = '';
3091    str += '';
3092    var end = str.length;
3093
3094    // BEGIN REDUNDANT
3095    this.php_js = this.php_js || {};
3096    this.php_js.ini = this.php_js.ini || {};
3097    // END REDUNDANT
3098    switch ((this.php_js.ini['unicode.semantics'] && this.php_js.ini['unicode.semantics'].local_value.toLowerCase())) {
3099    case 'on':
3100        // Full-blown Unicode including non-Basic-Multilingual-Plane characters
3101        // strlen()
3102        for (i = 0; i < str.length; i++) {
3103            if (/[\uD800-\uDBFF]/.test(str.charAt(i)) && /[\uDC00-\uDFFF]/.test(str.charAt(i + 1))) {
3104                allBMP = false;
3105                break;
3106            }
3107        }
3108
3109        if (!allBMP) {
3110            if (start < 0) {
3111                for (i = end - 1, es = (start += end); i >= es; i--) {
3112                    if (/[\uDC00-\uDFFF]/.test(str.charAt(i)) && /[\uD800-\uDBFF]/.test(str.charAt(i - 1))) {
3113                        start--;
3114                        es--;
3115                    }
3116                }
3117            } else {
3118                var surrogatePairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
3119                while ((surrogatePairs.exec(str)) != null) {
3120                    var li = surrogatePairs.lastIndex;
3121                    if (li - 2 < start) {
3122                        start++;
3123                    } else {
3124                        break;
3125                    }
3126                }
3127            }
3128
3129            if (start >= end || start < 0) {
3130                return false;
3131            }
3132            if (len < 0) {
3133                for (i = end - 1, el = (end += len); i >= el; i--) {
3134                    if (/[\uDC00-\uDFFF]/.test(str.charAt(i)) && /[\uD800-\uDBFF]/.test(str.charAt(i - 1))) {
3135                        end--;
3136                        el--;
3137                    }
3138                }
3139                if (start > end) {
3140                    return false;
3141                }
3142                return str.slice(start, end);
3143            } else {
3144                se = start + len;
3145                for (i = start; i < se; i++) {
3146                    ret += str.charAt(i);
3147                    if (/[\uD800-\uDBFF]/.test(str.charAt(i)) && /[\uDC00-\uDFFF]/.test(str.charAt(i + 1))) {
3148                        se++; // Go one further, since one of the "characters" is part of a surrogate pair
3149                    }
3150                }
3151                return ret;
3152            }
3153            break;
3154        }
3155        // Fall-through
3156    case 'off':
3157        // assumes there are no non-BMP characters;
3158        //    if there may be such characters, then it is best to turn it on (critical in true XHTML/XML)
3159    default:
3160        if (start < 0) {
3161            start += end;
3162        }
3163        end = typeof len === 'undefined' ? end : (len < 0 ? len + end : len + start);
3164        // PHP returns false if start does not fall within the string.
3165        // PHP returns false if the calculated end comes before the calculated start.
3166        // PHP returns an empty string if start and end are the same.
3167        // Otherwise, PHP returns the portion of the string from start to end.
3168        return start >= str.length || start < 0 || start > end ? !1 : str.slice(start, end);
3169    }
3170    return undefined; // Please Netbeans
3171}
3172
3173function trim (str, charlist) {
3174// Strips whitespace from the beginning and end of a string
3175//
3176// version: 1107.2516
3177// discuss at: http://phpjs.org/functions/trim
3178// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3179// +   improved by: mdsjack (http://www.mdsjack.bo.it)
3180// +   improved by: Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev)
3181// +      input by: Erkekjetter
3182// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3183// +      input by: DxGx
3184// +   improved by: Steven Levithan (http://blog.stevenlevithan.com)
3185// +    tweaked by: Jack
3186// +   bugfixed by: Onno Marsman
3187// *     example 1: trim('    Kevin van Zonneveld    ');
3188// *     returns 1: 'Kevin van Zonneveld'
3189// *     example 2: trim('Hello World', 'Hdle');
3190// *     returns 2: 'o Wor'
3191// *     example 3: trim(16, 1);
3192// *     returns 3: 6
3193var whitespace, l = 0,
3194i = 0;
3195str += '';
3196
3197if (!charlist) {
3198// default list
3199whitespace = " \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";
3200} else {
3201// preg_quote custom list
3202charlist += '';
3203whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
3204}
3205
3206l = str.length;
3207for (i = 0; i < l; i++) {
3208if (whitespace.indexOf(str.charAt(i)) === -1) {
3209str = str.substring(i);
3210break;
3211}
3212}
3213
3214l = str.length;
3215for (i = l - 1; i >= 0; i--) {
3216if (whitespace.indexOf(str.charAt(i)) === -1) {
3217str = str.substring(0, i + 1);
3218break;
3219}
3220}
3221
3222return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
3223}
3224
3225function ucwords (str) {
3226    // Uppercase the first character of every word in a string
3227    //
3228    // version: 1107.2516
3229    // discuss at: http://phpjs.org/functions/ucwords
3230    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
3231    // +   improved by: Waldo Malqui Silva
3232    // +   bugfixed by: Onno Marsman
3233    // +   improved by: Robin
3234    // +      input by: James (http://www.james-bell.co.uk/)
3235    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3236    // *     example 1: ucwords('kevin van  zonneveld');
3237    // *     returns 1: 'Kevin Van  Zonneveld'
3238    // *     example 2: ucwords('HELLO WORLD');
3239    // *     returns 2: 'HELLO WORLD'
3240    return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) {
3241        return $1.toUpperCase();
3242    });
3243}
3244
3245function checkdate (m, d, y) {
3246    // Returns true(1) if it is a valid date in gregorian calendar
3247    //
3248    // version: 1107.2516
3249    // discuss at: http://phpjs.org/functions/checkdate
3250    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3251    // +   improved by: Pyerre
3252    // +   improved by: Theriault
3253    // *     example 1: checkdate(12, 31, 2000);
3254    // *     returns 1: true
3255    // *     example 2: checkdate(2, 29, 2001);
3256    // *     returns 2: false
3257    // *     example 3: checkdate(3, 31, 2008);
3258    // *     returns 3: true
3259    // *     example 4: checkdate(1, 390, 2000);
3260    // *     returns 4: false
3261    return m > 0 && m < 13 && y > 0 && y < 32768 && d > 0 && d <= (new Date(y, m, 0)).getDate();
3262}
3263
3264function date (format, timestamp) {
3265    // http://kevin.vanzonneveld.net
3266    // +   original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
3267    // +      parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html)
3268    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3269    // +   improved by: MeEtc (http://yass.meetcweb.com)
3270    // +   improved by: Brad Touesnard
3271    // +   improved by: Tim Wiel
3272    // +   improved by: Bryan Elliott
3273    //
3274    // +   improved by: Brett Zamir (http://brett-zamir.me)
3275    // +   improved by: David Randall
3276    // +      input by: Brett Zamir (http://brett-zamir.me)
3277    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3278    // +   improved by: Brett Zamir (http://brett-zamir.me)
3279    // +   improved by: Brett Zamir (http://brett-zamir.me)
3280    // +   improved by: Theriault
3281    // +  derived from: gettimeofday
3282    // +      input by: majak
3283    // +   bugfixed by: majak
3284    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3285    // +      input by: Alex
3286    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
3287    // +   improved by: Theriault
3288    // +   improved by: Brett Zamir (http://brett-zamir.me)
3289    // +   improved by: Theriault
3290    // +   improved by: Thomas Beaucourt (http://www.webapp.fr)
3291    // +   improved by: JT
3292    // +   improved by: Theriault
3293    // +   improved by: Rafał Kukawski (http://blog.kukawski.pl)
3294    // +      input by: Martin
3295    // +      input by: Alex Wilson
3296    // %        note 1: Uses global: php_js to store the default timezone
3297    // %        note 2: Although the function potentially allows timezone info (see notes), it currently does not set
3298    // %        note 2: per a timezone specified by date_default_timezone_set(). Implementers might use
3299    // %        note 2: this.php_js.currentTimezoneOffset and this.php_js.currentTimezoneDST set by that function
3300    // %        note 2: in order to adjust the dates in this function (or our other date functions!) accordingly
3301    // *     example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400);
3302    // *     returns 1: '09:09:40 m is month'
3303    // *     example 2: date('F j, Y, g:i a', 1062462400);
3304    // *     returns 2: 'September 2, 2003, 2:26 am'
3305    // *     example 3: date('Y W o', 1062462400);
3306    // *     returns 3: '2003 36 2003'
3307    // *     example 4: x = date('Y m d', (new Date()).getTime()/1000);
3308    // *     example 4: (x+'').length == 10 // 2009 01 09
3309    // *     returns 4: true
3310    // *     example 5: date('W', 1104534000);
3311    // *     returns 5: '53'
3312    // *     example 6: date('B t', 1104534000);
3313    // *     returns 6: '999 31'
3314    // *     example 7: date('W U', 1293750000.82); // 2010-12-31
3315    // *     returns 7: '52 1293750000'
3316    // *     example 8: date('W', 1293836400); // 2011-01-01
3317    // *     returns 8: '52'
3318    // *     example 9: date('W Y-m-d', 1293974054); // 2011-01-02
3319    // *     returns 9: '52 2011-01-02'
3320    var that = this,
3321        jsdate, f, formatChr = /\\?([a-z])/gi,
3322        formatChrCb,
3323        // Keep this here (works, but for code commented-out
3324        // below for file size reasons)
3325        //, tal= [],
3326        _pad = function (n, c) {
3327            if ((n = n + '').length < c) {
3328                return new Array((++c) - n.length).join('0') + n;
3329            }
3330            return n;
3331        },
3332        txt_words = ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
3332November", "December"];
3333    formatChrCb = function (t, s) {
3334        return f[t] ? f[t]() : s;
3335    };
3336    f = {
3337        // Day
3338        d: function () { // Day of month w/leading 0; 01..31
3339            return _pad(f.j(), 2);
3340        },
3341        D: function () { // Shorthand day name; Mon...Sun
3342            return f.l().slice(0, 3);
3343        },
3344        j: function () { // Day of month; 1..31
3345            return jsdate.getDate();
3346        },
3347        l: function () { // Full day name; Monday...Sunday
3348            return txt_words[f.w()] + 'day';
3349        },
3350        N: function () { // ISO-8601 day of week; 1[Mon]..7[Sun]
3351            return f.w() || 7;
3352        },
3353        S: function () { // Ordinal suffix for day of month; st, nd, rd, th
3354            var j = f.j();
3355            return j > 4 || j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th';
3356        },
3357        w: function () { // Day of week; 0[Sun]..6[Sat]
3358            return jsdate.getDay();
3359        },
3360        z: function () { // Day of year; 0..365
3361            var a = new Date(f.Y(), f.n() - 1, f.j()),
3362                b = new Date(f.Y(), 0, 1);
3363            return Math.round((a - b) / 864e5) + 1;
3364        },
3365
3366        // Week
3367        W: function () { // ISO-8601 week number
3368            var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3),
3369                b = new Date(a.getFullYear(), 0, 4);
3370            return _pad(1 + Math.round((a - b) / 864e5 / 7), 2);
3371        },
3372
3373        // Month
3374        F: function () { // Full month name; January...December
3375            return txt_words[6 + f.n()];
3376        },
3377        m: function () { // Month w/leading 0; 01...12
3378            return _pad(f.n(), 2);
3379        },
3380        M: function () { // Shorthand month name; Jan...Dec
3381            return f.F().slice(0, 3);
3382        },
3383        n: function () { // Month; 1...12
3384            return jsdate.getMonth() + 1;
3385        },
3386        t: function () { // Days in month; 28...31
3387            return (new Date(f.Y(), f.n(), 0)).getDate();
3388        },
3389
3390        // Year
3391        L: function () { // Is leap year?; 0 or 1
3392            return new Date(f.Y(), 1, 29).getMonth() === 1 | 0;
3393        },
3394        o: function () { // ISO-8601 year
3395            var n = f.n(),
3396                W = f.W(),
3397                Y = f.Y();
3398            return Y + (n === 12 && W < 9 ? -1 : n === 1 && W > 9);
3399        },
3400        Y: function () { // Full year; e.g. 1980...2010
3401            return jsdate.getFullYear();
3402        },
3403        y: function () { // Last two digits of year; 00...99
3404            return (f.Y() + "").slice(-2);
3405        },
3406
3407        // Time
3408        a: function () { // am or pm
3409            return jsdate.getHours() > 11 ? "pm" : "am";
3410        },
3411        A: function () { // AM or PM
3412            return f.a().toUpperCase();
3413        },
3414        B: function () { // Swatch Internet time; 000..999
3415            var H = jsdate.getUTCHours() * 36e2,
3416                // Hours
3417                i = jsdate.getUTCMinutes() * 60,
3418                // Minutes
3419                s = jsdate.getUTCSeconds(); // Seconds
3420            return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3);
3421        },
3422        g: function () { // 12-Hours; 1..12
3423            return f.G() % 12 || 12;
3424        },
3425        G: function () { // 24-Hours; 0..23
3426            return jsdate.getHours();
3427        },
3428        h: function () { // 12-Hours w/leading 0; 01..12
3429            return _pad(f.g(), 2);
3430        },
3431        H: function () { // 24-Hours w/leading 0; 00..23
3432            return _pad(f.G(), 2);
3433        },
3434        i: function () { // Minutes w/leading 0; 00..59
3435            return _pad(jsdate.getMinutes(), 2);
3436        },
3437        s: function () { // Seconds w/leading 0; 00..59
3438            return _pad(jsdate.getSeconds(), 2);
3439        },
3440        u: function () { // Microseconds; 000000-999000
3441            return _pad(jsdate.getMilliseconds() * 1000, 6);
3442        },
3443
3444        // Timezone
3445        e: function () { // Timezone identifier; e.g. Atlantic/Azores, ...
3446            // The following works, but requires inclusion of the very large
3447            // timezone_abbreviations_list() function.
3448/*              return this.date_default_timezone_get();
vendor: 6,651 bytes, lines 3449-3617
3449*/
3450            throw 'Not supported (see source code of date() for timezone on how to add support)';
3451        },
3452        I: function () { // DST observed?; 0 or 1
3453            // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
3454            // If they are not equal, then DST is observed.
3455            var a = new Date(f.Y(), 0),
3456                // Jan 1
3457                c = Date.UTC(f.Y(), 0),
3458                // Jan 1 UTC
3459                b = new Date(f.Y(), 6),
3460                // Jul 1
3461                d = Date.UTC(f.Y(), 6); // Jul 1 UTC
3462            return 0 + ((a - c) !== (b - d));
3463        },
3464        O: function () { // Difference to GMT in hour format; e.g. +0200
3465            var a = jsdate.getTimezoneOffset();
3466            return (a > 0 ? "-" : "+") + _pad(Math.abs(a / 60 * 100), 4);
3467        },
3468        P: function () { // Difference to GMT w/colon; e.g. +02:00
3469            var O = f.O();
3470            return (O.substr(0, 3) + ":" + O.substr(3, 2));
3471        },
3472        T: function () { // Timezone abbreviation; e.g. EST, MDT, ...
3473            // The following works, but requires inclusion of the very
3474            // large timezone_abbreviations_list() function.
3475/*              var abbr = '', i = 0, os = 0, default = 0;
3476            if (!tal.length) {
3477                tal = that.timezone_abbreviations_list();
3478            }
3479            if (that.php_js && that.php_js.default_timezone) {
3480                default = that.php_js.default_timezone;
3481                for (abbr in tal) {
3482                    for (i=0; i < tal[abbr].length; i++) {
3483                        if (tal[abbr][i].timezone_id === default) {
3484                            return abbr.toUpperCase();
3485                        }
3486                    }
3487                }
3488            }
3489            for (abbr in tal) {
3490                for (i = 0; i < tal[abbr].length; i++) {
3491                    os = -jsdate.getTimezoneOffset() * 60;
3492                    if (tal[abbr][i].offset === os) {
3493                        return abbr.toUpperCase();
3494                    }
3495                }
3496            }
3497*/
3498            return 'UTC';
3499        },
3500        Z: function () { // Timezone offset in seconds (-43200...50400)
3501            return -jsdate.getTimezoneOffset() * 60;
3502        },
3503
3504        // Full Date/Time
3505        c: function () { // ISO-8601 date.
3506            return 'Y-m-d\\Th:i:sP'.replace(formatChr, formatChrCb);
3507        },
3508        r: function () { // RFC 2822
3509            return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb);
3510        },
3511        U: function () { // Seconds since UNIX epoch
3512            return jsdate.getTime() / 1000 | 0;
3513        }
3514    };
3515    this.date = function (format, timestamp) {
3516        that = this;
3517        jsdate = ((typeof timestamp === 'undefined') ? new Date() : // Not provided
3518        (timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
3519        new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
3520        );
3521        return format.replace(formatChr, formatChrCb);
3522    };
3523    return this.date(format, timestamp);
3524}
3525
3526function gmdate (format, timestamp) {
3527    // Format a GMT date/time
3528    //
3529    // version: 1107.2516
3530    // discuss at: http://phpjs.org/functions/gmdate
3531    // +   original by: Brett Zamir (http://brett-zamir.me)
3532    // +   input by: Alex
3533    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
3534    // -    depends on: date
3535    // *     example 1: gmdate('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400); // Return will depend on your timezone
3536    // *     returns 1: '07:09:40 m is month'
3537    var dt = typeof timestamp === 'undefined' ? new Date() : // Not provided
3538                        typeof timestamp === 'object' ? new Date(timestamp) : // Javascript Date()
3539                        new Date(timestamp * 1000); // UNIX timestamp (auto-convert to int)
3540    timestamp = Date.parse(dt.toUTCString().slice(0, -4)) / 1000;
3541    return this.date(format, timestamp);
3542}
3543
3544function idate (format, timestamp) {
3545    // Format a local time/date as integer
3546    //
3547    // version: 1107.2516
3548    // discuss at: http://phpjs.org/functions/idate
3549    // +   original by: Brett Zamir (http://brett-zamir.me)
3550    // +      input by: Alex
3551    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
3552    // +   improved by: Theriault
3553    // +  derived from: date
3554    // +  derived from: gettimeofday
3555    // *     example 1: idate('y');
3556    // *     returns 1: 9
3557    if (format === undefined) {
3558        throw 'idate() expects at least 1 parameter, 0 given';
3559    }
3560    if (!format.length || format.length > 1) {
3561        throw 'idate format is one char';
3562    }
3563
3564    // Fix: Need to allow date_default_timezone_set() (check for this.php_js.default_timezone and use)
3565    var date = ((typeof timestamp === 'undefined') ? new Date() : // Not provided
3566    (timestamp instanceof Date) ? new Date(timestamp) : // Javascript Date()
3567    new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
3568    ),
3569        a;
3570
3571    switch (format) {
3572    case 'B':
3573        return Math.floor(((date.getUTCHours() * 36e2) + (date.getUTCMinutes() * 60) + date.getUTCSeconds() + 36e2) / 86.4) % 1e3;
3574    case 'd':
3575        return date.getDate();
3576    case 'h':
3577        return date.getHours() % 12 || 12;
3578    case 'H':
3579        return date.getHours();
3580    case 'i':
3581        return date.getMinutes();
3582    case 'I':
3583        // capital 'i'
3584        // Logic derived from getimeofday().
3585        // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
3586        // If they are not equal, then DST is observed.
3587        a = date.getFullYear();
3588        return 0 + (((new Date(a, 0)) - Date.UTC(a, 0)) !== ((new Date(a, 6)) - Date.UTC(a, 6)));
3589    case 'L':
3590        a = date.getFullYear();
3591        return (!(a & 3) && (a % 1e2 || !(a % 4e2))) ? 1 : 0;
3592    case 'm':
3593        return date.getMonth() + 1;
3594    case 's':
3595        return date.getSeconds();
3596    case 't':
3597        return (new Date(date.getFullYear(), date.getMonth() + 1, 0)).getDate();
3598    case 'U':
3599        return Math.round(date.getTime() / 1000);
3600    case 'w':
3601        return date.getDay();
3602    case 'W':
3603        a = new Date(date.getFullYear(), date.getMonth(), date.getDate() - (date.getDay() || 7) + 3);
3604        return 1 + Math.round((a - (new Date(a.getFullYear(), 0, 4))) / 864e5 / 7);
3605    case 'y':
3606        return parseInt((date.getFullYear() + '').slice(2), 10); // This function returns an integer, unlike date()
3607    case 'Y':
3608        return date.getFullYear();
3609    case 'z':
3610        return Math.floor((date - new Date(date.getFullYear(), 0, 1)) / 864e5);
3611    case 'Z':
3612        return -date.getTimezoneOffset() * 60;
3613    default:
3614        throw 'Unrecognized date format token';
3615    }
3616}
3617
3618function mktime () {
3619    // Get UNIX timestamp for a date
3620    //
3621    // version: 1107.2516
3622    // discuss at: http://phpjs.org/functions/mktime
3623    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3624    // +   improved by: baris ozdil
3625    // +      input by: gabriel paderni
3626    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3627    // +   improved by: FGFEmperor
3628    // +      input by: Yannoo
3629    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3630    // +      input by: jakes
3631    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3632    // +   bugfixed by: Marc Palau
3633    // +   improved by: Brett Zamir (http://brett-zamir.me)
3634    // +      input by: 3D-GRAF
3635    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
3636    // +      input by: Chris
3637    // +    revised by: Theriault
3638    // %        note 1: The return values of the following examples are
3639    // %        note 1: received only if your system's timezone is UTC.
3640    // *     example 1: mktime(14, 10, 2, 2, 1, 2008);
3641    // *     returns 1: 1201875002
3642    // *     example 2: mktime(0, 0, 0, 0, 1, 2008);
3643    // *     returns 2: 1196467200
3644    // *     example 3: make = mktime();
3645    // *     example 3: td = new Date();
3646    // *     example 3: real = Math.floor(td.getTime() / 1000);
3647    // *     example 3: diff = (real - make);
3648    // *     results 3: diff < 5
3649    // *     example 4: mktime(0, 0, 0, 13, 1, 1997)
3650    // *     returns 4: 883612800
3651    // *     example 5: mktime(0, 0, 0, 1, 1, 1998)
3652    // *     returns 5: 883612800
3653    // *     example 6: mktime(0, 0, 0, 1, 1, 98)
3654    // *     returns 6: 883612800
3655    // *     example 7: mktime(23, 59, 59, 13, 0, 2010)
3656    // *     returns 7: 1293839999
3657    // *     example 8: mktime(0, 0, -1, 1, 1, 1970)
3658    // *     returns 8: -1
3659    var d = new Date(),
3660        r = arguments,
3661        i = 0,
3662        e = ['Hours', 'Minutes', 'Seconds', 'Month', 'Date', 'FullYear'];
3663
3664    for (i = 0; i < e.length; i++) {
3665        if (typeof r[i] === 'undefined') {
3666            r[i] = d['get' + e[i]]();
3667            r[i] += (i === 3); // +1 to fix JS months.
3668        } else {
3669            r[i] = parseInt(r[i], 10);
3670            if (isNaN(r[i])) {
3671                return false;
3672            }
3673        }
3674    }
3675
3676    // Map years 0-69 to 2000-2069 and years 70-100 to 1970-2000.
3677    r[5] += (r[5] >= 0 ? (r[5] <= 69 ? 2e3 : (r[5] <= 100 ? 1900 : 0)) : 0);
3678
3679    // Set year, month (-1 to fix JS months), and date.
3680    // !This must come before the call to setHours!
3681    d.setFullYear(r[5], r[3] - 1, r[4]);
3682
3683    // Set hours, minutes, and seconds.
3684    d.setHours(r[0], r[1], r[2]);
3685
3686    // Divide milliseconds by 1000 to return seconds and drop decimal.
3687    // Add 1 second if negative or it'll be off from PHP by 1 second.
3688    return (d.getTime() / 1e3 >> 0) - (d.getTime() < 0);
3689}
3690
3691function rand (min, max) {
3692    // http://kevin.vanzonneveld.net
3693    // +   original by: Leslie Hoare
3694    // +   bugfixed by: Onno Marsman
3695    // %          note 1: See the commented out code below for a version which will work with our experimental (though probably unnecessary) srand() function)
3696    // *     example 1: rand(1, 1);
3697    // *     returns 1: 1
3698    var argc = arguments.length;
3699    if (argc === 0) {
3700        min = 0;
3701        max = 2147483647;
3702    } else if (argc === 1) {
3703        throw new Error('Warning: rand() expects exactly 2 parameters, 1 given');
3704    }
3705    return Math.floor(Math.random() * (max - min + 1)) + min;
3706}
3707
3708function time () {
3709    // Return current UNIX timestamp
3710    //
3711    // version: 1107.2516
3712    // discuss at: http://phpjs.org/functions/time
3713    // +   original by: GeekFG (http://geekfg.blogspot.com)
3714    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
3715    // +   improved by: metjay
3716    // +   improved by: HKM
3717    // *     example 1: timeStamp = time();
3718    // *     results 1: timeStamp > 1000000000 && timeStamp < 2000000000
3719    return Math.floor(new Date().getTime() / 1000);
3720}
3721
3722/**
3723 * Updates the repeated headings in a dynamic table.
3724 * @param {string} questionId
3725 * @param {number} rep        - Repetition
3726 */
3727function updateHeadings(questionId, rep)
3728{
3729    var tab = $('#' + questionId).find('table.question');
3730    tab.find('.repeat').remove();
3731    var header = tab.find('thead>tr');
3732    var trs = tab.find('tr:visible');
3733    trs.each(function(i, tr)
3734    {
3735        // add heading but not for the first and the last rows
3736        if(i != 0 && i % rep == 0 && i != trs.length-1)
3737        {
3738            header.clone().addClass('repeat').addClass('headings').insertAfter(tr);
3739        }
3740    });
3741}
3742
3743/**
3744 * Updates the colors in a dynamic table.
3745 * No use of jQuery in this function due to speed reasons.
3746 * Get all "#questionId table.question tr:visible" and reset
3747 * class array1/array2.
3748 * @param {string} questionId
3749 */
3750function updateColors(questionId)
3751{
3752    var tab = $('#' + questionId).find('table.question');
3753    var trs = tab.find('tr:visible');
3754    trs.each(function(i, tr)
3755    {
3756        // fix line colors
3757        $(tr).removeClass('array1').removeClass('array2').addClass('array' + (1 + i % 2));
3758    });
3759}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.