PageSourceSearch

https://knowledgecheckr.at/tmp/assets/2bbbfab9/em_javascript.js

js knowledgecheckr.at collected 2026-10-06 07:59:43 UTC 132,895 bytes, 3,513 lines download raw bytes

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

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.