/** * @version $Id$ * @requires _ Underscore.js */ (function (window) { window._ = window._ || {}; var StringProto = String.prototype; var nativeTrim = StringProto.trim, nativeTrimLeft = StringProto.trimLeft, nativeTrimRight = StringProto.trimRight; var getTrimRegExp = function (chars, left, right) { chars = chars ? '' + chars : ''; chars = (chars) ? '[' + chars.replace(/([-^$.+*?|(){}[\]\/\\])/g, '\\$1') + ']' : '\\s'; var parts = []; if (left) { parts.push('^' + chars + '+'); } if (right) { parts.push(chars + '+$'); } return new RegExp(parts.join('|'), 'g'); }; _.obj = { /** * Get property specified by complex name from Object or Array * @param {Object|Array} source Source data * @param {String} field Parts of complex names should be separated with dot * @param {*} [defaultValue] * @return {*|undefined} */ get: function (source, field, defaultValue) { var data = source; var path = ('' + field).split('.'); for (var i = 0, count = path.length; i < count && null != data; ++i) { data = data[path[i]]; } return (undefined === data) ? defaultValue : data; }, /** * Set property specified by complex name in Object or Array * @param {Object|Array} target * @param {String} field Parts of complex names should be separated with dot * @param {*} value * @return {Boolean} */ set: function (target, field, value) { var isObject = function (obj) { var type = Object.prototype.toString.call(obj); return ('[object Object]' === type || '[object Array]' === type); }; if (isObject(target)) { var data = target; var path = ('' + field).split('.'); for (var i = 0, count = path.length - 1; i < count; ++i) { if ('undefined' === typeof data[path[i]]) { data[path[i]] = {}; } if (isObject(data[path[i]])) { data = data[path[i]]; } else { return false; } } data[path[path.length - 1]] = value; return true; } return false; } }; _.str = { toNumber: function (text) { var number = +text; return isFinite(number) ? number : 0; }, /** * @param {String} str * @param {String} [chars] * @return {String} */ trim: function (str, chars) { str = '' + str; return (nativeTrim && !chars) ? nativeTrim.call(str) : (str == null ? '' : str.replace(getTrimRegExp(chars, true, true), '')); }, /** * @param {String} str * @param {String} [chars] * @return {String} */ trimLeft: function (str, chars) { str = '' + str; return (nativeTrimLeft && !chars) ? nativeTrimLeft.call(str) : (str == null ? '' : str.replace(getTrimRegExp(chars, true, false), '')); }, /** * @param {String} str * @param {String} [chars] * @return {String} */ trimRight: function (str, chars) { str = '' + str; return (nativeTrimRight && !chars) ? nativeTrimRight.call(str) : (str == null ? '' : str.replace(getTrimRegExp(chars, false, true), '')); }, /** * Converts string's first character to uppercase * @param {String} str * @return {String} */ capitalize: function (str) { str = '' + str; return str.charAt(0).toUpperCase() + str.substr(1); }, /** * Converts underscored or dasherized string to camelized * @example * * _.str.camelize('-moz-box-shadow') * // MozBoxShadow * * @param {String} str * @return {String} */ camelize: function(str) { return _.str.trim(str).replace(/[-_\s]+(.)?/g, function(match, c) { return c ? c.toUpperCase() : ''; }); }, /** * * @param {String} str * @param {Number} count * @param {String} [separator] * @return {String} */ repeat: function (str, count, separator) { str = '' + str; count = +count; separator = separator ? '' + separator : ''; var result = ''; for (var i = 0; i < count; ++i) { if (i) { result += separator; } result += str; } return result; }, /** * Renders simple templates * @example * * _.str.tpl('Hello {name}', {name: 'World'}); * * @param {String} tpl * @param {Object} data * @return {String} */ tpl: function (tpl, data) { return ('' + tpl).replace(/\{([\w\d_-]+)\}/g, function (match, key) { return (key in data) ? data[key] : ''; }); }, /** * Returns a word in the plural * @param {Array|String} word * @param {Number} count * @return {String} */ plural: function (word, count) { count = +count; var result = ''; if (_.isArray(word)) { result = (0 === count || 1 < count) ? word[1] || '' : word[0]; } else if (_.isString(word)) { result = word + (0 === count || 1 < count ? 's' : ''); } return result; } }; _.num = { /** * Returns formatted number * * http://phpjs.org/functions/number_format:481 * Copyright 2011, Kevin van Zonneveld * Released under the MIT and GPL licenses. * More information: http://phpjs.org/pages/license * * @param {Number|String} number * @param {Number} [decimals] Default '0' * @param {String} [decPoint] Default '.' * @param {String} [thousandsSep] Default ',' * @return {String} */ format: function (number, decimals, decPoint, thousandsSep) { number = (number + '').replace(/[^0-9+\-Ee.]/g, ''); var n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), sep = (typeof thousandsSep === 'undefined') ? ',' : thousandsSep, dec = (typeof decPoint === 'undefined') ? '.' : decPoint, s, toFixedFix = function (n, prec) { var k = Math.pow(10, prec); return '' + Math.round(n * k) / k; }; // Fix for IE parseFloat(0.55).toFixed(0) = 0; s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.'); if (s[0].length > 3) { s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); } if ((s[1] || '').length < prec) { s[1] = s[1] || ''; s[1] += new Array(prec - s[1].length + 1).join('0'); } return s.join(dec); } }; /** * * @param {String} url * @param {String} [baseUrl] * @return {_.Uri} */ _.Uri = function (url, baseUrl) { return this.initialize.apply(this, arguments); }; _.Uri.prototype = { initialize: function (url, baseUrl) { url = '' + url; /** * @readonly * @type {Object} */ this.parts = this.parse(url); var fields = {'source': '', 'protocol': '', 'user': '', 'password': '', 'host': '', 'port': '', 'path': '', 'query': '', 'anchor': ''}; for (var name in fields) { if (fields.hasOwnProperty(name)) { this[name] = this.parts[name] || ''; } } if (baseUrl) { this.setBaseUrl(baseUrl); } return this; }, setBaseUrl: function (baseUrl) { baseUrl = '' + baseUrl; baseUrl = _.str.trimRight(baseUrl, '/'); var baseParts = this.parse(baseUrl); if (this.host) { if (-1 !== baseUrl.indexOf(this.host)) { if (this.host === baseParts.host && baseParts.path === this.path.substring(0, baseParts.path.length) ) { this.baseUrl = baseUrl; this.basePath = baseParts.path; this.relativePath = this.path.substring(baseParts.path.length); } } this.rebuild(); } else if ('/' !== this.path[0]) { this.baseUrl = baseUrl; this.basePath = baseParts.path; this.relativePath = '/' + this.path; var baseFields = ['protocol', 'user', 'password', 'host', 'port', 'path']; for (var i = 0, length = baseFields.length; i < length; ++i) { this[baseFields[i]] = baseParts[baseFields[i]]; } this.path = this.path + this.relativePath; this.rebuild(); } return this; }, /** * Returns path without '..' and '.'. * Works only with path, not entire url * @param {String} path * @return {String} */ realPath: function (path) { path = '' + path; if ((/(^|\/)\.\.?(\/|$)/).test(path)) { var result = [], parts = path.split('/'); for (var i = 0, len = parts.length; i < len; ++i) { if ('..' === parts[i]) { result.pop(); } else if ('.' !== parts[i]) { result.push(parts[i]); } } path = result.join('/'); } return path; }, /** * Returns object containing parts of parsed url * * Modified version of parseUri 1.2.2 * http://blog.stevenlevithan.com/archives/parseuri * Copyright 2007, Steven Levithan * MIT License * * @param {String} url * @return {Object} */ parse: function (url) { url = '' + url; var o = _.Uri.prototype.parserOptions, m = o.parser[o.strictMode ? "strict" : "loose"].exec(url), uri = {}, i = 14; while (i--) { uri[o.key[i]] = m[i] || ""; } uri[o.q.name] = {}; uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { if ($1) { uri[o.q.name][$1] = $2; } }); return uri; }, parserOptions: { strictMode: true, key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], q: { name: "queryKey", parser: /(?:^|&)([^&=]*)=?([^&]*)/g }, parser: { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ } }, rebuild: function () { this.source = this.toString(); return this.source; }, toString: function () { var url = ''; if (this.protocol) { url += this.protocol; if (':' !== this.protocol[this.protocol.length - 1]) { url += ':'; } url += '//'; } else { if (-1 !== this.source.indexOf('//') && this.host) { url += '//'; } } if (this.host && (this.user || this.password)) { url += this.user + (this.password ? ':' + this.password : '') + '@'; } if (this.host) { url += this.host; if (this.port) { url += ':' + this.port; } } if (this.path) { url += this.path; } else { if (this.host && (this.query || this.anchor)) { url += '/'; } } if (this.query) { if (0 !== this.query.indexOf('?')) { url += '?'; } url += this.query; } if (this.anchor) { if (0 !== this.anchor.indexOf('#')) { url += '#'; } url += this.anchor; } return url; } }; /** * Returns type of variable in original case * @param {*} mixed * @param {Boolean} [lowerCase] * @return {String} */ window._.getType = function (mixed, lowerCase) { var type = (null === mixed || undefined === mixed) ? '' + mixed : Object.prototype.toString.call(mixed).slice(8, -1); return (lowerCase) ? type.toLowerCase() : type; }; })(window);