From e39ad3d4d1b1683adc1d692066f91b451f4eef43 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Sat, 28 Sep 2013 21:24:21 +0200 Subject: [PATCH] Dev Fixed bug where insufficient permissions would return invalid session key status. --- .../remotecontrol/remotecontrol_handle.php | 54 +- .../ExpressionManager/js/em_javascript.js | 2895 +++++++++++++++++ 2 files changed, 2925 insertions(+), 24 deletions(-) create mode 100644 application/libraries/ExpressionManager/js/em_javascript.js diff --git a/application/helpers/remotecontrol/remotecontrol_handle.php b/application/helpers/remotecontrol/remotecontrol_handle.php index b22df89b01f..36cf649de9c 100644 --- a/application/helpers/remotecontrol/remotecontrol_handle.php +++ b/application/helpers/remotecontrol/remotecontrol_handle.php @@ -1939,31 +1939,37 @@ public function list_surveys($sSessionKey, $sUser=NULL) public function list_users($sSessionKey = null) { - if ($this->_checkSessionKey($sSessionKey)) - { - if( Permission::model()->hasGlobalPermission('superadmin','read') ) - { - $users = User::model()->findAll(); - - if(count($users)==0) - return array('status' => 'No surveys found'); - - foreach ($users as $user) - { - $attributes = $user->attributes; - - foreach ($user->permissions as $permission) - { - $attributes['permissions'][] = $permission->attributes; - } - unset($attributes['password']); - $data[] = $attributes; - } - return $data; - } - else + if ($this->_checkSessionKey($sSessionKey)) + { + if( Permission::model()->hasGlobalPermission('superadmin','read') ) + { + $users = User::model()->findAll(); + + if(count($users)==0) + return array('status' => 'No surveys found'); + + foreach ($users as $user) + { + $attributes = $user->attributes; + + foreach ($user->permissions as $permission) + { + $attributes['permissions'][] = $permission->attributes; + } + unset($attributes['password']); + $data[] = $attributes; + } + return $data; + } + else + { + return array('status' => 'Permission denied.'); + } + } + else + { return array('status' => 'Invalid session key'); - } + } } /** * RPC routine to to initialise the survey's collection of tokens where new participant tokens may be later added. diff --git a/application/libraries/ExpressionManager/js/em_javascript.js b/application/libraries/ExpressionManager/js/em_javascript.js new file mode 100644 index 00000000000..108362cf4da --- /dev/null +++ b/application/libraries/ExpressionManager/js/em_javascript.js @@ -0,0 +1,2895 @@ +/* + * This file is part of LimeSurvey + * Copyright (C) 2007-2013 The LimeSurvey Project Team / Carsten Schmitz + * All rights reserved. + * License: GNU/GPL License v2 or later, see LICENSE.php + * LimeSurvey is free software. This version may have been modified pursuant + * to the GNU General Public License, and as distributed it includes or + * is derivative of works licensed under the GNU General Public License or + * other free or open source software licenses. + * See COPYRIGHT.php for copyright notices and details. + * + * Core JavaScript functions needed by ExpressionManager + * @author Thomas M. White (TMSWhite) + * @author Denis Chenu (Shnoulle) + * + * Portion from php.js is copyright 2012 Kevin van Zonneveld. + * php.js is dual licensed under the MIT licenses. + */ + +function LEMcount() +{ + // takes variable number of arguments - returns count of those arguments that are not null/empty + var result=0; + for (i=0;i=': case 'ge': if (arg >= value) { ++result; } break; + case '>': case 'gt': if (arg > value) { ++result; } break; + case '<=': case 'le': if (arg <= value) { ++result; } break; + case '<': case 'lt': if (arg < value) { ++result; } break; + case '!=': case 'ne': if (arg != value) { ++result; } break; + case 'RX': { + try { + if (reg.test(arg)) { + ++result; + } + } + catch (err) { + return false; + } + } + } + } + return result; +} + +function LEMsumifop() +{ + // takes variable number of arguments - returns sum of answered questions which meet the criteria (arg op value) + var result=0; + var op=arguments[0]; + var value=arguments[1]; + if (op == 'RX') { + var reg = new RegExp(value.substr(1,value.length-2)); + } + for (i=2;i=': case 'ge': if (arg >= value) { result += arg; } break; + case '>': case 'gt': if (arg > value) { result += arg; } break; + case '<=': case 'le': if (arg <= value) { result += arg; } break; + case '<': case 'lt': if (arg < value) { result += arg; } break; + case '!=': case 'ne': if (arg != value) { result += arg; } break; + case 'RX': { + try { + if (reg.test(arg)) { + result += arg; + } + } + catch (err) { + return false; + } + } + } + } + return result; +} + +function LEMpi() +{ + return Math.PI; +} + +function LEMsum() +{ + // takes variable number of arguments, returns their sum + var result=0; + for (i=0;i 1) { + result += joiner + arg; + } + else { + result += arg; + } + ++j; + } + } + return result; +} + +/** + * Returns Natural logarithm of a number + */ + +function LEMlog() +{ + // takes variable number of arguments + if (arguments.length < 1) { + return NaN; + } + var base=Math.exp(1); + if(arguments.length>1){ + base = arguments[1]; + if (isNaN(base)) { return NaN;} + if (base<=0 ) { return NaN;} + base=Math.abs(parseFloat(arguments[1])); + } + if(base==Math.exp(1)){// Not needed + return Math.log(arguments[0]); + }else{ + return Math.log(arguments[0])/Math.log(base); + } +} + + /** + * Returns concatenates list + */ +function LEMjoin() +{ + var result=""; + for (i=0;i 1) { + result += joiner + arg; + } + else { + result += arg; + } + } + return result; +} + +/* + * Returns true if within matches the pattern. Pattern must start and end with the '/' character + */ +function LEMregexMatch(pattern,within) +{ + try { + var reg = new RegExp(pattern.substr(1,pattern.length-2)); + return reg.test(within); + } + catch (err) { + return false; + } +} + +function LEMstrlen(a) +{ + var str = new String(a); + return str.length; +} + +function LEMstr_replace(needle, replace, haystack) +{ + var str = new String(haystack); + return str.replace(needle, replace); +} + +function LEMstrpos(haystack,needle) +{ + var str = new String(haystack); + return str.search(needle); +} + +function LEMempty(v) +{ + if (v === "" || v === false) { + return true; + } + return false; +} + +function LEMbool(v) +{ + bool = new Boolean(v); + if (v.valueOf() && v != 'false') { + return true; // fix for JavaScript native processing that considers the value "false" to be true + } + return false; +} + +/** + * Match even if different types - especially true vs. 1, and false vs. 0 or blank + */ +function LEMeq(a,b) +{ + if ((a==="true" && b==="1") || (a==="1" && b==="true")) { + return true; + } + if ((a==="false" && (b==="0" || b==="")) || ((a==="0" || a==="") && b ==="false")) { + return true; + } + return a==b; +} + +/** + * Return the value for data element jsName, treating it as blank if its question is irrelevant on the current page. + * Also convert the string 'false' to '' to cope with a JavaScript type casting issue + */ +function LEMval(alias) +{ + // first find out whether it is using a suffix + var str = new String(alias); + var varName = alias; + var suffix = 'code'; // the default + + /* If passed a number, return that number */ + if (str == '') return ''; + newval = str; + if (LEMradix === ',') { + newval = str.split(',').join('.'); + } + if (newval == parseFloat(newval)) { + if (newval.length > 0 && newval[0]==0) { + return newval; // so keep 0 prefixes on numbers + } + return +newval; + } + + if (str.match(/^INSERTANS:/)) { + suffix = 'shown'; + varName = varName.substr(10); + } + else if (str.match(/\.(code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value)$/)) { + varName = str.replace(/\.(code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value)$/,'') + suffix = str.replace(/^(.+)\./,''); + } + + jsName = LEMalias2varName[varName]; + attr = LEMvarNameAttr[jsName]; + if ((suffix.match(/^code|NAOK|shown|valueNAOK|value$/)) && attr.qid!='') { + if (!LEMval(varName + '.relevanceStatus')) { + return ''; + } + } + var whichJsName; // correct name whether on- or off-page + if (LEMmode=='survey' || (LEMmode=='group' && attr.gseq == LEMgseq) || (LEMmode=='question' && attr.qid == LEMqid)) { + whichJsName = (typeof attr.jsName_on === 'undefined') ? attr.jsName : attr.jsName_on; + } + else { + whichJsName = attr.jsName; + } + if (whichJsName === null || typeof document.getElementById(whichJsName) === 'undefined' || document.getElementById(whichJsName) === null) { + an_error = true; // this line is here to make debugging easier + return ''; + } + + // values should always be stored encoded with htmlspecialchars() + switch (suffix) { + case 'relevanceStatus': { + grel = qrel = sgqarel = 1; + if (!(typeof attr.gseq === 'undefined') && !(document.getElementById('relevanceG' + attr.gseq) === null)) { + grel = parseInt(document.getElementById('relevanceG' + attr.gseq).value); + } + if (!(typeof attr.qid === 'undefined') && !(document.getElementById('relevance' + attr.qid) === null)) { + qrel = parseInt(document.getElementById('relevance' + attr.qid).value); + } + if (!(typeof attr.rowdivid === 'undefined') && !(document.getElementById('relevance' + attr.rowdivid) === null)) { + sgqarel = parseInt(document.getElementById('relevance' + attr.rowdivid).value); + } + return (grel && qrel && sgqarel); + } + case 'shown': { + value = htmlspecialchars_decode(document.getElementById(whichJsName).value); + switch(attr.type) + { + case 'G': //GENDER drop-down list + case 'Y': //YES/NO radio-buttons + case 'C': //ARRAY (YES/UNCERTAIN/NO) radio-buttons + case 'E': //ARRAY (Increase/Same/Decrease) radio-buttons + shown = (typeof attr.answers[value] === 'undefined') ? '' : attr.answers[value]; + break; + case '!': //List - dropdown + case 'L': //LIST drop-down/radio-button list + case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea + case 'H': //ARRAY (Flexible) - Column Format + case 'F': //ARRAY (Flexible) - Row Format + case 'R': //RANKING STYLE + if (attr.type == 'O' && varName.match(/comment$/)) { + answer = value; + } + else if ((attr.type == 'L' || attr.type == '!') && varName.match(/_other$/)) { + answer = value; + } + else { + which_ans = '0~' + value; + if (typeof attr.answers[which_ans] === 'undefined') { + answer = value; + } + else { + answerParts = attr.answers[which_ans].split('|'); + answerParts.shift(); // remove the first element + answer = answerParts.join('|'); + } + } + shown = answer; + break; + case '1': //Array (Flexible Labels) dual scale // need scale + prefix = (attr.jsName.match(/_1$/)) ? '1' : '0'; + which_ans = prefix + '~' + value; + if (typeof attr.answers[which_ans] === 'undefined') { + answer = ''; + } + else { + answerParts = attr.answers[which_ans].split('|'); + answerParts.shift(); // remove the first element + answer = answerParts.join('|'); + } + shown = answer; + break; + case 'A': //ARRAY (5 POINT CHOICE) radio-buttons + case 'B': //ARRAY (10 POINT CHOICE) radio-buttons + case ':': //ARRAY (Multi Flexi) 1 to 10 + case '5': //5 POINT CHOICE radio-buttons + case 'N': //NUMERICAL QUESTION TYPE + case 'K': //MULTIPLE NUMERICAL QUESTION + case 'Q': //MULTIPLE SHORT TEXT + case ';': //ARRAY (Multi Flexi) Text + case 'S': //SHORT FREE TEXT + case 'T': //LONG FREE TEXT + case 'U': //HUGE FREE TEXT + case 'D': //DATE + case '*': //Equation + case 'I': //Language Question + case '|': //File Upload + case 'X': //BOILERPLATE QUESTION + shown = value; // what about "no answer"? + break; + case 'M': //Multiple choice checkbox + case 'P': //Multiple choice with comments checkbox + text + if (typeof attr.question === 'undefined' || value == '') { + shown = ''; + } + else { + if (attr.type == 'P' && varName.match(/comment$/)) { + shown = value; + } + else { + shown = htmlspecialchars_decode(attr.question); + } + } + break; + } + } + return htmlspecialchars_decode(shown); + case 'gid': + return attr.gid; + case 'grelevance': + return attr.grelevance; + case 'mandatory': + return attr.mandatory; + case 'qid': + return attr.qid; + case 'question': + return htmlspecialchars_decode(attr.question); + case 'readWrite': + return attr.readWrite; + case 'relevance': + return htmlspecialchars_decode(attr.relevance); + case 'sgqa': + return attr.sgqa; + case 'type': + return attr.type; + case 'gseq': + return attr.gseq; + case 'qseq': + return attr.qseq; + case 'jsName': + return whichJsName; + case 'code': + case 'NAOK': + case 'value': + case 'valueNAOK': + { + value = htmlspecialchars_decode(document.getElementById(whichJsName).value); + if (value === '') { + return ''; + } + + if (suffix == 'value' || suffix == 'valueNAOK') { + // if in assessment mode, this returns the assessment value + // in non-assessment mode, this is identical to .code + switch (attr.type) { + case '!': //List - dropdown + case 'L': //LIST drop-down/radio-button list + case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea + case 'H': //ARRAY (Flexible) - Column Format + case 'F': //ARRAY (Flexible) - Row Format + case 'R': //RANKING STYLE + if (attr.type == 'O' && varName.match(/comment$/)) { +// value = value; + } + else if ((attr.type == 'L' || attr.type == '!') && varName.match(/_other$/)) { +// value = value; + } + else { + which_ans = '0~' + value; + if (typeof attr.answers[which_ans] === 'undefined') { + value = ''; + } + else { + answerParts = attr.answers[which_ans].split('|'); + value = answerParts[0]; + } + } + break; + case '1': //Array (Flexible Labels) dual scale // need scale + prefix = (attr.jsName.match(/_1$/)) ? '1' : '0'; + which_ans = prefix + '~' + value; + if (typeof attr.answers[which_ans] === 'undefined') { + value = ''; + } + else { + answerParts = attr.answers[which_ans].split('|'); + value = answerParts[0]; + } + break; + } + } + + if (typeof attr.onlynum !== 'undefined' && attr.onlynum==1) { + newval = value; + if (LEMradix === ',') { + newval = value.split(',').join('.'); + } + if (newval != parseFloat(newval)) { + newval = ''; + } + return +newval; + } + else if (isNaN(value)) { + if (value==='false') { + return ''; // so Boolean operations will treat it as false. In JavaScript, Boolean("false") is true since "false" is not a zero-length string + } + return value; + } + else { + if (value.length > 0 && value[0]==0) { + return value; // so keep 0 prefixes on numbers + } + return +value; // convert it to numeric return type + } + } + case 'rowdivid': + if (typeof attr.rowdivid === 'undefined' || attr.rowdivid == '') { + return ''; + } + return attr.rowdivid; + default: + return 'Unknown Attribute: ' . suffix; + } +} + +/** Display number with comma as radix separator, if needed + */ +function LEMfixnum(value) +{ + var newval = String(value); + if (parseFloat(newval) != value) { + return value; // unchanged + } + if (LEMradix===',') { + newval = newval.split('.').join(','); + return newval; + } + return value; +} + +/* + * Remove HTML and PHP tags from string + */ +function LEMstrip_tags(htmlString) +{ + var tmp = document.createElement("DIV"); + tmp.innerHTML = htmlString; + return tmp.textContent||tmp.innerText; +} + +/** + * Compute sample standard deviation of the input variables + */ +function LEMstddev() +{ + vals = new Array(); + j = 0; + for (i=0;i': '>'} + var entities = {}, + hash_map = {}, + decimal = 0, + symbol = ''; + var constMappingTable = {}, + constMappingQuoteStyle = {}; + var useTable = {}, + useQuoteStyle = {}; + + // Translate arguments + constMappingTable[0] = 'HTML_SPECIALCHARS'; + constMappingTable[1] = 'HTML_ENTITIES'; + constMappingQuoteStyle[0] = 'ENT_NOQUOTES'; + constMappingQuoteStyle[2] = 'ENT_COMPAT'; + constMappingQuoteStyle[3] = 'ENT_QUOTES'; + + useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS'; + useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT'; + + if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') { + throw new Error("Table: " + useTable + ' not supported'); + // return false; + } + + entities['38'] = '&'; + if (useTable === 'HTML_ENTITIES') { + entities['160'] = ' '; + entities['161'] = '¡'; + entities['162'] = '¢'; + entities['163'] = '£'; + entities['164'] = '¤'; + entities['165'] = '¥'; + entities['166'] = '¦'; + entities['167'] = '§'; + entities['168'] = '¨'; + entities['169'] = '©'; + entities['170'] = 'ª'; + entities['171'] = '«'; + entities['172'] = '¬'; + entities['173'] = '­'; + entities['174'] = '®'; + entities['175'] = '¯'; + entities['176'] = '°'; + entities['177'] = '±'; + entities['178'] = '²'; + entities['179'] = '³'; + entities['180'] = '´'; + entities['181'] = 'µ'; + entities['182'] = '¶'; + entities['183'] = '·'; + entities['184'] = '¸'; + entities['185'] = '¹'; + entities['186'] = 'º'; + entities['187'] = '»'; + entities['188'] = '¼'; + entities['189'] = '½'; + entities['190'] = '¾'; + entities['191'] = '¿'; + entities['192'] = 'À'; + entities['193'] = 'Á'; + entities['194'] = 'Â'; + entities['195'] = 'Ã'; + entities['196'] = 'Ä'; + entities['197'] = 'Å'; + entities['198'] = 'Æ'; + entities['199'] = 'Ç'; + entities['200'] = 'È'; + entities['201'] = 'É'; + entities['202'] = 'Ê'; + entities['203'] = 'Ë'; + entities['204'] = 'Ì'; + entities['205'] = 'Í'; + entities['206'] = 'Î'; + entities['207'] = 'Ï'; + entities['208'] = 'Ð'; + entities['209'] = 'Ñ'; + entities['210'] = 'Ò'; + entities['211'] = 'Ó'; + entities['212'] = 'Ô'; + entities['213'] = 'Õ'; + entities['214'] = 'Ö'; + entities['215'] = '×'; + entities['216'] = 'Ø'; + entities['217'] = 'Ù'; + entities['218'] = 'Ú'; + entities['219'] = 'Û'; + entities['220'] = 'Ü'; + entities['221'] = 'Ý'; + entities['222'] = 'Þ'; + entities['223'] = 'ß'; + entities['224'] = 'à'; + entities['225'] = 'á'; + entities['226'] = 'â'; + entities['227'] = 'ã'; + entities['228'] = 'ä'; + entities['229'] = 'å'; + entities['230'] = 'æ'; + entities['231'] = 'ç'; + entities['232'] = 'è'; + entities['233'] = 'é'; + entities['234'] = 'ê'; + entities['235'] = 'ë'; + entities['236'] = 'ì'; + entities['237'] = 'í'; + entities['238'] = 'î'; + entities['239'] = 'ï'; + entities['240'] = 'ð'; + entities['241'] = 'ñ'; + entities['242'] = 'ò'; + entities['243'] = 'ó'; + entities['244'] = 'ô'; + entities['245'] = 'õ'; + entities['246'] = 'ö'; + entities['247'] = '÷'; + entities['248'] = 'ø'; + entities['249'] = 'ù'; + entities['250'] = 'ú'; + entities['251'] = 'û'; + entities['252'] = 'ü'; + entities['253'] = 'ý'; + entities['254'] = 'þ'; + entities['255'] = 'ÿ'; + } + + if (useQuoteStyle !== 'ENT_NOQUOTES') { + entities['34'] = '"'; + } + if (useQuoteStyle === 'ENT_QUOTES') { + entities['39'] = '''; + } + entities['60'] = '<'; + entities['62'] = '>'; + + + // ascii decimals to real symbols + for (decimal in entities) { + symbol = String.fromCharCode(decimal); + hash_map[symbol] = entities[decimal]; + } + + return hash_map; +} + +function htmlspecialchars (string, quote_style, charset, double_encode) { + // Convert special characters to HTML entities + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/htmlspecialchars + // + original by: Mirek Slugen + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Nathan + // + bugfixed by: Arno + // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + input by: Ratheous + // + input by: Mailfaker (http://www.weedem.fr/) + // + reimplemented by: Brett Zamir (http://brett-zamir.me) + // + input by: felix + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // % note 1: charset argument not supported + // * example 1: htmlspecialchars("Test", 'ENT_QUOTES'); + // * returns 1: '<a href='test'>Test</a>' + // * example 2: htmlspecialchars("ab\"c'd", ['ENT_NOQUOTES', 'ENT_QUOTES']); + // * returns 2: 'ab"c'd' + // * example 3: htmlspecialchars("my "&entity;" is still here", null, null, false); + // * returns 3: 'my "&entity;" is still here' + var optTemp = 0, + i = 0, + noquotes = false; + if (typeof quote_style === 'undefined' || quote_style === null) { + quote_style = 3; // default for PHP is 2 - using 3 as default for LimeSurvey + } + string = string.toString(); + if (double_encode !== false) { // Put this first to avoid double-encoding + string = string.replace(/&/g, '&'); + } + string = string.replace(//g, '>'); + + var OPTS = { + 'ENT_NOQUOTES': 0, + 'ENT_HTML_QUOTE_SINGLE': 1, + 'ENT_HTML_QUOTE_DOUBLE': 2, + 'ENT_COMPAT': 2, + 'ENT_QUOTES': 3, + 'ENT_IGNORE': 4 + }; + if (quote_style === 0) { + noquotes = true; + } + if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags + quote_style = [].concat(quote_style); + for (i = 0; i < quote_style.length; i++) { + // Resolve string input to bitwise e.g. 'ENT_IGNORE' becomes 4 + if (OPTS[quote_style[i]] === 0) { + noquotes = true; + } + else if (OPTS[quote_style[i]]) { + optTemp = optTemp | OPTS[quote_style[i]]; + } + } + quote_style = optTemp; + } + if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) { + string = string.replace(/'/g, '''); + } + if (!noquotes) { + string = string.replace(/"/g, '"'); + } + + return string; +} + +function htmlspecialchars_decode (string, quote_style) { + // Convert special HTML entities back to characters + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/htmlspecialchars_decode + // + original by: Mirek Slugen + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Mateusz "loonquawl" Zalega + // + input by: ReverseSyntax + // + input by: Slawomir Kaniecki + // + input by: Scott Cariss + // + input by: Francois + // + bugfixed by: Onno Marsman + // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + input by: Ratheous + // + input by: Mailfaker (http://www.weedem.fr/) + // + reimplemented by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // * example 1: htmlspecialchars_decode("

this -> "

", 'ENT_NOQUOTES'); + // * returns 1: '

this -> "

' + // * example 2: htmlspecialchars_decode("&quot;"); + // * returns 2: '"' + var optTemp = 0, + i = 0, + noquotes = false; + if (typeof quote_style === 'undefined') { + quote_style = 3; // default for PHP is 2 - using 3 as default for LimeSurvey + } + string = string.toString().replace(/</g, '<').replace(/>/g, '>'); + var OPTS = { + 'ENT_NOQUOTES': 0, + 'ENT_HTML_QUOTE_SINGLE': 1, + 'ENT_HTML_QUOTE_DOUBLE': 2, + 'ENT_COMPAT': 2, + 'ENT_QUOTES': 3, + 'ENT_IGNORE': 4 + }; + if (quote_style === 0) { + noquotes = true; + } + if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags + quote_style = [].concat(quote_style); + for (i = 0; i < quote_style.length; i++) { + // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4 + if (OPTS[quote_style[i]] === 0) { + noquotes = true; + } else if (OPTS[quote_style[i]]) { + optTemp = optTemp | OPTS[quote_style[i]]; + } + } + quote_style = optTemp; + } + if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) { + string = string.replace(/�*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should + // string = string.replace(/'|�*27;/g, "'"); // This would also be useful here, but not a part of PHP + } + if (!noquotes) { + string = string.replace(/"/g, '"'); + } + // Put this in last place to avoid escape being double-decoded + string = string.replace(/&/g, '&'); + + return string; +} + +function ltrim (str, charlist) { + // Strips whitespace from the beginning of a string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/ltrim + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Erkekjetter + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // * example 1: ltrim(' Kevin van Zonneveld '); + // * returns 1: 'Kevin van Zonneveld ' + charlist = !charlist ? ' \\s\u00A0' : (charlist + '').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1'); + var re = new RegExp('^[' + charlist + ']+', 'g'); + return (str + '').replace(re, ''); +} + +function nl2br (str, is_xhtml) { + // Converts newlines to HTML line breaks + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/nl2br + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Philip Peterson + // + improved by: Onno Marsman + // + improved by: Atli Þór + // + bugfixed by: Onno Marsman + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: Maximusya + // * example 1: nl2br('Kevin\nvan\nZonneveld'); + // * returns 1: 'Kevin\nvan\nZonneveld' + // * example 2: nl2br("\nOne\nTwo\n\nThree\n", false); + // * returns 2: '
\nOne
\nTwo
\n
\nThree
\n' + // * example 3: nl2br("\nOne\nTwo\n\nThree\n", true); + // * returns 3: '\nOne\nTwo\n\nThree\n' + var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '' : '
'; + + return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2'); +} + +function number_format (number, decimals, dec_point, thousands_sep) { + // http://kevin.vanzonneveld.net + // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfix by: Michael White (http://getsprink.com) + // + bugfix by: Benjamin Lupton + // + bugfix by: Allan Jensen (http://www.winternet.no) + // + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // + bugfix by: Howard Yeend + // + revised by: Luke Smith (http://lucassmith.name) + // + bugfix by: Diogo Resende + // + bugfix by: Rival + // + input by: Kheang Hok Chin (http://www.distantia.ca/) + // + improved by: davook + // + improved by: Brett Zamir (http://brett-zamir.me) + // + input by: Jay Klehr + // + improved by: Brett Zamir (http://brett-zamir.me) + // + input by: Amir Habibi (http://www.residence-mixte.com/) + // + bugfix by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // + input by: Amirouche + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: number_format(1234.56); + // * returns 1: '1,235' + // * example 2: number_format(1234.56, 2, ',', ' '); + // * returns 2: '1 234,56' + // * example 3: number_format(1234.5678, 2, '.', ''); + // * returns 3: '1234.57' + // * example 4: number_format(67, 2, ',', '.'); + // * returns 4: '67,00' + // * example 5: number_format(1000); + // * returns 5: '1,000' + // * example 6: number_format(67.311, 2); + // * returns 6: '67.31' + // * example 7: number_format(1000.55, 1); + // * returns 7: '1,000.6' + // * example 8: number_format(67000, 5, ',', '.'); + // * returns 8: '67.000,00000' + // * example 9: number_format(0.9, 0); + // * returns 9: '1' + // * example 10: number_format('1.20', 2); + // * returns 10: '1.20' + // * example 11: number_format('1.20', 4); + // * returns 11: '1.2000' + // * example 12: number_format('1.2000', 3); + // * returns 12: '1.200' + // * example 13: number_format('1 000,50', 2, '.', ' '); + // * returns 13: '100 050.00' + // Strip all characters but numerical ones. + number = (number + '').replace(/[^0-9+\-Ee.]/g, ''); + var n = !isFinite(+number) ? 0 : +number, + prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), + sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, + dec = (typeof dec_point === 'undefined') ? '.' : dec_point, + s = '', + toFixedFix = function (n, prec) { + var k = Math.pow(10, prec); + return '' + Math.round(n * k) / k; + }; + // Fix for IE parseFloat(0.55).toFixed(0) = 0; + s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.'); + if (s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); + } + if ((s[1] || '').length < prec) { + s[1] = s[1] || ''; + s[1] += new Array(prec - s[1].length + 1).join('0'); + } + return s.join(dec); +} + +function quoted_printable_decode (str) { + // Convert a quoted-printable string to an 8 bit string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/quoted_printable_decode + // + original by: Ole Vrijenhoek + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + reimplemented by: Theriault + // + improved by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Theriault + // * example 1: quoted_printable_decode('a=3Db=3Dc'); + // * returns 1: 'a=b=c' + // * example 2: quoted_printable_decode('abc =20\r\n123 =20\r\n'); + // * returns 2: 'abc \r\n123 \r\n' + // * example 3: quoted_printable_decode('012345678901234567890123456789012345678901234567890123456789012345678901234=\r\n56789'); + // * returns 3: '01234567890123456789012345678901234567890123456789012345678901234567890123456789' + // * example 4: quoted_printable_decode("Lorem ipsum dolor sit amet=23, consectetur adipisicing elit"); + // * returns 4: Lorem ipsum dolor sit amet#, consectetur adipisicing elit + // Removes softline breaks + var RFC2045Decode1 = /=\r\n/gm, + // Decodes all equal signs followed by two hex digits + RFC2045Decode2IN = /=([0-9A-F]{2})/gim, + // the RFC states against decoding lower case encodings, but following apparent PHP behavior + // RFC2045Decode2IN = /=([0-9A-F]{2})/gm, + RFC2045Decode2OUT = function (sMatch, sHex) { + return String.fromCharCode(parseInt(sHex, 16)); + }; + return str.replace(RFC2045Decode1, '').replace(RFC2045Decode2IN, RFC2045Decode2OUT); +} + +function quoted_printable_encode (str) { + // + original by: Theriault + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // * example 1: quoted_printable_encode('a=b=c'); + // * returns 1: 'a=3Db=3Dc' + // * example 2: quoted_printable_encode('abc \r\n123 \r\n'); + // * returns 2: 'abc =20\r\n123 =20\r\n' + // * example 3: quoted_printable_encode('0123456789012345678901234567890123456789012345678901234567890123456789012345'); + // * returns 3: '012345678901234567890123456789012345678901234567890123456789012345678901234=\r\n5' + // 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 + // PHP does not encode any of the above; as does this function. + // RFC 2045: 6.7.3: Octets with values of 9 and 32 MAY be represented as US-ASCII TAB (HT) and SPACE characters, respectively, but MUST NOT be so represented at the end of an encoded line + // 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. + // 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 + // PHP does not encode a CRLF sequence, as does this function. + // 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. + // PHP breaks lines greater than 76 characters; as does this function. + var hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'], + RFC2045Encode1IN = / \r\n|\r\n|[^!-<>-~ ]/gm, + RFC2045Encode1OUT = function (sMatch) { + // Encode space before CRLF sequence to prevent spaces from being stripped + // Keep hard line breaks intact; CRLF sequences + if (sMatch.length > 1) { + return sMatch.replace(' ', '=20'); + } + // Encode matching character + var chr = sMatch.charCodeAt(0); + return '=' + hexChars[((chr >>> 4) & 15)] + hexChars[(chr & 15)]; + }, + // 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. + // 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. + RFC2045Encode2IN = /.{1,72}(?!\r\n)[^=]{0,3}/g, + RFC2045Encode2OUT = function (sMatch) { + if (sMatch.substr(sMatch.length - 2) === '\r\n') { + return sMatch; + } + return sMatch + '=\r\n'; + }; + str = str.replace(RFC2045Encode1IN, RFC2045Encode1OUT).replace(RFC2045Encode2IN, RFC2045Encode2OUT); + // Strip last softline break + return str.substr(0, str.length - 3); +} + +function quotemeta (str) { + // Quotes meta characters + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/quotemeta + // + original by: Paulo Freitas + // * example 1: quotemeta(". + * ? ^ ( $ )"); + // * returns 1: '\. \+ \* \? \^ \( \$ \)' + return (str + '').replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1'); +} + +function round (value, precision, mode) { + // http://kevin.vanzonneveld.net + // + original by: Philip Peterson + // + revised by: Onno Marsman + // + input by: Greenseed + // + revised by: T.Wild + // + input by: meo + // + input by: William + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + input by: Josep Sanz (http://www.ws3.es/) + // + revised by: Rafał Kukawski (http://blog.kukawski.pl/) + // % note 1: Great work. Ideas for improvement: + // % note 1: - code more compliant with developer guidelines + // % note 1: - for implementing PHP constant arguments look at + // % note 1: the pathinfo() function, it offers the greatest + // % note 1: flexibility & compatibility possible + // * example 1: round(1241757, -3); + // * returns 1: 1242000 + // * example 2: round(3.6); + // * returns 2: 4 + // * example 3: round(2.835, 2); + // * returns 3: 2.84 + // * example 4: round(1.1749999999999, 2); + // * returns 4: 1.17 + // * example 5: round(58551.799999999996, 2); + // * returns 5: 58551.8 + var m, f, isHalf, sgn; // helper variables + precision |= 0; // making sure precision is integer + m = Math.pow(10, precision); + value *= m; + sgn = (value > 0) | -(value < 0); // sign of the number + isHalf = value % 1 === 0.5 * sgn; + f = Math.floor(value); + + if (isHalf) { + switch (mode) { + case 'PHP_ROUND_HALF_DOWN': + value = f + (sgn < 0); // rounds .5 toward zero + break; + case 'PHP_ROUND_HALF_EVEN': + value = f + (f % 2 * sgn); // rouds .5 towards the next even integer + break; + case 'PHP_ROUND_HALF_ODD': + value = f + !(f % 2); // rounds .5 towards the next odd integer + break; + default: + value = f + (sgn > 0); // rounds .5 away from zero + } + } + + return (isHalf ? value : Math.round(value)) / m; +} + +function rtrim (str, charlist) { + // Removes trailing whitespace + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/rtrim + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Erkekjetter + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + input by: rem + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // * example 1: rtrim(' Kevin van Zonneveld '); + // * returns 1: ' Kevin van Zonneveld' + charlist = !charlist ? ' \\s\u00A0' : (charlist + '').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '\\$1'); + var re = new RegExp('[' + charlist + ']+$', 'g'); + return (str + '').replace(re, ''); +} + +function sprintf () { + // Return a formatted string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/sprintf + // + original by: Ash Searle (http://hexmen.com/blog/) + // + namespaced by: Michael White (http://getsprink.com) + // + tweaked by: Jack + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Paulo Freitas + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: sprintf("%01.2f", 123.1); + // * returns 1: 123.10 + // * example 2: sprintf("[%10s]", 'monkey'); + // * returns 2: '[ monkey]' + // * example 3: sprintf("[%'#10s]", 'monkey'); + // * returns 3: '[####monkey]' + var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g; + var a = arguments, + i = 0, + format = a[i++]; + + // pad() + var pad = function (str, len, chr, leftJustify) { + if (!chr) { + chr = ' '; + } + var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); + return leftJustify ? str + padding : padding + str; + }; + + // justify() + var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) { + var diff = minWidth - value.length; + if (diff > 0) { + if (leftJustify || !zeroPad) { + value = pad(value, minWidth, customPadChar, leftJustify); + } else { + value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); + } + } + return value; + }; + + // formatBaseX() + var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) { + // Note: casts negative numbers to positive ones + var number = value >>> 0; + prefix = prefix && number && { + '2': '0b', + '8': '0', + '16': '0x' + }[base] || ''; + value = prefix + pad(number.toString(base), precision || 0, '0', false); + return justify(value, prefix, leftJustify, minWidth, zeroPad); + }; + + // formatString() + var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) { + if (precision != null) { + value = value.slice(0, precision); + } + return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar); + }; + + // doFormat() + var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) { + var number; + var prefix; + var method; + var textTransform; + var value; + + if (substring == '%%') { + return '%'; + } + + // parse flags + var leftJustify = false, + positivePrefix = '', + zeroPad = false, + prefixBaseX = false, + customPadChar = ' '; + var flagsl = flags.length; + for (var j = 0; flags && j < flagsl; j++) { + switch (flags.charAt(j)) { + case ' ': + positivePrefix = ' '; + break; + case '+': + positivePrefix = '+'; + break; + case '-': + leftJustify = true; + break; + case "'": + customPadChar = flags.charAt(j + 1); + break; + case '0': + zeroPad = true; + break; + case '#': + prefixBaseX = true; + break; + } + } + + // parameters may be null, undefined, empty-string or real valued + // we want to ignore null, undefined and empty-string values + if (!minWidth) { + minWidth = 0; + } else if (minWidth == '*') { + minWidth = +a[i++]; + } else if (minWidth.charAt(0) == '*') { + minWidth = +a[minWidth.slice(1, -1)]; + } else { + minWidth = +minWidth; + } + + // Note: undocumented perl feature: + if (minWidth < 0) { + minWidth = -minWidth; + leftJustify = true; + } + + if (!isFinite(minWidth)) { + throw new Error('sprintf: (minimum-)width must be finite'); + } + + if (!precision) { + precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined; + } else if (precision == '*') { + precision = +a[i++]; + } else if (precision.charAt(0) == '*') { + precision = +a[precision.slice(1, -1)]; + } else { + precision = +precision; + } + + // grab value using valueIndex if required? + value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; + + switch (type) { + case 's': + return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar); + case 'c': + return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad); + case 'b': + return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad); + case 'o': + return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad); + case 'x': + return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad); + case 'X': + return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase(); + case 'u': + return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad); + case 'i': + case 'd': + number = (+value) | 0; + prefix = number < 0 ? '-' : positivePrefix; + value = prefix + pad(String(Math.abs(number)), precision, '0', false); + return justify(value, prefix, leftJustify, minWidth, zeroPad); + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + number = +value; + prefix = number < 0 ? '-' : positivePrefix; + method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; + textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; + value = prefix + Math.abs(number)[method](precision); + return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform](); + default: + return substring; + } + }; + + return format.replace(regex, doFormat); +} + +function str_pad (input, pad_length, pad_string, pad_type) { + // Returns input string padded on the left or right to specified length with pad_string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/str_pad + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + namespaced by: Michael White (http://getsprink.com) + // + input by: Marco van Oort + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // * example 1: str_pad('Kevin van Zonneveld', 30, '-=', 'STR_PAD_LEFT'); + // * returns 1: '-=-=-=-=-=-Kevin van Zonneveld' + // * example 2: str_pad('Kevin van Zonneveld', 30, '-', 'STR_PAD_BOTH'); + // * returns 2: '------Kevin van Zonneveld-----' + var half = '', + pad_to_go; + + var str_pad_repeater = function (s, len) { + var collect = '', + i; + + while (collect.length < len) { + collect += s; + } + collect = collect.substr(0, len); + + return collect; + }; + + input += ''; + pad_string = pad_string !== undefined ? pad_string : ' '; + + if (pad_type != 'STR_PAD_LEFT' && pad_type != 'STR_PAD_RIGHT' && pad_type != 'STR_PAD_BOTH') { + pad_type = 'STR_PAD_RIGHT'; + } + if ((pad_to_go = pad_length - input.length) > 0) { + if (pad_type == 'STR_PAD_LEFT') { + input = str_pad_repeater(pad_string, pad_to_go) + input; + } else if (pad_type == 'STR_PAD_RIGHT') { + input = input + str_pad_repeater(pad_string, pad_to_go); + } else if (pad_type == 'STR_PAD_BOTH') { + half = str_pad_repeater(pad_string, Math.ceil(pad_to_go / 2)); + input = half + input + half; + input = input.substr(0, pad_length); + } + } + + return input; +} + +function str_repeat (input, multiplier) { + // Returns the input string repeat mult times + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/str_repeat + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // * example 1: str_repeat('-=', 10); + // * returns 1: '-=-=-=-=-=-=-=-=-=-=' + return new Array(multiplier + 1).join(input); +} + +function strcasecmp (f_string1, f_string2) { + // Binary safe case-insensitive string comparison + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/strcasecmp + // + original by: Martijn Wieringa + // + bugfixed by: Onno Marsman + // * example 1: strcasecmp('Hello', 'hello'); + // * returns 1: 0 + var string1 = (f_string1 + '').toLowerCase(); + var string2 = (f_string2 + '').toLowerCase(); + + if (string1 > string2) { + return 1; + } else if (string1 == string2) { + return 0; + } + + return -1; +} + +function strcmp (str1, str2) { + // Binary safe string comparison + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/strcmp + // + original by: Waldo Malqui Silva + // + input by: Steve Hilder + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + revised by: gorthaur + // * example 1: strcmp( 'waldo', 'owald' ); + // * returns 1: 1 + // * example 2: strcmp( 'owald', 'waldo' ); + // * returns 2: -1 + return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1)); +} + +function strip_tags (input, allowed) { + // Strips HTML and PHP tags from a string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/strip_tags + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Luke Godfrey + // + input by: Pul + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + input by: Alex + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Marc Palau + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Eric Nagel + // + input by: Bobby Drake + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Tomasz Wesolowski + // + input by: Evertjan Garretsen + // + revised by: Rafał Kukawski (http://blog.kukawski.pl/) + // * example 1: strip_tags('

Kevin

van Zonneveld', ''); + // * returns 1: 'Kevin van Zonneveld' + // * example 2: strip_tags('

Kevin van Zonneveld

', '

'); + // * returns 2: '

Kevin van Zonneveld

' + // * example 3: strip_tags("Kevin van Zonneveld", ""); + // * returns 3: 'Kevin van Zonneveld' + // * example 4: strip_tags('1 < 5 5 > 1'); + // * returns 4: '1 < 5 5 > 1' + // * example 5: strip_tags('1
1'); + // * returns 5: '1 1' + // * example 6: strip_tags('1
1', '
'); + // * returns 6: '1 1' + // * example 7: strip_tags('1
1', '

'); + // * returns 7: '1
1' + allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase () + var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, + commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi; + return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) { + return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; + }); +} + +function stripslashes (str) { + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Ates Goral (http://magnetiq.com) + // + fixed by: Mick@el + // + improved by: marrtins + // + bugfixed by: Onno Marsman + // + improved by: rezna + // + input by: Rick Waldron + // + reimplemented by: Brett Zamir (http://brett-zamir.me) + // + input by: Brant Messenger (http://www.brantmessenger.com/) + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // * example 1: stripslashes('Kevin\'s code'); + // * returns 1: "Kevin's code" + // * example 2: stripslashes('Kevin\\\'s code'); + // * returns 2: "Kevin\'s code" + return (str + '').replace(/\\(.?)/g, function (s, n1) { + switch (n1) { + case '\\': + return '\\'; + case '0': + return '\u0000'; + case '': + return ''; + default: + return n1; + } + }); +} + +function stripos (f_haystack, f_needle, f_offset) { + // Finds position of first occurrence of a string within another, case insensitive + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/stripos + // + original by: Martijn Wieringa + // + revised by: Onno Marsman + // * example 1: stripos('ABC', 'a'); + // * returns 1: 0 + var haystack = (f_haystack + '').toLowerCase(); + var needle = (f_needle + '').toLowerCase(); + var index = 0; + + if ((index = haystack.indexOf(needle, f_offset)) !== -1) { + return index; + } + return false; +} + +function stristr (haystack, needle, bool) { + // Finds first occurrence of a string within another, case insensitive + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/stristr + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfxied by: Onno Marsman + // * example 1: stristr('Kevin van Zonneveld', 'Van'); + // * returns 1: 'van Zonneveld' + // * example 2: stristr('Kevin van Zonneveld', 'VAN', true); + // * returns 2: 'Kevin ' + var pos = 0; + + haystack += ''; + pos = haystack.toLowerCase().indexOf((needle + '').toLowerCase()); + if (pos == -1) { + return false; + } else { + if (bool) { + return haystack.substr(0, pos); + } else { + return haystack.slice(pos); + } + } +} + +function strrev (string) { + // Reverse a string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/strrev + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + reimplemented by: Brett Zamir (http://brett-zamir.me) + // * example 1: strrev('Kevin van Zonneveld'); + // * returns 1: 'dlevennoZ nav niveK' + // * example 2: strrev('a\u0301haB') === 'Baha\u0301'; // combining + // * returns 2: true + // * example 3: strrev('A\uD87E\uDC04Z') === 'Z\uD87E\uDC04A'; // surrogates + // * returns 2: true + string = string + ''; + + // Performance will be enhanced with the next two lines of code commented + // out if you don't care about combining characters + // Keep Unicode combining characters together with the character preceding + // them and which they are modifying (as in PHP 6) + // See http://unicode.org/reports/tr44/#Property_Table (Me+Mn) + // We also add the low surrogate range at the beginning here so it will be + // maintained with its preceding high surrogate + var grapheme_extend = /(.)([\uDC00-\uDFFF\u0300-\u036F\u0483-\u0489\u0591-\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; + string = string.replace(grapheme_extend, '$2$1'); // Temporarily reverse + return string.split('').reverse().join(''); +} + +function strstr (haystack, needle, bool) { + // Finds first occurrence of a string within another + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/strstr + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: strstr('Kevin van Zonneveld', 'van'); + // * returns 1: 'van Zonneveld' + // * example 2: strstr('Kevin van Zonneveld', 'van', true); + // * returns 2: 'Kevin ' + // * example 3: strstr('name@example.com', '@'); + // * returns 3: '@example.com' + // * example 4: strstr('name@example.com', '@', true); + // * returns 4: 'name' + var pos = 0; + + haystack += ''; + pos = haystack.indexOf(needle); + if (pos == -1) { + return false; + } else { + if (bool) { + return haystack.substr(0, pos); + } else { + return haystack.slice(pos); + } + } +} + +function strtotime (text, now) { + /* 2013-06-12: taken from phpjs.org + and adapted for limesurvey */ + + // Convert string representation of date and time to a timestamp + // + // version: 1109.2015 + // discuss at: http://phpjs.org/functions/strtotime + // + original by: Caio Ariede (http://caioariede.com) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: David + // + improved by: Caio Ariede (http://caioariede.com) + // + improved by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Wagner B. Soares + // + bugfixed by: Artur Tchernychev + // + improved by: A. Matías Quezada (http://amatiasq.com) + // % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) + // * example 1: strtotime('+1 day', 1129633200); + // * returns 1: 1129719600 + // * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); + // * returns 2: 1130425202 + // * example 3: strtotime('last month', 1129633200); + // * returns 3: 1127041200 + // * example 4: strtotime('2009-05-04 08:30:00'); + // * returns 4: 1241418600 + if (!text) + return null; + + // Unecessary spaces + text = text.trim() + .replace(/\s{2,}/g, ' ') + .replace(/[\t\r\n]/g, '') + .toLowerCase(); + + // in contrast to php, js Date.parse function interprets: + // dates given as yyyy-mm-dd as in timezone: UTC, + // dates with "." or "-" as MDY instead of DMY + // dates with two-digit years differently + // etc...etc... + // ...therefore we manually parse lots of common date formats + var match = text.match(/^(\d{1,4})([\-\.\/\:])(\d{1,2})([\-\.\/\:])(\d{1,4})(?:\s(\d{1,2}):(\d{2})?:?(\d{2})?)?(?:\s([A-Z]+)?)?$/); + if (match && match[2]==match[4]) { + if (match[1]>1901) { + switch (match[2]) { + case '-': { // YYYY-M-D + if (match[3]>12 | match[5]>31) return(0); + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + case '.': { // YYYY.M.D is not parsed by strtotime() + return(0); + break; + } + case '/': { // YYYY/M/D + if (match[3]>12 | match[5]>31) return(0); + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + } + } + else if (match[5]>1901) { + switch (match[2]) { + case '-': { // D-M-YYYY + if (match[3]>12 | match[1]>31) return(0); + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + case '.': { // D.M.YYYY + if (match[3]>12 | match[1]>31) return(0); + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + case '/': { // M/D/YYYY + if (match[1]>12 | match[3]>31) return(0); + return new Date(match[5], parseInt(match[1], 10) - 1, match[3], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + } + } + else { + switch (match[2]) { + case '-': { // YY-M-D + if (match[3]>12 | match[5]>31 | (match[1] < 70 & match[1]>38)) return(0); + var year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]; + return new Date(year, parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + case '.': { // D.M.YY or H.MM.SS + if (match[5]>=70) { // D.M.YY + if (match[3]>12 | match[1]>31) return(0); + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + } + else if (match[5]<60 & !(match[6])) { // H.MM.SS + if (match[1]>23 | match[3]>59) return(0); + var today = new Date(); + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), + match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000; + } + else return(0); // invalid format, cannot be parsed + break; + } + case '/': { // M/D/YY + if (match[1]>12 | match[3]>31 | (match[5] < 70 & match[5]>38)) return(0); + var year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]; + return new Date(year, parseInt(match[1], 10) - 1, match[3], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; + break; + } + case ':': { // HH:MM:SS + if (match[1]>23 | match[3]>59 | match[5]>59) return(0); + var today = new Date(); + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), + match[1] || 0, match[3] || 0, match[5] || 0) / 1000; + break; + } + } + } + } + + + // other formats and "now" should be parsed by Date.parse() + if (text === 'now') + return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0; + else if (!isNaN(parse = Date.parse(text))) + return parse / 1000 | 0; + + var date = now ? new Date(now * 1000) : new Date(); + var days = { + 'sun': 0, + 'mon': 1, + 'tue': 2, + 'wed': 3, + 'thu': 4, + 'fri': 5, + 'sat': 6 + }; + var ranges = { + 'yea': 'FullYear', + 'mon': 'Month', + 'day': 'Date', + 'hou': 'Hours', + 'min': 'Minutes', + 'sec': 'Seconds' + }; + + function lastNext(type, range, modifier) { + var day = days[range]; + + if (typeof(day) !== 'undefined') { + var diff = day - date.getDay(); + + if (diff === 0) + diff = 7 * modifier; + else if (diff > 0 && type === 'last') + diff -= 7; + else if (diff < 0 && type === 'next') + diff += 7; + + date.setDate(date.getDate() + diff); + } + } + function process(val) { + var split = val.split(' '); + var type = split[0]; + var range = split[1].substring(0, 3); + var typeIsNumber = /\d+/.test(type); + + var ago = split[2] === 'ago'; + var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1); + + if (typeIsNumber) + num *= parseInt(type, 10); + + if (ranges.hasOwnProperty(range)) + return date['set' + ranges[range]](date['get' + ranges[range]]() + num); + else if (range === 'wee') + return date.setDate(date.getDate() + (num * 7)); + + if (type === 'next' || type === 'last') + lastNext(type, range, num); + else if (!typeIsNumber) + return false; + + return true; + } + + var regex = '([+-]?\\d+\\s' + + '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?' + + '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday' + + '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)|(last|next)\\s' + + '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?' + + '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday' + + '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))(\\sago)?'; + + match = text.match(new RegExp(regex, 'gi')); + if (!match) + return false; + + for (var i = 0, len = match.length; i < len; i++) + if (!process(match[i])) + return false; + + // ECMAScript 5 only + //if (!match.every(process)) + // return false; + + return (date.getTime() / 1000); +} + +function substr (str, start, len) { + // Returns part of a string + // + // version: 909.322 + // discuss at: http://phpjs.org/functions/substr + // + original by: Martijn Wieringa + // + bugfixed by: T.Wild + // + tweaked by: Onno Marsman + // + revised by: Theriault + // + improved by: Brett Zamir (http://brett-zamir.me) + // % note 1: Handles rare Unicode characters if 'unicode.semantics' ini (PHP6) is set to 'on' + // * example 1: substr('abcdef', 0, -1); + // * returns 1: 'abcde' + // * example 2: substr(2, 0, -6); + // * returns 2: false + // * example 3: ini_set('unicode.semantics', 'on'); + // * example 3: substr('a\uD801\uDC00', 0, -1); + // * returns 3: 'a' + // * example 4: ini_set('unicode.semantics', 'on'); + // * example 4: substr('a\uD801\uDC00', 0, 2); + // * returns 4: 'a\uD801\uDC00' + // * example 5: ini_set('unicode.semantics', 'on'); + // * example 5: substr('a\uD801\uDC00', -1, 1); + // * returns 5: '\uD801\uDC00' + // * example 6: ini_set('unicode.semantics', 'on'); + // * example 6: substr('a\uD801\uDC00z\uD801\uDC00', -3, 2); + // * returns 6: '\uD801\uDC00z' + // * example 7: ini_set('unicode.semantics', 'on'); + // * example 7: substr('a\uD801\uDC00z\uD801\uDC00', -3, -1) + // * returns 7: '\uD801\uDC00z' + // Add: (?) Use unicode.runtime_encoding (e.g., with string wrapped in "binary" or "Binary" class) to + // allow access of binary (see file_get_contents()) by: charCodeAt(x) & 0xFF (see https://developer.mozilla.org/En/Using_XMLHttpRequest ) or require conversion first? + var i = 0, + allBMP = true, + es = 0, + el = 0, + se = 0, + ret = ''; + str += ''; + var end = str.length; + + // BEGIN REDUNDANT + this.php_js = this.php_js || {}; + this.php_js.ini = this.php_js.ini || {}; + // END REDUNDANT + switch ((this.php_js.ini['unicode.semantics'] && this.php_js.ini['unicode.semantics'].local_value.toLowerCase())) { + case 'on': + // Full-blown Unicode including non-Basic-Multilingual-Plane characters + // strlen() + for (i = 0; i < str.length; i++) { + if (/[\uD800-\uDBFF]/.test(str.charAt(i)) && /[\uDC00-\uDFFF]/.test(str.charAt(i + 1))) { + allBMP = false; + break; + } + } + + if (!allBMP) { + if (start < 0) { + for (i = end - 1, es = (start += end); i >= es; i--) { + if (/[\uDC00-\uDFFF]/.test(str.charAt(i)) && /[\uD800-\uDBFF]/.test(str.charAt(i - 1))) { + start--; + es--; + } + } + } else { + var surrogatePairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; + while ((surrogatePairs.exec(str)) != null) { + var li = surrogatePairs.lastIndex; + if (li - 2 < start) { + start++; + } else { + break; + } + } + } + + if (start >= end || start < 0) { + return false; + } + if (len < 0) { + for (i = end - 1, el = (end += len); i >= el; i--) { + if (/[\uDC00-\uDFFF]/.test(str.charAt(i)) && /[\uD800-\uDBFF]/.test(str.charAt(i - 1))) { + end--; + el--; + } + } + if (start > end) { + return false; + } + return str.slice(start, end); + } else { + se = start + len; + for (i = start; i < se; i++) { + ret += str.charAt(i); + if (/[\uD800-\uDBFF]/.test(str.charAt(i)) && /[\uDC00-\uDFFF]/.test(str.charAt(i + 1))) { + se++; // Go one further, since one of the "characters" is part of a surrogate pair + } + } + return ret; + } + break; + } + // Fall-through + case 'off': + // assumes there are no non-BMP characters; + // if there may be such characters, then it is best to turn it on (critical in true XHTML/XML) + default: + if (start < 0) { + start += end; + } + end = typeof len === 'undefined' ? end : (len < 0 ? len + end : len + start); + // PHP returns false if start does not fall within the string. + // PHP returns false if the calculated end comes before the calculated start. + // PHP returns an empty string if start and end are the same. + // Otherwise, PHP returns the portion of the string from start to end. + return start >= str.length || start < 0 || start > end ? !1 : str.slice(start, end); + } + return undefined; // Please Netbeans +} + +function trim (str, charlist) { +// Strips whitespace from the beginning and end of a string +// +// version: 1107.2516 +// discuss at: http://phpjs.org/functions/trim +// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) +// + improved by: mdsjack (http://www.mdsjack.bo.it) +// + improved by: Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev) +// + input by: Erkekjetter +// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) +// + input by: DxGx +// + improved by: Steven Levithan (http://blog.stevenlevithan.com) +// + tweaked by: Jack +// + bugfixed by: Onno Marsman +// * example 1: trim(' Kevin van Zonneveld '); +// * returns 1: 'Kevin van Zonneveld' +// * example 2: trim('Hello World', 'Hdle'); +// * returns 2: 'o Wor' +// * example 3: trim(16, 1); +// * returns 3: 6 +var whitespace, l = 0, +i = 0; +str += ''; + +if (!charlist) { +// default list +whitespace = " \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000"; +} else { +// preg_quote custom list +charlist += ''; +whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1'); +} + +l = str.length; +for (i = 0; i < l; i++) { +if (whitespace.indexOf(str.charAt(i)) === -1) { +str = str.substring(i); +break; +} +} + +l = str.length; +for (i = l - 1; i >= 0; i--) { +if (whitespace.indexOf(str.charAt(i)) === -1) { +str = str.substring(0, i + 1); +break; +} +} + +return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; +} + +function ucwords (str) { + // Uppercase the first character of every word in a string + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/ucwords + // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // + improved by: Waldo Malqui Silva + // + bugfixed by: Onno Marsman + // + improved by: Robin + // + input by: James (http://www.james-bell.co.uk/) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: ucwords('kevin van zonneveld'); + // * returns 1: 'Kevin Van Zonneveld' + // * example 2: ucwords('HELLO WORLD'); + // * returns 2: 'HELLO WORLD' + return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) { + return $1.toUpperCase(); + }); +} + +function checkdate (m, d, y) { + // Returns true(1) if it is a valid date in gregorian calendar + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/checkdate + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Pyerre + // + improved by: Theriault + // * example 1: checkdate(12, 31, 2000); + // * returns 1: true + // * example 2: checkdate(2, 29, 2001); + // * returns 2: false + // * example 3: checkdate(3, 31, 2008); + // * returns 3: true + // * example 4: checkdate(1, 390, 2000); + // * returns 4: false + return m > 0 && m < 13 && y > 0 && y < 32768 && d > 0 && d <= (new Date(y, m, 0)).getDate(); +} + +function date (format, timestamp) { + // http://kevin.vanzonneveld.net + // + original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com) + // + parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: MeEtc (http://yass.meetcweb.com) + // + improved by: Brad Touesnard + // + improved by: Tim Wiel + // + improved by: Bryan Elliott + // + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: David Randall + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // + derived from: gettimeofday + // + input by: majak + // + bugfixed by: majak + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Alex + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // + improved by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // + improved by: Thomas Beaucourt (http://www.webapp.fr) + // + improved by: JT + // + improved by: Theriault + // + improved by: Rafał Kukawski (http://blog.kukawski.pl) + // + input by: Martin + // + input by: Alex Wilson + // % note 1: Uses global: php_js to store the default timezone + // % note 2: Although the function potentially allows timezone info (see notes), it currently does not set + // % note 2: per a timezone specified by date_default_timezone_set(). Implementers might use + // % note 2: this.php_js.currentTimezoneOffset and this.php_js.currentTimezoneDST set by that function + // % note 2: in order to adjust the dates in this function (or our other date functions!) accordingly + // * example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400); + // * returns 1: '09:09:40 m is month' + // * example 2: date('F j, Y, g:i a', 1062462400); + // * returns 2: 'September 2, 2003, 2:26 am' + // * example 3: date('Y W o', 1062462400); + // * returns 3: '2003 36 2003' + // * example 4: x = date('Y m d', (new Date()).getTime()/1000); + // * example 4: (x+'').length == 10 // 2009 01 09 + // * returns 4: true + // * example 5: date('W', 1104534000); + // * returns 5: '53' + // * example 6: date('B t', 1104534000); + // * returns 6: '999 31' + // * example 7: date('W U', 1293750000.82); // 2010-12-31 + // * returns 7: '52 1293750000' + // * example 8: date('W', 1293836400); // 2011-01-01 + // * returns 8: '52' + // * example 9: date('W Y-m-d', 1293974054); // 2011-01-02 + // * returns 9: '52 2011-01-02' + var that = this, + jsdate, f, formatChr = /\\?([a-z])/gi, + formatChrCb, + // Keep this here (works, but for code commented-out + // below for file size reasons) + //, tal= [], + _pad = function (n, c) { + if ((n = n + '').length < c) { + return new Array((++c) - n.length).join('0') + n; + } + return n; + }, + txt_words = ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; + formatChrCb = function (t, s) { + return f[t] ? f[t]() : s; + }; + f = { + // Day + d: function () { // Day of month w/leading 0; 01..31 + return _pad(f.j(), 2); + }, + D: function () { // Shorthand day name; Mon...Sun + return f.l().slice(0, 3); + }, + j: function () { // Day of month; 1..31 + return jsdate.getDate(); + }, + l: function () { // Full day name; Monday...Sunday + return txt_words[f.w()] + 'day'; + }, + N: function () { // ISO-8601 day of week; 1[Mon]..7[Sun] + return f.w() || 7; + }, + S: function () { // Ordinal suffix for day of month; st, nd, rd, th + var j = f.j(); + return j > 4 || j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th'; + }, + w: function () { // Day of week; 0[Sun]..6[Sat] + return jsdate.getDay(); + }, + z: function () { // Day of year; 0..365 + var a = new Date(f.Y(), f.n() - 1, f.j()), + b = new Date(f.Y(), 0, 1); + return Math.round((a - b) / 864e5) + 1; + }, + + // Week + W: function () { // ISO-8601 week number + var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3), + b = new Date(a.getFullYear(), 0, 4); + return _pad(1 + Math.round((a - b) / 864e5 / 7), 2); + }, + + // Month + F: function () { // Full month name; January...December + return txt_words[6 + f.n()]; + }, + m: function () { // Month w/leading 0; 01...12 + return _pad(f.n(), 2); + }, + M: function () { // Shorthand month name; Jan...Dec + return f.F().slice(0, 3); + }, + n: function () { // Month; 1...12 + return jsdate.getMonth() + 1; + }, + t: function () { // Days in month; 28...31 + return (new Date(f.Y(), f.n(), 0)).getDate(); + }, + + // Year + L: function () { // Is leap year?; 0 or 1 + return new Date(f.Y(), 1, 29).getMonth() === 1 | 0; + }, + o: function () { // ISO-8601 year + var n = f.n(), + W = f.W(), + Y = f.Y(); + return Y + (n === 12 && W < 9 ? -1 : n === 1 && W > 9); + }, + Y: function () { // Full year; e.g. 1980...2010 + return jsdate.getFullYear(); + }, + y: function () { // Last two digits of year; 00...99 + return (f.Y() + "").slice(-2); + }, + + // Time + a: function () { // am or pm + return jsdate.getHours() > 11 ? "pm" : "am"; + }, + A: function () { // AM or PM + return f.a().toUpperCase(); + }, + B: function () { // Swatch Internet time; 000..999 + var H = jsdate.getUTCHours() * 36e2, + // Hours + i = jsdate.getUTCMinutes() * 60, + // Minutes + s = jsdate.getUTCSeconds(); // Seconds + return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3); + }, + g: function () { // 12-Hours; 1..12 + return f.G() % 12 || 12; + }, + G: function () { // 24-Hours; 0..23 + return jsdate.getHours(); + }, + h: function () { // 12-Hours w/leading 0; 01..12 + return _pad(f.g(), 2); + }, + H: function () { // 24-Hours w/leading 0; 00..23 + return _pad(f.G(), 2); + }, + i: function () { // Minutes w/leading 0; 00..59 + return _pad(jsdate.getMinutes(), 2); + }, + s: function () { // Seconds w/leading 0; 00..59 + return _pad(jsdate.getSeconds(), 2); + }, + u: function () { // Microseconds; 000000-999000 + return _pad(jsdate.getMilliseconds() * 1000, 6); + }, + + // Timezone + e: function () { // Timezone identifier; e.g. Atlantic/Azores, ... + // The following works, but requires inclusion of the very large + // timezone_abbreviations_list() function. +/* return this.date_default_timezone_get(); +*/ + throw 'Not supported (see source code of date() for timezone on how to add support)'; + }, + I: function () { // DST observed?; 0 or 1 + // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. + // If they are not equal, then DST is observed. + var a = new Date(f.Y(), 0), + // Jan 1 + c = Date.UTC(f.Y(), 0), + // Jan 1 UTC + b = new Date(f.Y(), 6), + // Jul 1 + d = Date.UTC(f.Y(), 6); // Jul 1 UTC + return 0 + ((a - c) !== (b - d)); + }, + O: function () { // Difference to GMT in hour format; e.g. +0200 + var a = jsdate.getTimezoneOffset(); + return (a > 0 ? "-" : "+") + _pad(Math.abs(a / 60 * 100), 4); + }, + P: function () { // Difference to GMT w/colon; e.g. +02:00 + var O = f.O(); + return (O.substr(0, 3) + ":" + O.substr(3, 2)); + }, + T: function () { // Timezone abbreviation; e.g. EST, MDT, ... + // The following works, but requires inclusion of the very + // large timezone_abbreviations_list() function. +/* var abbr = '', i = 0, os = 0, default = 0; + if (!tal.length) { + tal = that.timezone_abbreviations_list(); + } + if (that.php_js && that.php_js.default_timezone) { + default = that.php_js.default_timezone; + for (abbr in tal) { + for (i=0; i < tal[abbr].length; i++) { + if (tal[abbr][i].timezone_id === default) { + return abbr.toUpperCase(); + } + } + } + } + for (abbr in tal) { + for (i = 0; i < tal[abbr].length; i++) { + os = -jsdate.getTimezoneOffset() * 60; + if (tal[abbr][i].offset === os) { + return abbr.toUpperCase(); + } + } + } +*/ + return 'UTC'; + }, + Z: function () { // Timezone offset in seconds (-43200...50400) + return -jsdate.getTimezoneOffset() * 60; + }, + + // Full Date/Time + c: function () { // ISO-8601 date. + return 'Y-m-d\\Th:i:sP'.replace(formatChr, formatChrCb); + }, + r: function () { // RFC 2822 + return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); + }, + U: function () { // Seconds since UNIX epoch + return jsdate.getTime() / 1000 | 0; + } + }; + this.date = function (format, timestamp) { + that = this; + jsdate = ((typeof timestamp === 'undefined') ? new Date() : // Not provided + (timestamp instanceof Date) ? new Date(timestamp) : // JS Date() + new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) + ); + return format.replace(formatChr, formatChrCb); + }; + return this.date(format, timestamp); +} + +function gmdate (format, timestamp) { + // Format a GMT date/time + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/gmdate + // + original by: Brett Zamir (http://brett-zamir.me) + // + input by: Alex + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // - depends on: date + // * example 1: gmdate('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400); // Return will depend on your timezone + // * returns 1: '07:09:40 m is month' + var dt = typeof timestamp === 'undefined' ? new Date() : // Not provided + typeof timestamp === 'object' ? new Date(timestamp) : // Javascript Date() + new Date(timestamp * 1000); // UNIX timestamp (auto-convert to int) + timestamp = Date.parse(dt.toUTCString().slice(0, -4)) / 1000; + return this.date(format, timestamp); +} + +function idate (format, timestamp) { + // Format a local time/date as integer + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/idate + // + original by: Brett Zamir (http://brett-zamir.me) + // + input by: Alex + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + improved by: Theriault + // + derived from: date + // + derived from: gettimeofday + // * example 1: idate('y'); + // * returns 1: 9 + if (format === undefined) { + throw 'idate() expects at least 1 parameter, 0 given'; + } + if (!format.length || format.length > 1) { + throw 'idate format is one char'; + } + + // Fix: Need to allow date_default_timezone_set() (check for this.php_js.default_timezone and use) + var date = ((typeof timestamp === 'undefined') ? new Date() : // Not provided + (timestamp instanceof Date) ? new Date(timestamp) : // Javascript Date() + new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) + ), + a; + + switch (format) { + case 'B': + return Math.floor(((date.getUTCHours() * 36e2) + (date.getUTCMinutes() * 60) + date.getUTCSeconds() + 36e2) / 86.4) % 1e3; + case 'd': + return date.getDate(); + case 'h': + return date.getHours() % 12 || 12; + case 'H': + return date.getHours(); + case 'i': + return date.getMinutes(); + case 'I': + // capital 'i' + // Logic derived from getimeofday(). + // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. + // If they are not equal, then DST is observed. + a = date.getFullYear(); + return 0 + (((new Date(a, 0)) - Date.UTC(a, 0)) !== ((new Date(a, 6)) - Date.UTC(a, 6))); + case 'L': + a = date.getFullYear(); + return (!(a & 3) && (a % 1e2 || !(a % 4e2))) ? 1 : 0; + case 'm': + return date.getMonth() + 1; + case 's': + return date.getSeconds(); + case 't': + return (new Date(date.getFullYear(), date.getMonth() + 1, 0)).getDate(); + case 'U': + return Math.round(date.getTime() / 1000); + case 'w': + return date.getDay(); + case 'W': + a = new Date(date.getFullYear(), date.getMonth(), date.getDate() - (date.getDay() || 7) + 3); + return 1 + Math.round((a - (new Date(a.getFullYear(), 0, 4))) / 864e5 / 7); + case 'y': + return parseInt((date.getFullYear() + '').slice(2), 10); // This function returns an integer, unlike date() + case 'Y': + return date.getFullYear(); + case 'z': + return Math.floor((date - new Date(date.getFullYear(), 0, 1)) / 864e5); + case 'Z': + return -date.getTimezoneOffset() * 60; + default: + throw 'Unrecognized date format token'; + } +} + +function mktime () { + // Get UNIX timestamp for a date + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/mktime + // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: baris ozdil + // + input by: gabriel paderni + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: FGFEmperor + // + input by: Yannoo + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: jakes + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Marc Palau + // + improved by: Brett Zamir (http://brett-zamir.me) + // + input by: 3D-GRAF + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + input by: Chris + // + revised by: Theriault + // % note 1: The return values of the following examples are + // % note 1: received only if your system's timezone is UTC. + // * example 1: mktime(14, 10, 2, 2, 1, 2008); + // * returns 1: 1201875002 + // * example 2: mktime(0, 0, 0, 0, 1, 2008); + // * returns 2: 1196467200 + // * example 3: make = mktime(); + // * example 3: td = new Date(); + // * example 3: real = Math.floor(td.getTime() / 1000); + // * example 3: diff = (real - make); + // * results 3: diff < 5 + // * example 4: mktime(0, 0, 0, 13, 1, 1997) + // * returns 4: 883612800 + // * example 5: mktime(0, 0, 0, 1, 1, 1998) + // * returns 5: 883612800 + // * example 6: mktime(0, 0, 0, 1, 1, 98) + // * returns 6: 883612800 + // * example 7: mktime(23, 59, 59, 13, 0, 2010) + // * returns 7: 1293839999 + // * example 8: mktime(0, 0, -1, 1, 1, 1970) + // * returns 8: -1 + var d = new Date(), + r = arguments, + i = 0, + e = ['Hours', 'Minutes', 'Seconds', 'Month', 'Date', 'FullYear']; + + for (i = 0; i < e.length; i++) { + if (typeof r[i] === 'undefined') { + r[i] = d['get' + e[i]](); + r[i] += (i === 3); // +1 to fix JS months. + } else { + r[i] = parseInt(r[i], 10); + if (isNaN(r[i])) { + return false; + } + } + } + + // Map years 0-69 to 2000-2069 and years 70-100 to 1970-2000. + r[5] += (r[5] >= 0 ? (r[5] <= 69 ? 2e3 : (r[5] <= 100 ? 1900 : 0)) : 0); + + // Set year, month (-1 to fix JS months), and date. + // !This must come before the call to setHours! + d.setFullYear(r[5], r[3] - 1, r[4]); + + // Set hours, minutes, and seconds. + d.setHours(r[0], r[1], r[2]); + + // Divide milliseconds by 1000 to return seconds and drop decimal. + // Add 1 second if negative or it'll be off from PHP by 1 second. + return (d.getTime() / 1e3 >> 0) - (d.getTime() < 0); +} + +function rand (min, max) { + // http://kevin.vanzonneveld.net + // + original by: Leslie Hoare + // + bugfixed by: Onno Marsman + // % note 1: See the commented out code below for a version which will work with our experimental (though probably unnecessary) srand() function) + // * example 1: rand(1, 1); + // * returns 1: 1 + var argc = arguments.length; + if (argc === 0) { + min = 0; + max = 2147483647; + } else if (argc === 1) { + throw new Error('Warning: rand() expects exactly 2 parameters, 1 given'); + } + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function time () { + // Return current UNIX timestamp + // + // version: 1107.2516 + // discuss at: http://phpjs.org/functions/time + // + original by: GeekFG (http://geekfg.blogspot.com) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: metjay + // + improved by: HKM + // * example 1: timeStamp = time(); + // * results 1: timeStamp > 1000000000 && timeStamp < 2000000000 + return Math.floor(new Date().getTime() / 1000); +}