0) {
for (x = a; x < b; x += step) {
r.push(x);
}
} else {
for (x = a; x > b; x += step) {
r.push(x);
}
}
return r;
},
/**
* Inserts a value on a specified index
*
* @method insert
* @param {Array} arr Array where the value will be inserted
* @param {Number} idx Index of the array where the value should be inserted
* @param {Mixed} value Value to be inserted
* @public
* @static
* @sample Ink_Util_Array_insert.html
*/
insert: function(arr, idx, value) {
arr.splice(idx, 0, value);
},
/**
* Removes a range of values from the array
*
* @method remove
* @param {Array} arr Array where the value will be removed
* @param {Number} from Index of the array where the removal will start removing.
* @param {Number} rLen Number of items to be removed from the index onwards.
* @return {Array} An array with the remaining values
* @public
* @static
* @sample Ink_Util_Array_remove.html
*/
remove: function(arr, from, rLen){
var output = [];
for(var i = 0, iLen = arr.length; i < iLen; i++){
if(i >= from && i < from + rLen){
continue;
}
output.push(arr[i]);
}
return output;
}
};
return InkArray;
});
/**
* Binary Packing algorithm implementation
* @module Ink.Util.BinPack_1
* @version 1
*/
Ink.createModule('Ink.Util.BinPack', '1', [], function() {
'use strict';
/*jshint boss:true */
// https://github.com/jakesgordon/bin-packing/
/*
Copyright (c) 2011, 2012, 2013 Jake Gordon and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above 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.
*/
var Packer = function(w, h) {
this.init(w, h);
};
Packer.prototype = {
init: function(w, h) {
this.root = { x: 0, y: 0, w: w, h: h };
},
fit: function(blocks) {
var n, node, block;
for (n = 0; n < blocks.length; ++n) {
block = blocks[n];
if (node = this.findNode(this.root, block.w, block.h)) {
block.fit = this.splitNode(node, block.w, block.h);
}
}
},
findNode: function(root, w, h) {
if (root.used) {
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
}
else if ((w <= root.w) && (h <= root.h)) {
return root;
}
else {
return null;
}
},
splitNode: function(node, w, h) {
node.used = true;
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
return node;
}
};
var GrowingPacker = function() {};
GrowingPacker.prototype = {
fit: function(blocks) {
var n, node, block, len = blocks.length;
var w = len > 0 ? blocks[0].w : 0;
var h = len > 0 ? blocks[0].h : 0;
this.root = { x: 0, y: 0, w: w, h: h };
for (n = 0; n < len ; n++) {
block = blocks[n];
if (node = this.findNode(this.root, block.w, block.h)) {
block.fit = this.splitNode(node, block.w, block.h);
}
else {
block.fit = this.growNode(block.w, block.h);
}
}
},
findNode: function(root, w, h) {
if (root.used) {
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
}
else if ((w <= root.w) && (h <= root.h)) {
return root;
}
else {
return null;
}
},
splitNode: function(node, w, h) {
node.used = true;
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
return node;
},
growNode: function(w, h) {
var canGrowDown = (w <= this.root.w);
var canGrowRight = (h <= this.root.h);
var shouldGrowRight = canGrowRight && (this.root.h >= (this.root.w + w)); // attempt to keep square-ish by growing right when height is much greater than width
var shouldGrowDown = canGrowDown && (this.root.w >= (this.root.h + h)); // attempt to keep square-ish by growing down when width is much greater than height
if (shouldGrowRight) {
return this.growRight(w, h);
}
else if (shouldGrowDown) {
return this.growDown(w, h);
}
else if (canGrowRight) {
return this.growRight(w, h);
}
else if (canGrowDown) {
return this.growDown(w, h);
}
else {
return null; // need to ensure sensible root starting size to avoid this happening
}
},
growRight: function(w, h) {
this.root = {
used: true,
x: 0,
y: 0,
w: this.root.w + w,
h: this.root.h,
down: this.root,
right: { x: this.root.w, y: 0, w: w, h: this.root.h }
};
var node;
if (node = this.findNode(this.root, w, h)) {
return this.splitNode(node, w, h);
}
else {
return null;
}
},
growDown: function(w, h) {
this.root = {
used: true,
x: 0,
y: 0,
w: this.root.w,
h: this.root.h + h,
down: { x: 0, y: this.root.h, w: this.root.w, h: h },
right: this.root
};
var node;
if (node = this.findNode(this.root, w, h)) {
return this.splitNode(node, w, h);
}
else {
return null;
}
}
};
var sorts = {
random: function() { return Math.random() - 0.5; },
w: function(a, b) { return b.w - a.w; },
h: function(a, b) { return b.h - a.h; },
a: function(a, b) { return b.area - a.area; },
max: function(a, b) { return Math.max(b.w, b.h) - Math.max(a.w, a.h); },
min: function(a, b) { return Math.min(b.w, b.h) - Math.min(a.w, a.h); },
height: function(a, b) { return sorts.msort(a, b, ['h', 'w']); },
width: function(a, b) { return sorts.msort(a, b, ['w', 'h']); },
area: function(a, b) { return sorts.msort(a, b, ['a', 'h', 'w']); },
maxside: function(a, b) { return sorts.msort(a, b, ['max', 'min', 'h', 'w']); },
msort: function(a, b, criteria) { /* sort by multiple criteria */
var diff, n;
for (n = 0; n < criteria.length; ++n) {
diff = sorts[ criteria[n] ](a, b);
if (diff !== 0) {
return diff;
}
}
return 0;
}
};
// end of Jake's code
// aux, used to display blocks in unfitted property
var toString = function() {
return [this.w, ' x ', this.h].join('');
};
/**
* Binary Packing algorithm implementation
*
* Based on the work of Jake Gordon
*
* see https://github.com/jakesgordon/bin-packing/
*
* @namespace Ink.Util.BinPack
* @version 1
* @static
*/
var BinPack = {
/**
* @method binPack
* @param {Object} o Options
* @param {Array} o.blocks Array of items with width and height integer attributes.
* @param {Array} [o.dimensions] Flag to fix container dimensions
* @param {String} [o.sorter] Sorting function. One of: random, height, width, area, maxside
* @return {Object} Returns an object containing container dimensions, filled ratio, fitted blocks, unfitted blocks and all blocks
* @static
*/
binPack: function(o) {
var i, f, bl;
// calculate area if not there already
for (i = 0, f = o.blocks.length; i < f; ++i) {
bl = o.blocks[i];
if (! ('area' in bl) ) {
bl.area = bl.w * bl.h;
}
}
// apply algorithm
var packer = o.dimensions ? new Packer(o.dimensions[0], o.dimensions[1]) : new GrowingPacker();
if (!o.sorter) { o.sorter = 'maxside'; }
o.blocks.sort( sorts[ o.sorter ] );
packer.fit(o.blocks);
var dims2 = [packer.root.w, packer.root.h];
// layout is done here, generating report data...
var fitted = [];
var unfitted = [];
for (i = 0, f = o.blocks.length; i < f; ++i) {
bl = o.blocks[i];
if (bl.fit) {
fitted.push(bl);
}
else {
bl.toString = toString; // TO AID SERIALIZATION
unfitted.push(bl);
}
}
var area = dims2[0] * dims2[1];
var fit = 0;
for (i = 0, f = fitted.length; i < f; ++i) {
bl = fitted[i];
fit += bl.area;
}
return {
dimensions: dims2,
filled: fit / area,
blocks: o.blocks,
fitted: fitted,
unfitted: unfitted
};
}
};
return BinPack;
});
/**
* Cookie Utilities
* @module Ink.Util.Cookie_1
* @version 1
*/
Ink.createModule('Ink.Util.Cookie', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.Cookie_1
*/
var Cookie = {
/**
* Gets an object with the current page cookies.
*
* @method get
* @param {String} name The cookie name.
* @return {String|Object} If the name is specified, it returns the value of that key. Otherwise it returns the full cookie object
* @public
* @static
* @sample Ink_Util_Cookie_get.html
*/
get: function(name)
{
var cookie = document.cookie || false;
var _Cookie = {};
if(cookie) {
cookie = cookie.replace(new RegExp("; ", "g"), ';');
var aCookie = cookie.split(';');
var aItem = [];
if(aCookie.length > 0) {
for(var i=0; i < aCookie.length; i++) {
aItem = aCookie[i].split('=');
if(aItem.length === 2) {
_Cookie[aItem[0]] = decodeURIComponent(aItem[1]);
}
aItem = [];
}
}
}
if(name) {
if(typeof(_Cookie[name]) !== 'undefined') {
return _Cookie[name];
} else {
return null;
}
}
return _Cookie;
},
/**
* Sets a cookie.
*
* @method set
* @param {String} name Cookie name.
* @param {String} value Cookie value.
* @param {Number} [expires] Number of seconds the cookie will be valid for.
* @param {String} [path] Path for the cookie. Defaults to '/'.
* @param {String} [domain] Domain for the cookie. Defaults to current hostname.
* @param {Boolean} [secure] Flag for secure. Default 'false'.
* @public
* @static
* @sample Ink_Util_Cookie_set.html
*/
set: function(name, value, expires, path, domain, secure)
{
var sName;
if(!name || value===false || typeof(name) === 'undefined' || typeof(value) === 'undefined') {
return false;
} else {
sName = name+'='+encodeURIComponent(value);
}
var sExpires = false;
var sPath = false;
var sDomain = false;
var sSecure = false;
if(expires && typeof(expires) !== 'undefined' && !isNaN(expires)) {
var oDate = new Date();
var sDate = (parseInt(Number(oDate.valueOf()), 10) + (Number(parseInt(expires, 10)) * 1000));
var nDate = new Date(sDate);
var expiresString = nDate.toGMTString();
var re = new RegExp("([^\\s]+)(\\s\\d\\d)\\s(\\w\\w\\w)\\s(.*)");
expiresString = expiresString.replace(re, "$1$2-$3-$4");
sExpires = 'expires='+expiresString;
} else {
if(typeof(expires) !== 'undefined' && !isNaN(expires) && Number(parseInt(expires, 10))===0) {
sExpires = '';
} else {
sExpires = 'expires=Thu, 01-Jan-2037 00:00:01 GMT';
}
}
if(path && typeof(path) !== 'undefined') {
sPath = 'path='+path;
} else {
sPath = 'path=/';
}
if(domain && typeof(domain) !== 'undefined') {
sDomain = 'domain='+domain;
} else {
var portClean = new RegExp(":(.*)");
sDomain = 'domain='+window.location.host;
sDomain = sDomain.replace(portClean,"");
}
if(secure && typeof(secure) !== 'undefined') {
sSecure = secure;
} else {
sSecure = false;
}
document.cookie = sName+'; '+sExpires+'; '+sPath+'; '+sDomain+'; '+sSecure;
},
/**
* Deletes a cookie.
*
* @method remove
* @param {String} cookieName Cookie name.
* @param {String} [path] Path of the cookie. Defaults to '/'.
* @param {String} [domain] Domain of the cookie. Defaults to current hostname.
* @public
* @static
* @sample Ink_Util_Cookie_remove.html
*/
remove: function(cookieName, path, domain)
{
//var expiresDate = 'Thu, 01-Jan-1970 00:00:01 GMT';
var sPath = false;
var sDomain = false;
var expiresDate = -999999999;
if(path && typeof(path) !== 'undefined') {
sPath = path;
} else {
sPath = '/';
}
if(domain && typeof(domain) !== 'undefined') {
sDomain = domain;
} else {
sDomain = window.location.host;
}
this.set(cookieName, 'deleted', expiresDate, sPath, sDomain);
}
};
return Cookie;
});
/**
* Date utility functions
* @module Ink.Util.Date_1
* @version 1
*/
Ink.createModule('Ink.Util.Date', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.Date_1
*/
var InkDate = {
/**
* Function that returns the string representation of the month [PT only]
*
* @method _months
* @param {Number} index Month javascript (0 to 11)
* @return {String} The month's name
* @private
* @static
* @example
* console.log( InkDate._months(0) ); // Result: Janeiro
*/
_months: function(index){
var _m = ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'];
return _m[index];
},
/**
* Function that returns the month [PT only] ( 0 to 11 )
*
* @method _iMonth
* @param {String} month Month javascript (0 to 11)
* @return {Number} The month's number
* @private
* @static
* @example
* console.log( InkDate._iMonth('maio') ); // Result: 4
*/
_iMonth : function( month )
{
if ( Number( month ) ) { return +month - 1; }
return {
'janeiro' : 0 ,
'jan' : 0 ,
'fevereiro' : 1 ,
'fev' : 1 ,
'março' : 2 ,
'mar' : 2 ,
'abril' : 3 ,
'abr' : 3 ,
'maio' : 4 ,
'mai' : 4 ,
'junho' : 5 ,
'jun' : 5 ,
'julho' : 6 ,
'jul' : 6 ,
'agosto' : 7 ,
'ago' : 7 ,
'setembro' : 8 ,
'set' : 8 ,
'outubro' : 9 ,
'out' : 9 ,
'novembro' : 10 ,
'nov' : 10 ,
'dezembro' : 11 ,
'dez' : 11
}[ month.toLowerCase( ) ];
} ,
/**
* Function that returns the representation the day of the week [PT Only]
*
* @method _wDays
* @param {Number} index Week's day index
* @return {String} The week's day name
* @private
* @static
* @example
* console.log( InkDate._wDays(0) ); // Result: Domingo
*/
_wDays: function(index){
var _d = ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'];
return _d[index];
},
/**
* Function that returns day of the week in javascript 1 to 7
*
* @method _iWeek
* @param {String} week Week's day name
* @return {Number} The week's day index
* @private
* @static
* @example
* console.log( InkDate._iWeek('quarta') ); // Result: 3
*/
_iWeek: function( week )
{
if ( Number( week ) ) { return +week || 7; }
return {
'segunda' : 1 ,
'seg' : 1 ,
'terça' : 2 ,
'ter' : 2 ,
'quarta' : 3 ,
'qua' : 3 ,
'quinta' : 4 ,
'qui' : 4 ,
'sexta' : 5 ,
'sex' : 5 ,
'sábado' : 6 ,
'sáb' : 6 ,
'domingo' : 7 ,
'dom' : 7
}[ week.toLowerCase( ) ];
},
/**
* Function that returns the number of days of a given month (m) on a given year (y)
*
* @method _daysInMonth
* @param {Number} _m Month
* @param {Number} _y Year
* @return {Number} Number of days of a give month on a given year
* @private
* @static
* @example
* console.log( InkDate._daysInMonth(2,2013) ); // Result: 28
*/
_daysInMonth: function(_m,_y){
var nDays;
if(_m===1 || _m===3 || _m===5 || _m===7 || _m===8 || _m===10 || _m===12)
{
nDays= 31;
}
else if ( _m===4 || _m===6 || _m===9 || _m===11)
{
nDays = 30;
}
else
{
if((_y%400===0) || (_y%4===0 && _y%100!==0))
{
nDays = 29;
}
else
{
nDays = 28;
}
}
return nDays;
},
/**
* Formats a date object.
* This works exactly as php date() function. http://php.net/manual/en/function.date.php
*
* @method get
* @param {String} format The format in which the date it will be formatted.
* @param {Date} [_date] The date to format. Can receive unix timestamp or a date object. Defaults to current time.
* @return {String} Formatted date
* @public
* @static
* @sample Ink_Util_Date_get.html
*/
get: function(format, _date){
/*jshint maxcomplexity:65 */
if(typeof(format) === 'undefined' || format === ''){
format = "Y-m-d";
}
var iFormat = format.split("");
var result = new Array(iFormat.length);
var escapeChar = "\\";
var jsDate;
if (typeof(_date) === 'undefined'){
jsDate = new Date();
} else if (typeof(_date)==='number'){
jsDate = new Date(_date*1000);
} else {
jsDate = new Date(_date);
}
var jsFirstDay, jsThisDay, jsHour;
/* This switch is presented in the same order as in php date function (PHP 5.2.2) */
for (var i = 0; i < iFormat.length; i++) {
switch(iFormat[i]) {
case escapeChar:
result[i] = iFormat[i+1];
i++;
break;
/* DAY */
case "d": /* Day of the month, 2 digits with leading zeros; ex: 01 to 31 */
var jsDay = jsDate.getDate();
result[i] = (String(jsDay).length > 1) ? jsDay : "0" + jsDay;
break;
case "D": /* A textual representation of a day, three letters; Seg to Dom */
result[i] = this._wDays(jsDate.getDay()).substring(0, 3);
break;
case "j": /* Day of the month without leading zeros; ex: 1 to 31 */
result[i] = jsDate.getDate();
break;
case "l": /* A full textual representation of the day of the week; Domingo to Sabado */
result[i] = this._wDays(jsDate.getDay());
break;
case "N": /* ISO-8601 numeric representation of the day of the week; 1 (Segunda) to 7 (Domingo) */
result[i] = jsDate.getDay() || 7;
break;
case "S": /* English ordinal suffix for the day of the month, 2 characters; st, nd, rd or th. Works well with j */
var temp = jsDate.getDate();
var suffixes = ["st", "nd", "rd"];
var suffix = "";
if (temp >= 11 && temp <= 13) {
result[i] = "th";
} else {
result[i] = (suffix = suffixes[String(temp).substr(-1) - 1]) ? (suffix) : ("th");
}
break;
case "w": /* Numeric representation of the day of the week; 0 (for Sunday) through 6 (for Saturday) */
result[i] = jsDate.getDay();
break;
case "z": /* The day of the year (starting from 0); 0 to 365 */
jsFirstDay = Date.UTC(jsDate.getFullYear(), 0, 0);
jsThisDay = Date.UTC(jsDate.getFullYear(), jsDate.getMonth(), jsDate.getDate());
result[i] = Math.floor((jsThisDay - jsFirstDay) / (1000 * 60 * 60 * 24));
break;
/* WEEK */
case "W": /* ISO-8601 week number of year, weeks starting on Monday; ex: 42 (the 42nd week in the year) */
var jsYearStart = new Date( jsDate.getFullYear( ) , 0 , 1 );
jsFirstDay = jsYearStart.getDay() || 7;
var days = Math.floor( ( jsDate - jsYearStart ) / ( 24 * 60 * 60 * 1000 ) + 1 );
result[ i ] = Math.ceil( ( days - ( 8 - jsFirstDay ) ) / 7 ) + 1;
break;
/* MONTH */
case "F": /* A full textual representation of a month, such as Janeiro or Marco; Janeiro a Dezembro */
result[i] = this._months(jsDate.getMonth());
break;
case "m": /* Numeric representation of a month, with leading zeros; 01 to 12 */
var jsMonth = String(jsDate.getMonth() + 1);
result[i] = (jsMonth.length > 1) ? jsMonth : "0" + jsMonth;
break;
case "M": /* A short textual representation of a month, three letters; Jan a Dez */
result[i] = this._months(jsDate.getMonth()).substring(0,3);
break;
case "n": /* Numeric representation of a month, without leading zeros; 1 a 12 */
result[i] = jsDate.getMonth() + 1;
break;
case "t": /* Number of days in the given month; ex: 28 */
result[i] = this._daysInMonth(jsDate.getMonth()+1,jsDate.getYear());
break;
/* YEAR */
case "L": /* Whether it's a leap year; 1 if it is a leap year, 0 otherwise. */
var jsYear = jsDate.getFullYear();
result[i] = (jsYear % 4) ? false : ( (jsYear % 100) ? true : ( (jsYear % 400) ? false : true ) );
break;
case "o": /* ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. */
throw '"o" not implemented!';
case "Y": /* A full numeric representation of a year, 4 digits; 1999 */
result[i] = jsDate.getFullYear();
break;
case "y": /* A two digit representation of a year; 99 */
result[i] = String(jsDate.getFullYear()).substring(2);
break;
/* TIME */
case "a": /* Lowercase Ante meridiem and Post meridiem; am or pm */
result[i] = (jsDate.getHours() < 12) ? "am" : "pm";
break;
case "A": /* Uppercase Ante meridiem and Post meridiem; AM or PM */
result[i] = (jsDate.getHours < 12) ? "AM" : "PM";
break;
case "B": /* Swatch Internet time; 000 through 999 */
throw '"B" not implemented!';
case "g": /* 12-hour format of an hour without leading zeros; 1 to 12 */
jsHour = jsDate.getHours();
result[i] = (jsHour <= 12) ? jsHour : (jsHour - 12);
break;
case "G": /* 24-hour format of an hour without leading zeros; 1 to 23 */
result[i] = String(jsDate.getHours());
break;
case "h": /* 12-hour format of an hour with leading zeros; 01 to 12 */
jsHour = String(jsDate.getHours());
jsHour = (jsHour <= 12) ? jsHour : (jsHour - 12);
result[i] = (jsHour.length > 1) ? jsHour : "0" + jsHour;
break;
case "H": /* 24-hour format of an hour with leading zeros; 01 to 24 */
jsHour = String(jsDate.getHours());
result[i] = (jsHour.length > 1) ? jsHour : "0" + jsHour;
break;
case "i": /* Minutes with leading zeros; 00 to 59 */
var jsMinute = String(jsDate.getMinutes());
result[i] = (jsMinute.length > 1) ? jsMinute : "0" + jsMinute;
break;
case "s": /* Seconds with leading zeros; 00 to 59; */
var jsSecond = String(jsDate.getSeconds());
result[i] = (jsSecond.length > 1) ? jsSecond : "0" + jsSecond;
break;
case "u": /* Microseconds */
throw '"u" not implemented!';
/* TIMEZONE */
case "e": /* Timezone identifier */
throw '"e" not implemented!';
case "I": /* "1" if Daylight Savings Time, "0" otherwise. Works only on the northern hemisphere */
jsFirstDay = new Date(jsDate.getFullYear(), 0, 1);
result[i] = (jsDate.getTimezoneOffset() !== jsFirstDay.getTimezoneOffset()) ? (1) : (0);
break;
case "O": /* Difference to Greenwich time (GMT) in hours */
var jsMinZone = jsDate.getTimezoneOffset();
var jsMinutes = jsMinZone % 60;
jsHour = String(((jsMinZone - jsMinutes) / 60) * -1);
if (jsHour.charAt(0) !== "-") {
jsHour = "+" + jsHour;
}
jsHour = (jsHour.length === 3) ? (jsHour) : (jsHour.replace(/([+\-])(\d)/, "$1" + 0 + "$2"));
result[i] = jsHour + jsMinutes + "0";
break;
case "P": /* Difference to Greenwich time (GMT) with colon between hours and minutes */
throw '"P" not implemented!';
case "T": /* Timezone abbreviation */
throw '"T" not implemented!';
case "Z": /* Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. */
result[i] = jsDate.getTimezoneOffset() * 60;
break;
/* FULL DATE/TIME */
case "c": /* ISO 8601 date */
throw '"c" not implemented!';
case "r": /* RFC 2822 formatted date */
var jsDayName = this._wDays(jsDate.getDay()).substr(0, 3);
var jsMonthName = this._months(jsDate.getMonth()).substr(0, 3);
result[i] = jsDayName + ", " + jsDate.getDate() + " " + jsMonthName + this.get(" Y H:i:s O",jsDate);
break;
case "U": /* Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) */
result[i] = Math.floor(jsDate.getTime() / 1000);
break;
default:
result[i] = iFormat[i];
}
}
return result.join('');
},
/**
* Creates a date object based on a format string.
* This works exactly as php date() function. http://php.net/manual/en/function.date.php
*
* @method set
* @param {String} [format] The format in which the date will be formatted. Defaults to 'Y-m-d'
* @param {String} str_date The date formatted.
* @return {Date} Date object based on the formatted date and format
* @public
* @static
* @sample Ink_Util_Date_set.html
*/
set : function( format , str_date ) {
if ( typeof str_date === 'undefined' ) { return ; }
if ( typeof format === 'undefined' || format === '' ) { format = "Y-m-d"; }
var iFormat = format.split("");
var result = new Array( iFormat.length );
var escapeChar = "\\";
var mList;
var objIndex = {
year : undefined ,
month : undefined ,
day : undefined ,
dayY : undefined ,
dayW : undefined ,
week : undefined ,
hour : undefined ,
hourD : undefined ,
min : undefined ,
sec : undefined ,
msec : undefined ,
ampm : undefined ,
diffM : undefined ,
diffH : undefined ,
date : undefined
};
var matches = 0;
/* This switch is presented in the same order as in php date function (PHP 5.2.2) */
for ( var i = 0; i < iFormat.length; i++) {
switch( iFormat[ i ] ) {
case escapeChar:
result[i] = iFormat[ i + 1 ];
i++;
break;
/* DAY */
case "d": /* Day of the month, 2 digits with leading zeros; ex: 01 to 31 */
result[ i ] = '(\\d{2})';
objIndex.day = { original : i , match : matches++ };
break;
case "j": /* Day of the month without leading zeros; ex: 1 to 31 */
result[ i ] = '(\\d{1,2})';
objIndex.day = { original : i , match : matches++ };
break;
case "D": /* A textual representation of a day, three letters; Seg to Dom */
result[ i ] = '([\\wá]{3})';
objIndex.dayW = { original : i , match : matches++ };
break;
case "l": /* A full textual representation of the day of the week; Domingo to Sabado */
result[i] = '([\\wá]{5,7})';
objIndex.dayW = { original : i , match : matches++ };
break;
case "N": /* ISO-8601 numeric representation of the day of the week; 1 (Segunda) to 7 (Domingo) */
result[ i ] = '(\\d)';
objIndex.dayW = { original : i , match : matches++ };
break;
case "w": /* Numeric representation of the day of the week; 0 (for Sunday) through 6 (for Saturday) */
result[ i ] = '(\\d)';
objIndex.dayW = { original : i , match : matches++ };
break;
case "S": /* English ordinal suffix for the day of the month, 2 characters; st, nd, rd or th. Works well with j */
result[ i ] = '\\w{2}';
break;
case "z": /* The day of the year (starting from 0); 0 to 365 */
result[ i ] = '(\\d{1,3})';
objIndex.dayY = { original : i , match : matches++ };
break;
/* WEEK */
case "W": /* ISO-8601 week number of year, weeks starting on Monday; ex: 42 (the 42nd week in the year) */
result[ i ] = '(\\d{1,2})';
objIndex.week = { original : i , match : matches++ };
break;
/* MONTH */
case "F": /* A full textual representation of a month, such as Janeiro or Marco; Janeiro a Dezembro */
result[ i ] = '([\\wç]{4,9})';
objIndex.month = { original : i , match : matches++ };
break;
case "M": /* A short textual representation of a month, three letters; Jan a Dez */
result[ i ] = '(\\w{3})';
objIndex.month = { original : i , match : matches++ };
break;
case "m": /* Numeric representation of a month, with leading zeros; 01 to 12 */
result[ i ] = '(\\d{2})';
objIndex.month = { original : i , match : matches++ };
break;
case "n": /* Numeric representation of a month, without leading zeros; 1 a 12 */
result[ i ] = '(\\d{1,2})';
objIndex.month = { original : i , match : matches++ };
break;
case "t": /* Number of days in the given month; ex: 28 */
result[ i ] = '\\d{2}';
break;
/* YEAR */
case "L": /* Whether it's a leap year; 1 if it is a leap year, 0 otherwise. */
result[ i ] = '\\w{4,5}';
break;
case "o": /* ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. */
throw '"o" not implemented!';
case "Y": /* A full numeric representation of a year, 4 digits; 1999 */
result[ i ] = '(\\d{4})';
objIndex.year = { original : i , match : matches++ };
break;
case "y": /* A two digit representation of a year; 99 */
result[ i ] = '(\\d{2})';
if ( typeof objIndex.year === 'undefined' || iFormat[ objIndex.year.original ] !== 'Y' ) {
objIndex.year = { original : i , match : matches++ };
}
break;
/* TIME */
case "a": /* Lowercase Ante meridiem and Post meridiem; am or pm */
result[ i ] = '(am|pm)';
objIndex.ampm = { original : i , match : matches++ };
break;
case "A": /* Uppercase Ante meridiem and Post meridiem; AM or PM */
result[ i ] = '(AM|PM)';
objIndex.ampm = { original : i , match : matches++ };
break;
case "B": /* Swatch Internet time; 000 through 999 */
throw '"B" not implemented!';
case "g": /* 12-hour format of an hour without leading zeros; 1 to 12 */
result[ i ] = '(\\d{1,2})';
objIndex.hourD = { original : i , match : matches++ };
break;
case "G": /* 24-hour format of an hour without leading zeros; 1 to 23 */
result[ i ] = '(\\d{1,2})';
objIndex.hour = { original : i , match : matches++ };
break;
case "h": /* 12-hour format of an hour with leading zeros; 01 to 12 */
result[ i ] = '(\\d{2})';
objIndex.hourD = { original : i , match : matches++ };
break;
case "H": /* 24-hour format of an hour with leading zeros; 01 to 24 */
result[ i ] = '(\\d{2})';
objIndex.hour = { original : i , match : matches++ };
break;
case "i": /* Minutes with leading zeros; 00 to 59 */
result[ i ] = '(\\d{2})';
objIndex.min = { original : i , match : matches++ };
break;
case "s": /* Seconds with leading zeros; 00 to 59; */
result[ i ] = '(\\d{2})';
objIndex.sec = { original : i , match : matches++ };
break;
case "u": /* Microseconds */
throw '"u" not implemented!';
/* TIMEZONE */
case "e": /* Timezone identifier */
throw '"e" not implemented!';
case "I": /* "1" if Daylight Savings Time, "0" otherwise. Works only on the northern hemisphere */
result[i] = '\\d';
break;
case "O": /* Difference to Greenwich time (GMT) in hours */
result[ i ] = '([-+]\\d{4})';
objIndex.diffH = { original : i , match : matches++ };
break;
case "P": /* Difference to Greenwich time (GMT) with colon between hours and minutes */
throw '"P" not implemented!';
case "T": /* Timezone abbreviation */
throw '"T" not implemented!';
case "Z": /* Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. */
result[ i ] = '(\\-?\\d{1,5})';
objIndex.diffM = { original : i , match : matches++ };
break;
/* FULL DATE/TIME */
case "c": /* ISO 8601 date */
throw '"c" not implemented!';
case "r": /* RFC 2822 formatted date */
result[ i ] = '([\\wá]{3}, \\d{1,2} \\w{3} \\d{4} \\d{2}:\\d{2}:\\d{2} [+\\-]\\d{4})';
objIndex.date = { original : i , match : matches++ };
break;
case "U": /* Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) */
result[ i ] = '(\\d{1,13})';
objIndex.date = { original : i , match : matches++ };
break;
default:
result[ i ] = iFormat[ i ];
}
}
var pattr = new RegExp( result.join('') );
try {
mList = str_date.match( pattr );
if ( !mList ) { return; }
}
catch ( e ) { return ; }
var _haveDatetime = typeof objIndex.date !== 'undefined';
var _haveYear = typeof objIndex.year !== 'undefined';
var _haveYDay = typeof objIndex.dayY !== 'undefined';
var _haveDay = typeof objIndex.day !== 'undefined';
var _haveMonth = typeof objIndex.month !== 'undefined';
var _haveMonthDay = _haveMonth && _haveDay;
var _haveOnlyDay = !_haveMonth && _haveDay;
var _haveWDay = typeof objIndex.dayW !== 'undefined';
var _haveWeek = typeof objIndex.week !== 'undefined';
var _haveWeekWDay = _haveWeek && _haveWDay;
var _haveOnlyWDay = !_haveWeek && _haveWDay;
var _validDate = _haveYDay || _haveMonthDay || !_haveYear && _haveOnlyDay || _haveWeekWDay || !_haveYear && _haveOnlyWDay;
var _noDate = !_haveYear && !_haveYDay && !_haveDay && !_haveMonth && !_haveWDay && !_haveWeek;
var _haveHour12 = typeof objIndex.hourD !== 'undefined' && typeof objIndex.ampm !== 'undefined';
var _haveHour24 = typeof objIndex.hour !== 'undefined';
var _haveHour = _haveHour12 || _haveHour24;
var _haveMin = typeof objIndex.min !== 'undefined';
var _haveSec = typeof objIndex.sec !== 'undefined';
var _haveMSec = typeof objIndex.msec !== 'undefined';
var _haveMoreM = !_noDate || _haveHour;
var _haveMoreS = _haveMoreM || _haveMin;
var _haveDiffM = typeof objIndex.diffM !== 'undefined';
var _haveDiffH = typeof objIndex.diffH !== 'undefined';
//var _haveGMT = _haveDiffM || _haveDiffH;
var hour;
var min;
if ( _haveDatetime ) {
if ( iFormat[ objIndex.date.original ] === 'U' ) {
return new Date( +mList[ objIndex.date.match + 1 ] * 1000 );
}
var dList = mList[ objIndex.date.match + 1 ].match( /\w{3}, (\d{1,2}) (\w{3}) (\d{4}) (\d{2}):(\d{2}):(\d{2}) ([+\-]\d{4})/ );
hour = +dList[ 4 ] + ( +dList[ 7 ].slice( 0 , 3 ) );
min = +dList[ 5 ] + ( dList[ 7 ].slice( 0 , 1 ) + dList[ 7 ].slice( 3 ) ) / 100 * 60;
return new Date( dList[ 3 ] , this._iMonth( dList[ 2 ] ) , dList[ 1 ] , hour , min , dList[ 6 ] );
}
var _d = new Date( );
var year;
var month;
var day;
var sec;
var msec;
var gmt;
if ( !_validDate && !_noDate ) { return ; }
if ( _validDate ) {
if ( _haveYear ) {
var _y = _d.getFullYear( ) - 50 + '';
year = mList[ objIndex.year.match + 1 ];
if ( iFormat[ objIndex.year.original ] === 'y' ) {
year = +_y.slice( 0 , 2 ) + ( year >= ( _y ).slice( 2 ) ? 0 : 1 ) + year;
}
} else {
year = _d.getFullYear();
}
if ( _haveYDay ) {
month = 0;
day = mList[ objIndex.dayY.match + 1 ];
} else if ( _haveDay ) {
if ( _haveMonth ) {
month = this._iMonth( mList[ objIndex.month.match + 1 ] );
} else {
month = _d.getMonth( );
}
day = mList[ objIndex.day.match + 1 ];
} else {
month = 0;
var week;
if ( _haveWeek ) {
week = mList[ objIndex.week.match + 1 ];
} else {
week = this.get( 'W' , _d );
}
day = ( week - 2 ) * 7 + ( 8 - ( ( new Date( year , 0 , 1 ) ).getDay( ) || 7 ) ) + this._iWeek( mList[ objIndex.week.match + 1 ] );
}
if ( month === 0 && day > 31 ) {
var aux = new Date( year , month , day );
month = aux.getMonth( );
day = aux.getDate( );
}
}
else {
year = _d.getFullYear( );
month = _d.getMonth( );
day = _d.getDate( );
}
if ( _haveHour12 ) { hour = +mList[ objIndex.hourD.match + 1 ] + ( mList[ objIndex.ampm.match + 1 ] === 'pm' ? 12 : 0 ); }
else if ( _haveHour24 ) { hour = mList[ objIndex.hour.match + 1 ]; }
else if ( _noDate ) { hour = _d.getHours( ); }
else { hour = '00'; }
if ( _haveMin ) { min = mList[ objIndex.min.match + 1 ]; }
else if ( !_haveMoreM ) { min = _d.getMinutes( ); }
else { min = '00'; }
if ( _haveSec ) { sec = mList[ objIndex.sec.match + 1 ]; }
else if ( !_haveMoreS ) { sec = _d.getSeconds( ); }
else { sec = '00'; }
if ( _haveMSec ) { msec = mList[ objIndex.msec.match + 1 ]; }
else { msec = '000'; }
if ( _haveDiffH ) { gmt = mList[ objIndex.diffH.match + 1 ]; }
else if ( _haveDiffM ) { gmt = String( -1 * mList[ objIndex.diffM.match + 1 ] / 60 * 100 ).replace( /^(\d)/ , '+$1' ).replace( /(^[\-+])(\d{3}$)/ , '$10$2' ); }
else { gmt = '+0000'; }
return new Date( year, month, day, hour, min, sec );
}
};
return InkDate;
});
/**
* Dump/Profiling Utilities
* @module Ink.Util.Dumper_1
* @version 1
*/
Ink.createModule('Ink.Util.Dumper', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.Dumper_1
*/
var Dumper = {
/**
* Hex code for the 'tab'
*
* @property _tab
* @type {String}
* @private
* @readOnly
* @static
*
*/
_tab: '\xA0\xA0\xA0\xA0',
/**
* Function that returns the argument passed formatted
*
* @method _formatParam
* @param {Mixed} param
* @return {String} The argument passed formatted
* @private
* @static
*/
_formatParam: function(param)
{
var formated = '';
switch(typeof(param)) {
case 'string':
formated = '(string) '+param;
break;
case 'number':
formated = '(number) '+param;
break;
case 'boolean':
formated = '(boolean) '+param;
break;
case 'object':
if(param !== null) {
if(param.constructor === Array) {
formated = 'Array \n{\n' + this._outputFormat(param, 0) + '\n}';
} else {
formated = 'Object \n{\n' + this._outputFormat(param, 0) + '\n}';
}
} else {
formated = 'null';
}
break;
default:
formated = false;
}
return formated;
},
/**
* Function that returns the tabs concatenated
*
* @method _getTabs
* @param {Number} numberOfTabs Number of Tabs
* @return {String} Tabs concatenated
* @private
* @static
*/
_getTabs: function(numberOfTabs)
{
var tabs = '';
for(var _i = 0; _i < numberOfTabs; _i++) {
tabs += this._tab;
}
return tabs;
},
/**
* Function that formats the parameter to display.
*
* @method _outputFormat
* @param {Any} param
* @param {Number} dim
* @return {String} The parameter passed formatted to displat
* @private
* @static
*/
_outputFormat: function(param, dim)
{
var formated = '';
//var _strVal = false;
var _typeof = false;
for(var key in param) {
if(param[key] !== null) {
if(typeof(param[key]) === 'object' && (param[key].constructor === Array || param[key].constructor === Object)) {
if(param[key].constructor === Array) {
_typeof = 'Array';
} else if(param[key].constructor === Object) {
_typeof = 'Object';
}
formated += this._tab + this._getTabs(dim) + '[' + key + '] => '+_typeof+'\n';
formated += this._tab + this._getTabs(dim) + '{\n';
formated += this._outputFormat(param[key], dim + 1) + this._tab + this._getTabs(dim) + '}\n';
} else if(param[key].constructor === Function) {
continue;
} else {
formated = formated + this._tab + this._getTabs(dim) + '[' + key + '] => ' + param[key] + '\n';
}
} else {
formated = formated + this._tab + this._getTabs(dim) + '[' + key + '] => null \n';
}
}
return formated;
},
/**
* Prints variable structure.
*
* @method printDump
* @param {Any} param Variable to be dumped.
* @param {DOMElement|String} [target] Element to print the dump on.
* @public
* @static
* @sample Ink_Util_Dumper_printDump.html
*/
printDump: function(param, target)
{
/*jshint evil:true */
if(!target || typeof(target) === 'undefined') {
document.write(''+this._formatParam(param)+'
');
} else {
if(typeof(target) === 'string') {
document.getElementById(target).innerHTML = '' + this._formatParam(param) + '
';
} else if(typeof(target) === 'object') {
target.innerHTML = ''+this._formatParam(param)+'
';
} else {
throw "TARGET must be an element or an element ID";
}
}
},
/**
* Get a variable's structure.
*
* @method returnDump
* @param {Any} param Variable to get the structure.
* @return {String} The variable's structure.
* @public
* @static
* @sample Ink_Util_Dumper_returnDump.html
*/
returnDump: function(param)
{
return this._formatParam(param);
},
/**
* Alert a variable's structure.
*
* @method alertDump
* @param {Any} param Variable to be dumped.
* @public
* @static
* @sample Ink_Util_Dumper_alertDump.html
*/
alertDump: function(param)
{
window.alert(this._formatParam(param).replace(/()(Array|Object)(<\/b>)/g, "$2"));
},
/**
* Prints the variable structure to a new window.
*
* @method windowDump
* @param {Any} param Variable to be dumped.
* @public
* @static
* @sample Ink_Util_Dumper_windowDump.html
*/
windowDump: function(param)
{
var dumperwindow = 'dumperwindow_'+(Math.random() * 10000);
var win = window.open('',
dumperwindow,
'width=400,height=300,left=50,top=50,status,menubar,scrollbars,resizable'
);
win.document.open();
win.document.write(''+this._formatParam(param)+'
');
win.document.close();
win.focus();
}
};
return Dumper;
});
/**
* Internationalization Utilities
* @module Ink.Util.I18n_1
* @version 1
*/
Ink.createModule('Ink.Util.I18n', '1', [], function () {
'use strict';
var pattrText = /\{(?:(\{.*?})|(?:%s:)?(\d+)|(?:%s)?|([\w-]+))}/g;
var funcOrVal = function( ret , args ) {
if ( typeof ret === 'function' ) {
return ret.apply(this, args);
} else if (typeof ret !== undefined) {
return ret;
} else {
return '';
}
};
/**
* You can use this module to internationalize your applications. It roughly emulates GNU gettext's API.
*
* @class Ink.Util.I18n
* @constructor
*
* @param {Object} dict Object mapping language codes (in the form of `pt_PT`, `pt_BR`, `fr`, `en_US`, etc.) to their `dictionaries`
* @param {String} [lang='pt_PT'] language code of the target language
*
* @sample Ink_Util_I18n_1.html
*/
var I18n = function( dict , lang , testMode ) {
if ( !( this instanceof I18n ) ) { return new I18n( dict , lang , testMode ); }
this.reset( )
.lang( lang )
.testMode( testMode )
.append( dict || { } , lang );
};
I18n.prototype = {
reset: function( ) {
this._dicts = [ ];
this._dict = { };
this._testMode = false;
this._lang = this._gLang;
return this;
},
/**
* Adds translation strings for the helper to use.
*
* @method append
* @param {Object} dict Object containing language objects identified by their language code
*
* @sample Ink_Util_I18n_1_append.html
*/
append: function( dict ) {
this._dicts.push( dict );
this._dict = Ink.extendObj(this._dict , dict[ this._lang ] );
return this;
},
/**
* Gets or sets the language.
* If there are more dictionaries available in cache, they will be loaded.
*
* @method lang
* @param {String} lang Language code to set this instance to.
*/
lang: function( lang ) {
if ( !arguments.length ) { return this._lang; }
if ( lang && this._lang !== lang ) {
this._lang = lang;
this._dict = { };
for ( var i = 0, l = this._dicts.length; i < l; i++ ) {
this._dict = Ink.extendObj( this._dict , this._dicts[ i ][ lang ] || { } );
}
}
return this;
},
/**
* Sets or unsets test mode.
* In test mode, unknown strings are wrapped in `[ ... ]`. This is useful for debugging your application and to make sure all your translation keys are in place.
*
* @method testMode
* @param {Boolean} bool Flag to set the test mode state
*/
testMode: function( bool ) {
if ( !arguments.length ) { return !!this._testMode; }
if ( bool !== undefined ) { this._testMode = !!bool; }
return this;
},
/**
* Gest a key from the current dictionary
*
* @method getKey
* @param {String} key
* @return {Mixed} The object which happened to be in the current language dictionary on the given key.
*
* @sample Ink_Util_I18n_1_getKey.html
*/
getKey: function( key ) {
var ret;
var gLang = this._gLang;
var lang = this._lang;
if ( key in this._dict ) {
ret = this._dict[ key ];
} else {
I18n.langGlobal( lang );
ret = this._gDict[ key ];
I18n.langGlobal( gLang );
}
return ret;
},
/**
* Translates a string.
* Given a translation key, return a translated string, with replaced parameters.
* When a translated string is not available, the original string is returned unchanged.
*
* @method text
* @param {String} str Key to look for in i18n dictionary (which is returned verbatim if unknown)
* @param {Object} [namedParms] Named replacements. Replaces {named} with values in this object.
* @param {String} [args] Replacement #1 (replaces first {} and all {1})
* @param {String} [arg2] Replacement #2 (replaces second {} and all {2})
* @param {String} [argn*] Replacement #n (replaces nth {} and all {n})
*
* @sample Ink_Util_I18n_1_text.html
*/
text: function( str /*, replacements...*/ ) {
if ( typeof str !== 'string' ) { return; } // Backwards-compat
var pars = Array.prototype.slice.call( arguments , 1 );
var idx = 0;
var isObj = typeof pars[ 0 ] === 'object';
var original = this.getKey( str );
if ( original === undefined ) { original = this._testMode ? '[' + str + ']' : str; }
if ( typeof original === 'number' ) { original += ''; }
if (typeof original === 'string') {
original = original.replace( pattrText , function( m , $1 , $2 , $3 ) {
var ret =
$1 ? $1 :
$2 ? pars[ $2 - ( isObj ? 0 : 1 ) ] :
$3 ? pars[ 0 ][ $3 ] || '' :
pars[ (idx++) + ( isObj ? 1 : 0 ) ];
return funcOrVal( ret , [idx].concat(pars) );
});
return original;
}
return (
typeof original === 'function' ? original.apply( this , pars ) :
original instanceof Array ? funcOrVal( original[ pars[ 0 ] ] , pars ) :
typeof original === 'object' ? funcOrVal( original[ pars[ 0 ] ] , pars ) :
'');
},
/**
* Translates and pluralizes text.
* Given a singular string, a plural string and a number, translates either the singular or plural string.
*
* @method ntext
* @return {String}
*
* @param {String} strSin Word to use when count is 1
* @param {String} strPlur Word to use otherwise
* @param {Number} count Number which defines which word to use
* @param [args*] Extra arguments, to be passed to `text()`
*
* @sample Ink_Util_I18n_1_ntext.html
*/
ntext: function( strSin , strPlur , count ) {
var pars = Array.prototype.slice.apply( arguments );
var original;
if ( pars.length === 2 && typeof strPlur === 'number' ) {
original = this.getKey( strSin );
if ( !( original instanceof Array ) ) { return ''; }
pars.splice( 0 , 1 );
original = original[ strPlur === 1 ? 0 : 1 ];
} else {
pars.splice( 0 , 2 );
original = count === 1 ? strSin : strPlur;
}
return this.text.apply( this , [ original ].concat( pars ) );
},
/**
* Gets the ordinal suffix of a number.
*
* This works by using transforms (in the form of Objects or Functions) passed into the function or found in the special key `_ordinals` in the active language dictionary.
*
* @method ordinal
*
* @param {Number} num Input number
* @param {Object|Function} [options]={} Dictionaries for translating. Each of these options' fallback is found in the current language's dictionary. The lookup order is the following: `exceptions`, `byLastDigit`, `default`. Each of these may be either an `Object` or a `Function`. If it's a function, it is called (with `number` and `digit` for any function except for byLastDigit, which is called with the `lastDigit` of the number in question), and if the function returns a string, that is used. If it's an object, the property is looked up using `obj[prop]`. If what is found is a string, it is used directly.
* @param {Object|Function} [options.byLastDigit]={} If the language requires the last digit to be considered, mappings of last digits to ordinal suffixes can be created here.
* @param {Object|Function} [options.exceptions]={} Map unique, special cases to their ordinal suffixes.
*
* @returns {String} Ordinal suffix for `num`.
*
* @sample Ink_Util_I18n_1_ordinal.html
**/
ordinal: function( num ) {
if ( num === undefined ) { return ''; }
var lastDig = +num.toString( ).slice( -1 );
var ordDict = this.getKey( '_ordinals' );
if ( ordDict === undefined ) { return ''; }
if ( typeof ordDict === 'string' ) { return ordDict; }
var ret;
if ( typeof ordDict === 'function' ) {
ret = ordDict( num , lastDig );
if ( typeof ret === 'string' ) { return ret; }
}
if ( 'exceptions' in ordDict ) {
ret = typeof ordDict.exceptions === 'function' ? ordDict.exceptions( num , lastDig ) :
num in ordDict.exceptions ? funcOrVal( ordDict.exceptions[ num ] , [num , lastDig] ) :
undefined;
if ( typeof ret === 'string' ) { return ret; }
}
if ( 'byLastDigit' in ordDict ) {
ret = typeof ordDict.byLastDigit === 'function' ? ordDict.byLastDigit( lastDig , num ) :
lastDig in ordDict.byLastDigit ? funcOrVal( ordDict.byLastDigit[ lastDig ] , [lastDig , num] ) :
undefined;
if ( typeof ret === 'string' ) { return ret; }
}
if ( 'default' in ordDict ) {
ret = funcOrVal( ordDict['default'] , [ num , lastDig ] );
if ( typeof ret === 'string' ) { return ret; }
}
return '';
},
/**
* Create an alias.
*
* Returns an alias to this I18n instance. It contains the I18n methods documented here, but is also a function. If you call it, it just calls `text()`. This is commonly assigned to "_".
*
* @method alias
* @returns {Function} an alias to `text()` on this instance. You can also access the rest of the translation API through this alias.
*
* @sample Ink_Util_I18n_1_alias.html
*/
alias: function( ) {
var ret = Ink.bind( I18n.prototype.text , this );
ret.ntext = Ink.bind( I18n.prototype.ntext , this );
ret.append = Ink.bind( I18n.prototype.append , this );
ret.ordinal = Ink.bind( I18n.prototype.ordinal , this );
ret.testMode = Ink.bind( I18n.prototype.testMode , this );
return ret;
}
};
/**
* Resets I18n global state (global dictionaries, and default language for instances)
*
* @method reset
* @static
*
**/
I18n.reset = function( ) {
I18n.prototype._gDicts = [ ];
I18n.prototype._gDict = { };
I18n.prototype._gLang = 'pt_PT';
};
I18n.reset( );
/**
* Adds a dictionary to be used in all I18n instances for the corresponding language.
*
* @method appendGlobal
* @static
*
* @param dict {Object} Dictionary to be added
* @param lang {String} Language fo the dictionary being added
*
*/
I18n.appendGlobal = function( dict , lang ) {
if ( lang ) {
if ( !( lang in dict ) ) {
var obj = { };
obj[ lang ] = dict;
dict = obj;
}
if ( lang !== I18n.prototype._gLang ) { I18n.langGlobal( lang ); }
}
I18n.prototype._gDicts.push( dict );
Ink.extendObj( I18n.prototype._gDict , dict[ I18n.prototype._gLang ] );
};
I18n.append = function () {
// [3.1.0] remove this alias
Ink.warn('Ink.Util.I18n.append() was renamed to appendGlobal().');
return I18n.appendGlobal.apply(I18n, [].slice.call(arguments));
};
/**
* Gets or sets the current default language of I18n instances.
*
* @method langGlobal
* @param lang the new language for all I18n instances
*
* @static
*
* @return {String} language code
*/
I18n.langGlobal = function( lang ) {
if ( !arguments.length ) { return I18n.prototype._gLang; }
if ( lang && I18n.prototype._gLang !== lang ) {
I18n.prototype._gLang = lang;
I18n.prototype._gDict = { };
for ( var i = 0, l = I18n.prototype._gDicts.length; i < l; i++ ) {
Ink.extendObj( I18n.prototype._gDict , I18n.prototype._gDicts[ i ][ lang ] || { } );
}
}
};
I18n.lang = function () {
// [3.1.0] remove this alias
Ink.warn('Ink.Util.I18n.lang() was renamed to langGlobal().');
return I18n.langGlobal.apply(I18n, [].slice.call(arguments));
};
return I18n;
});
/**
* JSON Utilities
* @module Ink.Util.Json_1
* @version 1
*/
Ink.createModule('Ink.Util.Json', '1', [], function() {
'use strict';
var function_call = Function.prototype.call;
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
function twoDigits(n) {
var r = '' + n;
if (r.length === 1) {
return '0' + r;
} else {
return r;
}
}
var dateToISOString = Date.prototype.toISOString ?
Ink.bind(function_call, Date.prototype.toISOString) :
function(date) {
// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
return date.getUTCFullYear() +
'-' + twoDigits( date.getUTCMonth() + 1 ) +
'-' + twoDigits( date.getUTCDate() ) +
'T' + twoDigits( date.getUTCHours() ) +
':' + twoDigits( date.getUTCMinutes() ) +
':' + twoDigits( date.getUTCSeconds() ) +
'.' + String( (date.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 ) +
'Z';
};
/**
* Use this class to convert JSON strings to JavaScript objects
* `.parse()` and also to do the opposite operation `.stringify()`.
* Internally, the standard JSON implementation is used if available
* Otherwise, the functions mimic the standard implementation.
*
* Here's how to produce JSON from an existing object:
*
* Ink.requireModules(['Ink.Util.Json_1'], function (Json) {
* var obj = {
* key1: 'value1',
* key2: 'value2',
* keyArray: ['arrayValue1', 'arrayValue2', 'arrayValue3']
* };
* Json.stringify(obj); // The above object as a JSON string
* });
*
* And here is how to parse JSON:
*
* Ink.requireModules(['Ink.Util.Json_1'], function (Json) {
* var source = '{"key": "value", "array": [true, null, false]}';
* Json.parse(source); // The above JSON string as an object
* });
*
* @namespace Ink.Util.Json_1
* @static
*
*/
var InkJson = {
_nativeJSON: window.JSON || null,
_convertToUnicode: false,
// Escape characters so as to embed them in JSON strings
_escape: function (theString) {
var _m = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };
if (/["\\\x00-\x1f]/.test(theString)) {
theString = theString.replace(/([\x00-\x1f\\"])/g, function(a, b) {
var c = _m[b];
if (c) {
return c;
}
c = b.charCodeAt();
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
});
}
return theString;
},
// A character conversion map
_toUnicode: function (theString)
{
if(!this._convertToUnicode) {
return this._escape(theString);
} else {
var unicodeString = '';
var inInt = false;
var theUnicode = false;
var i = 0;
var total = theString.length;
while(i < total) {
inInt = theString.charCodeAt(i);
if( (inInt >= 32 && inInt <= 126) ||
//(inInt >= 48 && inInt <= 57) ||
//(inInt >= 65 && inInt <= 90) ||
//(inInt >= 97 && inInt <= 122) ||
inInt === 8 ||
inInt === 9 ||
inInt === 10 ||
inInt === 12 ||
inInt === 13 ||
inInt === 32 ||
inInt === 34 ||
inInt === 47 ||
inInt === 58 ||
inInt === 92) {
if(inInt === 34 || inInt === 92 || inInt === 47) {
theUnicode = '\\'+theString.charAt(i);
} else if(inInt === 8) {
theUnicode = '\\b';
} else if(inInt === 9) {
theUnicode = '\\t';
} else if(inInt === 10) {
theUnicode = '\\n';
} else if(inInt === 12) {
theUnicode = '\\f';
} else if(inInt === 13) {
theUnicode = '\\r';
} else {
theUnicode = theString.charAt(i);
}
} else {
if(this._convertToUnicode) {
theUnicode = theString.charCodeAt(i).toString(16)+''.toUpperCase();
while (theUnicode.length < 4) {
theUnicode = '0' + theUnicode;
}
theUnicode = '\\u' + theUnicode;
} else {
theUnicode = theString.charAt(i);
}
}
unicodeString += theUnicode;
i++;
}
return unicodeString;
}
},
_stringifyValue: function(param) {
if (typeof param === 'string') {
return '"' + this._toUnicode(param) + '"';
} else if (typeof param === 'number' && (isNaN(param) || !isFinite(param))) { // Unusable numbers go null
return 'null';
} else if (typeof param === 'undefined' || param === null) { // And so does undefined
return 'null';
} else if (typeof param.toJSON === 'function') {
var t = param.toJSON();
if (typeof t === 'string') {
return '"' + this._escape(t) + '"';
} else {
return this._escape(t.toString());
}
} else if (typeof param === 'number' || typeof param === 'boolean') { // These ones' toString methods return valid JSON.
return '' + param;
} else if (typeof param === 'function') {
return 'null'; // match JSON.stringify
} else if (param.constructor === Date) {
return '"' + this._escape(dateToISOString(param)) + '"';
} else if (param.constructor === Array) {
var arrayString = '';
for (var i = 0, len = param.length; i < len; i++) {
if (i > 0) {
arrayString += ',';
}
arrayString += this._stringifyValue(param[i]);
}
return '[' + arrayString + ']';
} else { // Object
var objectString = '';
for (var k in param) {
if ({}.hasOwnProperty.call(param, k)) {
if (objectString !== '') {
objectString += ',';
}
objectString += '"' + this._escape(k) + '": ' + this._stringifyValue(param[k]);
}
}
return '{' + objectString + '}';
}
},
/**
* Serializes a JSON object into a string.
*
* @method stringify
* @param {Object} input Data to be serialized into JSON
* @param {Boolean} convertToUnicode When `true`, converts string contents to unicode \uXXXX
* @return {String} Serialized string
*
* @sample Ink_Util_Json_stringify.html
*/
stringify: function(input, convertToUnicode) {
this._convertToUnicode = !!convertToUnicode;
if(!this._convertToUnicode && this._nativeJSON) {
return this._nativeJSON.stringify(input);
}
return this._stringifyValue(input); // And recurse.
},
/**
* Parses a JSON text through a function
*
* @method parse
* @param text {String} Input string
* @param reviver {Function} Function receiving `(key, value)`, and `this`=(containing object), used to walk objects.
*
* @return {Object} JSON object
*
* @sample Ink_Util_Json_parse.html
*/
/* From https://github.com/douglascrockford/JSON-js/blob/master/json.js */
parse: function (text, reviver) {
/*jshint evil:true*/
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') :
j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
}
};
return InkJson;
});
/**
* String Utilities
* @module Ink.Util.String_1
* @version 1
*/
Ink.createModule('Ink.Util.String', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.String_1
*/
var InkUtilString = {
/**
* List of special chars
*
* @property _chars
* @type {Array}
* @private
* @readOnly
* @static
*/
_chars: ['&','à','á','â','ã','ä','å','æ','ç','è','é',
'ê','ë','ì','í','î','ï','ð','ñ','ò','ó','ô',
'õ','ö','ø','ù','ú','û','ü','ý','þ','ÿ','À',
'Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë',
'Ì','Í','Î','Ï','Ð','Ñ','Ò','Ó','Ô','Õ','Ö',
'Ø','Ù','Ú','Û','Ü','Ý','Þ','€','\"','ß','<',
'>','¢','£','¤','¥','¦','§','¨','©','ª','«',
'¬','\xad','®','¯','°','±','²','³','´','µ','¶',
'·','¸','¹','º','»','¼','½','¾'],
/**
* List of the special characters' html entities
*
* @property _entities
* @type {Array}
* @private
* @readOnly
* @static
*/
_entities: ['amp','agrave','aacute','acirc','atilde','auml','aring',
'aelig','ccedil','egrave','eacute','ecirc','euml','igrave',
'iacute','icirc','iuml','eth','ntilde','ograve','oacute',
'ocirc','otilde','ouml','oslash','ugrave','uacute','ucirc',
'uuml','yacute','thorn','yuml','Agrave','Aacute','Acirc',
'Atilde','Auml','Aring','AElig','Ccedil','Egrave','Eacute',
'Ecirc','Euml','Igrave','Iacute','Icirc','Iuml','ETH','Ntilde',
'Ograve','Oacute','Ocirc','Otilde','Ouml','Oslash','Ugrave',
'Uacute','Ucirc','Uuml','Yacute','THORN','euro','quot','szlig',
'lt','gt','cent','pound','curren','yen','brvbar','sect','uml',
'copy','ordf','laquo','not','shy','reg','macr','deg','plusmn',
'sup2','sup3','acute','micro','para','middot','cedil','sup1',
'ordm','raquo','frac14','frac12','frac34'],
/**
* List of accented chars
*
* @property _accentedChars
* @type {Array}
* @private
* @readOnly
* @static
*/
_accentedChars:['à','á','â','ã','ä','å',
'è','é','ê','ë',
'ì','í','î','ï',
'ò','ó','ô','õ','ö',
'ù','ú','û','ü',
'ç','ñ',
'À','Á','Â','Ã','Ä','Å',
'È','É','Ê','Ë',
'Ì','Í','Î','Ï',
'Ò','Ó','Ô','Õ','Ö',
'Ù','Ú','Û','Ü',
'Ç','Ñ'],
/**
* List of the accented chars (above), but without the accents
*
* @property _accentedRemovedChars
* @type {Array}
* @private
* @readOnly
* @static
*/
_accentedRemovedChars:['a','a','a','a','a','a',
'e','e','e','e',
'i','i','i','i',
'o','o','o','o','o',
'u','u','u','u',
'c','n',
'A','A','A','A','A','A',
'E','E','E','E',
'I','I','I','I',
'O','O','O','O','O',
'U','U','U','U',
'C','N'],
/**
* Object that contains the basic HTML unsafe chars, as keys, and their HTML entities as values
*
* @property _htmlUnsafeChars
* @type {Object}
* @private
* @readOnly
* @static
*/
_htmlUnsafeChars:{'<':'<','>':'>','&':'&','"':'"',"'":'''},
/**
* Capitalizes a word.
* If param as more than one word, it converts first letter of all words that have more than 2 letters
*
* @method ucFirst
* @param {String} string String to capitalize.
* @param {Boolean} [firstWordOnly]=false Flag to capitalize only the first word.
* @return {String} Camel cased string.
* @public
* @static
* @sample Ink_Util_String_ucFirst.html
*/
ucFirst: function(string, firstWordOnly) {
var replacer = firstWordOnly ? /(^|\s)(\w)(\S{2,})/ : /(^|\s)(\w)(\S{2,})/g;
return string ? String(string).replace(replacer, function(_, $1, $2, $3){
return $1 + $2.toUpperCase() + $3.toLowerCase();
}) : string;
},
/**
* Trims whitespace from strings
*
* @method trim
* @param {String} string String to be trimmed
* @return {String} Trimmed string
* @public
* @static
* @sample Ink_Util_String_trim.html
*/
trim: function(string)
{
if (typeof string === 'string') {
return string.replace(/^\s+|\s+$|\n+$/g, '');
}
return string;
},
/**
* Strips HTML tags from strings
*
* @method stripTags
* @param {String} string String to strip tags from.
* @param {String} allowed Comma separated list of allowed tags.
* @return {String} Stripped string
* @public
* @static
* @sample Ink_Util_String_stripTags.html
*/
stripTags: function(string, allowed)
{
if (allowed && typeof allowed === 'string') {
var aAllowed = InkUtilString.trim(allowed).split(',');
var aNewAllowed = [];
var cleanedTag = false;
for(var i=0; i < aAllowed.length; i++) {
if(InkUtilString.trim(aAllowed[i]) !== '') {
cleanedTag = InkUtilString.trim(aAllowed[i].replace(/(<|\>)/g, '').replace(/\s/, ''));
aNewAllowed.push('(<'+cleanedTag+'\\s[^>]+>|<(\\s|\\/)?(\\s|\\/)?'+cleanedTag+'>)');
}
}
var strAllowed = aNewAllowed.join('|');
var reAllowed = new RegExp(strAllowed, "i");
var aFoundTags = string.match(new RegExp("<[^>]*>", "g"));
for(var j=0; j < aFoundTags.length; j++) {
if(!aFoundTags[j].match(reAllowed)) {
string = string.replace((new RegExp(aFoundTags[j], "gm")), '');
}
}
return string;
} else {
return string.replace(/<[^\>]+\>/g, '');
}
},
/**
* Encodes string into HTML entities.
*
* @method htmlEntitiesEncode
* @param {String} string
* @return {String} string encoded
* @public
* @static
* @sample Ink_Util_String_htmlEntitiesEncode.html
*/
htmlEntitiesEncode: function(string)
{
if (string && string.replace) {
var re = false;
for (var i = 0; i < InkUtilString._chars.length; i++) {
re = new RegExp(InkUtilString._chars[i], "gm");
string = string.replace(re, '&' + InkUtilString._entities[i] + ';');
}
}
return string;
},
/**
* Decodes string from HTML entities.
*
* @method htmlEntitiesDecode
* @param {String} string String to be decoded
* @return {String} Decoded string
* @public
* @static
* @sample Ink_Util_String_htmlEntitiesDecode.html
*/
htmlEntitiesDecode: function(string)
{
if (string && string.replace) {
var re = false;
for (var i = 0; i < InkUtilString._entities.length; i++) {
re = new RegExp("&"+InkUtilString._entities[i]+";", "gm");
string = string.replace(re, InkUtilString._chars[i]);
}
string = string.replace(/[^;]+;?/g, function($0){
if ($0.charAt(2) === 'x') {
return String.fromCharCode(parseInt($0.substring(3), 16));
}
else {
return String.fromCharCode(parseInt($0.substring(2), 10));
}
});
}
return string;
},
/**
* Encode a string to UTF-8.
*
* @method utf8Encode
* @param {String} string String to be encoded
* @return {String} string UTF-8 encoded string
* @public
* @static
*/
utf8Encode: function(string) {
/*jshint bitwise:false*/
string = string.replace(/\r\n/g,"\n");
var utfstring = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utfstring += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utfstring += String.fromCharCode((c >> 6) | 192);
utfstring += String.fromCharCode((c & 63) | 128);
}
else {
utfstring += String.fromCharCode((c >> 12) | 224);
utfstring += String.fromCharCode(((c >> 6) & 63) | 128);
utfstring += String.fromCharCode((c & 63) | 128);
}
}
return utfstring;
},
/**
* Truncates a string without breaking words.
*
* @method shortString
* @param {String} str String to truncate
* @param {Number} n Number of chars of the short string
* @return {String}
* @public
* @static
* @sample Ink_Util_String_shortString.html
*/
shortString: function(str,n) {
var words = str.split(' ');
var resultstr = '';
for(var i = 0; i < words.length; i++ ){
if((resultstr + words[i] + ' ').length>=n){
resultstr += '…';
break;
}
resultstr += words[i] + ' ';
}
return resultstr;
},
/**
* Truncates a string, breaking words and adding ... at the end.
*
* @method truncateString
* @param {String} str String to truncate
* @param {Number} length Limit for the returned string, ellipsis included.
* @return {String} Truncated String
* @public
* @static
* @sample Ink_Util_String_truncateString.html
*/
truncateString: function(str, length) {
if(str.length - 1 > length) {
return str.substr(0, length - 1) + "\u2026";
} else {
return str;
}
},
/**
* Decodes a string from UTF-8.
*
* @method utf8Decode
* @param {String} string String to be decoded
* @return {String} Decoded string
* @public
* @static
*/
utf8Decode: function(utfstring) {
/*jshint bitwise:false*/
var string = "";
var i = 0, c = 0, c2 = 0, c3 = 0;
while ( i < utfstring.length ) {
c = utfstring.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utfstring.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utfstring.charCodeAt(i+1);
c3 = utfstring.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
},
/**
* Removes all accented characters from a string.
*
* @method removeAccentedChars
* @param {String} string String to remove accents from
* @return {String} String without accented chars
* @public
* @static
* @sample Ink_Util_String_removeAccentedChars.html
*/
removeAccentedChars: function(string)
{
var newString = string;
var re = false;
for (var i = 0; i < InkUtilString._accentedChars.length; i++) {
re = new RegExp(InkUtilString._accentedChars[i], "gm");
newString = newString.replace(re, '' + InkUtilString._accentedRemovedChars[i] + '');
}
return newString;
},
/**
* Count the number of occurrences of a specific needle in a haystack
*
* @method substrCount
* @param {String} haystack String to search in
* @param {String} needle String to search for
* @return {Number} Number of occurrences
* @public
* @static
* @sample Ink_Util_String_substrCount.html
*/
substrCount: function(haystack,needle)
{
return haystack ? haystack.split(needle).length - 1 : 0;
},
/**
* Eval a JSON - We recommend you Ink.Util.Json
*
* @method evalJSON
* @param {String} strJSON JSON string to eval
* @param {Boolean} sanitize Flag to sanitize input
* @return {Object} JS Object
* @public
* @static
*/
evalJSON: function(strJSON, sanitize) {
/* jshint evil:true */
if( (typeof sanitize === 'undefined' || sanitize === null) || InkUtilString.isJSON(strJSON)) {
try {
if(typeof(JSON) !== "undefined" && typeof(JSON.parse) !== 'undefined'){
return JSON.parse(strJSON);
}
return eval('('+strJSON+')');
} catch(e) {
throw new Error('ERROR: Bad JSON string...');
}
}
},
/**
* Checks if a string is a valid JSON object (string encoded)
*
* @method isJSON
* @param {String} str String to check
* @return {Boolean}
* @public
* @static
*/
isJSON: function(str)
{
str = str.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
},
/**
* Escapes unsafe html chars as HTML entities
*
* @method htmlEscapeUnsafe
* @param {String} str String to escape
* @return {String} Escaped string
* @public
* @static
* @sample Ink_Util_String_htmlEscapeUnsafe.html
*/
htmlEscapeUnsafe: function(str){
var chars = InkUtilString._htmlUnsafeChars;
return str !== null ? String(str).replace(/[<>&'"]/g,function(c){return chars[c];}) : str;
},
/**
* Normalizes whitespace in string.
* String is trimmed and sequences of whitespaces are collapsed.
*
* @method normalizeWhitespace
* @param {String} str String to normalize
* @return {String} Normalized string
* @public
* @static
* @sample Ink_Util_String_normalizeWhitespace.html
*/
normalizeWhitespace: function(str){
return str !== null ? InkUtilString.trim(String(str).replace(/\s+/g,' ')) : str;
},
/**
* Converts string to unicode.
*
* @method toUnicode
* @param {String} str String to convert
* @return {String} Unicoded String
* @public
* @static
* @sample Ink_Util_String_toUnicode.html
*/
toUnicode: function(str) {
if (typeof str === 'string') {
var unicodeString = '';
var inInt = false;
var theUnicode = false;
var total = str.length;
var i=0;
while(i < total)
{
inInt = str.charCodeAt(i);
if( (inInt >= 32 && inInt <= 126) ||
inInt === 8 ||
inInt === 9 ||
inInt === 10 ||
inInt === 12 ||
inInt === 13 ||
inInt === 32 ||
inInt === 34 ||
inInt === 47 ||
inInt === 58 ||
inInt === 92) {
/*
if(inInt == 34 || inInt == 92 || inInt == 47) {
theUnicode = '\\'+str.charAt(i);
} else {
}
*/
if(inInt === 8) {
theUnicode = '\\b';
} else if(inInt === 9) {
theUnicode = '\\t';
} else if(inInt === 10) {
theUnicode = '\\n';
} else if(inInt === 12) {
theUnicode = '\\f';
} else if(inInt === 13) {
theUnicode = '\\r';
} else {
theUnicode = str.charAt(i);
}
} else {
theUnicode = str.charCodeAt(i).toString(16)+''.toUpperCase();
while (theUnicode.length < 4) {
theUnicode = '0' + theUnicode;
}
theUnicode = '\\u' + theUnicode;
}
unicodeString += theUnicode;
i++;
}
return unicodeString;
}
},
/**
* Escapes a unicode character.
*
* @method escape
* @param {String} c Character to escape
* @return {String} Escaped character. Returns \xXX if hex smaller than 0x100, otherwise \uXXXX
* @public
* @static
* @sample Ink_Util_String_escape.html
*/
escape: function(c) {
var hex = (c).charCodeAt(0).toString(16).split('');
if (hex.length < 3) {
while (hex.length < 2) { hex.unshift('0'); }
hex.unshift('x');
}
else {
while (hex.length < 4) { hex.unshift('0'); }
hex.unshift('u');
}
hex.unshift('\\');
return hex.join('');
},
/**
* Unescapes a unicode character escape sequence
*
* @method unescape
* @param {String} es Escape sequence
* @return {String} String un-unicoded
* @public
* @static
* @sample Ink_Util_String_unescape.html
*/
unescape: function(es) {
var idx = es.lastIndexOf('0');
idx = idx === -1 ? 2 : Math.min(idx, 2);
//console.log(idx);
var hexNum = es.substring(idx);
//console.log(hexNum);
var num = parseInt(hexNum, 16);
return String.fromCharCode(num);
},
/**
* Escapes a string to unicode characters
*
* @method escapeText
* @param {String} txt
* @param {Array} [whiteList] Whitelist of characters
* @return {String} String escaped to Unicode
* @public
* @static
* @sample Ink_Util_String_escapeText.html
*/
escapeText: function(txt, whiteList) {
if (whiteList === undefined) {
whiteList = ['[', ']', '\'', ','];
}
var txt2 = [];
var c, C;
for (var i = 0, f = txt.length; i < f; ++i) {
c = txt[i];
C = c.charCodeAt(0);
if (C < 32 || C > 126 && whiteList.indexOf(c) === -1) {
c = InkUtilString.escape(c);
}
txt2.push(c);
}
return txt2.join('');
},
/**
* Regex to check escaped strings
*
* @property escapedCharRegex
* @type {Regex}
* @public
* @readOnly
* @static
*/
escapedCharRegex: /(\\x[0-9a-fA-F]{2})|(\\u[0-9a-fA-F]{4})/g,
/**
* Unescapes a string
*
* @method unescapeText
* @param {String} txt
* @return {String} Unescaped string
* @public
* @static
* @sample Ink_Util_String_unescapeText.html
*/
unescapeText: function(txt) {
/*jshint boss:true */
var m;
while (m = InkUtilString.escapedCharRegex.exec(txt)) {
m = m[0];
txt = txt.replace(m, InkUtilString.unescape(m));
InkUtilString.escapedCharRegex.lastIndex = 0;
}
return txt;
},
/**
* Compares two strings.
*
* @method strcmp
* @param {String} str1 First String
* @param {String} str2 Second String
* @return {Number}
* @public
* @static
* @sample Ink_Util_String_strcmp.html
*/
strcmp: function(str1, str2) {
return ((str1 === str2) ? 0 : ((str1 > str2) ? 1 : -1));
},
/**
* Splits a string into smaller chunks
*
* @method packetize
* @param {String} str String to divide
* @param {Number} maxLen Maximum chunk size (in characters)
* @return {Array} Chunks of the original string
* @public
* @static
* @sample Ink_Util_String_packetize.html
*/
packetize: function(str, maxLen) {
var len = str.length;
var parts = new Array( Math.ceil(len / maxLen) );
var chars = str.split('');
var sz, i = 0;
while (len) {
sz = Math.min(maxLen, len);
parts[i++] = chars.splice(0, sz).join('');
len -= sz;
}
return parts;
}
};
return InkUtilString;
});
/**
* URL Utilities
* @module Ink.Util.Url_1
* @version 1
*/
Ink.createModule('Ink.Util.Url', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.Url_1
*/
var Url = {
/**
* Auxiliary string for encoding
*
* @property _keyStr
* @type {String}
* @readOnly
* @private
*/
_keyStr : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
/**
* Gets URL of current page
*
* @method getUrl
* @return Current URL
* @public
* @static
* @sample Ink_Util_Url_getUrl.html
*/
getUrl: function()
{
return window.location.href;
},
/**
* Generates an URL string.
*
* @method genQueryString
* @param {String} uri Base URL
* @param {Object} params Object to transform to query string
* @return {String} URI with query string set
* @public
* @static
* @sample Ink_Util_Url_genQueryString.html
*/
genQueryString: function(uri, params) {
var hasQuestionMark = uri.indexOf('?') !== -1;
var sep, pKey, pValue, parts = [uri];
for (pKey in params) {
if (params.hasOwnProperty(pKey)) {
if (!hasQuestionMark) {
sep = '?';
hasQuestionMark = true;
} else {
sep = '&';
}
pValue = params[pKey];
if (typeof pValue !== 'number' && !pValue) {
pValue = '';
}
parts = parts.concat([sep, encodeURIComponent(pKey), '=', encodeURIComponent(pValue)]);
}
}
return parts.join('');
},
/**
* Gets an object from an URL encoded string.
*
* @method getQueryString
* @param {String} [str] URL String. When not specified it uses the current URL.
* @return {Object} Key-Value pair object
* @public
* @static
* @sample Ink_Util_Url_getQueryString.html
*/
getQueryString: function(str)
{
var url;
if(str && typeof(str) !== 'undefined') {
url = str;
} else {
url = this.getUrl();
}
var aParams = {};
if(url.match(/\?(.+)/i)) {
var queryStr = url.replace(/^(.*)\?([^\#]+)(\#(.*))?/g, "$2");
if(queryStr.length > 0) {
var aQueryStr = queryStr.split(/[;&]/);
for(var i=0; i < aQueryStr.length; i++) {
var pairVar = aQueryStr[i].split('=');
aParams[decodeURIComponent(pairVar[0])] = (typeof(pairVar[1]) !== 'undefined' && pairVar[1]) ? decodeURIComponent(pairVar[1]) : false;
}
}
}
return aParams;
},
/**
* Gets the URL hash value
*
* @method getAnchor
* @param {String} [str] URL String. Defaults to current page URL.
* @return {String|Boolean} Hash in the URL. If there's no hash, returns false.
* @public
* @static
* @sample Ink_Util_Url_getAnchor.html
*/
getAnchor: function(str)
{
var url;
if(str && typeof(str) !== 'undefined') {
url = str;
} else {
url = this.getUrl();
}
var anchor = false;
if(url.match(/#(.+)/)) {
anchor = url.replace(/([^#]+)#(.*)/, "$2");
}
return anchor;
},
/**
* Gets the anchor string of an URL
*
* @method getAnchorString
* @param {String} [string] URL to parse. Defaults to current URL.
* @return {Object} Key-value pair object of the URL's hashtag 'variables'
* @public
* @static
* @sample Ink_Util_Url_getAnchorString.html
*/
getAnchorString: function(string)
{
var url;
if(string && typeof(string) !== 'undefined') {
url = string;
} else {
url = this.getUrl();
}
var aParams = {};
if(url.match(/#(.+)/i)) {
var anchorStr = url.replace(/^([^#]+)#(.*)?/g, "$2");
if(anchorStr.length > 0) {
var aAnchorStr = anchorStr.split(/[;&]/);
for(var i=0; i < aAnchorStr.length; i++) {
var pairVar = aAnchorStr[i].split('=');
aParams[decodeURIComponent(pairVar[0])] = (typeof(pairVar[1]) !== 'undefined' && pairVar[1]) ? decodeURIComponent(pairVar[1]) : false;
}
}
}
return aParams;
},
/**
* Parses URL string into URL parts
*
* @method parseUrl
* @param {String} url URL to be parsed
* @return {Object} Parsed URL as a key-value object.
* @public
* @static
* @sample Ink_Util_Url_parseUrl.html
*/
parseUrl: function(url) {
var aURL = {};
if(url && typeof url === 'string') {
if(url.match(/^([^:]+):\/\//i)) {
var re = /^([^:]+):\/\/([^\/]*)\/?([^\?#]*)\??([^#]*)#?(.*)/i;
if(url.match(re)) {
aURL.scheme = url.replace(re, "$1");
aURL.host = url.replace(re, "$2");
aURL.path = '/'+url.replace(re, "$3");
aURL.query = url.replace(re, "$4") || false;
aURL.fragment = url.replace(re, "$5") || false;
}
} else {
var re1 = new RegExp("^([^\\?]+)\\?([^#]+)#(.*)", "i");
var re2 = new RegExp("^([^\\?]+)\\?([^#]+)#?", "i");
var re3 = new RegExp("^([^\\?]+)\\??", "i");
if(url.match(re1)) {
aURL.scheme = false;
aURL.host = false;
aURL.path = url.replace(re1, "$1");
aURL.query = url.replace(re1, "$2");
aURL.fragment = url.replace(re1, "$3");
} else if(url.match(re2)) {
aURL.scheme = false;
aURL.host = false;
aURL.path = url.replace(re2, "$1");
aURL.query = url.replace(re2, "$2");
aURL.fragment = false;
} else if(url.match(re3)) {
aURL.scheme = false;
aURL.host = false;
aURL.path = url.replace(re3, "$1");
aURL.query = false;
aURL.fragment = false;
}
}
if(aURL.host) {
var regPort = /^(.*?)\\:(\\d+)$/i;
// check for port
if(aURL.host.match(regPort)) {
var tmpHost1 = aURL.host;
aURL.host = tmpHost1.replace(regPort, "$1");
aURL.port = tmpHost1.replace(regPort, "$2");
} else {
aURL.port = false;
}
// check for user and pass
if(aURL.host.match(/@/i)) {
var tmpHost2 = aURL.host;
aURL.host = tmpHost2.split('@')[1];
var tmpUserPass = tmpHost2.split('@')[0];
if(tmpUserPass.match(/\:/)) {
aURL.user = tmpUserPass.split(':')[0];
aURL.pass = tmpUserPass.split(':')[1];
} else {
aURL.user = tmpUserPass;
aURL.pass = false;
}
}
}
}
return aURL;
},
/**
* Formats an URL object into an URL string.
*
* @method format
* @param urlObj Window.location, a.href, or parseUrl object to format
* @return {String} Full URL.
*/
format: function (urlObj) {
var protocol = '';
var host = '';
var path = '';
var frag = '';
var query = '';
if (typeof urlObj.protocol === 'string') {
protocol = urlObj.protocol + '//'; // here it comes with the colon
} else if (typeof urlObj.scheme === 'string') {
protocol = urlObj.scheme + '://';
}
host = urlObj.host || urlObj.hostname || '';
path = urlObj.path || '';
if (typeof urlObj.query === 'string') {
query = urlObj.query;
} else if (typeof urlObj.search === 'string') {
query = urlObj.search.replace(/^\?/, '');
}
if (typeof urlObj.fragment === 'string') {
frag = urlObj.fragment;
} else if (typeof urlObj.hash === 'string') {
frag = urlObj.hash.replace(/#$/, '');
}
return [
protocol,
host,
path,
query && '?' + query,
frag && '#' + frag
].join('');
},
/**
* Gets the last loaded script element
*
* @method currentScriptElement
* @param {String} [match] String to match against the script src attribute
* @return {DOMElement|Boolean} Returns the `script` DOM Element or false if unable to find it.
* @public
* @static
* @sample Ink_Util_Url_currentScriptElement.html
*/
currentScriptElement: function(match)
{
var aScripts = document.getElementsByTagName('script');
if(typeof(match) === 'undefined') {
if(aScripts.length > 0) {
return aScripts[(aScripts.length - 1)];
} else {
return false;
}
} else {
var curScript = false;
var re = new RegExp(""+match+"", "i");
for(var i=0, total = aScripts.length; i < total; i++) {
curScript = aScripts[i];
if(re.test(curScript.src)) {
return curScript;
}
}
return false;
}
},
/*
base64Encode: function(string)
{
/**
* --function {String} ?
* --Convert a string to BASE 64
* @param {String} string - string to convert
* @return base64 encoded string
*
*
if(!SAPO.Utility.String || typeof(SAPO.Utility.String) === 'undefined') {
throw "SAPO.Utility.Url.base64Encode depends of SAPO.Utility.String, which has not been referred.";
}
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
var input = SAPO.Utility.String.utf8Encode(string);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
base64Decode: function(string)
{
* --function {String} ?
* Decode a BASE 64 encoded string
* --param {String} string base64 encoded string
* --return string decoded
if(!SAPO.Utility.String || typeof(SAPO.Utility.String) === 'undefined') {
throw "SAPO.Utility.Url.base64Decode depends of SAPO.Utility.String, which has not been referred.";
}
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
var input = string.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
output = SAPO.Utility.String.utf8Decode(output);
return output;
},
*/
/**
* Debug function ?
*
* @method _debug
* @private
* @static
*/
_debug: function() {}
};
return Url;
});
/**
* Validation Utilities
* @module Ink.Util.Validator_1
* @version 1
*/
Ink.createModule('Ink.Util.Validator', '1', [], function() {
'use strict';
/**
* @namespace Ink.Util.Validator_1
*/
var Validator = {
/**
* List of country codes avaible for the isPhone method
*
* @property _countryCodes
* @type {Array}
* @private
* @static
* @readOnly
*/
_countryCodes : [
'AO',
'CV',
'MZ',
'PT'
],
/**
* International number for portugal
*
* @property _internacionalPT
* @type {Number}
* @private
* @static
* @readOnly
*
*/
_internacionalPT: 351,
/**
* List of all portuguese number prefixes
*
* @property _indicativosPT
* @type {Object}
* @private
* @static
* @readOnly
*
*/
_indicativosPT: {
21: 'lisboa',
22: 'porto',
231: 'mealhada',
232: 'viseu',
233: 'figueira da foz',
234: 'aveiro',
235: 'arganil',
236: 'pombal',
238: 'seia',
239: 'coimbra',
241: 'abrantes',
242: 'ponte de sôr',
243: 'santarém',
244: 'leiria',
245: 'portalegre',
249: 'torres novas',
251: 'valença',
252: 'vila nova de famalicão',
253: 'braga',
254: 'peso da régua',
255: 'penafiel',
256: 'são joão da madeira',
258: 'viana do castelo',
259: 'vila real',
261: 'torres vedras',
262: 'caldas da raínha',
263: 'vila franca de xira',
265: 'setúbal',
266: 'évora',
268: 'estremoz',
269: 'santiago do cacém',
271: 'guarda',
272: 'castelo branco',
273: 'bragança',
274: 'proença-a-nova',
275: 'covilhã',
276: 'chaves',
277: 'idanha-a-nova',
278: 'mirandela',
279: 'moncorvo',
281: 'tavira',
282: 'portimão',
283: 'odemira',
284: 'beja',
285: 'moura',
286: 'castro verde',
289: 'faro',
291: 'funchal, porto santo',
292: 'corvo, faial, flores, horta, pico',
295: 'angra do heroísmo, graciosa, são jorge, terceira',
296: 'ponta delgada, são miguel, santa maria',
91 : 'rede móvel 91 (Vodafone / Yorn)',
93 : 'rede móvel 93 (Optimus)',
96 : 'rede móvel 96 (TMN)',
92 : 'rede móvel 92 (TODOS)',
//925 : 'rede móvel 925 (TMN 925)',
//926 : 'rede móvel 926 (TMN 926)',
//927 : 'rede móvel 927 (TMN 927)',
//922 : 'rede móvel 922 (Phone-ix)',
707: 'número único',
760: 'número único',
800: 'número grátis',
808: 'chamada local',
30: 'voip'
},
/**
* International number for Cabo Verde
*
* @property _internacionalCV
* @type {Number}
* @private
* @static
* @readOnly
*/
_internacionalCV: 238,
/**
* List of all Cabo Verde number prefixes
*
* @property _indicativosCV
* @type {Object}
* @private
* @static
* @readOnly
*/
_indicativosCV: {
2: 'fixo',
91: 'móvel 91',
95: 'móvel 95',
97: 'móvel 97',
98: 'móvel 98',
99: 'móvel 99'
},
/**
* International number for Angola
*
* @property _internacionalAO
* @type {Number}
* @private
* @static
* @readOnly
*/
_internacionalAO: 244,
/**
* List of all Angola number prefixes
*
* @property _indicativosAO
* @type {Object}
* @private
* @static
* @readOnly
*/
_indicativosAO: {
2: 'fixo',
91: 'móvel 91',
92: 'móvel 92'
},
/**
* International number for Mozambique
*
* @property _internacionalMZ
* @type {Number}
* @private
* @static
* @readOnly
*/
_internacionalMZ: 258,
/**
* List of all Mozambique number prefixes
*
* @property _indicativosMZ
* @type {Object}
* @private
* @static
* @readOnly
*/
_indicativosMZ: {
2: 'fixo',
82: 'móvel 82',
84: 'móvel 84'
},
/**
* International number for Timor
*
* @property _internacionalTL
* @type {Number}
* @private
* @static
* @readOnly
*/
_internacionalTL: 670,
/**
* List of all Timor number prefixes
*
* @property _indicativosTL
* @type {Object}
* @private
* @static
* @readOnly
*/
_indicativosTL: {
3: 'fixo',
7: 'móvel 7'
},
/**
* Regular expression groups for several groups of characters
*
* http://en.wikipedia.org/wiki/C0_Controls_and_Basic_Latin
* http://en.wikipedia.org/wiki/Plane_%28Unicode%29#Basic_Multilingual_Plane
* http://en.wikipedia.org/wiki/ISO_8859-1
*
* @property _characterGroups
* @type {Object}
* @private
* @static
* @readOnly
*/
_characterGroups: {
numbers: ['0-9'],
asciiAlpha: ['a-zA-Z'],
latin1Alpha: ['a-zA-Z', '\u00C0-\u00FF'],
unicodeAlpha: ['a-zA-Z', '\u00C0-\u00FF', '\u0100-\u1FFF', '\u2C00-\uD7FF'],
/* whitespace characters */
space: [' '],
dash: ['-'],
underscore: ['_'],
nicknamePunctuation: ['_.-'],
singleLineWhitespace: ['\t '],
newline: ['\n'],
whitespace: ['\t\n\u000B\f\r\u00A0 '],
asciiPunctuation: ['\u0021-\u002F', '\u003A-\u0040', '\u005B-\u0060', '\u007B-\u007E'],
latin1Punctuation: ['\u0021-\u002F', '\u003A-\u0040', '\u005B-\u0060', '\u007B-\u007E', '\u00A1-\u00BF', '\u00D7', '\u00F7'],
unicodePunctuation: ['\u0021-\u002F', '\u003A-\u0040', '\u005B-\u0060', '\u007B-\u007E', '\u00A1-\u00BF', '\u00D7', '\u00F7', '\u2000-\u206F', '\u2E00-\u2E7F', '\u3000-\u303F']
},
/**
* Creates a regular expression for several character groups.
*
* @method createRegExp
*
* @param Groups* {Object}
* Groups to build regular expressions for. Possible keys are:
*
* - **numbers**: 0-9
* - **asciiAlpha**: a-z, A-Z
* - **latin1Alpha**: asciiAlpha, plus printable characters in latin-1
* - **unicodeAlpha**: unicode alphanumeric characters.
* - **space**: ' ', the space character.
* - **dash**: dash character.
* - **underscore**: underscore character.
* - **nicknamePunctuation**: dash, dot, underscore
* - **singleLineWhitespace**: space and tab (whitespace which only spans one line).
* - **newline**: newline character ('\n')
* - **whitespace**: whitespace characters in the ASCII character set.
* - **asciiPunctuation**: punctuation characters in the ASCII character set.
* - **latin1Punctuation**: punctuation characters in latin-1.
* - **unicodePunctuation**: punctuation characters in unicode.
*
*/
createRegExp: function (groups) {
var re = '^[';
for (var key in groups) if (groups.hasOwnProperty(key)) {
if (!(key in Validator._characterGroups)) {
throw new Error('group ' + key + ' is not a valid character group');
} else if (groups[key]) {
re += Validator._characterGroups[key].join('');
}
}
if (re === '^[') {
// No changes
return new RegExp('$^'); // match nothing
}
return new RegExp(re + ']*?$');
},
/**
* Checks if a field has the required groups.
*
* @method checkCharacterGroups
* @param {String} s The validation string
* @param {Object} [groups]={} What groups are included. See createRegexp
* @sample Ink_Util_Validator_checkCharacterGroups.html
*/
checkCharacterGroups: function (s, groups) {
return Validator.createRegExp(groups).test(s);
},
/**
* Checks if a field contains unicode printable characters.
*
* @method unicode
* @param {String} s The validation string
* @param {Object} [options]={} Optional configuration object. See createRegexp
*/
unicode: function (s, options) {
return Validator.checkCharacterGroups(s, Ink.extendObj({
unicodeAlpha: true}, options));
},
/**
* Checks if a field only contains latin-1 alphanumeric characters.
* Takes options for allowing singleline whitespace, cross-line whitespace and punctuation.
*
* @method latin1
*
* @param {String} s The validation string
* @param {Object} [options]={} Optional configuration object. See createRegexp
* @sample Ink_Util_Validator_latin1.html
*/
latin1: function (s, options) {
return Validator.checkCharacterGroups(s, Ink.extendObj({
latin1Alpha: true}, options));
},
/**
* Checks if a field only contains only ASCII alphanumeric characters.
* Takes options for allowing singleline whitespace, cross-line whitespace and punctuation.
*
* @method ascii
*
* @param {String} s The validation string
* @param {Object} [options]={} Optional configuration object. See createRegexp
* @sample Ink_Util_Validator_ascii.html
*/
ascii: function (s, options) {
return Validator.checkCharacterGroups(s, Ink.extendObj({
asciiAlpha: true}, options));
},
/**
* Checks if a number is a valid
*
* @method number
* @param {String} numb The number
* @param {Object} [options] Further options
* @param [options.decimalSep]='.' Allow decimal separator.
* @param [options.thousandSep]="," Strip this character from the number.
* @param [options.negative]=false Allow negative numbers.
* @param [options.decimalPlaces]=null Maximum number of decimal places. Use `0` for an integer number.
* @param [options.max]=null Maximum number
* @param [options.min]=null Minimum number
* @param [options.returnNumber]=false When this option is true, return the number itself when the value is valid.
* @sample Ink_Util_Validator_number.html
*/
number: function (numb, inOptions) {
numb = numb + '';
var options = Ink.extendObj({
decimalSep: '.',
thousandSep: '',
negative: true,
decimalPlaces: null,
maxDigits: null,
max: null,
min: null,
returnNumber: false
}, inOptions || {});
// smart recursion thing sets up aliases for options.
if (options.thousandSep) {
numb = numb.replace(new RegExp('\\' + options.thousandSep, 'g'), '');
options.thousandSep = '';
return Validator.number(numb, options);
}
if (options.negative === false) {
options.min = 0;
options.negative = true;
return Validator.number(numb, options);
}
if (options.decimalSep !== '.') {
numb = numb.replace(new RegExp('\\' + options.decimalSep, 'g'), '.');
}
if (!/^(-)?(\d+)?(\.\d+)?$/.test(numb) || numb === '') {
return false; // forbidden character found
}
var split;
if (options.decimalSep && numb.indexOf(options.decimalSep) !== -1) {
split = numb.split(options.decimalSep);
if (options.decimalPlaces !== null &&
split[1].length > options.decimalPlaces) {
return false;
}
} else {
split = ['' + numb, ''];
}
if (options.maxDigits!== null) {
if (split[0].replace(/-/g, '').length > options.maxDigits) {
return split;
}
}
// Now look at the actual float
var ret = parseFloat(numb);
if (options.maxExcl !== null && ret >= options.maxExcl ||
options.minExcl !== null && ret <= options.minExcl) {
return false;
}
if (options.max !== null && ret > options.max ||
options.min !== null && ret < options.min) {
return false;
}
if (options.returnNumber) {
return ret;
} else {
return true;
}
},
/**
* Checks if a year is Leap "Bissexto"
*
* @method _isLeapYear
* @param {Number} year Year to be checked
* @return {Boolean} True if it is a leap year.
* @private
* @static
* @example
* Ink.requireModules(['Ink.Util.Validator_1'], function( InkValidator ){
* console.log( InkValidator._isLeapYear( 2004 ) ); // Result: true
* console.log( InkValidator._isLeapYear( 2006 ) ); // Result: false
* });
*/
_isLeapYear: function(year){
var yearRegExp = /^\d{4}$/;
if(yearRegExp.test(year)){
return ((year%4) ? false: ((year%100) ? true : ((year%400)? false : true)) );
}
return false;
},
/**
* Object with the date formats available for validation
*
* @property _dateParsers
* @type {Object}
* @private
* @static
* @readOnly
*/
_dateParsers: {
'yyyy-mm-dd': {day:5, month:3, year:1, sep: '-', parser: /^(\d{4})(\-)(\d{1,2})(\-)(\d{1,2})$/},
'yyyy/mm/dd': {day:5, month:3, year:1, sep: '/', parser: /^(\d{4})(\/)(\d{1,2})(\/)(\d{1,2})$/},
'yy-mm-dd': {day:5, month:3, year:1, sep: '-', parser: /^(\d{2})(\-)(\d{1,2})(\-)(\d{1,2})$/},
'yy/mm/dd': {day:5, month:3, year:1, sep: '/', parser: /^(\d{2})(\/)(\d{1,2})(\/)(\d{1,2})$/},
'dd-mm-yyyy': {day:1, month:3, year:5, sep: '-', parser: /^(\d{1,2})(\-)(\d{1,2})(\-)(\d{4})$/},
'dd/mm/yyyy': {day:1, month:3, year:5, sep: '/', parser: /^(\d{1,2})(\/)(\d{1,2})(\/)(\d{4})$/},
'dd-mm-yy': {day:1, month:3, year:5, sep: '-', parser: /^(\d{1,2})(\-)(\d{1,2})(\-)(\d{2})$/},
'dd/mm/yy': {day:1, month:3, year:5, sep: '/', parser: /^(\d{1,2})(\/)(\d{1,2})(\/)(\d{2})$/}
},
/**
* Gets the number of days in a given month of a given year
*
* @method _daysInMonth
* @param {Number} _m Month (1 to 12)
* @param {Number} _y Year
* @return {Number} Returns the number of days in a given month of a given year
* @private
* @static
* @example
* Ink.requireModules(['Ink.Util.Validator_1'], function( InkValidator ){
* console.log( InkValidator._daysInMonth( 2, 2004 ) ); // Result: 29
* console.log( InkValidator._daysInMonth( 2, 2006 ) ); // Result: 28
* });
*/
_daysInMonth: function(_m,_y){
var nDays=0;
_m = parseInt(_m, 10);
_y = parseInt(_y, 10);
if(_m===1 || _m===3 || _m===5 || _m===7 || _m===8 || _m===10 || _m===12) {
nDays= 31;
} else if ( _m===4 || _m===6 || _m===9 || _m===11) {
nDays = 30;
} else if (_m===2) {
if((_y%400===0) || (_y%4===0 && _y%100!==0)) {
nDays = 29;
} else {
nDays = 28;
}
}
return nDays;
},
/**
* Checks if a date is valid
*
* @method _isValidDate
* @param {Number} year
* @param {Number} month
* @param {Number} day
* @return {Boolean} True if valid
* @private
* @static
* @example
* Ink.requireModules(['Ink.Util.Validator_1'], function( InkValidator ){
* console.log( InkValidator._isValidDate( 2004, 2, 29 ) ); // Result: true
* console.log( InkValidator._isValidDate( 2006, 2, 29 ) ); // Result: false
* });
*/
_isValidDate: function(year, month, day){
var yearRegExp = /^\d{4}$/;
var validOneOrTwo = /^\d{1,2}$/;
if(yearRegExp.test(year) && validOneOrTwo.test(month) && validOneOrTwo.test(day)){
if(month>=1 && month<=12 && day>=1 && this._daysInMonth(month,year)>=day){
return true;
}
}
return false;
},
/**
* Checks if an email is valid
*
* @method mail
* @param {String} email
* @return {Boolean} True if it's valid
* @public
* @static
* @sample Ink_Util_Validator_mail.html
*/
email: function(email)
{
var emailValido = new RegExp("^[_a-z0-9-]+((\\.|\\+)[_a-z0-9-]+)*@([\\w]*-?[\\w]*\\.)+[a-z]{2,4}$", "i");
if(!emailValido.test(email)) {
return false;
} else {
return true;
}
},
/**
* Deprecated. Alias for email(). Use it instead.
*
* @method mail
* @public
* @static
* @private
*/
mail: function (mail) { return Validator.email(mail); },
/**
* Checks if an url is valid
*
* @method url
* @param {String} url URL to be checked
* @param {Boolean} [full] If true, validates a full URL (one that should start with 'http')
* @return {Boolean} True if valid
* @public
* @static
* @sample Ink_Util_Validator_url.html
*/
url: function(url, full)
{
if(typeof full === "undefined" || full === false) {
var reHTTP = new RegExp("(^(http\\:\\/\\/|https\\:\\/\\/)(.+))", "i");
if(reHTTP.test(url) === false) {
url = 'http://'+url;
}
}
var reUrl = new RegExp("^(http:\\/\\/|https:\\/\\/)([\\w]*(-?[\\w]*)*\\.)+[a-z]{2,4}", "i");
if(reUrl.test(url) === false) {
return false;
} else {
return true;
}
},
/**
* Checks if a phone is valid in Portugal
*
* @method isPTPhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid Portuguese Phone
* @public
* @static
* @sample Ink_Util_Validator_isPTPhone.html
*/
isPTPhone: function(phone)
{
phone = phone.toString();
var aInd = [];
for(var i in this._indicativosPT) {
if(typeof(this._indicativosPT[i]) === 'string') {
aInd.push(i);
}
}
var strInd = aInd.join('|');
var re351 = /^(00351|\+351)/;
if(re351.test(phone)) {
phone = phone.replace(re351, "");
}
var reSpecialChars = /(\s|\-|\.)+/g;
phone = phone.replace(reSpecialChars, '');
//var reInt = new RegExp("\\d", "i");
var reInt = /[\d]{9}/i;
if(phone.length === 9 && reInt.test(phone)) {
var reValid = new RegExp("^("+strInd+")");
if(reValid.test(phone)) {
return true;
}
}
return false;
},
/**
* Alias function for isPTPhone
*
* @method isPortuguesePhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid Portuguese Phone
* @public
* @static
*/
isPortuguesePhone: function(phone)
{
return this.isPTPhone(phone);
},
/**
* Checks if a phone is valid in Cabo Verde
*
* @method isCVPhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid Cape Verdean Phone
* @public
* @static
* @sample Ink_Util_Validator_isCVPhone.html
*/
isCVPhone: function(phone)
{
phone = phone.toString();
var aInd = [];
for(var i in this._indicativosCV) {
if(typeof(this._indicativosCV[i]) === 'string') {
aInd.push(i);
}
}
var strInd = aInd.join('|');
var re238 = /^(00238|\+238)/;
if(re238.test(phone)) {
phone = phone.replace(re238, "");
}
var reSpecialChars = /(\s|\-|\.)+/g;
phone = phone.replace(reSpecialChars, '');
//var reInt = new RegExp("\\d", "i");
var reInt = /[\d]{7}/i;
if(phone.length === 7 && reInt.test(phone)) {
var reValid = new RegExp("^("+strInd+")");
if(reValid.test(phone)) {
return true;
}
}
return false;
},
/**
* Checks if a phone is valid in Angola
*
* @method isAOPhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid Angolan Phone
* @public
* @static
* @sample Ink_Util_Validator_isAOPhone.html
*/
isAOPhone: function(phone)
{
phone = phone.toString();
var aInd = [];
for(var i in this._indicativosAO) {
if(typeof(this._indicativosAO[i]) === 'string') {
aInd.push(i);
}
}
var strInd = aInd.join('|');
var re244 = /^(00244|\+244)/;
if(re244.test(phone)) {
phone = phone.replace(re244, "");
}
var reSpecialChars = /(\s|\-|\.)+/g;
phone = phone.replace(reSpecialChars, '');
//var reInt = new RegExp("\\d", "i");
var reInt = /[\d]{9}/i;
if(phone.length === 9 && reInt.test(phone)) {
var reValid = new RegExp("^("+strInd+")");
if(reValid.test(phone)) {
return true;
}
}
return false;
},
/**
* Checks if a phone is valid in Mozambique
*
* @method isMZPhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid Mozambican Phone
* @public
* @static
* @sample Ink_Util_Validator_isMZPhone.html
*/
isMZPhone: function(phone)
{
phone = phone.toString();
var aInd = [];
for(var i in this._indicativosMZ) {
if(typeof(this._indicativosMZ[i]) === 'string') {
aInd.push(i);
}
}
var strInd = aInd.join('|');
var re258 = /^(00258|\+258)/;
if(re258.test(phone)) {
phone = phone.replace(re258, "");
}
var reSpecialChars = /(\s|\-|\.)+/g;
phone = phone.replace(reSpecialChars, '');
//var reInt = new RegExp("\\d", "i");
var reInt = /[\d]{8,9}/i;
if((phone.length === 9 || phone.length === 8) && reInt.test(phone)) {
var reValid = new RegExp("^("+strInd+")");
if(reValid.test(phone)) {
if(phone.indexOf('2') === 0 && phone.length === 8) {
return true;
} else if(phone.indexOf('8') === 0 && phone.length === 9) {
return true;
}
}
}
return false;
},
/**
* Checks if a phone is valid in Timor
*
* @method isTLPhone
* @param {Number} phone Phone number to be checked
* @return {Boolean} True if it's a valid phone from Timor-Leste
* @public
* @static
* @sample Ink_Util_Validator_isTLPhone.html
*/
isTLPhone: function(phone)
{
phone = phone.toString();
var aInd = [];
for(var i in this._indicativosTL) {
if(typeof(this._indicativosTL[i]) === 'string') {
aInd.push(i);
}
}
var strInd = aInd.join('|');
var re670 = /^(00670|\+670)/;
if(re670.test(phone)) {
phone = phone.replace(re670, "");
}
var reSpecialChars = /(\s|\-|\.)+/g;
phone = phone.replace(reSpecialChars, '');
//var reInt = new RegExp("\\d", "i");
var reInt = /[\d]{7}/i;
if(phone.length === 7 && reInt.test(phone)) {
var reValid = new RegExp("^("+strInd+")");
if(reValid.test(phone)) {
return true;
}
}
return false;
},
/**
* Checks if a number is a phone number.
* This method validates the number in all country codes available the ones set in the second param
*
* @method isPhone
* @param {String} phone Phone number to validate
* @param {String|Array} [countryCode] Country code or array of countries to validate
* @return {Boolean} True if it's a valid phone in any country available
* @public
* @static
* @sample Ink_Util_Validator_isPhone.html
*/
isPhone: function(){
var index;
if(arguments.length===0){
return false;
}
var phone = arguments[0];
if(arguments.length>1){
if(arguments[1].constructor === Array){
var func;
for(index=0; index= 0 && match[i-1] <= 100){
valid = true;
} else {
return false;
}
}
// check 0 to 255 values
if(i===1 || i===3 || i===5 && (typeof match[i+1] === "undefined" || match[i+1] === "")){
if(typeof match[i] !== "undefined" && match[i] >= 0 && match[i] <= 255){
valid = true;
} else {
return false;
}
}
}
}
// hsl range check
if((match = hsl.exec(str)) !== null || (match = hsla.exec(str)) !== null){
i = match.length;
while(i--){
// check percentage values
if(i===3 || i===5){
if(typeof match[i-1] !== "undefined" && typeof match[i] !== "undefined" && match[i] !== "" &&
match[i-1] >= 0 && match[i-1] <= 100){
valid = true;
} else {
return false;
}
}
// check 0 to 360 value
if(i===1){
if(typeof match[i] !== "undefined" && match[i] >= 0 && match[i] <= 360){
valid = true;
} else {
return false;
}
}
}
}
return valid;
},
/**
* Checks if the value is a valid IP.
*
* @method isIP
* @param {String} value Value to be checked
* @param {String} ipType Type of IP to be validated. The values are: ipv4, ipv6. By default is ipv4.
* @return {Boolean} True if the value is a valid IP address. False if not.
* @sample Ink_Util_Validator_isIP.html
*/
isIP: function( value, ipType ){
if( typeof value !== 'string' ){
return false;
}
ipType = (ipType || 'ipv4').toLowerCase();
switch( ipType ){
case 'ipv4':
return (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/).test(value);
case 'ipv6':
return (/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/).test(value);
default:
return false;
}
},
/**
* Credit Card specifications, to be used in the credit card verification.
*
* @property _creditCardSpecs
* @type {Object}
* @private
*/
_creditCardSpecs: {
'default': {
'length': '13,14,15,16,17,18,19',
'prefix': /^.+/,
'luhn': true
},
'american express': {
'length': '15',
'prefix': /^3[47]/,
'luhn' : true
},
'diners club': {
'length': '14,16',
'prefix': /^36|55|30[0-5]/,
'luhn' : true
},
'discover': {
'length': '16',
'prefix': /^6(?:5|011)/,
'luhn' : true
},
'jcb': {
'length': '15,16',
'prefix': /^3|1800|2131/,
'luhn' : true
},
'maestro': {
'length': '16,18',
'prefix': /^50(?:20|38)|6(?:304|759)/,
'luhn' : true
},
'mastercard': {
'length': '16',
'prefix': /^5[1-5]/,
'luhn' : true
},
'visa': {
'length': '13,16',
'prefix': /^4/,
'luhn' : true
}
},
/**
* Luhn function, to be used when validating credit cards
*
*/
_luhn: function (num){
num = parseInt(num,10);
if ( (typeof num !== 'number') && (num % 1 !== 0) ){
// Luhn can only be used on nums!
return false;
}
num = num+'';
// Check num length
var length = num.length;
// Checksum of the card num
var
i, checksum = 0
;
for (i = length - 1; i >= 0; i -= 2)
{
// Add up every 2nd digit, starting from the right
checksum += parseInt(num.substr(i, 1),10);
}
for (i = length - 2; i >= 0; i -= 2)
{
// Add up every 2nd digit doubled, starting from the right
var dbl = parseInt(num.substr(i, 1) * 2,10);
// Subtract 9 from the dbl where value is greater than 10
checksum += (dbl >= 10) ? (dbl - 9) : dbl;
}
// If the checksum is a multiple of 10, the number is valid
return (checksum % 10 === 0);
},
/**
* Checks if a number is of a specific credit card type
* @method isCreditCard
* @param {String} num Number to be validates
* @param {String|Array} creditCardType Credit card type. See _creditCardSpecs for the list of supported values.
* @return {Boolean}
* @sample Ink_Util_Validator_isCreditCard.html
*/
isCreditCard: function(num, creditCardType){
if ( /\d+/.test(num) === false ){
return false;
}
if ( typeof creditCardType === 'undefined' ){
creditCardType = 'default';
}
else if ( creditCardType instanceof Array ){
var i, ccLength = creditCardType.length;
for ( i=0; i < ccLength; i++ ){
// Test each type for validity
if (this.isCreditCard(num, creditCardType[i]) ){
return true;
}
}
return false;
}
// Check card type
creditCardType = creditCardType.toLowerCase();
if ( typeof this._creditCardSpecs[creditCardType] === 'undefined' ){
return false;
}
// Check card number length
var length = num.length+'';
// Validate the card length by the card type
if ( this._creditCardSpecs[creditCardType]['length'].split(",").indexOf(length) === -1 ){
return false;
}
// Check card number prefix
if ( !this._creditCardSpecs[creditCardType]['prefix'].test(num) ){
return false;
}
// No Luhn check required
if (this._creditCardSpecs[creditCardType]['luhn'] === false){
return true;
}
return this._luhn(num);
}
};
return Validator;
});