/*!
* Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
*
* This Software is licenced under the LGPL Licence (GNU Lesser General
* Public License).
* In addition to the LGPL Licence the Software is subject to the
* following conditions:
*
* i every modification must be public and comunicated to the Author
* ii every "jsbean" added to this library must be self consistent
* except for the dependence from jsbeans-x.x.x.js
* iii copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Validator extensions for counting words in inputs.
* @for Validator
* @namespace jsbeans
* @static
* */
jsbeans.Validator.wordCount = {
/**
* Default messages for Validator.wordCount extension
* @property wordCount.messages
* @type JSON
* @static
* */
messages: {
en: "exceedes the maximum number of words allowed for the field",
it: "supera il numero di parole massimo consentito per il campo"
},
/**
* Core counter. It takes same arguments (and it is used by) as {@method} <code class="methd">assertWordCount</code>.<br/>
* It may be useful to display current number of words. Beware of firing with <code>onkeyup</code> event.
* @method wordCount.getCount
* @param input {DOM} input to check
* @param params {Integer | JSON} limit for number of words OR a JSON with <code>limit</code> (Integer, required), <code>skip</code> (Integer, optional) and <code>excludes</code> (Array, optional).
* @return {Integer} the current number of words in <code class="param">input</code>; default <code>0</code>
* @static
*/
getCount: function(input, params) {
if (input == null || input.value == null || input.value == "") {
return 0;
}
// see jsbeans.string.trim
var trim = function(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
var ps = {};
try {
// if (skipParsing) {
// ps = params;
// // limit is required
// if (typeof ps.limit == "undefined") {
// return Number.MAX_VALUE;
// }
// // defaults
// if (typeof ps.skip == "undefined") {
// ps.skip = -1;
// }
// if (typeof ps.excludes == "undefined") {
// ps.excludes = jsbeans.Validator.wordCount.EXCLUDES;
// }
// }
// else {
ps = jsbeans.Validator.wordCount._parseParams(params);
// }
}
catch(err) {
return Number.MAX_VALUE;
}
// core validation
// trimming value
var str = trim(input.value);
// try to check email to count as one word
var sp = str.split(" ");
var res = [];
var s;
var email = /^[^\s()<>@,;:\/]+@\w[\w\.-]+\.[a-z]{2,}$/i;
var http = /((http:\/\/)|(www\.))+(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(\/[a-zA-Z0-9\&%_\.\/-~-]*)?/;
var https = /((https:\/\/)|(www\.))+(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(\/[a-zA-Z0-9\&%_\.\/-~-]*)?/;
for (var i = 0; i < sp.length; i++) {
s = trim(sp[i]);
if (email.test(s) || http.test(s) || https.test(s)) {
var t = "";
for (var j = 0; j < s.length; j++) {
t += "a";
}
// now word has as many "a" as original length (supposing "a" isn't in the excludes)
s = t;
}
// it's a Number
else if (!isNaN(parseFloat(s))) {
// replacing with a word longer than 'skip' (as it has -1 as default we always use skip+2)
var t = "";
for (var j = 0; j < (ps.skip + 2); j++) {
t += "a";
}
s = t;
}
res.push(s);
}
str = res.join(" ");
// skipping excludes
for (var i = 0; i < ps.excludes.length; i++) {
var s = str;
str = trim(str.split(new RegExp(ps.excludes[i], 'gi')).join(" "));
}
// skipping words shorter than 'skip'
if (ps.skip != -1) {
var sp = str.split(" ");
var res = [];
for (var i = 0; i < sp.length; i++) {
if (sp[i].length > ps.skip) {
res.push(sp[i]);
}
}
str = res.join(" ");
}
// removes two or more spaces
while (str.indexOf(" ") != -1) {
str = str.split(" ").join(" ");
}
return str.split(" ").length;
},
/**
* List of words and marks excluded from counting. It has punctation marks, brackets, arithmetics marks, ...<br/>
* It may be modified according to yours needs (just remember it's static) or use the optional <code>excludes</code> parameter invoking the validation method.
* See <a href="http://brajeshwar.github.io/entities/">here<a> and <a href="http://www.w3.org/TR/html4/sgml/entities.html">here</a> for a list of HTML/javascript entities.
* @property wordCount.EXCLUDES
* @type Array
* @static
*/
EXCLUDES: [
// marks
"\\.", ",", ";", ":", "!", "\\?",
// brackets
"\\(", "\\)", "\\[", "\\]", "\\{", "\\}",
// arithmetics
"\\-", "\\+", "\\/", "\\*", "=", "\\^",
// others
"#", "@", '"', '~', '`', '\n', '\t', '\\\\', '_',
'\u0022',
'\42',
'\u0026',
'\u003c',
'\u003e',
'\u00a5',
'\241',
'\243',
'\245',
'\246',
'\247',
'\251',
'\253',
'\256',
'\260',
'\261',
'\262',
'\263',
'\264',
'\265',
'\271',
'\272',
'\273',
'\277',
'\327',
'\367',
'\u02c6',
'\u02dc',
'\u2018',
'\u2019',
'\u201a',
'\u201c',
'\u201d',
'\u201e',
'\u2039',
'\u203a',
'\u2026',
'\u2032',
'\u2033',
'\u8260',
'\u2122',
'\u2212',
'\u2217',
'\u223c',
'\u22c5',
'\u2329',
'\u232a',
'\74',
'\76',
'\u20ac',
'\u2227',
'\u2228',
'\u2122'
],
/**
* Checks if {@param} <code class="param">input</code>'s value contains not more than a given number of words.<br/>
* This method does NOT take into account some characters as defined by <code>jsbeans.Validator.wordCount.EXLUDES</code> array.<br/>
* It also try to intercept email and web (http|https) addresses counting them as one word (each).<br/>
* Numbers are parsed as floats and <strong>always</strong> counted, regardless of their length as strings.<br/>
* {@param} <code class="param">params</code> may be an just Integer to check the maximum mumber of words or a JSON in the following form:
* <ul>
* <li><code>limit</code> <Integer> (<strong>required</strong>): maximum number of words aspected</li>
* <li><code>skip</code> <Integer>: skips words shorter than {@param} <code class="param">skip</code>. Default: no skip</li>
* <li><code>excludes</code> <Array>: if provided this array overrides <code>jsbeans.Validator.wordCount.EXCLUDES</code></li>
* </ul>
* {@param} <code class="param">excludes</code> items are checked using regular expression: <code>new RegExp(ps.excludes[i], 'gi')</code>.
* <br/>Samples:
* <ol>
* <li><code>["wordCount=10"]</code>: returns false if {@param} <code class="param">input</code>'s value contains 11 or more words</li>
* <li><code>["wordCount={limit:10,skip:3}"]</code>: returns false if {@param} <code class="param">input</code>'s value contains 11 or more words longer than 3 characters</li>
* </ol>
* @method assertWordCount
* @param input {DOM} input to check
* @param params {Integer | JSON} limit for number of words OR a JSON with <code>limit</code> (Integer, required), <code>skip</code> (Integer, optional) and <code>excludes</code> (Array, optional).
* @return {boolean} true if <code class="param">input</code>'s value has less than specified rules (see description).
* @static
* */
assertWordCount: function(input, params) {
var ps = {};
try {
ps = jsbeans.Validator.wordCount._parseParams(params);
}
catch(err) {
return false;
}
return jsbeans.Validator.wordCount.getCount(input, ps) <= ps.limit;
},
/**
* Utility method. It parses parameters for <code class="methd">getCount</code> and <code class="methd">assertWordCount</code> methods.<br/>
* Method intended to be private.
* @method wordCount._parseParams
* @param params {Integer | JSON} limit for number of words OR a JSON with <code>limit</code> (Integer, required), <code>skip</code> (Integer, optional) and <code>excludes</code> (Array, optional).
* @return {JSON} a JSON with parsed parameters as passed and/or with default values. It throws <code>Error</code>s as validation issue.
* @private
* @static
*/
_parseParams: function(params) {
// defaults
var ps = {
limit: -1,
skip: -1,
excludes: jsbeans.Validator.wordCount.EXCLUDES
};
// checks for params
var test = parseInt(params);
if (isNaN(test)) {
// presuming a JSON has been passed
if (params.limit) {
test = params;
}
else {
test = eval("(" + params + ")");
}
// if params is a JSON it MUST have the 'limit' property
if (!test.limit) {
throw new Error('limit');
}
else {
var n = parseInt(test.limit);
// 'limit' MUST be an Integer
if (isNaN(n)) {
throw new Error('limit');
}
ps.limit = test.limit;
}
if (!!test.skip) {
var n = parseInt(test.skip);
// 'skip' MUST be an Integer
if (isNaN(n)) {
throw new Error('skip');
}
else {
ps.skip = n;
}
}
if (!!test.excludes) {
// see jsbeans.Array
var ex = (typeof(test.excludes) === "object" && (test.excludes).constructor == Array);
// 'excludes' MUST be an Array
if (ex == false) {
throw new Error('excludes');
}
else {
// overrride default jsbeans.Validator.wordCount.EXCLUDES
ps.excludes = test.excludes;
}
}
}
// just passed an Integer
else {
ps.limit = test;
}
return ps;
}
};