From a8059056ddacdc06e88e3ed11229eca39a024245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Fl=C3=BCr?= Date: Fri, 22 Jul 2016 11:49:31 +0200 Subject: [PATCH] dev replaced parsing with moment.js --- .../helpers/expressions/em_core_helper.php | 12 +- scripts/expressions/em_javascript.js | 500 ++++++++++-------- 2 files changed, 284 insertions(+), 228 deletions(-) diff --git a/application/helpers/expressions/em_core_helper.php b/application/helpers/expressions/em_core_helper.php index 470ceed73ce..fdd1dc6ea4a 100644 --- a/application/helpers/expressions/em_core_helper.php +++ b/application/helpers/expressions/em_core_helper.php @@ -1269,7 +1269,7 @@ public function GetJavaScriptEquivalentOfExpression() elseif ($i+1<$numTokens && $tokens[$i+1][2] == 'ASSIGN') { $jsName = $this->GetVarAttribute($token[0],'jsName',''); - $stringParts[] = "document.getElementById('" . $jsName . "').value"; + $stringParts[] = "$('" . $jsName . "').val()"; if ($tokens[$i+1][0] == '+=') { // Javascript does concatenation unless both left and right side are numbers, so refactor the equation @@ -1361,8 +1361,8 @@ public function GetJavascriptTestforExpression($expected,$num) $jsParts = array(); $jsParts[] = "val = " . $jsmultiline_expr . ";\n"; $jsParts[] = "klass = (LEMeq(addslashes(val),'" . $jsmultiline_expected . "')) ? 'ok' : 'error';\n"; - $jsParts[] = "document.getElementById('test_" . $num . "').innerHTML=(val);\n"; - $jsParts[] = "document.getElementById('test_" . $num . "').className=klass;\n"; + $jsParts[] = "$('test_" . $num . "').html(val);\n"; + $jsParts[] = "$('test_" . $num . "').addClass(klass);\n"; return implode('',$jsParts); } @@ -1377,10 +1377,10 @@ public function GetJavaScriptFunctionForReplacement($questionNum, $name,$eqn) $jsParts = array(); // $jsParts[] = "\n // Tailor Question " . $questionNum . " - " . $name . ": { " . $eqn . " }\n"; $jsParts[] = " try{\n"; - $jsParts[] = " document.getElementById('" . $name . "').innerHTML=LEMfixnum(\n "; + $jsParts[] = " $('" . $name . "').html(LEMfixnum(\n "; $jsParts[] = $this->GetJavaScriptEquivalentOfExpression(); - $jsParts[] = ");\n"; - $jsParts[] = " } catch (e) { }\n"; + $jsParts[] = "));\n"; + $jsParts[] = " } catch (e) { console.log(e); }\n"; return implode('',$jsParts); } diff --git a/scripts/expressions/em_javascript.js b/scripts/expressions/em_javascript.js index e10d97d3829..faee5e606d6 100644 --- a/scripts/expressions/em_javascript.js +++ b/scripts/expressions/em_javascript.js @@ -453,6 +453,7 @@ function LEMval(alias) var str = new String(alias); var varName = alias; var suffix = 'code'; // the default + var value = ""; if(typeof bNumRealValue == 'undefined'){bNumRealValue=false;} // Allow to update {QCODE} even with text /* If passed a number, return that number */ @@ -635,6 +636,7 @@ function LEMval(alias) 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 @@ -675,6 +677,7 @@ function LEMval(alias) break; } } + if (typeof attr.onlynum !== 'undefined' && attr.onlynum==1) { if(value=="") { @@ -710,6 +713,7 @@ function LEMval(alias) // } return +newval; } + // convert content in date questions to standard format yy-mm-dd to facilitate use in EM (comparisons, min/max etc.) else if (attr.type=='D') { // get date format pattern of referenced question @@ -725,7 +729,7 @@ function LEMval(alias) value=""; } else { - value=date('Y-m-d H:i', Date.parseString(trim(value), sdatetimePattern)); + value= moment(value,sdatetimePattern).format('YYYY-MM-DD HH:mm'); //date('Y-m-d H:i', Date.parseString(trim(value), sdatetimePattern)); } return value; } @@ -2165,252 +2169,304 @@ function strstr (haystack, needle, bool) { } function strtotime (text, now) { - // Convert string representation of date and time to a timestamp - // - // version: 1109.2016 - // 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) - // + bugfixed by: Wagner B. Soares - // + bugfixed by: Artur Tchernychev - // + improved by: A. Matías Quezada (http://amatiasq.com) - // + improved by: preuter - // + improved by: Brett Zamir (http://brett-zamir.me) - // + improved by: Mirko Faber - // % 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 GMT'); - // * returns 4: 1241425800 - var parsed, match, today, year, date, days, ranges, len, times, regex, i, fail = false; - - if (!text) { - return fail; - } - - // Unecessary spaces - text = text.replace(/^\s+|\s+$/g, '') - .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 - 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 fail; - } - - 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; - } - case '.': { // YYYY.M.D is not parsed by strtotime() - return fail; - } - case '/': { // YYYY/M/D - if (match[3] > 12 || match[5] > 31) { - return fail; - } + // discuss at: http://locutus.io/php/strtotime/ + // original by: Caio Ariede (http://caioariede.com) + // improved by: Kevin van Zonneveld (http://kvz.io) + // improved by: Caio Ariede (http://caioariede.com) + // improved by: A. Matías Quezada (http://amatiasq.com) + // improved by: preuter + // improved by: Brett Zamir (http://brett-zamir.me) + // improved by: Mirko Faber + // input by: David + // bugfixed by: Wagner B. Soares + // bugfixed by: Artur Tchernychev + // bugfixed by: Stephan Bösch-Plepelits (http://github.com/plepe) + // note 1: Examples all have a fixed timestamp to prevent + // note 1: 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 GMT') + // returns 4: 1241425800 + // example 5: strtotime('2009-05-04 08:30:00+00') + // returns 5: 1241425800 + // example 6: strtotime('2009-05-04 08:30:00+02:00') + // returns 6: 1241418600 + // example 7: strtotime('2009-05-04T08:30:00Z') + // returns 7: 1241425800 + + var parsed + var match + var today + var year + var date + var days + var ranges + var len + var times + var regex + var i + var fail = false + + if (!text) { + return fail + } - 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; - } + // Unecessary spaces + text = text.replace(/^\s+|\s+$/g, '') + .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 pattern = new RegExp([ + '^(\\d{1,4})', + '([\\-\\.\\/:])', + '(\\d{1,2})', + '([\\-\\.\\/:])', + '(\\d{1,4})', + '(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?', + '(?:\\s([A-Z]+)?)?$' + ].join('')) + match = text.match(pattern) + + 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 fail + } + + 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 + case '.': + // YYYY.M.D is not parsed by strtotime() + return fail + case '/': + // YYYY/M/D + if (match[3] > 12 || match[5] > 31) { + return fail + } + + 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 + } + } else if (match[5] > 1901) { + switch (match[2]) { + case '-': + // D-M-YYYY + if (match[3] > 12 || match[1] > 31) { + return fail + } + + 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 + case '.': + // D.M.YYYY + if (match[3] > 12 || match[1] > 31) { + return fail + } + + 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 + case '/': + // M/D/YYYY + if (match[1] > 12 || match[3] > 31) { + return fail + } + + 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 + } + } else { + switch (match[2]) { + case '-': + // YY-M-D + if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) { + return fail + } + + 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 + case '.': + // D.M.YY or H.MM.SS + if (match[5] >= 70) { + // D.M.YY + if (match[3] > 12 || match[1] > 31) { + return fail } - } else if (match[5] > 1901) { - switch (match[2]) { - case '-': { // D-M-YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - - 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; - } - case '.': { // D.M.YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - 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; - } - case '/': { // M/D/YYYY - if (match[1] > 12 || match[3] > 31) { - return fail; - } - - 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; - } + 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 + } + if (match[5] < 60 && !match[6]) { + // H.MM.SS + if (match[1] > 23 || match[3] > 59) { + return fail } - } - else { - switch (match[2]) { - case '-': { // YY-M-D - if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) { - return fail; - } - - 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; - } - case '.': { // D.M.YY or H.MM.SS - if (match[5] >= 70) { // D.M.YY - if (match[3]>12 || match[1]>31) { - return fail; - } - 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; - } - if (match[5] < 60 && !match[6]) { // H.MM.SS - if (match[1] > 23 || match[3] > 59) { - return fail; - } - - 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; - } - - return fail; // invalid format, cannot be parsed - } - case '/': { // M/D/YY - if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) { - return fail; - } - - 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; - } - case ':': { // HH:MM:SS - if (match[1] > 23 || match[3] > 59 || match[5] > 59) { - return fail; - } - - today = new Date(); - return new Date(today.getFullYear(), today.getMonth(), today.getDate(), - match[1] || 0, match[3] || 0, match[5] || 0) / 1000; - } - } - } + 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 + } + + // invalid format, cannot be parsed + return fail + case '/': + // M/D/YY + if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) { + return fail + } + + 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 + case ':': + // HH:MM:SS + if (match[1] > 23 || match[3] > 59 || match[5] > 59) { + return fail + } + + today = new Date() + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), + match[1] || 0, match[3] || 0, match[5] || 0) / 1000 + } } + } - - // 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; - } - if (!isNaN(parsed = Date.parse(text))) { - return parsed / 1000 | 0; + // 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 + } + if (!isNaN(parsed = Date.parse(text))) { + return parsed / 1000 | 0 + } + // Browsers !== Chrome have problems parsing ISO 8601 date strings, as they do + // not accept lower case characters, space, or shortened time zones. + // Therefore, fix these problems and try again. + // Examples: + // 2015-04-15 20:33:59+02 + // 2015-04-15 20:33:59z + // 2015-04-15t20:33:59+02:00 + pattern = new RegExp([ + '^([0-9]{4}-[0-9]{2}-[0-9]{2})', + '[ t]', + '([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)', + '([\\+-][0-9]{2}(:[0-9]{2})?|z)' + ].join('')) + match = text.match(pattern) + if (match) { + // @todo: time zone information + if (match[4] === 'z') { + match[4] = 'Z' + } else if (match[4].match(/^([\+-][0-9]{2})$/)) { + match[4] = match[4] + ':00' + } + + if (!isNaN(parsed = Date.parse(match[1] + 'T' + match[2] + match[4]))) { + return parsed / 1000 | 0 } + } - date = now ? new Date(now * 1000) : new Date(); - days = { - 'sun': 0, - 'mon': 1, - 'tue': 2, - 'wed': 3, - 'thu': 4, - 'fri': 5, - 'sat': 6 - }; - ranges = { - 'yea': 'FullYear', - 'mon': 'Month', - 'day': 'Date', - 'hou': 'Hours', - 'min': 'Minutes', - 'sec': 'Seconds' - }; + date = now ? new Date(now * 1000) : new Date() + days = { + 'sun': 0, + 'mon': 1, + 'tue': 2, + 'wed': 3, + 'thu': 4, + 'fri': 5, + 'sat': 6 + } + ranges = { + 'yea': 'FullYear', + 'mon': 'Month', + 'day': 'Date', + 'hou': 'Hours', + 'min': 'Minutes', + 'sec': 'Seconds' + } - function lastNext(type, range, modifier) { - var diff, day = days[range]; + function lastNext (type, range, modifier) { + var diff + var day = days[range] - if (typeof day !== 'undefined') { - diff = day - date.getDay(); + if (typeof day !== 'undefined') { + 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; - } + 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); - } + date.setDate(date.getDate() + diff) } - function process(val) { - var splt = val.split(' '), // Todo: Reconcile this with regex using \s, taking into account browser issues with split and regexes - type = splt[0], - range = splt[1].substring(0, 3), - typeIsNumber = /\d+/.test(type), - ago = splt[2] === 'ago', - num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1); + } - if (typeIsNumber) { - num *= parseInt(type, 10); - } + function process (val) { + // @todo: Reconcile this with regex using \s, taking into account + // browser issues with split and regexes + var splt = val.split(' ') + var type = splt[0] + var range = splt[1].substring(0, 3) + var typeIsNumber = /\d+/.test(type) + var ago = splt[2] === 'ago' + var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1) - if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { - return date['set' + ranges[range]](date['get' + ranges[range]]() + num); - } + if (typeIsNumber) { + num *= parseInt(type, 10) + } - if (range === 'wee') { - return date.setDate(date.getDate() + (num * 7)); - } + if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { + return date['set' + ranges[range]](date['get' + ranges[range]]() + num) + } - if (type === 'next' || type === 'last') { - lastNext(type, range, num); - } - else if (!typeIsNumber) { - return false; - } + if (range === 'wee') { + return date.setDate(date.getDate() + (num * 7)) + } - return true; + if (type === 'next' || type === 'last') { + lastNext(type, range, num) + } else if (!typeIsNumber) { + return false } - times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + - '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + - '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'; - regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'; + return true + } - match = text.match(new RegExp(regex, 'gi')); - if (!match) { - return fail; - } + times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + + '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + + '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)' + regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?' - for (i = 0, len = match.length; i < len; i++) { - if (!process(match[i])) { - return fail; - } - } + match = text.match(new RegExp(regex, 'gi')) + if (!match) { + return fail + } - // ECMAScript 5 only - // if (!match.every(process)) - // return false; + for (i = 0, len = match.length; i < len; i++) { + if (!process(match[i])) { + return fail + } + } - return (date.getTime() / 1000); + return (date.getTime() / 1000) } function substr (str, start, len) {