/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ dojo.provide("layer.validation"); dojo.provide("dojo.experimental"); dojo.experimental = function(/* String */ moduleName, /* String? */ extra){ // summary: Marks code as experimental. // description: // This can be used to mark a function, file, or module as experimental. // Experimental code is not ready to be used, and the APIs are subject // to change without notice. Experimental code may be completed deleted // without going through the normal deprecation process. // moduleName: The name of a module, or the name of a module file or a specific function // extra: some additional message for the user // examples: // dojo.experimental("dojo.data.Result"); // dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA"); var message = "EXPERIMENTAL: " + moduleName; message += " -- Not yet ready for use. APIs subject to change without notice."; if(extra){ message += " " + extra; } dojo.debug(message); } dojo.provide("dojo.regexp"); dojo.evalObjPath("dojo.regexp.us", true); // this file also defines stuff in the dojo.regexp.us module (TODO: move to separate file?) // *** Regular Expression Generators *** dojo.regexp.tld = function(/*Object?*/flags){ // summary: Builds a RE that matches a top-level domain // // flags: // flags.allowCC Include 2 letter country code domains. Default is true. // flags.allowGeneric Include the generic domains. Default is true. // flags.allowInfra Include infrastructure domains. Default is true. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.allowCC != "boolean"){ flags.allowCC = true; } if(typeof flags.allowInfra != "boolean"){ flags.allowInfra = true; } if(typeof flags.allowGeneric != "boolean"){ flags.allowGeneric = true; } // Infrastructure top-level domain - only one at present var infraRE = "arpa"; // Generic top-level domains RE. var genericRE = "aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|xxx|jobs|mobi|post"; // Country Code top-level domains RE var ccRE = "ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|" + "bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|" + "ec|ee|eg|er|eu|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|" + "gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kr|kw|ky|kz|" + "la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|" + "my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|" + "re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sk|sl|sm|sn|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|" + "tn|to|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw"; // Build top-level domain RE var a = []; if(flags.allowInfra){ a.push(infraRE); } if(flags.allowGeneric){ a.push(genericRE); } if(flags.allowCC){ a.push(ccRE); } var tldRE = ""; if (a.length > 0) { tldRE = "(" + a.join("|") + ")"; } return tldRE; // String } dojo.regexp.ipAddress = function(/*Object?*/flags){ // summary: Builds a RE that matches an IP Address // // description: // Supports 5 formats for IPv4: dotted decimal, dotted hex, dotted octal, decimal and hexadecimal. // Supports 2 formats for Ipv6. // // flags An object. All flags are boolean with default = true. // flags.allowDottedDecimal Example, 207.142.131.235. No zero padding. // flags.allowDottedHex Example, 0x18.0x11.0x9b.0x28. Case insensitive. Zero padding allowed. // flags.allowDottedOctal Example, 0030.0021.0233.0050. Zero padding allowed. // flags.allowDecimal Example, 3482223595. A decimal number between 0-4294967295. // flags.allowHex Example, 0xCF8E83EB. Hexadecimal number between 0x0-0xFFFFFFFF. // Case insensitive. Zero padding allowed. // flags.allowIPv6 IPv6 address written as eight groups of four hexadecimal digits. // flags.allowHybrid IPv6 address written as six groups of four hexadecimal digits // followed by the usual 4 dotted decimal digit notation of IPv4. x:x:x:x:x:x:d.d.d.d // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.allowDottedDecimal != "boolean"){ flags.allowDottedDecimal = true; } if(typeof flags.allowDottedHex != "boolean"){ flags.allowDottedHex = true; } if(typeof flags.allowDottedOctal != "boolean"){ flags.allowDottedOctal = true; } if(typeof flags.allowDecimal != "boolean"){ flags.allowDecimal = true; } if(typeof flags.allowHex != "boolean"){ flags.allowHex = true; } if(typeof flags.allowIPv6 != "boolean"){ flags.allowIPv6 = true; } if(typeof flags.allowHybrid != "boolean"){ flags.allowHybrid = true; } // decimal-dotted IP address RE. var dottedDecimalRE = // Each number is between 0-255. Zero padding is not allowed. "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])"; // dotted hex IP address RE. Each number is between 0x0-0xff. Zero padding is allowed, e.g. 0x00. var dottedHexRE = "(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]"; // dotted octal IP address RE. Each number is between 0000-0377. // Zero padding is allowed, but each number must have at least 4 characters. var dottedOctalRE = "(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]"; // decimal IP address RE. A decimal number between 0-4294967295. var decimalRE = "(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|" + "4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])"; // hexadecimal IP address RE. // A hexadecimal number between 0x0-0xFFFFFFFF. Case insensitive. Zero padding is allowed. var hexRE = "0[xX]0*[\\da-fA-F]{1,8}"; // IPv6 address RE. // The format is written as eight groups of four hexadecimal digits, x:x:x:x:x:x:x:x, // where x is between 0000-ffff. Zero padding is optional. Case insensitive. var ipv6RE = "([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}"; // IPv6/IPv4 Hybrid address RE. // The format is written as six groups of four hexadecimal digits, // followed by the 4 dotted decimal IPv4 format. x:x:x:x:x:x:d.d.d.d var hybridRE = "([\\da-fA-F]{1,4}\\:){6}" + "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])"; // Build IP Address RE var a = []; if(flags.allowDottedDecimal){ a.push(dottedDecimalRE); } if(flags.allowDottedHex){ a.push(dottedHexRE); } if(flags.allowDottedOctal){ a.push(dottedOctalRE); } if(flags.allowDecimal){ a.push(decimalRE); } if(flags.allowHex){ a.push(hexRE); } if(flags.allowIPv6){ a.push(ipv6RE); } if(flags.allowHybrid){ a.push(hybridRE); } var ipAddressRE = ""; if(a.length > 0){ ipAddressRE = "(" + a.join("|") + ")"; } return ipAddressRE; // String } dojo.regexp.host = function(/*Object?*/flags){ // summary: Builds a RE that matches a host // description: A host is a domain name or an IP address, possibly followed by a port number. // flags: An object. // flags.allowIP Allow an IP address for hostname. Default is true. // flags.allowLocal Allow the host to be "localhost". Default is false. // flags.allowPort Allow a port number to be present. Default is true. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.allowIP != "boolean"){ flags.allowIP = true; } if(typeof flags.allowLocal != "boolean"){ flags.allowLocal = false; } if(typeof flags.allowPort != "boolean"){ flags.allowPort = true; } // Domain names can not end with a dash. var domainNameRE = "([0-9a-zA-Z]([-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?\\.)+" + dojo.regexp.tld(flags); // port number RE var portRE = ( flags.allowPort ) ? "(\\:" + dojo.regexp.integer({signed: false}) + ")?" : ""; // build host RE var hostNameRE = domainNameRE; if(flags.allowIP){ hostNameRE += "|" + dojo.regexp.ipAddress(flags); } if(flags.allowLocal){ hostNameRE += "|localhost"; } return "(" + hostNameRE + ")" + portRE; // String } dojo.regexp.url = function(/*Object?*/flags){ // summary: Builds a regular expression that matches a URL // // flags: An object // flags.scheme Can be true, false, or [true, false]. // This means: required, not allowed, or match either one. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.scheme == "undefined"){ flags.scheme = [true, false]; } // Scheme RE var protocolRE = dojo.regexp.buildGroupRE(flags.scheme, function(q){ if(q){ return "(https?|ftps?)\\://"; } return ""; } ); // Path and query and anchor RE var pathRE = "(/([^?#\\s/]+/)*)?([^?#\\s/]+(\\?[^?#\\s/]*)?(#[A-Za-z][\\w.:-]*)?)?"; return protocolRE + dojo.regexp.host(flags) + pathRE; } dojo.regexp.emailAddress = function(/*Object?*/flags){ // summary: Builds a regular expression that matches an email address // //flags: An object // flags.allowCruft Allow address like . Default is false. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if (typeof flags.allowCruft != "boolean") { flags.allowCruft = false; } flags.allowPort = false; // invalid in email addresses // user name RE - apostrophes are valid if there's not 2 in a row var usernameRE = "([\\da-z]+[-._+&'])*[\\da-z]+"; // build emailAddress RE var emailAddressRE = usernameRE + "@" + dojo.regexp.host(flags); // Allow email addresses with cruft if ( flags.allowCruft ) { emailAddressRE = "?"; } return emailAddressRE; // String } dojo.regexp.emailAddressList = function(/*Object?*/flags){ // summary: Builds a regular expression that matches a list of email addresses. // // flags: An object. // flags.listSeparator The character used to separate email addresses. Default is ";", ",", "\n" or " ". // flags in regexp.emailAddress can be applied. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.listSeparator != "string"){ flags.listSeparator = "\\s;,"; } // build a RE for an Email Address List var emailAddressRE = dojo.regexp.emailAddress(flags); var emailAddressListRE = "(" + emailAddressRE + "\\s*[" + flags.listSeparator + "]\\s*)*" + emailAddressRE + "\\s*[" + flags.listSeparator + "]?\\s*"; return emailAddressListRE; // String } dojo.regexp.integer = function(/*Object?*/flags){ // summary: Builds a regular expression that matches an integer // // flags: An object // flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. // Default is [true, false], (i.e. will match if it is signed or unsigned). // flags.separator The character used as the thousands separator. Default is no separator. // For more than one symbol use an array, e.g. [",", ""], makes ',' optional. // flags.groupSize group size between separators // flags.groupSize2 second grouping (for India) // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.signed == "undefined"){ flags.signed = [true, false]; } if(typeof flags.separator == "undefined"){ flags.separator = ""; } else if(typeof flags.groupSize == "undefined"){ flags.groupSize = 3; } // build sign RE var signRE = dojo.regexp.buildGroupRE(flags.signed, function(q) { return q ? "[-+]" : ""; } ); // number RE var numberRE = dojo.regexp.buildGroupRE(flags.separator, function(sep){ if(sep == ""){ return "(0|[1-9]\\d*)"; } var grp = flags.groupSize, grp2 = flags.groupSize2; if(typeof grp2 != "undefined"){ var grp2RE = "(0|[1-9]\\d{0," + (grp2-1) + "}([" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})"; return ((grp-grp2) > 0) ? "(" + grp2RE + "|(0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE; } return "(0|[1-9]\\d{0," + (grp-1) + "}([" + sep + "]\\d{" + grp + "})*)"; } ); // integer RE return signRE + numberRE; // String } dojo.regexp.realNumber = function(/*Object?*/flags){ // summary: Builds a regular expression to match a real number in exponential notation // // flags:An object // flags.places The integer number of decimal places. // If not given, the decimal part is optional and the number of places is unlimited. // flags.decimal A string for the character used as the decimal point. Default is ".". // flags.fractional Whether decimal places are allowed. // Can be true, false, or [true, false]. Default is [true, false] // flags.exponent Express in exponential notation. Can be true, false, or [true, false]. // Default is [true, false], (i.e. will match if the exponential part is present are not). // flags.eSigned The leading plus-or-minus sign on the exponent. Can be true, false, // or [true, false]. Default is [true, false], (i.e. will match if it is signed or unsigned). // flags in regexp.integer can be applied. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.places != "number"){ flags.places = Infinity; } if(typeof flags.decimal != "string"){ flags.decimal = "."; } if(typeof flags.fractional == "undefined"){ flags.fractional = [true, false]; } if(typeof flags.exponent == "undefined"){ flags.exponent = [true, false]; } if(typeof flags.eSigned == "undefined"){ flags.eSigned = [true, false]; } // integer RE var integerRE = dojo.regexp.integer(flags); // decimal RE var decimalRE = dojo.regexp.buildGroupRE(flags.fractional, function(q){ var re = ""; if(q && (flags.places > 0)){ re = "\\" + flags.decimal; if(flags.places == Infinity){ re = "(" + re + "\\d+)?"; }else{ re = re + "\\d{" + flags.places + "}"; } } return re; } ); // exponent RE var exponentRE = dojo.regexp.buildGroupRE(flags.exponent, function(q){ if(q){ return "([eE]" + dojo.regexp.integer({ signed: flags.eSigned}) + ")"; } return ""; } ); // real number RE return integerRE + decimalRE + exponentRE; // String } dojo.regexp.currency = function(/*Object?*/flags){ // summary: Builds a regular expression to match a monetary value // // flags: An object // flags.symbol A currency symbol such as Yen "�", Pound "�", or the Euro sign "�". // Default is "$". For more than one symbol use an array, e.g. ["$", ""], makes $ optional. // flags.placement The symbol can come "before" the number or "after" the number. Default is "before". // flags.signPlacement The sign can come "before" the number or "after" the sign, // "around" to put parentheses around negative values, or "end" for the final char. Default is "before". // flags.cents deprecated, in favor of flags.fractional // flags in regexp.realNumber can be applied except exponent, eSigned. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.signed == "undefined"){ flags.signed = [true, false]; } if(typeof flags.symbol == "undefined"){ flags.symbol = "$"; } if(typeof flags.placement != "string"){ flags.placement = "before"; } if(typeof flags.signPlacement != "string"){ flags.signPlacement = "before"; } if(typeof flags.separator == "undefined"){ flags.separator = ","; } if(typeof flags.fractional == "undefined" && typeof flags.cents != "undefined"){ dojo.deprecated("dojo.regexp.currency: flags.cents", "use flags.fractional instead", "0.5"); flags.fractional = flags.cents; } if(typeof flags.decimal != "string"){ flags.decimal = "."; } // build sign RE var signRE = dojo.regexp.buildGroupRE(flags.signed, function(q){ if (q){ return "[-+]"; } return ""; } ); // build symbol RE var symbolRE = dojo.regexp.buildGroupRE(flags.symbol, function(symbol){ // escape all special characters return "\\s?" + symbol.replace( /([.$?*!=:|\\\/^])/g, "\\$1") + "\\s?"; } ); switch (flags.signPlacement){ case "before": symbolRE = signRE + symbolRE; break; case "after": symbolRE = symbolRE + signRE; break; } // number RE var flagsCopy = flags; //TODO: copy by value? flagsCopy.signed = false; flagsCopy.exponent = false; var numberRE = dojo.regexp.realNumber(flagsCopy); // build currency RE var currencyRE; switch (flags.placement){ case "before": currencyRE = symbolRE + numberRE; break; case "after": currencyRE = numberRE + symbolRE; break; } switch (flags.signPlacement){ case "around": currencyRE = "(" + currencyRE + "|" + "\\(" + currencyRE + "\\)" + ")"; break; case "begin": currencyRE = signRE + currencyRE; break; case "end": currencyRE = currencyRE + signRE; break; } return currencyRE; // String } dojo.regexp.us.state = function(/*Object?*/flags){ // summary: A regular expression to match US state and territory abbreviations // // flags An object. // flags.allowTerritories Allow Guam, Puerto Rico, etc. Default is true. // flags.allowMilitary Allow military 'states', e.g. Armed Forces Europe (AE). Default is true. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.allowTerritories != "boolean"){ flags.allowTerritories = true; } if(typeof flags.allowMilitary != "boolean"){ flags.allowMilitary = true; } // state RE var statesRE = "AL|AK|AZ|AR|CA|CO|CT|DE|DC|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|" + "NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY"; // territories RE var territoriesRE = "AS|FM|GU|MH|MP|PW|PR|VI"; // military states RE var militaryRE = "AA|AE|AP"; // Build states and territories RE if(flags.allowTerritories){ statesRE += "|" + territoriesRE; } if(flags.allowMilitary){ statesRE += "|" + militaryRE; } return "(" + statesRE + ")"; // String } dojo.regexp.time = function(/*Object?*/flags){ // summary: Builds a regular expression to match any International format for time // description: The RE can match one format or one of multiple formats. // // Format // h 12 hour, no zero padding. // hh 12 hour, has leading zero. // H 24 hour, no zero padding. // HH 24 hour, has leading zero. // m minutes, no zero padding. // mm minutes, has leading zero. // s seconds, no zero padding. // ss seconds, has leading zero. // t am or pm, case insensitive. // All other characters must appear literally in the expression. // // Example // "h:m:s t" -> 2:5:33 PM // "HH:mm:ss" -> 14:05:33 // // flags: An object // flags.format A string or an array of strings. Default is "h:mm:ss t". // flags.amSymbol The symbol used for AM. Default is "AM". // flags.pmSymbol The symbol used for PM. Default is "PM". dojo.deprecated("dojo.regexp.time", "Use dojo.date.parse instead", "0.5"); // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.format == "undefined"){ flags.format = "h:mm:ss t"; } if(typeof flags.amSymbol != "string"){ flags.amSymbol = "AM"; } if(typeof flags.pmSymbol != "string"){ flags.pmSymbol = "PM"; } // Converts a time format to a RE var timeRE = function(format){ // escape all special characters format = format.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); var amRE = flags.amSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); var pmRE = flags.pmSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); // replace tokens with Regular Expressions format = format.replace("hh", "(0[1-9]|1[0-2])"); format = format.replace("h", "([1-9]|1[0-2])"); format = format.replace("HH", "([01][0-9]|2[0-3])"); format = format.replace("H", "([0-9]|1[0-9]|2[0-3])"); format = format.replace("mm", "([0-5][0-9])"); format = format.replace("m", "([1-5][0-9]|[0-9])"); format = format.replace("ss", "([0-5][0-9])"); format = format.replace("s", "([1-5][0-9]|[0-9])"); format = format.replace("t", "\\s?(" + amRE + "|" + pmRE + ")\\s?" ); return format; // String }; // build RE for multiple time formats return dojo.regexp.buildGroupRE(flags.format, timeRE); // String } dojo.regexp.numberFormat = function(/*Object?*/flags){ // summary: Builds a regular expression to match any sort of number based format // description: // Use this method for phone numbers, social security numbers, zip-codes, etc. // The RE can match one format or one of multiple formats. // // Format // # Stands for a digit, 0-9. // ? Stands for an optional digit, 0-9 or nothing. // All other characters must appear literally in the expression. // // Example // "(###) ###-####" -> (510) 542-9742 // "(###) ###-#### x#???" -> (510) 542-9742 x153 // "###-##-####" -> 506-82-1089 i.e. social security number // "#####-####" -> 98225-1649 i.e. zip code // // flags: An object // flags.format A string or an Array of strings for multiple formats. // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; if(typeof flags.format == "undefined"){ flags.format = "###-###-####"; } // Converts a number format to RE. var digitRE = function(format){ // escape all special characters, except '?' format = format.replace( /([.$*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); // Now replace '?' with Regular Expression format = format.replace(/\?/g, "\\d?"); // replace # with Regular Expression format = format.replace(/#/g, "\\d"); return format; // String }; // build RE for multiple number formats return dojo.regexp.buildGroupRE(flags.format, digitRE); //String } dojo.regexp.buildGroupRE = function(/*value or Array of values*/a, /*Function(x) returns a regular expression as a String*/re){ // summary: Builds a regular expression that groups subexpressions // description: A utility function used by some of the RE generators. // The subexpressions are constructed by the function, re, in the second parameter. // re builds one subexpression for each elem in the array a, in the first parameter. // Returns a string for a regular expression that groups all the subexpressions. // // a: A single value or an array of values. // re: A function. Takes one parameter and converts it to a regular expression. // case 1: a is a single value. if(!(a instanceof Array)){ return re(a); // String } // case 2: a is an array var b = []; for (var i = 0; i < a.length; i++){ // convert each elem to a RE b.push(re(a[i])); } // join the REs as alternatives in a RE group. return "(" + b.join("|") + ")"; // String } dojo.provide("dojo.i18n.number"); dojo.require("dojo.i18n.common"); dojo.require("dojo.lang.common"); /** * Method to Format and validate a given number * * @param Number value * The number to be formatted and validated. * @param Object flags * flags.places number of decimal places to show, default is 0 (cannot be Infinity) * flags.round true to round the number, false to truncate * @param String locale * The locale used to determine the number format. * @return String * the formatted number of type String if successful * or null if an unsupported locale value was provided **/ dojo.i18n.number.format = function(value, flags /*optional*/, locale /*optional*/){ flags = (typeof flags == "object") ? flags : {}; var formatData = dojo.i18n.number._mapToLocalizedFormatData(dojo.i18n.number.FORMAT_TABLE, locale); if (typeof flags.separator == "undefined") {flags.separator = formatData[1];} if (typeof flags.decimal == "undefined") {flags.decimal = formatData[2];} if (typeof flags.groupSize == "undefined") {flags.groupSize = formatData[3];} if (typeof flags.groupSize2 == "undefined") {flags.groupSize2 = formatData[4];} if (typeof flags.round == "undefined") {flags.round = true;} if (typeof flags.signed == "undefined") {flags.signed = true;} var output = (flags.signed && (value < 0)) ? "-" : ""; value = Math.abs(value); var whole = String((((flags.places > 0) || !flags.round) ? Math.floor : Math.round)(value)); // Splits str into substrings of size count, starting from right to left. Is there a more clever way to do this in JS? function splitSubstrings(str, count){ for(var subs = []; str.length >= count; str = str.substr(0, str.length - count)){ subs.push(str.substr(-count)); } if (str.length > 0){subs.push(str);} return subs.reverse(); } if (flags.groupSize2 && (whole.length > flags.groupSize)){ var groups = splitSubstrings(whole.substr(0, whole.length - flags.groupSize), flags.groupSize2); groups.push(whole.substr(-flags.groupSize)); output = output + groups.join(flags.separator); }else if (flags.groupSize){ output = output + splitSubstrings(whole, flags.groupSize).join(flags.separator); }else{ output = output + whole; } //TODO: what if flags.places is Infinity? if (flags.places > 0){ //Q: Is it safe to convert to a string and split on ".", or might that be locale dependent? Use Math for now. var fract = value - Math.floor(value); fract = (flags.round ? Math.round : Math.floor)(fract * Math.pow(10, flags.places)); output = output + flags.decimal + fract; } //TODO: exp return output; }; /** * Method to convert a properly formatted int string to a primative numeric value. * * @param String value * The int string to be convertted * @param string locale * The locale used to convert the number string * @param Object flags * flags.validate true to check the string for strict adherence to the locale settings for separator, sign, etc. * Default is true * @return Number * Returns a value of type Number, Number.NaN if not a number, or null if locale is not supported. **/ dojo.i18n.number.parse = function(value, locale /*optional*/, flags /*optional*/){ flags = (typeof flags == "object") ? flags : {}; var formatData = dojo.i18n.number._mapToLocalizedFormatData(dojo.i18n.number.FORMAT_TABLE, locale); if (typeof flags.separator == "undefined") {flags.separator = formatData[1];} if (typeof flags.decimal == "undefined") {flags.decimal = formatData[2];} if (typeof flags.groupSize == "undefined") {flags.groupSize = formatData[3];} if (typeof flags.groupSize2 == "undefined") {flags.groupSize2 = formatData[4];} if (typeof flags.validate == "undefined") {flags.validate = true;} if (flags.validate && !dojo.i18n.number.isReal(value, locale, flags)) { return Number.NaN; } var numbers = value.split(flags.decimal); if (numbers.length > 2){return Number.NaN; } var whole; if (flags.separator != ""){ whole = Number(numbers[0].replace(new RegExp("\\" + flags.separator, "g"), "")); } else { whole = Number(numbers[0]); } var fract = (numbers.length == 1) ? 0 : Number(numbers[1]) / Math.pow(10, String(numbers[1]).length); //TODO: exp return whole + fract; }; /** Validates whether a string is in an integer format. @param value A string. @param locale the locale to determine formatting used. By default, the locale defined by the host environment: dojo.locale @param flags An object. flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. Default is [true, false], (i.e. sign is optional). flags.separator The character used as the thousands separator. Default is specified by the locale. For more than one symbol use an array, e.g. [",", ""], makes ',' optional. The empty array [] makes the default separator optional. @return true or false. */ dojo.i18n.number.isInteger = function(value, locale /*optional*/, flags /*optional*/) { flags = (typeof flags == "object") ? flags : {}; var formatData = dojo.i18n.number._mapToLocalizedFormatData(dojo.i18n.number.FORMAT_TABLE, locale); if (typeof flags.separator == "undefined") {flags.separator = formatData[1];} else if (dojo.lang.isArray(flags.separator) && flags.separator.length ===0){ flags.separator = [formatData[1],""]; } if (typeof flags.groupSize == "undefined") {flags.groupSize = formatData[3];} if (typeof flags.groupSize2 == "undefined") {flags.groupSize2 = formatData[4];} var re = new RegExp("^" + dojo.regexp.integer(flags) + "$"); return re.test(value); }; /** Validates whether a string is a real valued number. Format is the usual exponential notation. @param value A string. @param locale the locale to determine formatting used. By default, the locale defined by the host environment: dojo.locale @param flags An object. flags.places The integer number of decimal places. If not given, the decimal part is optional and the number of places is unlimited. flags.decimal The character used for the decimal point. The default is specified by the locale. flags.exponent Express in exponential notation. Can be true, false, or [true, false]. Default is [true, false], (i.e. the exponential part is optional). flags.eSigned The leading plus-or-minus sign on the exponent. Can be true, false, or [true, false]. Default is [true, false], (i.e. sign is optional). flags in regexp.integer can be applied. @return true or false. */ dojo.i18n.number.isReal = function(value, locale /*optional*/, flags /*optional*/) { flags = (typeof flags == "object") ? flags : {}; var formatData = dojo.i18n.number._mapToLocalizedFormatData(dojo.i18n.number.FORMAT_TABLE, locale); if (typeof flags.separator == "undefined") {flags.separator = formatData[1];} else if (dojo.lang.isArray(flags.separator) && flags.separator.length ===0){ flags.separator = [formatData[1],""]; } if (typeof flags.decimal == "undefined") {flags.decimal = formatData[2];} if (typeof flags.groupSize == "undefined") {flags.groupSize = formatData[3];} if (typeof flags.groupSize2 == "undefined") {flags.groupSize2 = formatData[4];} var re = new RegExp("^" + dojo.regexp.realNumber(flags) + "$"); return re.test(value); }; //TODO: hide in a closure? //TODO: change to use hashes and mixins, rather than arrays //Q: fallback algorithm/how to structure table: // does it make sense to look by country code most of the time (wildcard match on // language, except where it's relevant) and provide default country when only // a language is given? (function() { dojo.i18n.number.FORMAT_TABLE = { //0: thousand seperator for monetary, 1: thousand seperator for number, 2: decimal seperator, 3: group size, 4: second group size because of india 'ar-ae': ["","", ",", 1], 'ar-bh': ["","",",", 1], 'ar-dz': ["","",",", 1], 'ar-eg': ["","", ",", 1], 'ar-jo': ["","",",", 1], 'ar-kw': ["","", ",", 1], 'ar-lb': ["","", ",", 1], 'ar-ma': ["","", ",", 1], 'ar-om': ["","", ",", 1], 'ar-qa': ["","", ",", 1], 'ar-sa': ["","", ",", 1], 'ar-sy': ["","", ",", 1], 'ar-tn': ["","", ",", 1], 'ar-ye': ["","", ",", 1], 'cs-cz': [".",".", ",", 3], 'da-dk': [".",".", ",", 3], 'de-at': [".",".", ",", 3], 'de-de': [".",".", ",", 3], 'de-lu': [".",".", ",", 3], //IBM JSL defect 51278. right now we have problem with single quote. //IBM: explain? 'de-ch': ["'","'", ".", 3], //Q: comma as decimal separator for currency?? //'de-ch': [".",".", ",", 3], 'el-gr': [".",".", ",", 3], 'en-au': [",",",", ".", 3], 'en-ca': [",",",", ".", 3], 'en-gb': [",",",", ".", 3], 'en-hk': [",",",", ".", 3], 'en-ie': [",",",", ".", 3], 'en-in': [",",",", ".", 3,2],//india-english, 1,23,456.78 'en-nz': [",",",", ".", 3], 'en-us': [",",",", ".", 3], 'en-za': [",",",", ".", 3], 'es-ar': [".",".", ",", 3], 'es-bo': [".",".", ",", 3], 'es-cl': [".",".", ",", 3], 'es-co': [".",".", ",", 3], 'es-cr': [".",".", ",", 3], 'es-do': [".",".", ",", 3], 'es-ec': [".",".", ",", 3], 'es-es': [".",".", ",", 3], 'es-gt': [",",",", ".", 3], 'es-hn': [",",",", ".", 3], 'es-mx': [",",",", ".", 3], 'es-ni': [",",",", ".", 3], 'es-pa': [",",",", ".", 3], 'es-pe': [",",",", ".", 3], 'es-pr': [",",",", ".", 3], 'es-py': [".",".",",", 3], 'es-sv': [",", ",",".", 3], 'es-uy': [".",".",",", 3], 'es-ve': [".",".", ",", 3], 'fi-fi': [" "," ", ",", 3], 'fr-be': [".",".",",", 3], 'fr-ca': [" ", " ", ",", 3], 'fr-ch': [" ", " ",".", 3], 'fr-fr': [" "," ", ",", 3], 'fr-lu': [".",".", ",", 3], 'he-il': [",",",", ".", 3], 'hu-hu': [" ", " ",",", 3], 'it-ch': [" "," ", ".", 3], 'it-it': [".",".", ",", 3], 'ja-jp': [",",",", ".", 3], 'ko-kr': [",", ",",".", 3], 'no-no': [".",".", ",", 3], 'nl-be': [" "," ", ",", 3], 'nl-nl': [".",".", ",", 3], 'pl-pl': [".", ".",",", 3], 'pt-br': [".",".", ",", 3], 'pt-pt': [".",".", "$", 3], 'ru-ru': [" ", " ",",", 3], 'sv-se': ["."," ", ",", 3], 'tr-tr': [".",".", ",", 3], 'zh-cn': [",",",", ".", 3], 'zh-hk': [",",",",".", 3], 'zh-tw': [",", ",",".", 3], '*': [",",",", ".", 3] }; })(); dojo.i18n.number._mapToLocalizedFormatData = function(table, locale){ locale = dojo.hostenv.normalizeLocale(locale); //TODO: most- to least-specific search? search by country code? //TODO: implement aliases to simplify and shorten tables var data = table[locale]; if (typeof data == 'undefined'){data = table['*'];} return data; } dojo.provide("dojo.validate.common"); dojo.validate.isText = function(/*String*/value, /*Object?*/flags){ // summary: // Checks if a string has non whitespace characters. // Parameters allow you to constrain the length. // // value: A string // flags: {length: Number, minlength: Number, maxlength: Number} // flags.length If set, checks if there are exactly flags.length number of characters. // flags.minlength If set, checks if there are at least flags.minlength number of characters. // flags.maxlength If set, checks if there are at most flags.maxlength number of characters. flags = (typeof flags == "object") ? flags : {}; // test for text if(/^\s*$/.test(value)){ return false; } // Boolean // length tests if(typeof flags.length == "number" && flags.length != value.length){ return false; } // Boolean if(typeof flags.minlength == "number" && flags.minlength > value.length){ return false; } // Boolean if(typeof flags.maxlength == "number" && flags.maxlength < value.length){ return false; } // Boolean return true; // Boolean } dojo.validate.isInteger = function(/*String*/value, /*Object?*/flags){ // summary: // Validates whether a string is in an integer format // // value A string // flags {signed: Boolean|[true,false], separator: String} // flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. // Default is [true, false], (i.e. sign is optional). // flags.separator The character used as the thousands separator. Default is no separator. // For more than one symbol use an array, e.g. [",", ""], makes ',' optional. var re = new RegExp("^" + dojo.regexp.integer(flags) + "$"); return re.test(value); // Boolean } dojo.validate.isRealNumber = function(/*String*/value, /*Object?*/flags){ // summary: // Validates whether a string is a real valued number. // Format is the usual exponential notation. // // value: A string // flags: {places: Number, decimal: String, exponent: Boolean|[true,false], eSigned: Boolean|[true,false], ...} // flags.places The integer number of decimal places. // If not given, the decimal part is optional and the number of places is unlimited. // flags.decimal The character used for the decimal point. Default is ".". // flags.exponent Express in exponential notation. Can be true, false, or [true, false]. // Default is [true, false], (i.e. the exponential part is optional). // flags.eSigned The leading plus-or-minus sign on the exponent. Can be true, false, // or [true, false]. Default is [true, false], (i.e. sign is optional). // flags in regexp.integer can be applied. var re = new RegExp("^" + dojo.regexp.realNumber(flags) + "$"); return re.test(value); // Boolean } dojo.validate.isCurrency = function(/*String*/value, /*Object?*/flags){ // summary: // Validates whether a string denotes a monetary value. // value: A string // flags: {signed:Boolean|[true,false], symbol:String, placement:String, separator:String, // fractional:Boolean|[true,false], decimal:String} // flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. // Default is [true, false], (i.e. sign is optional). // flags.symbol A currency symbol such as Yen "�", Pound "�", or the Euro sign "�". // Default is "$". For more than one symbol use an array, e.g. ["$", ""], makes $ optional. // flags.placement The symbol can come "before" the number or "after". Default is "before". // flags.separator The character used as the thousands separator. The default is ",". // flags.fractional The appropriate number of decimal places for fractional currency (e.g. cents) // Can be true, false, or [true, false]. Default is [true, false], (i.e. cents are optional). // flags.decimal The character used for the decimal point. Default is ".". var re = new RegExp("^" + dojo.regexp.currency(flags) + "$"); return re.test(value); // Boolean } dojo.validate._isInRangeCache = {}; dojo.validate.isInRange = function(/*String*/value, /*Object?*/flags){ //summary: // Validates whether a string denoting an integer, // real number, or monetary value is between a max and min. // // value: A string // flags: {max:Number, min:Number, decimal:String} // flags.max A number, which the value must be less than or equal to for the validation to be true. // flags.min A number, which the value must be greater than or equal to for the validation to be true. // flags.decimal The character used for the decimal point. Default is ".". //stripping the separator allows NaN to perform as expected, if no separator, we assume ',' //once i18n support is ready for this, instead of assuming, we default to i18n's recommended value value = value.replace(dojo.lang.has(flags,'separator')?flags.separator:',', '', 'g'). replace(dojo.lang.has(flags,'symbol')?flags.symbol:'$', ''); if(isNaN(value)){ return false; // Boolean } // assign default values to missing paramters flags = (typeof flags == "object") ? flags : {}; var max = (typeof flags.max == "number") ? flags.max : Infinity; var min = (typeof flags.min == "number") ? flags.min : -Infinity; var dec = (typeof flags.decimal == "string") ? flags.decimal : "."; var cache = dojo.validate._isInRangeCache; var cacheIdx = value+"max"+max+"min"+min+"dec"+dec; if(typeof cache[cacheIdx] != "undefined"){ return cache[cacheIdx]; } // splice out anything not part of a number var pattern = "[^" + dec + "\\deE+-]"; value = value.replace(RegExp(pattern, "g"), ""); // trim ends of things like e, E, or the decimal character value = value.replace(/^([+-]?)(\D*)/, "$1"); value = value.replace(/(\D*)$/, ""); // replace decimal with ".". The minus sign '-' could be the decimal! pattern = "(\\d)[" + dec + "](\\d)"; value = value.replace(RegExp(pattern, "g"), "$1.$2"); value = Number(value); if ( value < min || value > max ) { cache[cacheIdx] = false; return false; } // Boolean cache[cacheIdx] = true; return true; // Boolean } dojo.validate.isNumberFormat = function(/*String*/value, /*Object?*/flags){ // summary: // Validates any sort of number based format // // description: // Use it for phone numbers, social security numbers, zip-codes, etc. // The value can be validated against one format or one of multiple formats. // // Format // # Stands for a digit, 0-9. // ? Stands for an optional digit, 0-9 or nothing. // All other characters must appear literally in the expression. // // Example // "(###) ###-####" -> (510) 542-9742 // "(###) ###-#### x#???" -> (510) 542-9742 x153 // "###-##-####" -> 506-82-1089 i.e. social security number // "#####-####" -> 98225-1649 i.e. zip code // // value: A string // flags: {format:String} // flags.format A string or an Array of strings for multiple formats. var re = new RegExp("^" + dojo.regexp.numberFormat(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.isValidLuhn = function(/*String*/value){ //summary: Compares value against the Luhn algorithm to verify its integrity var sum, parity, curDigit; if(typeof value!='string'){ value = String(value); } value = value.replace(/[- ]/g,''); //ignore dashes and whitespaces parity = value.length%2; sum=0; for(var i=0;i9){ curDigit-=9; } sum+=curDigit; } return !(sum%10); //Boolean } /** Procedural API Description The main aim is to make input validation expressible in a simple format. You define profiles which declare the required and optional fields and any constraints they might have. The results are provided as an object that makes it easy to handle missing and invalid input. Usage var results = dojo.validate.check(form, profile); Profile Object var profile = { // filters change the field value and are applied before validation. trim: ["tx1", "tx2"], uppercase: ["tx9"], lowercase: ["tx5", "tx6", "tx7"], ucfirst: ["tx10"], digit: ["tx11"], // required input fields that are blank will be reported missing. // required radio button groups and drop-down lists with no selection will be reported missing. // checkbox groups and selectboxes can be required to have more than one value selected. // List required fields by name and use this notation to require more than one value: {checkboxgroup: 2}, {selectboxname: 3}. required: ["tx7", "tx8", "pw1", "ta1", "rb1", "rb2", "cb3", "s1", {"doubledip":2}, {"tripledip":3}], // dependant/conditional fields are required if the target field is present and not blank. // At present only textbox, password, and textarea fields are supported. dependencies: { cc_exp: "cc_no", cc_type: "cc_no", }, // Fields can be validated using any boolean valued function. // Use arrays to specify parameters in addition to the field value. constraints: { field_name1: myValidationFunction, field_name2: dojo.validate.isInteger, field_name3: [myValidationFunction, additional parameters], field_name4: [dojo.validate.isValidDate, "YYYY.MM.DD"], field_name5: [dojo.validate.isEmailAddress, false, true], }, // Confirm is a sort of conditional validation. // It associates each field in its property list with another field whose value should be equal. // If the values are not equal, the field in the property list is reported as Invalid. Unless the target field is blank. confirm: { email_confirm: "email", pw2: "pw1", } }; Results Object isSuccessful(): Returns true if there were no invalid or missing fields, else it returns false. hasMissing(): Returns true if the results contain any missing fields. getMissing(): Returns a list of required fields that have values missing. isMissing(field): Returns true if the field is required and the value is missing. hasInvalid(): Returns true if the results contain fields with invalid data. getInvalid(): Returns a list of fields that have invalid values. isInvalid(field): Returns true if the field has an invalid value. */ dojo.provide("dojo.validate.check"); dojo.require("dojo.lang.common"); dojo.validate.check = function(/*HTMLFormElement*/form, /*Object*/profile){ // summary: validates user input of an HTML form based on input profile // // description: // returns an object that contains several methods summarizing the results of the validation // // form: form to be validated // profile: specifies how the form fields are to be validated // {trim:Array, uppercase:Array, lowercase:Array, ucfirst:Array, digit:Array, // required:Array, dependencies:Object, constraints:Object, confirm:Object} // Essentially private properties of results object var missing = []; var invalid = []; // results object summarizes the validation var results = { isSuccessful: function() {return ( !this.hasInvalid() && !this.hasMissing() );}, hasMissing: function() {return ( missing.length > 0 );}, getMissing: function() {return missing;}, isMissing: function(elemname) { for(var i = 0; i < missing.length; i++){ if(elemname == missing[i]){ return true; } } return false; }, hasInvalid: function() {return ( invalid.length > 0 );}, getInvalid: function() {return invalid;}, isInvalid: function(elemname){ for(var i = 0; i < invalid.length; i++){ if(elemname == invalid[i]){ return true; } } return false; } }; // Filters are applied before fields are validated. // Trim removes white space at the front and end of the fields. if(profile.trim instanceof Array){ for(var i = 0; i < profile.trim.length; i++){ var elem = form[profile.trim[i]]; if(dj_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } elem.value = elem.value.replace(/(^\s*|\s*$)/g, ""); } } // Convert to uppercase if(profile.uppercase instanceof Array){ for(var i = 0; i < profile.uppercase.length; i++){ var elem = form[profile.uppercase[i]]; if(dj_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } elem.value = elem.value.toUpperCase(); } } // Convert to lowercase if(profile.lowercase instanceof Array){ for (var i = 0; i < profile.lowercase.length; i++){ var elem = form[profile.lowercase[i]]; if(dj_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } elem.value = elem.value.toLowerCase(); } } // Uppercase first letter if(profile.ucfirst instanceof Array){ for(var i = 0; i < profile.ucfirst.length; i++){ var elem = form[profile.ucfirst[i]]; if(dj_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } elem.value = elem.value.replace(/\b\w+\b/g, function(word) { return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase(); }); } } // Remove non digits characters from the input. if(profile.digit instanceof Array){ for(var i = 0; i < profile.digit.length; i++){ var elem = form[profile.digit[i]]; if(dj_undef("type", elem) || elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } elem.value = elem.value.replace(/\D/g, ""); } } // See if required input fields have values missing. if(profile.required instanceof Array){ for(var i = 0; i < profile.required.length; i++){ if(!dojo.lang.isString(profile.required[i])){ continue; } var elem = form[profile.required[i]]; // Are textbox, textarea, or password fields blank. if(!dj_undef("type", elem) && (elem.type == "text" || elem.type == "textarea" || elem.type == "password" || elem.type == "file") && /^\s*$/.test(elem.value)){ missing[missing.length] = elem.name; } // Does drop-down box have option selected. else if(!dj_undef("type", elem) && (elem.type == "select-one" || elem.type == "select-multiple") && (elem.selectedIndex == -1 || /^\s*$/.test(elem.options[elem.selectedIndex].value))){ missing[missing.length] = elem.name; } // Does radio button group (or check box group) have option checked. else if(elem instanceof Array){ var checked = false; for(var j = 0; j < elem.length; j++){ if (elem[j].checked) { checked = true; } } if(!checked){ missing[missing.length] = elem[0].name; } } } } // See if checkbox groups and select boxes have x number of required values. if(profile.required instanceof Array){ for (var i = 0; i < profile.required.length; i++){ if(!dojo.lang.isObject(profile.required[i])){ continue; } var elem, numRequired; for(var name in profile.required[i]){ elem = form[name]; numRequired = profile.required[i][name]; } // case 1: elem is a check box group if(elem instanceof Array){ var checked = 0; for(var j = 0; j < elem.length; j++){ if(elem[j].checked){ checked++; } } if(checked < numRequired){ missing[missing.length] = elem[0].name; } } // case 2: elem is a select box else if(!dj_undef("type", elem) && elem.type == "select-multiple" ){ var selected = 0; for(var j = 0; j < elem.options.length; j++){ if (elem.options[j].selected && !/^\s*$/.test(elem.options[j].value)) { selected++; } } if(selected < numRequired){ missing[missing.length] = elem.name; } } } } // Dependent fields are required when the target field is present (not blank). // Todo: Support dependent and target fields that are radio button groups, or select drop-down lists. // Todo: Make the dependency based on a specific value of the target field. // Todo: allow dependent fields to have several required values, like {checkboxgroup: 3}. if(dojo.lang.isObject(profile.dependencies) || dojo.lang.isObject(profile.dependancies)){ if(profile["dependancies"]){ dojo.deprecated("dojo.validate.check", "profile 'dependancies' is deprecated, please use " + "'dependencies'", "0.5"); profile.dependencies=profile.dependancies; } // properties of dependencies object are the names of dependent fields to be checked for(name in profile.dependencies){ var elem = form[name]; // the dependent element if(dj_undef("type", elem)){continue;} if(elem.type != "text" && elem.type != "textarea" && elem.type != "password"){ continue; } // limited support if(/\S+/.test(elem.value)){ continue; } // has a value already if(results.isMissing(elem.name)){ continue; } // already listed as missing var target = form[profile.dependencies[name]]; if(target.type != "text" && target.type != "textarea" && target.type != "password"){ continue; } // limited support if(/^\s*$/.test(target.value)){ continue; } // skip if blank missing[missing.length] = elem.name; // ok the dependent field is missing } } // Find invalid input fields. if(dojo.lang.isObject(profile.constraints)){ // constraint properties are the names of fields to bevalidated for(name in profile.constraints){ var elem = form[name]; if(!elem) {continue;} // skip if blank - its optional unless required, in which case it // is already listed as missing. if(!dj_undef("tagName",elem) && (elem.tagName.toLowerCase().indexOf("input") >= 0 || elem.tagName.toLowerCase().indexOf("textarea") >= 0) && /^\s*$/.test(elem.value)){ continue; } var isValid = true; // case 1: constraint value is validation function if(dojo.lang.isFunction(profile.constraints[name])){ isValid = profile.constraints[name](elem.value); }else if(dojo.lang.isArray(profile.constraints[name])){ // handle nested arrays for multiple constraints if(dojo.lang.isArray(profile.constraints[name][0])){ for (var i=0; i firstDay ? 7 : 0)); return Math.floor((dateObject.getTime() - firstDayOfYear.getTime()) / 604800000); } dojo.date.setIsoWeekOfYear = function(/*Date*/dateObject, /*Number*/week, /*Number*/firstDay){ // summary: unimplemented if (arguments.length == 1) { firstDay = 1; } // Monday dojo.unimplemented("dojo.date.setIsoWeekOfYear"); } dojo.date.getIsoWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDay) { // summary: unimplemented if (arguments.length == 1) { firstDay = 1; } // Monday dojo.unimplemented("dojo.date.getIsoWeekOfYear"); } /* Informational Functions **************************/ //DEPRECATED: These timezone arrays will be deprecated in 0.5 dojo.date.shortTimezones = ["IDLW", "BET", "HST", "MART", "AKST", "PST", "MST", "CST", "EST", "AST", "NFT", "BST", "FST", "AT", "GMT", "CET", "EET", "MSK", "IRT", "GST", "AFT", "AGTT", "IST", "NPT", "ALMT", "MMT", "JT", "AWST", "JST", "ACST", "AEST", "LHST", "VUT", "NFT", "NZT", "CHAST", "PHOT", "LINT"]; dojo.date.timezoneOffsets = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -240, -210, -180, -120, -60, 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840]; /* dojo.date.timezones = ["International Date Line West", "Bering Standard Time", "Hawaiian Standard Time", "Marquesas Time", "Alaska Standard Time", "Pacific Standard Time (USA)", "Mountain Standard Time", "Central Standard Time (USA)", "Eastern Standard Time (USA)", "Atlantic Standard Time", "Newfoundland Time", "Brazil Standard Time", "Fernando de Noronha Standard Time (Brazil)", "Azores Time", "Greenwich Mean Time", "Central Europe Time", "Eastern Europe Time", "Moscow Time", "Iran Standard Time", "Gulf Standard Time", "Afghanistan Time", "Aqtobe Time", "Indian Standard Time", "Nepal Time", "Almaty Time", "Myanmar Time", "Java Time", "Australian Western Standard Time", "Japan Standard Time", "Australian Central Standard Time", "Lord Hove Standard Time (Australia)", "Vanuata Time", "Norfolk Time (Australia)", "New Zealand Standard Time", "Chatham Standard Time (New Zealand)", "Phoenix Islands Time (Kribati)", "Line Islands Time (Kribati)"]; */ dojo.date.getDaysInMonth = function(/*Date*/dateObject){ // summary: returns the number of days in the month used by dateObject var month = dateObject.getMonth(); var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if (month == 1 && dojo.date.isLeapYear(dateObject)) { return 29; } // Number else { return days[month]; } // Number } dojo.date.isLeapYear = function(/*Date*/dateObject){ // summary: // Determines if the year of the dateObject is a leap year // // description: // Leap years are years with an additional day YYYY-02-29, where the year // number is a multiple of four with the following exception: If a year // is a multiple of 100, then it is only a leap year if it is also a // multiple of 400. For example, 1900 was not a leap year, but 2000 is one. var year = dateObject.getFullYear(); return (year%400 == 0) ? true : (year%100 == 0) ? false : (year%4 == 0) ? true : false; // Boolean } // FIXME: This is not localized dojo.date.getTimezoneName = function(/*Date*/dateObject){ // summary: // Get the user's time zone as provided by the browser // // dateObject: needed because the timezone may vary with time (daylight savings) // // description: // Try to get time zone info from toString or toLocaleString // method of the Date object -- UTC offset is not a time zone. // See http://www.twinsun.com/tz/tz-link.htm // Note: results may be inconsistent across browsers. var str = dateObject.toString(); // Start looking in toString var tz = ''; // The result -- return empty string if nothing found var match; // First look for something in parentheses -- fast lookup, no regex var pos = str.indexOf('('); if (pos > -1) { pos++; tz = str.substring(pos, str.indexOf(')')); } // If at first you don't succeed ... else{ // If IE knows about the TZ, it appears before the year // Capital letters or slash before a 4-digit year // at the end of string var pat = /([A-Z\/]+) \d{4}$/; if((match = str.match(pat))) { tz = match[1]; } // Some browsers (e.g. Safari) glue the TZ on the end // of toLocaleString instead of putting it in toString else{ str = dateObject.toLocaleString(); // Capital letters or slash -- end of string, // after space pat = / ([A-Z\/]+)$/; if((match = str.match(pat))) { tz = match[1]; } } } // Make sure it doesn't somehow end up return AM or PM return tz == 'AM' || tz == 'PM' ? '' : tz; //String } //FIXME: not localized dojo.date.getOrdinal = function(dateObject){ // summary: returns the appropriate suffix (English only) for the day of the month, e.g. 'st' for 1, 'nd' for 2, etc.) var date = dateObject.getDate(); if(date%100 != 11 && date%10 == 1){ return "st"; } // String else if(date%100 != 12 && date%10 == 2){ return "nd"; } // String else if(date%100 != 13 && date%10 == 3){ return "rd"; } // String else{ return "th"; } // String } /* compare and add ******************/ dojo.date.compareTypes={ // summary // bitmask for comparison operations. DATE:1, TIME:2 }; dojo.date.compare=function(/* Date */ dateA, /* Date */ dateB, /* dojo.date.compareTypes */ options){ // summary // Compare two date objects by date, time, or both. Returns 0 if equal, positive if a > b, else negative. var dA=dateA; var dB=dateB||new Date(); var now=new Date(); //FIXME: shorten this code with(dojo.date.compareTypes){ var opt=options||(DATE|TIME); var d1=new Date( (opt&DATE)?dA.getFullYear():now.getFullYear(), (opt&DATE)?dA.getMonth():now.getMonth(), (opt&DATE)?dA.getDate():now.getDate(), (opt&TIME)?dA.getHours():0, (opt&TIME)?dA.getMinutes():0, (opt&TIME)?dA.getSeconds():0 ); var d2=new Date( (opt&DATE)?dB.getFullYear():now.getFullYear(), (opt&DATE)?dB.getMonth():now.getMonth(), (opt&DATE)?dB.getDate():now.getDate(), (opt&TIME)?dB.getHours():0, (opt&TIME)?dB.getMinutes():0, (opt&TIME)?dB.getSeconds():0 ); } if(d1.valueOf()>d2.valueOf()){ return 1; // int } if(d1.valueOf() 0) ? 5 : -5; weeks = (incr > 0) ? ((incr-5)/5) : ((incr+5)/5); } else { days = mod; weeks = parseInt(incr/5); } // Get weekday value for orig date param strt = dt.getDay(); // Orig date is Sat / positive incrementer // Jump over Sun if (strt == 6 && incr > 0) { adj = 1; } // Orig date is Sun / negative incrementer // Jump back over Sat else if (strt == 0 && incr < 0) { adj = -1; } // Get weekday val for the new date trgt = (strt + days); // New date is on Sat or Sun if (trgt == 0 || trgt == 6) { adj = (incr > 0) ? 2 : -2; } // Increment by number of weeks plus leftover days plus // weekend adjustments sum.setDate(dat + (7*weeks) + days + adj); break; case HOUR: sum.setHours(sum.getHours()+incr); break; case MINUTE: sum.setMinutes(sum.getMinutes()+incr); break; case SECOND: sum.setSeconds(sum.getSeconds()+incr); break; case MILLISECOND: sum.setMilliseconds(sum.getMilliseconds()+incr); break; default: // Do nothing break; } } return sum; // Date }; dojo.date.diff = function(/* Date */ dtA, /* Date */ dtB, /* dojo.date.dateParts */ interv){ // summary: // Get the difference in a specific unit of time (e.g., number of months, weeks, // days, etc.) between two dates. // // dtA: // A Javascript Date object // // dtB: // A Javascript Date object // // interv: // A constant representing the interval, e.g. YEAR, MONTH, DAY. See dojo.date.dateParts. // Accept timestamp input if(typeof dtA == 'number'){dtA = new Date(dtA);} if(typeof dtB == 'number'){dtB = new Date(dtB);} var yeaDiff = dtB.getFullYear() - dtA.getFullYear(); var monDiff = (dtB.getMonth() - dtA.getMonth()) + (yeaDiff * 12); var msDiff = dtB.getTime() - dtA.getTime(); // Millisecs var secDiff = msDiff/1000; var minDiff = secDiff/60; var houDiff = minDiff/60; var dayDiff = houDiff/24; var weeDiff = dayDiff/7; var delta = 0; // Integer return value with(dojo.date.dateParts){ switch(interv){ case YEAR: delta = yeaDiff; break; case QUARTER: var mA = dtA.getMonth(); var mB = dtB.getMonth(); // Figure out which quarter the months are in var qA = Math.floor(mA/3) + 1; var qB = Math.floor(mB/3) + 1; // Add quarters for any year difference between the dates qB += (yeaDiff * 4); delta = qB - qA; break; case MONTH: delta = monDiff; break; case WEEK: // Truncate instead of rounding // Don't use Math.floor -- value may be negative delta = parseInt(weeDiff); break; case DAY: delta = dayDiff; break; case WEEKDAY: var days = Math.round(dayDiff); var weeks = parseInt(days/7); var mod = days % 7; // Even number of weeks if (mod == 0) { days = weeks*5; } // Weeks plus spare change (< 7 days) else { var adj = 0; var aDay = dtA.getDay(); var bDay = dtB.getDay(); weeks = parseInt(days/7); mod = days % 7; // Mark the date advanced by the number of // round weeks (may be zero) var dtMark = new Date(dtA); dtMark.setDate(dtMark.getDate()+(weeks*7)); var dayMark = dtMark.getDay(); // Spare change days -- 6 or less // ---------- // Positive diff if (dayDiff > 0) { switch (true) { // Range starts on Sat case aDay == 6: adj = -1; break; // Range starts on Sun case aDay == 0: adj = 0; break; // Range ends on Sat case bDay == 6: adj = -1; break; // Range ends on Sun case bDay == 0: adj = -2; break; // Range contains weekend case (dayMark + mod) > 5: adj = -2; break; default: // Do nothing break; } } // Negative diff else if (dayDiff < 0) { switch (true) { // Range starts on Sat case aDay == 6: adj = 0; break; // Range starts on Sun case aDay == 0: adj = 1; break; // Range ends on Sat case bDay == 6: adj = 2; break; // Range ends on Sun case bDay == 0: adj = 1; break; // Range contains weekend case (dayMark + mod) < 0: adj = 2; break; default: // Do nothing break; } } days += adj; days -= (weeks*2); } delta = days; break; case HOUR: delta = houDiff; break; case MINUTE: delta = minDiff; break; case SECOND: delta = secDiff; break; case MILLISECOND: delta = msDiff; break; default: // Do nothing break; } } // Round for fractional values and DST leaps return Math.round(delta); // Number (integer) }; dojo.provide("dojo.date.supplemental"); dojo.date.getFirstDayOfWeek = function(/*String?*/locale){ // summary: Returns a zero-based index for first day of the week // description: // Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar. // e.g. Sunday (returns 0), or Monday (returns 1) // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay var firstDay = {/*default is 1=Monday*/ mv:5, ae:6,af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6,lb:6,ly:6,ma:6,om:6,qa:6,sa:6, sd:6,so:6,tn:6,ye:6, as:0,au:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,ie:0,il:0,is:0,jm:0,jp:0,kg:0,kr:0,la:0, mh:0,mo:0,mp:0,mt:0,nz:0,ph:0,pk:0,sg:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,vi:0,za:0,zw:0, et:0,mw:0,ng:0,tj:0, gb:0, sy:4 }; locale = dojo.hostenv.normalizeLocale(locale); var country = locale.split("-")[1]; var dow = firstDay[country]; return (typeof dow == 'undefined') ? 1 : dow; /*Number*/ }; dojo.date.getWeekend = function(/*String?*/locale){ // summary: Returns a hash containing the start and end days of the weekend // description: // Returns a hash containing the start and end days of the weekend according to local custom using locale, // or by default in the user's locale. // e.g. {start:6, end:0} // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End} var weekendStart = {/*default is 6=Saturday*/ eg:5,il:5,sy:5, 'in':0, ae:4,bh:4,dz:4,iq:4,jo:4,kw:4,lb:4,ly:4,ma:4,om:4,qa:4,sa:4,sd:4,tn:4,ye:4 }; var weekendEnd = {/*default is 0=Sunday*/ ae:5,bh:5,dz:5,iq:5,jo:5,kw:5,lb:5,ly:5,ma:5,om:5,qa:5,sa:5,sd:5,tn:5,ye:5,af:5,ir:5, eg:6,il:6,sy:6 }; locale = dojo.hostenv.normalizeLocale(locale); var country = locale.split("-")[1]; var start = weekendStart[country]; var end = weekendEnd[country]; if(typeof start == 'undefined'){start=6;} if(typeof end == 'undefined'){end=0;} return {start:start, end:end}; /*Object {start,end}*/ }; dojo.date.isWeekend = function(/*Date?*/dateObj, /*String?*/locale){ // summary: // Determines if the date falls on a weekend, according to local custom. var weekend = dojo.date.getWeekend(locale); var day = (dateObj || new Date()).getDay(); if(weekend.end= weekend.start && day <= weekend.end; // Boolean }; dojo.provide("dojo.date.format"); dojo.require("dojo.lang.array"); dojo.require("dojo.lang.common"); dojo.require("dojo.lang.func"); dojo.require("dojo.string.common"); dojo.require("dojo.i18n.common"); // Load the bundles containing localization information for // names and formats dojo.requireLocalization("dojo.i18n.calendar", "gregorian"); dojo.requireLocalization("dojo.i18n.calendar", "gregorianExtras"); //NOTE: Everything in this module assumes Gregorian calendars. // Other calendars will be implemented in separate modules. (function(){ dojo.date.format = function(/*Date*/dateObject, /*Object?*/options){ // // summary: // Format a Date object as a String, using locale-specific settings. // // description: // Create a string from a Date object using a known localized pattern. // By default, this method formats both date and time from dateObject. // Formatting patterns are chosen appropriate to the locale. Different // formatting lengths may be chosen, with "full" used by default. // Custom patterns may be used or registered with translations using // the addCustomBundle method. // Formatting patterns are implemented using the syntax described at // http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns // // dateObject: // the date and/or time to be formatted. If a time only is formatted, // the values in the year, month, and day fields are irrelevant. The // opposite is true when formatting only dates. // // options: object {selector: string, formatLength: string, datePattern: string, timePattern: string, locale: string} // selector- choice of timeOnly,dateOnly (default: date and time) // formatLength- choice of long, short, medium or full (plus any custom additions). Defaults to 'full' // datePattern,timePattern- override pattern with this string // am,pm- override strings for am/pm in times // locale- override the locale used to determine formatting rules // if(typeof options == "string"){ dojo.deprecated("dojo.date.format", "To format dates with POSIX-style strings, please use dojo.date.strftime instead", "0.5"); return dojo.date.strftime(dateObject, options); } // Format a pattern without literals function formatPattern(dateObject, pattern){ return pattern.replace(/([a-z])\1*/ig, function(match){ var s; var c = match.charAt(0); var l = match.length; var pad; var widthList = ["abbr", "wide", "narrow"]; switch(c){ case 'G': if(l>3){dojo.unimplemented("Era format not implemented");} s = bundle.eras[dateObject.getFullYear() < 0 ? 1 : 0]; break; case 'y': s = dateObject.getFullYear(); switch(l){ case 1: break; case 2: s = String(s); s = s.substr(s.length - 2); break; default: pad = true; } break; case 'Q': case 'q': s = Math.ceil((dateObject.getMonth()+1)/3); switch(l){ case 1: case 2: pad = true; break; case 3: case 4: dojo.unimplemented("Quarter format not implemented"); } break; case 'M': case 'L': var m = dateObject.getMonth(); var width; switch(l){ case 1: case 2: s = m+1; pad = true; break; case 3: case 4: case 5: width = widthList[l-3]; break; } if(width){ var type = (c == "L") ? "standalone" : "format"; var prop = ["months",type,width].join("-"); s = bundle[prop][m]; } break; case 'w': var firstDay = 0; s = dojo.date.getWeekOfYear(dateObject, firstDay); pad = true; break; case 'd': s = dateObject.getDate(); pad = true; break; case 'D': s = dojo.date.getDayOfYear(dateObject); pad = true; break; case 'E': case 'e': case 'c': // REVIEW: don't see this in the spec? var d = dateObject.getDay(); var width; switch(l){ case 1: case 2: if(c == 'e'){ var first = dojo.date.getFirstDayOfWeek(options.locale); d = (d-first+7)%7; } if(c != 'c'){ s = d+1; pad = true; break; } // else fallthrough... case 3: case 4: case 5: width = widthList[l-3]; break; } if(width){ var type = (c == "c") ? "standalone" : "format"; var prop = ["days",type,width].join("-"); s = bundle[prop][d]; } break; case 'a': var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm'; s = bundle[timePeriod]; break; case 'h': case 'H': case 'K': case 'k': var h = dateObject.getHours(); // strange choices in the date format make it impossible to write this succinctly switch (c) { case 'h': // 1-12 s = (h % 12) || 12; break; case 'H': // 0-23 s = h; break; case 'K': // 0-11 s = (h % 12); break; case 'k': // 1-24 s = h || 24; break; } pad = true; break; case 'm': s = dateObject.getMinutes(); pad = true; break; case 's': s = dateObject.getSeconds(); pad = true; break; case 'S': s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); break; case 'v': // FIXME: don't know what this is. seems to be same as z? case 'z': // We only have one timezone to offer; the one from the browser s = dojo.date.getTimezoneName(dateObject); if(s){break;} l=4; // fallthrough... use GMT if tz not available case 'Z': var offset = dateObject.getTimezoneOffset(); var tz = [ (offset<=0 ? "+" : "-"), dojo.string.pad(Math.floor(Math.abs(offset)/60), 2), dojo.string.pad(Math.abs(offset)% 60, 2) ]; if(l==4){ tz.splice(0, 0, "GMT"); tz.splice(3, 0, ":"); } s = tz.join(""); break; case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': // dojo.debug(match+" modifier not yet implemented"); s = "?"; break; default: dojo.raise("dojo.date.format: invalid pattern char: "+pattern); } if(pad){ s = dojo.string.pad(s, l); } return s; }); } options = options || {}; var locale = dojo.hostenv.normalizeLocale(options.locale); var formatLength = options.formatLength || 'short'; var bundle = dojo.date._getGregorianBundle(locale); var str = []; var sauce = dojo.lang.curry(this, formatPattern, dateObject); if(options.selector == "yearOnly"){ // Special case as this is not yet driven by CLDR data var year = dateObject.getFullYear(); if(locale.match(/^zh|^ja/)){ year += "\u5E74"; } return year; } if(options.selector != "timeOnly"){ var datePattern = options.datePattern || bundle["dateFormat-"+formatLength]; if(datePattern){str.push(_processPattern(datePattern, sauce));} } if(options.selector != "dateOnly"){ var timePattern = options.timePattern || bundle["timeFormat-"+formatLength]; if(timePattern){str.push(_processPattern(timePattern, sauce));} } var result = str.join(" "); //TODO: use locale-specific pattern to assemble date + time return result; /*String*/ }; dojo.date.parse = function(/*String*/value, /*Object?*/options){ // // summary: // Convert a properly formatted string to a primitive Date object, // using locale-specific settings. // // description: // Create a Date object from a string using a known localized pattern. // By default, this method parses looking for both date and time in the string. // Formatting patterns are chosen appropriate to the locale. Different // formatting lengths may be chosen, with "full" used by default. // Custom patterns may be used or registered with translations using // the addCustomBundle method. // Formatting patterns are implemented using the syntax described at // http://www.unicode.org/reports/tr35/#Date_Format_Patterns // // value: // A string representation of a date // // options: object {selector: string, formatLength: string, datePattern: string, timePattern: string, locale: string, strict: boolean} // selector- choice of timeOnly, dateOnly, dateTime (default: dateOnly) // formatLength- choice of long, short, medium or full (plus any custom additions). Defaults to 'full' // datePattern,timePattern- override pattern with this string // am,pm- override strings for am/pm in times // locale- override the locale used to determine formatting rules // strict- strict parsing, off by default // options = options || {}; var locale = dojo.hostenv.normalizeLocale(options.locale); var info = dojo.date._getGregorianBundle(locale); var formatLength = options.formatLength || 'full'; if(!options.selector){ options.selector = 'dateOnly'; } var datePattern = options.datePattern || info["dateFormat-" + formatLength]; var timePattern = options.timePattern || info["timeFormat-" + formatLength]; var pattern; if(options.selector == 'dateOnly'){ pattern = datePattern; } else if(options.selector == 'timeOnly'){ pattern = timePattern; }else if(options.selector == 'dateTime'){ pattern = datePattern + ' ' + timePattern; //TODO: use locale-specific pattern to assemble date + time }else{ var msg = "dojo.date.parse: Unknown selector param passed: '" + options.selector + "'."; msg += " Defaulting to date pattern."; dojo.debug(msg); pattern = datePattern; } var groups = []; var dateREString = _processPattern(pattern, dojo.lang.curry(this, _buildDateTimeRE, groups, info, options)); var dateRE = new RegExp("^" + dateREString + "$"); var match = dateRE.exec(value); if(!match){ return null; } var widthList = ['abbr', 'wide', 'narrow']; //1972 is a leap year. We want to avoid Feb 29 rolling over into Mar 1, //in the cases where the year is parsed after the month and day. var result = new Date(1972, 0); var expected = {}; for(var i=1; i2) { if(!options.strict){ //Tolerate abbreviating period in month part v = v.replace(/\./g,''); //Case-insensitive v = v.toLowerCase(); } var months = info['months-format-' + widthList[l-3]].concat(); for (var j=0; j 15 } else if(v == am && hours == 12){ result.setHours(0); //12am -> 0 } break; case 'K': //hour (1-24) if(v==24){v=0;} // fallthrough... case 'h': //hour (1-12) case 'H': //hour (0-23) case 'k': //hour (0-11) //TODO: strict bounds checking, padding if(v>23){ dojo.debug("dojo.date.parse: Illegal hours value"); return null; } //in the 12-hour case, adjusting for am/pm requires the 'a' part //which for now we will assume always comes after the 'h' part result.setHours(v); break; case 'm': //minutes result.setMinutes(v); break; case 's': //seconds result.setSeconds(v); break; case 'S': //milliseconds result.setMilliseconds(v); break; default: dojo.unimplemented("dojo.date.parse: unsupported pattern char=" + grp.charAt(0)); } } //validate parse date fields versus input date fields if(expected.year && result.getFullYear() != expected.year){ dojo.debug("Parsed year: '" + result.getFullYear() + "' did not match input year: '" + expected.year + "'."); return null; } if(expected.month && result.getMonth() != expected.month){ dojo.debug("Parsed month: '" + result.getMonth() + "' did not match input month: '" + expected.month + "'."); return null; } if(expected.date && result.getDate() != expected.date){ dojo.debug("Parsed day of month: '" + result.getDate() + "' did not match input day of month: '" + expected.date + "'."); return null; } //TODO: implement a getWeekday() method in order to test //validity of input strings containing 'EEE' or 'EEEE'... return result; /*Date*/ }; function _processPattern(pattern, applyPattern, applyLiteral, applyAll){ // Process a pattern with literals in it // Break up on single quotes, treat every other one as a literal, except '' which becomes ' var identity = function(x){return x;}; applyPattern = applyPattern || identity; applyLiteral = applyLiteral || identity; applyAll = applyAll || identity; //split on single quotes (which escape literals in date format strings) //but preserve escaped single quotes (e.g., o''clock) var chunks = pattern.match(/(''|[^'])+/g); var literal = false; for(var i=0; i2) ? '\\S+' : '\\d{1,2}'; break; case 'd': s = '\\d{1,2}'; break; case 'E': s = '\\S+'; break; case 'h': case 'H': case 'K': case 'k': s = '\\d{1,2}'; break; case 'm': case 's': s = '[0-5]\\d'; break; case 'S': s = '\\d{1,3}'; break; case 'a': var am = options.am || info.am || 'AM'; var pm = options.pm || info.pm || 'PM'; if(options.strict){ s = am + '|' + pm; }else{ s = am; s += (am != am.toLowerCase()) ? '|' + am.toLowerCase() : ''; s += '|'; s += (pm != pm.toLowerCase()) ? pm + '|' + pm.toLowerCase() : pm; } break; default: dojo.unimplemented("parse of date format, pattern=" + pattern); } if(groups){ groups.push(match); } //FIXME: replace whitespace within final regexp with more flexible whitespace match instead? //tolerate whitespace return '\\s*(' + s + ')\\s*'; }); } })(); //TODO: try to common strftime and format code somehow? dojo.date.strftime = function(/*Date*/dateObject, /*String*/format, /*String?*/locale){ // // summary: // Formats the date object using the specifications of the POSIX strftime function // // description: // see // zero pad var padChar = null; function _(s, n){ return dojo.string.pad(s, n || 2, padChar || "0"); } var info = dojo.date._getGregorianBundle(locale); function $(property){ switch (property){ case "a": // abbreviated weekday name according to the current locale return dojo.date.getDayShortName(dateObject, locale); case "A": // full weekday name according to the current locale return dojo.date.getDayName(dateObject, locale); case "b": case "h": // abbreviated month name according to the current locale return dojo.date.getMonthShortName(dateObject, locale); case "B": // full month name according to the current locale return dojo.date.getMonthName(dateObject, locale); case "c": // preferred date and time representation for the current // locale return dojo.date.format(dateObject, {locale: locale}); case "C": // century number (the year divided by 100 and truncated // to an integer, range 00 to 99) return _(Math.floor(dateObject.getFullYear()/100)); case "d": // day of the month as a decimal number (range 01 to 31) return _(dateObject.getDate()); case "D": // same as %m/%d/%y return $("m") + "/" + $("d") + "/" + $("y"); case "e": // day of the month as a decimal number, a single digit is // preceded by a space (range ' 1' to '31') if(padChar == null){ padChar = " "; } return _(dateObject.getDate()); case "f": // month as a decimal number, a single digit is // preceded by a space (range ' 1' to '12') if(padChar == null){ padChar = " "; } return _(dateObject.getMonth()+1); case "g": // like %G, but without the century. break; case "G": // The 4-digit year corresponding to the ISO week number // (see %V). This has the same format and value as %Y, // except that if the ISO week number belongs to the // previous or next year, that year is used instead. dojo.unimplemented("unimplemented modifier 'G'"); break; case "F": // same as %Y-%m-%d return $("Y") + "-" + $("m") + "-" + $("d"); case "H": // hour as a decimal number using a 24-hour clock (range // 00 to 23) return _(dateObject.getHours()); case "I": // hour as a decimal number using a 12-hour clock (range // 01 to 12) return _(dateObject.getHours() % 12 || 12); case "j": // day of the year as a decimal number (range 001 to 366) return _(dojo.date.getDayOfYear(dateObject), 3); case "k": // Hour as a decimal number using a 24-hour clock (range // 0 to 23 (space-padded)) if (padChar == null) { padChar = " "; } return _(dateObject.getHours()); case "l": // Hour as a decimal number using a 12-hour clock (range // 1 to 12 (space-padded)) if (padChar == null) { padChar = " "; } return _(dateObject.getHours() % 12 || 12); case "m": // month as a decimal number (range 01 to 12) return _(dateObject.getMonth() + 1); case "M": // minute as a decimal number return _(dateObject.getMinutes()); case "n": return "\n"; case "p": // either `am' or `pm' according to the given time value, // or the corresponding strings for the current locale return info[dateObject.getHours() < 12 ? "am" : "pm"]; case "r": // time in a.m. and p.m. notation return $("I") + ":" + $("M") + ":" + $("S") + " " + $("p"); case "R": // time in 24 hour notation return $("H") + ":" + $("M"); case "S": // second as a decimal number return _(dateObject.getSeconds()); case "t": return "\t"; case "T": // current time, equal to %H:%M:%S return $("H") + ":" + $("M") + ":" + $("S"); case "u": // weekday as a decimal number [1,7], with 1 representing // Monday return String(dateObject.getDay() || 7); case "U": // week number of the current year as a decimal number, // starting with the first Sunday as the first day of the // first week return _(dojo.date.getWeekOfYear(dateObject)); case "V": // week number of the year (Monday as the first day of the // week) as a decimal number [01,53]. If the week containing // 1 January has four or more days in the new year, then it // is considered week 1. Otherwise, it is the last week of // the previous year, and the next week is week 1. return _(dojo.date.getIsoWeekOfYear(dateObject)); case "W": // week number of the current year as a decimal number, // starting with the first Monday as the first day of the // first week return _(dojo.date.getWeekOfYear(dateObject, 1)); case "w": // day of the week as a decimal, Sunday being 0 return String(dateObject.getDay()); case "x": // preferred date representation for the current locale // without the time return dojo.date.format(dateObject, {selector:'dateOnly', locale:locale}); case "X": // preferred time representation for the current locale // without the date return dojo.date.format(dateObject, {selector:'timeOnly', locale:locale}); case "y": // year as a decimal number without a century (range 00 to // 99) return _(dateObject.getFullYear()%100); case "Y": // year as a decimal number including the century return String(dateObject.getFullYear()); case "z": // time zone or name or abbreviation var timezoneOffset = dateObject.getTimezoneOffset(); return (timezoneOffset > 0 ? "-" : "+") + _(Math.floor(Math.abs(timezoneOffset)/60)) + ":" + _(Math.abs(timezoneOffset)%60); case "Z": // time zone or name or abbreviation return dojo.date.getTimezoneName(dateObject); case "%": return "%"; } } // parse the formatting string and construct the resulting string var string = ""; var i = 0; var index = 0; var switchCase = null; while ((index = format.indexOf("%", i)) != -1){ string += format.substring(i, index++); // inspect modifier flag switch (format.charAt(index++)) { case "_": // Pad a numeric result string with spaces. padChar = " "; break; case "-": // Do not pad a numeric result string. padChar = ""; break; case "0": // Pad a numeric result string with zeros. padChar = "0"; break; case "^": // Convert characters in result string to uppercase. switchCase = "upper"; break; case "*": // Convert characters in result string to lowercase switchCase = "lower"; break; case "#": // Swap the case of the result string. switchCase = "swap"; break; default: // no modifier flag so decrement the index padChar = null; index--; break; } // toggle case if a flag is set var property = $(format.charAt(index++)); switch (switchCase){ case "upper": property = property.toUpperCase(); break; case "lower": property = property.toLowerCase(); break; case "swap": // Upper to lower, and versey-vicea var compareString = property.toLowerCase(); var swapString = ''; var j = 0; var ch = ''; while (j < property.length){ ch = property.charAt(j); swapString += (ch == compareString.charAt(j)) ? ch.toUpperCase() : ch.toLowerCase(); j++; } property = swapString; break; default: break; } switchCase = null; string += property; i = index; } string += format.substring(i); return string; // String }; (function(){ var _customFormats = []; dojo.date.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){ // // summary: // Add a reference to a bundle containing localized custom formats to be // used by date/time formatting and parsing routines. // // description: // The user may add custom localized formats where the bundle has properties following the // same naming convention used by dojo for the CLDR data: dateFormat-xxxx / timeFormat-xxxx // The pattern string should match the format used by the CLDR. // See dojo.date.format for details. // The resources must be loaded by dojo.requireLocalization() prior to use _customFormats.push({pkg:packageName,name:bundleName}); }; dojo.date._getGregorianBundle = function(/*String*/locale){ var gregorian = {}; dojo.lang.forEach(_customFormats, function(desc){ var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale); gregorian = dojo.lang.mixin(gregorian, bundle); }, this); return gregorian; /*Object*/ }; })(); dojo.date.addCustomFormats("dojo.i18n.calendar","gregorian"); dojo.date.addCustomFormats("dojo.i18n.calendar","gregorianExtras"); dojo.date.getNames = function(/*String*/item, /*String*/type, /*String?*/use, /*String?*/locale){ // // summary: // Used to get localized strings for day or month names. // // item: 'months' || 'days' // type: 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English) // use: 'standAlone' || 'format' (default) // locale: override locale used to find the names var label; var lookup = dojo.date._getGregorianBundle(locale); var props = [item, use, type]; if(use == 'standAlone'){ label = lookup[props.join('-')]; } props[1] = 'format'; // return by copy so changes won't be made accidentally to the in-memory model return (label || lookup[props.join('-')]).concat(); /*Array*/ }; // Convenience methods dojo.date.getDayName = function(/*Date*/dateObject, /*String?*/locale){ // summary: gets the full localized day of the week corresponding to the date object return dojo.date.getNames('days', 'wide', 'format', locale)[dateObject.getDay()]; /*String*/ }; dojo.date.getDayShortName = function(/*Date*/dateObject, /*String?*/locale){ // summary: gets the abbreviated localized day of the week corresponding to the date object return dojo.date.getNames('days', 'abbr', 'format', locale)[dateObject.getDay()]; /*String*/ }; dojo.date.getMonthName = function(/*Date*/dateObject, /*String?*/locale){ // summary: gets the full localized month name corresponding to the date object return dojo.date.getNames('months', 'wide', 'format', locale)[dateObject.getMonth()]; /*String*/ }; dojo.date.getMonthShortName = function(/*Date*/dateObject, /*String?*/locale){ // summary: gets the abbreviated localized month name corresponding to the date object return dojo.date.getNames('months', 'abbr', 'format', locale)[dateObject.getMonth()]; /*String*/ }; //FIXME: not localized dojo.date.toRelativeString = function(/*Date*/dateObject){ // summary: // Returns an description in English of the date relative to the current date. Note: this is not localized yet. English only. // // description: Example returns: // - "1 minute ago" // - "4 minutes ago" // - "Yesterday" // - "2 days ago" var now = new Date(); var diff = (now - dateObject) / 1000; var end = " ago"; var future = false; if(diff < 0){ future = true; end = " from now"; diff = -diff; } if(diff < 60){ diff = Math.round(diff); return diff + " second" + (diff == 1 ? "" : "s") + end; } if(diff < 60*60){ diff = Math.round(diff/60); return diff + " minute" + (diff == 1 ? "" : "s") + end; } if(diff < 60*60*24){ diff = Math.round(diff/3600); return diff + " hour" + (diff == 1 ? "" : "s") + end; } if(diff < 60*60*24*7){ diff = Math.round(diff/(3600*24)); if(diff == 1){ return future ? "Tomorrow" : "Yesterday"; }else{ return diff + " days" + end; } } return dojo.date.format(dateObject); // String }; //FIXME: SQL methods can probably be moved to a different module without i18n deps dojo.date.toSql = function(/*Date*/dateObject, /*Boolean?*/noTime){ // summary: // Convert a Date to a SQL string // noTime: whether to ignore the time portion of the Date. Defaults to false. return dojo.date.strftime(dateObject, "%F" + !noTime ? " %T" : ""); // String }; dojo.date.fromSql = function(/*String*/sqlDate){ // summary: // Convert a SQL date string to a JavaScript Date object var parts = sqlDate.split(/[\- :]/g); while(parts.length < 6){ parts.push(0); } return new Date(parts[0], (parseInt(parts[1],10)-1), parts[2], parts[3], parts[4], parts[5]); // Date }; dojo.provide("dojo.date.serialize"); dojo.require("dojo.string.common"); /* ISO 8601 Functions *********************/ dojo.date.setIso8601 = function(/*Date*/dateObject, /*String*/formattedString){ // summary: sets a Date object based on an ISO 8601 formatted string (uses date and time) var comps = (formattedString.indexOf("T") == -1) ? formattedString.split(" ") : formattedString.split("T"); dateObject = dojo.date.setIso8601Date(dateObject, comps[0]); if(comps.length == 2){ dateObject = dojo.date.setIso8601Time(dateObject, comps[1]); } return dateObject; /* Date or null */ }; dojo.date.fromIso8601 = function(/*String*/formattedString){ // summary: returns a Date object based on an ISO 8601 formatted string (uses date and time) return dojo.date.setIso8601(new Date(0, 0), formattedString); }; dojo.date.setIso8601Date = function(/*String*/dateObject, /*String*/formattedString){ // summary: sets a Date object based on an ISO 8601 formatted string (date only) var regexp = "^([0-9]{4})((-?([0-9]{2})(-?([0-9]{2}))?)|" + "(-?([0-9]{3}))|(-?W([0-9]{2})(-?([1-7]))?))?$"; var d = formattedString.match(new RegExp(regexp)); if(!d){ dojo.debug("invalid date string: " + formattedString); return null; // null } var year = d[1]; var month = d[4]; var date = d[6]; var dayofyear = d[8]; var week = d[10]; var dayofweek = d[12] ? d[12] : 1; dateObject.setFullYear(year); if(dayofyear){ dateObject.setMonth(0); dateObject.setDate(Number(dayofyear)); } else if(week){ dateObject.setMonth(0); dateObject.setDate(1); var gd = dateObject.getDay(); var day = gd ? gd : 7; var offset = Number(dayofweek) + (7 * Number(week)); if(day <= 4){ dateObject.setDate(offset + 1 - day); } else{ dateObject.setDate(offset + 8 - day); } } else{ if(month){ dateObject.setDate(1); dateObject.setMonth(month - 1); } if(date){ dateObject.setDate(date); } } return dateObject; // Date }; dojo.date.fromIso8601Date = function(/*String*/formattedString){ // summary: returns a Date object based on an ISO 8601 formatted string (date only) return dojo.date.setIso8601Date(new Date(0, 0), formattedString); }; dojo.date.setIso8601Time = function(/*Date*/dateObject, /*String*/formattedString){ // summary: sets a Date object based on an ISO 8601 formatted string (time only) // first strip timezone info from the end var timezone = "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"; var d = formattedString.match(new RegExp(timezone)); var offset = 0; // local time if no tz info if(d){ if(d[0] != 'Z'){ offset = (Number(d[3]) * 60) + Number(d[5]); offset *= ((d[2] == '-') ? 1 : -1); } offset -= dateObject.getTimezoneOffset(); formattedString = formattedString.substr(0, formattedString.length - d[0].length); } // then work out the time var regexp = "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"; d = formattedString.match(new RegExp(regexp)); if(!d){ dojo.debug("invalid time string: " + formattedString); return null; // null } var hours = d[1]; var mins = Number((d[3]) ? d[3] : 0); var secs = (d[5]) ? d[5] : 0; var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0; dateObject.setHours(hours); dateObject.setMinutes(mins); dateObject.setSeconds(secs); dateObject.setMilliseconds(ms); if(offset !== 0){ dateObject.setTime(dateObject.getTime() + offset * 60000); } return dateObject; // Date }; dojo.date.fromIso8601Time = function(/*String*/formattedString){ // summary: returns a Date object based on an ISO 8601 formatted string (date only) return dojo.date.setIso8601Time(new Date(0, 0), formattedString); }; /* RFC-3339 Date Functions *************************/ dojo.date.toRfc3339 = function(/*Date?*/dateObject, /*String?*/selector){ // summary: // Format a JavaScript Date object as a string according to RFC 3339 // // dateObject: // A JavaScript date, or the current date and time, by default // // selector: // "dateOnly" or "timeOnly" to format selected portions of the Date object. // Date and time will be formatted by default. //FIXME: tolerate Number, string input? if(!dateObject){ dateObject = new Date(); } var _ = dojo.string.pad; var formattedDate = []; if(selector != "timeOnly"){ var date = [_(dateObject.getFullYear(),4), _(dateObject.getMonth()+1,2), _(dateObject.getDate(),2)].join('-'); formattedDate.push(date); } if(selector != "dateOnly"){ var time = [_(dateObject.getHours(),2), _(dateObject.getMinutes(),2), _(dateObject.getSeconds(),2)].join(':'); var timezoneOffset = dateObject.getTimezoneOffset(); time += (timezoneOffset > 0 ? "-" : "+") + _(Math.floor(Math.abs(timezoneOffset)/60),2) + ":" + _(Math.abs(timezoneOffset)%60,2); formattedDate.push(time); } return formattedDate.join('T'); // String }; dojo.date.fromRfc3339 = function(/*String*/rfcDate){ // summary: // Create a JavaScript Date object from a string formatted according to RFC 3339 // // rfcDate: // A string such as 2005-06-30T08:05:00-07:00 // "any" is also supported in place of a time. // backwards compatible support for use of "any" instead of just not // including the time if(rfcDate.indexOf("Tany")!=-1){ rfcDate = rfcDate.replace("Tany",""); } var dateObject = new Date(); return dojo.date.setIso8601(dateObject, rfcDate); // Date or null }; dojo.provide("dojo.validate.datetime"); /** Validates a time value in any International format. The value can be validated against one format or one of multiple formats. Format h 12 hour, no zero padding. hh 12 hour, has leading zero. H 24 hour, no zero padding. HH 24 hour, has leading zero. m minutes, no zero padding. mm minutes, has leading zero. s seconds, no zero padding. ss seconds, has leading zero. All other characters must appear literally in the expression. Example "h:m:s t" -> 2:5:33 PM "HH:mm:ss" -> 14:05:33 @param value A string. @param flags An object. flags.format A string or an array of strings. Default is "h:mm:ss t". flags.amSymbol The symbol used for AM. Default is "AM". flags.pmSymbol The symbol used for PM. Default is "PM". @return true or false */ dojo.validate.isValidTime = function(value, flags) { dojo.deprecated("dojo.validate.datetime", "use dojo.date.parse instead", "0.5"); var re = new RegExp("^" + dojo.regexp.time(flags) + "$", "i"); return re.test(value); } /** Validates 12-hour time format. Zero-padding is not allowed for hours, required for minutes and seconds. Seconds are optional. @param value A string. @return true or false */ dojo.validate.is12HourTime = function(value) { dojo.deprecated("dojo.validate.datetime", "use dojo.date.parse instead", "0.5"); return dojo.validate.isValidTime(value, {format: ["h:mm:ss t", "h:mm t"]}); } /** Validates 24-hour military time format. Zero-padding is required for hours, minutes, and seconds. Seconds are optional. @param value A string. @return true or false */ dojo.validate.is24HourTime = function(value) { dojo.deprecated("dojo.validate.datetime", "use dojo.date.parse instead", "0.5"); return dojo.validate.isValidTime(value, {format: ["HH:mm:ss", "HH:mm"]} ); } /** Returns true if the date conforms to the format given and is a valid date. Otherwise returns false. @param dateValue A string for the date. @param format A string, default is "MM/DD/YYYY". @return true or false Accepts any type of format, including ISO8601. All characters in the format string are treated literally except the following tokens: YYYY - matches a 4 digit year M - matches a non zero-padded month MM - matches a zero-padded month D - matches a non zero-padded date DD - matches a zero-padded date DDD - matches an ordinal date, 001-365, and 366 on leapyear ww - matches week of year, 01-53 d - matches day of week, 1-7 Examples: These are all today's date. Date Format 2005-W42-3 YYYY-Www-d 2005-292 YYYY-DDD 20051019 YYYYMMDD 10/19/2005 M/D/YYYY 19.10.2005 D.M.YYYY */ dojo.validate.isValidDate = function(dateValue, format) { dojo.deprecated("dojo.validate.datetime", "use dojo.date.parse instead", "0.5"); // Default is the American format if (typeof format == "object" && typeof format.format == "string"){ format = format.format; } if (typeof format != "string") { format = "MM/DD/YYYY"; } // Create a literal regular expression based on format var reLiteral = format.replace(/([$^.*+?=!:|\/\\\(\)\[\]\{\}])/g, "\\$1"); // Convert all the tokens to RE elements reLiteral = reLiteral.replace( "YYYY", "([0-9]{4})" ); reLiteral = reLiteral.replace( "MM", "(0[1-9]|10|11|12)" ); reLiteral = reLiteral.replace( "M", "([1-9]|10|11|12)" ); reLiteral = reLiteral.replace( "DDD", "(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])" ); reLiteral = reLiteral.replace( "DD", "(0[1-9]|[12][0-9]|30|31)" ); reLiteral = reLiteral.replace( "D", "([1-9]|[12][0-9]|30|31)" ); reLiteral = reLiteral.replace( "ww", "(0[1-9]|[1-4][0-9]|5[0-3])" ); reLiteral = reLiteral.replace( "d", "([1-7])" ); // Anchor pattern to begining and end of string reLiteral = "^" + reLiteral + "$"; // Dynamic RE that parses the original format given var re = new RegExp(reLiteral); // Test if date is in a valid format if (!re.test(dateValue)) return false; // Parse date to get elements and check if date is valid // Assume valid values for date elements not given. var year = 0, month = 1, date = 1, dayofyear = 1, week = 1, day = 1; // Capture tokens var tokens = format.match( /(YYYY|MM|M|DDD|DD|D|ww|d)/g ); // Capture date values var values = re.exec(dateValue); // Match up tokens with date values for (var i = 0; i < tokens.length; i++) { switch (tokens[i]) { case "YYYY": year = Number(values[i+1]); break; case "M": case "MM": month = Number(values[i+1]); break; case "D": case "DD": date = Number(values[i+1]); break; case "DDD": dayofyear = Number(values[i+1]); break; case "ww": week = Number(values[i+1]); break; case "d": day = Number(values[i+1]); break; } } // Leap years are divisible by 4, but not by 100, unless by 400 var leapyear = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); // 31st of a month with 30 days if (date == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) return false; // February 30th or 31st if (date >= 30 && month == 2) return false; // February 29th outside a leap year if (date == 29 && month == 2 && !leapyear) return false; if (dayofyear == 366 && !leapyear) return false; return true; } dojo.provide("dojo.validate.web"); dojo.validate.isIpAddress = function(/*String*/value, /*Object?*/flags) { // summary: Validates an IP address // // description: // Supports 5 formats for IPv4: dotted decimal, dotted hex, dotted octal, decimal and hexadecimal. // Supports 2 formats for Ipv6. // // value A string. // flags An object. All flags are boolean with default = true. // flags.allowDottedDecimal Example, 207.142.131.235. No zero padding. // flags.allowDottedHex Example, 0x18.0x11.0x9b.0x28. Case insensitive. Zero padding allowed. // flags.allowDottedOctal Example, 0030.0021.0233.0050. Zero padding allowed. // flags.allowDecimal Example, 3482223595. A decimal number between 0-4294967295. // flags.allowHex Example, 0xCF8E83EB. Hexadecimal number between 0x0-0xFFFFFFFF. // Case insensitive. Zero padding allowed. // flags.allowIPv6 IPv6 address written as eight groups of four hexadecimal digits. // flags.allowHybrid IPv6 address written as six groups of four hexadecimal digits // followed by the usual 4 dotted decimal digit notation of IPv4. x:x:x:x:x:x:d.d.d.d var re = new RegExp("^" + dojo.regexp.ipAddress(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.isUrl = function(/*String*/value, /*Object?*/flags) { // summary: Checks if a string could be a valid URL // value: A string // flags: An object // flags.scheme Can be true, false, or [true, false]. // This means: required, not allowed, or either. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. var re = new RegExp("^" + dojo.regexp.url(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.isEmailAddress = function(/*String*/value, /*Object?*/flags) { // summary: Checks if a string could be a valid email address // // value: A string // flags: An object // flags.allowCruft Allow address like . Default is false. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. var re = new RegExp("^" + dojo.regexp.emailAddress(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.isEmailAddressList = function(/*String*/value, /*Object?*/flags) { // summary: Checks if a string could be a valid email address list. // // value A string. // flags An object. // flags.listSeparator The character used to separate email addresses. Default is ";", ",", "\n" or " ". // flags in regexp.emailAddress can be applied. // flags in regexp.host can be applied. // flags in regexp.ipAddress can be applied. // flags in regexp.tld can be applied. var re = new RegExp("^" + dojo.regexp.emailAddressList(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.getEmailAddressList = function(/*String*/value, /*Object?*/flags) { // summary: Check if value is an email address list. If an empty list // is returned, the value didn't pass the test or it was empty. // // value: A string // flags: An object (same as dojo.validate.isEmailAddressList) if(!flags) { flags = {}; } if(!flags.listSeparator) { flags.listSeparator = "\\s;,"; } if ( dojo.validate.isEmailAddressList(value, flags) ) { return value.split(new RegExp("\\s*[" + flags.listSeparator + "]\\s*")); // Array } return []; // Array } dojo.provide('dojo.validate.creditCard'); dojo.require("dojo.lang.common"); /* Validates Credit Cards using account number rules in conjunction with the Luhn algorigthm */ dojo.validate.isValidCreditCard = function(/*String|Int*/value, /*String*/ccType){ //Summary: // checks if type matches the # scheme, and if Luhn checksum is accurate (unless its an Enroute card, the checkSum is skipped) //Value: Boolean if(value&&ccType&&((ccType.toLowerCase()=='er'||dojo.validate.isValidLuhn(value))&&(dojo.validate.isValidCreditCardNumber(value,ccType.toLowerCase())))){ return true; //Boolean } return false; //Boolean } dojo.validate.isValidCreditCardNumber = function(/*String|Int*/value,/*String?*/ccType) { //Summary: // checks if the # matches the pattern for that card or any card types if none is specified // value == CC #, white spaces and dashes are ignored // ccType is of the values in cardinfo -- if Omitted it it returns a | delimited string of matching card types, or false if no matches found //Value: Boolean if(typeof value!='string'){ value = String(value); } value = value.replace(/[- ]/g,''); //ignore dashes and whitespaces /* FIXME: not sure on all the abbreviations for credit cards,below is what each stands for atleast to my knowledge mc: Mastercard ec: Eurocard vi: Visa ax: American Express dc: Diners Club bl: Carte Blanch di: Discover jcb: JCB er: Enroute */ var results=[]; var cardinfo = { 'mc':'5[1-5][0-9]{14}','ec':'5[1-5][0-9]{14}','vi':'4([0-9]{12}|[0-9]{15})', 'ax':'3[47][0-9]{13}', 'dc':'3(0[0-5][0-9]{11}|[68][0-9]{12})', 'bl':'3(0[0-5][0-9]{11}|[68][0-9]{12})','di':'6011[0-9]{12}', 'jcb':'(3[0-9]{15}|(2131|1800)[0-9]{11})','er':'2(014|149)[0-9]{11}' }; if(ccType&&dojo.lang.has(cardinfo,ccType.toLowerCase())){ return Boolean(value.match(cardinfo[ccType.toLowerCase()])); // boolean }else{ for(var p in cardinfo){ if(value.match('^'+cardinfo[p]+'$')!=null){ results.push(p); } } return (results.length)?results.join('|'):false; // string | boolean } } dojo.validate.isValidCvv = function(/*String|Int*/value, /*String*/ccType) { //Summary: // returns true if the security code (CCV) matches the correct format for supplied ccType //Value: Boolean if(typeof value!='string'){ value=String(value); } var format; switch (ccType.toLowerCase()){ case 'mc': case 'ec': case 'vi': case 'di': format = '###'; break; case 'ax': format = '####'; break; default: return false; //Boolean } var flags = {format:format}; //FIXME? Why does isNumberFormat take an object for flags when its only parameter is either a string or an array inside the object? if ((value.length == format.length)&&(dojo.validate.isNumberFormat(value, flags))){ return true; //Boolean } return false; //Boolean } dojo.provide("dojo.validate.us"); dojo.validate.us.isCurrency = function(/*String*/value, /*Object?*/flags){ // summary: Validates U.S. currency // value: the representation to check // flags: flags in validate.isCurrency can be applied. return dojo.validate.isCurrency(value, flags); // Boolean } dojo.validate.us.isState = function(/*String*/value, /*Object?*/flags){ // summary: Validates US state and territory abbreviations. // // value: A two character string // flags: An object // flags.allowTerritories Allow Guam, Puerto Rico, etc. Default is true. // flags.allowMilitary Allow military 'states', e.g. Armed Forces Europe (AE). Default is true. var re = new RegExp("^" + dojo.regexp.us.state(flags) + "$", "i"); return re.test(value); // Boolean } dojo.validate.us.isPhoneNumber = function(/*String*/value){ // summary: Validates 10 US digit phone number for several common formats // value: The telephone number string var flags = { format: [ "###-###-####", "(###) ###-####", "(###) ### ####", "###.###.####", "###/###-####", "### ### ####", "###-###-#### x#???", "(###) ###-#### x#???", "(###) ### #### x#???", "###.###.#### x#???", "###/###-#### x#???", "### ### #### x#???", "##########" ] }; return dojo.validate.isNumberFormat(value, flags); // Boolean } dojo.validate.us.isSocialSecurityNumber = function(/*String*/value){ // summary: Validates social security number var flags = { format: [ "###-##-####", "### ## ####", "#########" ] }; return dojo.validate.isNumberFormat(value, flags); // Boolean } dojo.validate.us.isZipCode = function(/*String*/value){ // summary: Validates U.S. zip-code var flags = { format: [ "#####-####", "##### ####", "#########", "#####" ] }; return dojo.validate.isNumberFormat(value, flags); // Boolean }