src/utils.js
/**
* __ _____ _______
* .-----.---.-.| |.--------.-----.-----._| | __|
* |__ --| _ || || | _ | | |__ |
* |_____|___._||__||__|__|__|_____|__|__|_______|_______|
*
* salmonJS v0.5.0
*
* Copyright (C) 2014 Fabio Cicerchia <info@fabiocicerchia.it>
*
* 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.
*/
/**
* Utils Class
*
* A set of utility methods
* (string, array, html, encryption, normalisation, dom).
*
* @class Utils
*/
var Utils = function (crypto) {
/**
* Serialise an object as questring.
*
* @method serialise
* @param {Object} obj The object to be converted.
* @return {String} The querysting based on the input.
*/
this.serialise = function (obj) {
var str = [], p;
if (typeof obj !== 'object') {
return '';
}
for (p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
}
}
return str.join('&');
};
/**
* Hash a string with SHA1.
*
* @method hashString
* @param {String} plainText The string to be converted to hash
* @return {String}
*/
this.sha1 = function (plainText) {
return crypto.createHash('sha1').update(plainText).digest('hex');
};
/**
* Escape a HTML string.
*
* @method htmlEscape
* @param {String} str The HTML to be escaped.
* @return {String}
*/
this.htmlEscape = function (str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
};
/**
* Normalise the data, ordering the array.
*
* @method normaliseData
* @param {String} data The data to be normalised.
* @return {Object}
*/
this.normaliseData = function (data) {
var i, pair, vars, dataContainer = {},
keys = [],
sorted = {};
if (typeof data !== 'string') {
return {};
}
vars = data.replace(/.+\?/, '').split('&');
if (vars.length === 0 || vars[0] === '') {
return {};
}
for (i = 0; i < vars.length; i++) {
pair = vars[i].split('=');
keys.push(decodeURIComponent(pair[0]));
dataContainer[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
keys = keys.sort();
for (i in keys) {
if (keys.hasOwnProperty(i)) {
sorted[keys[i]] = dataContainer[keys[i]];
}
}
return sorted;
};
/**
* Sleep function.
*
* @method sleep
* @param {Integer} millis The number of millisecond to wait for
* @return undefined
*/
this.sleep = function (millis) {
var date = new Date(),
curDate = null;
do {
curDate = new Date();
} while ((curDate - date) < millis);
};
/**
* Fire an event to an object (document, window, ...).
*
* @method fireEventObject
* @return {Object}|undefined
*/
this.fireEventObject = function () {
var obj,
evt,
xPath = arguments[0].xPath,
event = arguments[0].event;
eval('obj = ' + xPath);
if (obj !== undefined) {
evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, false, false, null);
obj.dispatchEvent(evt);
return evt;
}
return undefined;
};
/**
* Fire an event to a DOM element.
*
* @method fireEventDOM
* @return {Object}|undefined
*/
this.fireEventDOM = function () {
var xPath = arguments[0].xPath,
event = arguments[0].event,
element = window.eventContainer.getElementByXpath(xPath),
evt;
if (element !== undefined) {
evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, false, false, null);
element.dispatchEvent(evt);
return evt;
}
return undefined;
};
/**
* Utility function to be used with .filter() to get back only the unique
* elements.
*
* @method onlyUnique
* @param {Mixed} value The value of the current element.
* @param {Integer|String} index The index of the current element.
* @param {Object} self The object itself.
* @return {Boolean}
*/
this.onlyUnique = function (value, index, self) {
return self.indexOf(value) === index && value !== undefined;
};
/**
* Convert an array to a querystring.
*
* @method arrayToQuery
* @param {Object} obj The obj to be converted.
* @param {String} prefix The eventual prefix to be concatenated if array.
* @return {String}
*/
this.arrayToQuery = function (obj, prefix) {
var p, k, v, str = [];
if (typeof obj !== 'object') {
return '';
}
for (p in obj) {
if (obj.hasOwnProperty(p)) {
k = prefix ? prefix + '[' + p + ']' : p;
v = obj[p];
str.push(
typeof v === 'object' ?
this.arrayToQuery(v, k) :
encodeURIComponent(k) + '=' + encodeURIComponent(v)
);
}
}
return str.join('&').replace('%23', '#');
};
/**
* Utility function to normalise an absolute / relative URL.
*
* @method normaliseUrl
* @param {String} url The URL to be normalised.
* @param {String} baseUrl The base URL used to normalise.
* @return {String} The normalised URL.
*/
this.normaliseUrl = function (url, baseUrl) {
var normalised,
qs;
if (url === undefined) {
return undefined;
}
var URI = require('URIjs');
normalised = URI(url);
if (url.indexOf(':') === -1 || url.match(/:[0-9\/]/g)) {
normalised = normalised.absoluteTo(baseUrl);
}
normalised = normalised.normalizePathname();
normalised = normalised.normalizeSearch();
normalised = normalised.toString();
var domain = URI(baseUrl).domain();
if (domain !== URI(normalised).domain()) {
return undefined;
}
if (normalised !== undefined && normalised.indexOf('?') > 0) {
qs = normalised.replace(/.*\?(.+)(#.*)?/, '$1');
normalised = normalised.replace('?' + qs, '?' + this.arrayToQuery(this.normaliseData(qs)));
}
return normalised;
};
/**
* Parse an INI file (from string to object).
*
* @method parseINIString
* @param {String} data The data to be parsed.
* @return {Object}
*/
this.parseINIString = function (data) {
var regex = {
section: /^\s*\[\s*([^\]]*)\s*\]\s*$/,
param: /^\s*([\w\.\-\_\[\]]+)\s*=\s*(.*?)\s*$/,
comment: /^\s*;.*$/
},
value = {},
lines = data.split(/\r\n|\r|\n/),
section = null,
match,
isArray;
lines.forEach(function (line) {
isArray = false;
if (regex.comment.test(line)) {
return;
} else if(regex.param.test(line)) {
match = line.match(regex.param);
if (match[1].substr(match[1].length - 2, 2) === '[]') {
match[1] = match[1].substr(0, match[1].length - 2);
isArray = true;
}
if (match[2][0] === '"' && match[2][match[2].length - 1] === '"') {
match[2] = match[2].substr(1, match[2].length - 2);
}
if (isArray) {
if (section) {
if (typeof value[section][match[1]] === 'undefined') {
value[section][match[1]] = [];
}
value[section][match[1]].push(match[2]);
} else {
if (typeof value[match[1]] === 'undefined') {
value[match[1]] = [];
}
value[match[1]].push(match[2]);
}
} else {
if (section) {
value[section][match[1]] = match[2];
} else {
value[match[1]] = match[2];
}
}
} else if (regex.section.test(line)) {
match = line.match(regex.section);
value[match[1]] = {};
section = match[1];
}
});
return value;
};
/**
* For each for objects.
*
* @method loopEach
* @param {Object} object The object to loop through.
* @param {Function} callback The callback invoked with key and value for each item.
* @return undefined
*/
this.loopEach = function(object, callback) {
for (var item in object) {
if (object.hasOwnProperty(item)) {
callback(item, object[item]);
}
}
};
};
module.exports = Utils;