| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810 |
- /*
- * Utilities: A classic collection of JavaScript utilities
- * Copyright 2112 Matthew Eernisse ([email protected])
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- var core = require('./core')
- , inflection = require('./inflection')
- , string;
- /**
- @name string
- @namespace string
- */
- string = new (function () {
- // Regexes for trimming, and character maps for escaping
- var _LTR = /^\s+/
- , _RTR = /\s+$/
- , _TR = /^\s+|\s+$/g
- , _NL = /\n|\r|\r\n/g
- , _CHARS = {
- '&': '&'
- , '<': '<'
- , '>': '>'
- , '"': '"'
- , '\'': '''
- }
- , _UUID_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
- , _buildEscape
- , _buildEscapeTest;
- // Builds the escape/unescape methods using a
- // map of characters
- _buildEscape = function (direction) {
- return function (str) {
- var string = str;
- // If string is NaN, null or undefined then provide an empty default
- if((typeof string === 'undefined') ||
- string === null ||
- (!string && isNaN(string))) {
- string = '';
- }
- string = string.toString();
- var from, to, p;
- for (p in _CHARS) {
- from = direction == 'to' ? p : _CHARS[p];
- to = direction == 'to' ? _CHARS[p] : p;
- string = string.replace(new RegExp(from, 'gm'), to);
- }
- return string;
- }
- };
- // Builds a method that tests for any escapable
- // characters, useful for avoiding double-scaping if
- // you're not sure if a string has already been escaped
- _buildEscapeTest = function (direction) {
- return function (string) {
- var pat = ''
- , p;
- for (p in _CHARS) {
- pat += direction == 'to' ? p : _CHARS[p];
- pat += '|';
- }
- pat = pat.substr(0, pat.length - 1);
- pat = new RegExp(pat, "gm");
- return pat.test(string)
- }
- };
- // Escape special XMl chars
- this.escapeXML = _buildEscape('to');
- // Unescape XML chars to literal representation
- this.unescapeXML = _buildEscape('from');
- // Test if a string includes special chars
- // that need escaping
- this.needsEscape = _buildEscapeTest('to');
- // Test if a string includes escaped chars
- // that need unescaping
- this.needsUnescape = _buildEscapeTest('from');
- /**
- @name string#escapeRegExpChars
- @public
- @function
- @return {String} A string of escaped characters
- @description Escapes regex control-characters in strings
- used to build regexes dynamically
- @param {String} string The string of chars to escape
- */
- this.escapeRegExpChars = (function () {
- var specials = [ '^', '$', '/', '.', '*', '+', '?', '|', '(', ')',
- '[', ']', '{', '}', '\\' ];
- var sRE = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
- return function (string) {
- var str = string || '';
- str = String(str);
- return str.replace(sRE, '\\$1');
- };
- }).call(this);
- /**
- @name string#toArray
- @public
- @function
- @return {Array} Returns an array of characters
- @description Converts a string to an array
- @param {String} string The string to convert
- */
- this.toArray = function (string) {
- var str = string || ''
- , arr = []
- , i = -1;
- str = String(str);
- while (++i < str.length) {
- arr.push(str.substr(i, 1));
- }
- return arr;
- };
- /**
- @name string#reverse
- @public
- @function
- @return {String} Returns the `string` reversed
- @description Reverses a string
- @param {String} string The string to reverse
- */
- this.reverse = function (string) {
- var str = string || '';
- str = String(str);
- return this.toArray(str).reverse().join('');
- };
- /**
- @name string#ltrim
- @public
- @function
- @return {String} Returns the trimmed string
- @description Ltrim trims `char` from the left of a `string` and returns it
- if no `char` is given it will trim spaces
- @param {String} string The string to trim
- @param {String} character The character to trim
- */
- this.ltrim = function (string, character) {
- var str = string || ''
- , pat = character ? new RegExp('^' + character + '+') : _LTR;
- str = String(str);
- return str.replace(pat, '');
- };
- /**
- @name string#rtrim
- @public
- @function
- @return {String} Returns the trimmed string
- @description Rtrim trims `char` from the right of a `string` and returns it
- if no `char` is given it will trim spaces
- @param {String} string The string to trim
- @param {String} character The character to trim
- */
- this.rtrim = function (string, character) {
- var str = string || ''
- , pat = character ? new RegExp(character + '+$') : _RTR;
- str = String(str);
- return str.replace(pat, '');
- };
- // Alias
- this.chomp = this.rtrim;
- /**
- @name string#trim
- @public
- @function
- @return {String} Returns the trimmed string
- @description Trim trims `char` from the left and right of a `string` and returns it
- if no `char` is given it will trim spaces
- @param {String} string The string to trim
- @param {String} character The character to trim
- */
- this.trim = function (string, character) {
- var str = string || ''
- , pat = character ? new RegExp('^' + character + '+|' + character + '+$', 'g') : _TR;
- str = String(str);
- return str.replace(pat, '');
- };
- /**
- @name string#chop
- @public
- @function
- @description Returns a new String with the last character removed. If the
- string ends with \r\n, both characters are removed. Applying chop to an
- empty string returns an empty string.
- @param {String} string to return with the last character removed.
- */
- this.chop = function (string) {
- var index
- , str = string || '';
- str = String(str);
- if (str.length) {
- // Special-case for \r\n
- index = str.indexOf('\r\n');
- if (index == str.length - 2) {
- return str.substring(0, index);
- }
- return str.substring(0, str.length - 1);
- }
- else {
- return '';
- }
- };
- /**
- @name string#lpad
- @public
- @function
- @return {String} Returns the padded string
- @description Lpad adds `char` to the left of `string` until the length
- of `string` is more than `width`
- @param {String} string The string to pad
- @param {String} character The character to pad with
- @param {Number} width the width to pad to
- */
- this.lpad = function (string, character, width) {
- var str = string || ''
- , width;
- str = String(str);
- // Should width be string.length + 1? or the same to be safe
- width = parseInt(width, 10) || str.length;
- character = character || ' ';
- while (str.length < width) {
- str = character + str;
- }
- return str;
- };
- /**
- @name string#rpad
- @public
- @function
- @return {String} Returns the padded string
- @description Rpad adds `char` to the right of `string` until the length
- of `string` is more than `width`
- @param {String} string The string to pad
- @param {String} character The character to pad with
- @param {Number} width the width to pad to
- */
- this.rpad = function (string, character, width) {
- var str = string || ''
- , width;
- str = String(str);
- // Should width be string.length + 1? or the same to be safe
- width = parseInt(width, 10) || str.length;
- character = character || ' ';
- while (str.length < width) {
- str += character;
- }
- return str;
- };
- /**
- @name string#pad
- @public
- @function
- @return {String} Returns the padded string
- @description Pad adds `char` to the left and right of `string` until the length
- of `string` is more than `width`
- @param {String} string The string to pad
- @param {String} character The character to pad with
- @param {Number} width the width to pad to
- */
- this.pad = function (string, character, width) {
- var str = string || ''
- , width;
- str = String(str);
- // Should width be string.length + 1? or the same to be safe
- width = parseInt(width, 10) || str.length;
- character = character || ' ';
- while (str.length < width) {
- str = character + str + character;
- }
- return str;
- };
- /**
- @name string#truncate
- @public
- @function
- @return {String} Returns the truncated string
- @description Truncates a given `string` after a specified `length` if `string` is longer than
- `length`. The last characters will be replaced with an `omission` for a total length
- not exceeding `length`. If `callback` is given it will fire if `string` is truncated.
- @param {String} string The string to truncate
- @param {Integer/Object} options Options for truncation, If options is an Integer it will be length
- @param {Integer} [options.length=string.length] Length the output string will be
- @param {Integer} [options.len] Alias for `length`
- @param {String} [options.omission='...'] Replace last characters with an omission
- @param {String} [options.ellipsis='...'] Alias for `omission`
- @param {String/RegExp} [options.seperator] Break the truncated test at the nearest `seperator`
- @param {Function} callback Callback is called only if a truncation is done
- */
- this.truncate = function (string, options, callback) {
- var str = string || ''
- , stringLen
- , opts
- , stringLenWithOmission
- , last
- , ignoreCase
- , multiLine
- , stringToWorkWith
- , lastIndexOf
- , nextStop
- , result
- , returnString;
- str = String(str);
- stringLen = str.length
- // If `options` is a number, assume it's the length and
- // create a options object with length
- if (typeof options === 'number') {
- opts = {
- length: options
- };
- }
- else {
- opts = options || {};
- }
- // Set `opts` defaults
- opts.length = opts.length || stringLen;
- opts.omission = opts.omission || opts.ellipsis || '...';
- stringLenWithOmission = opts.length - opts.omission.length;
- // Set the index to stop at for `string`
- if (opts.seperator) {
- if (opts.seperator instanceof RegExp) {
- // If `seperator` is a regex
- if (opts.seperator.global) {
- opts.seperator = opts.seperator;
- } else {
- ignoreCase = opts.seperator.ignoreCase ? 'i' : ''
- multiLine = opts.seperator.multiLine ? 'm' : '';
- opts.seperator = new RegExp(opts.seperator.source,
- 'g' + ignoreCase + multiLine);
- }
- stringToWorkWith = str.substring(0, stringLenWithOmission + 1)
- lastIndexOf = -1
- nextStop = 0
- while ((result = opts.seperator.exec(stringToWorkWith))) {
- lastIndexOf = result.index;
- opts.seperator.lastIndex = ++nextStop;
- }
- last = lastIndexOf;
- }
- else {
- // Seperator is a String
- last = str.lastIndexOf(opts.seperator, stringLenWithOmission);
- }
- // If the above couldn't be found, they'll default to -1 so,
- // we need to just set it as `stringLenWithOmission`
- if (last === -1) {
- last = stringLenWithOmission;
- }
- }
- else {
- last = stringLenWithOmission;
- }
- if (stringLen < opts.length) {
- return str;
- }
- else {
- returnString = str.substring(0, last) + opts.omission;
- returnString += callback && typeof callback === 'function' ? callback() : '';
- return returnString;
- }
- };
- /**
- @name string#truncateHTML
- @public
- @function
- @return {String} Returns the HTML safe truncated string
- @description Truncates a given `string` inside HTML tags after a specified `length` if string`
- is longer than `length`. The last characters will be replaced with an `omission`
- for a total length not exceeding `length`. If `callback` is given it will fire if
- `string` is truncated. If `once` is true only the first string in the first HTML
- tags will be truncated leaving the others as they were
- @param {String} string The string to truncate
- @param {Integer/Object} options Options for truncation, If options is an Integer it will be length
- all Object options are the same as `truncate`
- @param {Boolean} [options.once=false] If true, it will only be truncated once, otherwise the
- truncation will loop through all text inside HTML tags
- @param {Function} callback Callback is called only if a truncation is done
- */
- this.truncateHTML = function (string, options, callback) {
- var str = string || ''
- , returnString = ''
- , opts = options;
- str = String(str);
- // If `options` is a number assume it's the length and create a options object with length
- if (typeof opts === 'number') {
- var num = opts;
- opts = {};
- opts.length = num;
- } else opts = opts || {};
- // Set `default` options for HTML specifics
- opts.once = opts.once || false;
- var pat = /(<[^>]*>)/ // Patter for matching HTML tags
- , arr = [] // Holds the HTML tags and content seperately
- , truncated = false
- , result = pat.exec(str)
- , item
- , firstPos
- , lastPos
- , i;
- // Gather the HTML tags and content into the array
- while (result) {
- firstPos = result.index;
- lastPos = pat.lastIndex;
- if (firstPos !== 0) {
- // Should be content not HTML tags
- arr.push(str.substring(0, firstPos));
- // Slice content from string
- str = str.slice(firstPos);
- }
- arr.push(result[0]); // Push HTML tags
- str = str.slice(result[0].length);
- // Re-run the pattern on the new string
- result = pat.exec(str);
- }
- if (str !== '') {
- arr.push(str);
- }
- // Loop through array items appending the tags to the string,
- // - and truncating the text then appending it to content
- i = -1;
- while (++i < arr.length) {
- item = arr[i];
- switch (true) {
- // Closing tag
- case item.indexOf('</') == 0:
- returnString += item;
- openTag = null;
- break;
- // Opening tag
- case item.indexOf('<') == 0:
- returnString += item;
- openTag = item;
- break;
- // Normal text
- default:
- if (opts.once && truncated) {
- returnString += item;
- } else {
- returnString += this.truncate(item, opts, callback);
- truncated = true;
- }
- break;
- }
- }
- return returnString;
- };
- /**
- @name string#nl2br
- @public
- @function
- @return {String} The string with converted newlines chars to br tags
- @description Nl2br returns a string where all newline chars are turned
- into line break HTML tags
- @param {String} string The string to convert
- */
- this.nl2br = function (string) {
- var str = string || '';
- str = String(str);
- return str.replace(_NL,'<br />');
- };
- /**
- @name string#snakeize
- @public
- @function
- @return {String} The string in a snake_case version
- @description Snakeize converts camelCase and CamelCase strings to snake_case strings
- @param {String} string The string to convert to snake_case
- @param {String} separ='_' The seperator to use
- */
- this.snakeize = (function () {
- // Only create regexes once on initial load
- var repl = /([A-Z]+)/g
- , lead = /^_/g;
- return function (string, separ) {
- var str = string || ''
- , sep = separ || '_'
- , leading = separ ? new RegExp('^' + sep, 'g') : lead;
- str = String(str);
- return str.replace(repl, sep + '$1').toLowerCase().
- replace(leading, '');
- };
- }).call(this);
- // Aliases
- /**
- @name string#underscorize
- @public
- @function
- @return {String} The string in a underscorized version
- @description Underscorize returns the given `string` converting camelCase and snakeCase to underscores
- @param {String} string The string to underscorize
- */
- this.underscorize = this.snakeize;
- this.underscoreize = this.snakeize;
- this.decamelize = this.snakeize;
- /**
- @name string#camelize
- @public
- @function
- @return {String} The string in a camelCase version
- @description Camelize takes a string and optional options and
- returns a camelCase version of the given `string`
- @param {String} string The string to convert to camelCase
- @param {Object} options
- @param {Boolean} [options.initialCap] If initialCap is true the returned
- string will have a capitalized first letter
- @param {Boolean} [options.leadingUnderscore] If leadingUnderscore os true then if
- an underscore exists at the beggining
- of the string, it will stay there.
- Otherwise it'll be removed.
- */
- this.camelize = (function () {
- // Only create regex once on initial load
- var repl = /[-_](\w)/g;
- return function (string, options) {
- var str = string || ''
- , ret
- , config = {
- initialCap: false
- , leadingUnderscore: false
- }
- , opts = options || {};
- str = String(str);
- // Backward-compat
- if (typeof opts == 'boolean') {
- config = {
- initialCap: true
- };
- }
- else {
- core.mixin(config, opts);
- }
- ret = str.replace(repl, function (m, m1) {
- return m1.toUpperCase();
- });
- if (config.leadingUnderscore & str.indexOf('_') === 0) {
- ret = '_' + this.decapitalize(ret);
- }
- // If initialCap is true capitalize it
- ret = config.initialCap ? this.capitalize(ret) : this.decapitalize(ret);
- return ret;
- };
- }).call(this);
- /**
- @name string#decapitalize
- @public
- @function
- @return {String} The string with the first letter decapitalized
- @description Decapitalize returns the given string with the first letter uncapitalized.
- @param {String} string The string to decapitalize
- */
- this.decapitalize = function (string) {
- var str = string || '';
- str = String(str);
- return str.substr(0, 1).toLowerCase() + str.substr(1);
- };
- /**
- @name string#capitalize
- @public
- @function
- @return {String} The string with the first letter capitalized
- @description capitalize returns the given string with the first letter capitalized.
- @param {String} string The string to capitalize
- */
- this.capitalize = function (string) {
- var str = string || '';
- str = String(str);
- return str.substr(0, 1).toUpperCase() + str.substr(1);
- };
- /**
- @name string#dasherize
- @public
- @function
- @return {String} The string in a dashed version
- @description Dasherize returns the given `string` converting camelCase and snakeCase
- to dashes or replace them with the `replace` character.
- @param {String} string The string to dasherize
- @param {String} replace='-' The character to replace with
- */
- this.dasherize = function (string, replace) {
- var repl = replace || '-'
- return this.snakeize(string, repl);
- };
- /**
- @name string#include
- @public
- @function
- @return {Boolean} Returns true if the string is found in the string to search
- @description Searches for a particular string in another string
- @param {String} searchIn The string to search for the other string in
- @param {String} searchFor The string to search for
- */
- this.include = function (searchIn, searchFor) {
- var str = searchFor;
- if (!str && typeof string != 'string') {
- return false;
- }
- str = String(str);
- return (searchIn.indexOf(str) > -1);
- };
- /*
- * getInflections(name<String>, initialCap<String>)
- *
- * Inflection returns an object that contains different inflections
- * created from the given `name`
- */
- /**
- @name string#getInflections
- @public
- @function
- @return {Object} A Object containing multiple different inflects for the given `name`
- @description Inflection returns an object that contains different inflections
- created from the given `name`
- @param {String} name The string to create inflections from
- */
- this.getInflections = function (name) {
- if (!name) {
- return;
- }
- var self = this
- // Use plural version to fix possible mistakes(e,g,. thingie instead of thingy)
- , normalizedName = this.snakeize(inflection.pluralize(name))
- , nameSingular = inflection.singularize(normalizedName)
- , namePlural = inflection.pluralize(normalizedName);
- return {
- // For filepaths or URLs
- filename: {
- // neil_peart
- singular: nameSingular
- // neil_pearts
- , plural: namePlural
- }
- // Constructor names
- , constructor: {
- // NeilPeart
- singular: self.camelize(nameSingular, {initialCap: true})
- // NeilPearts
- , plural: self.camelize(namePlural, {initialCap: true})
- }
- , property: {
- // neilPeart
- singular: self.camelize(nameSingular)
- // neilPearts
- , plural: self.camelize(namePlural)
- }
- };
- };
- /**
- @name string#getInflection
- @public
- @function
- @return {Object} A Object containing multiple different inflects for the given `name`
- @description Inflection returns an object that contains different inflections
- created from the given `name`
- @param {String} name The string to create inflections from
- */
- this.getInflection = function (name, key, pluralization) {
- var infl = this.getInflections(name);
- return infl[key][pluralization];
- };
- // From Math.uuid.js, https://github.com/broofa/node-uuid
- // Robert Kieffer ([email protected]), MIT license
- this.uuid = function (length, radix) {
- var chars = _UUID_CHARS
- , uuid = []
- , r
- , i;
- radix = radix || chars.length;
- if (length) {
- // Compact form
- i = -1;
- while (++i < length) {
- uuid[i] = chars[0 | Math.random()*radix];
- }
- } else {
- // rfc4122, version 4 form
- // rfc4122 requires these characters
- uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
- uuid[14] = '4';
- // Fill in random data. At i==19 set the high bits of clock sequence as
- // per rfc4122, sec. 4.1.5
- i = -1;
- while (++i < 36) {
- if (!uuid[i]) {
- r = 0 | Math.random()*16;
- uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
- }
- }
- }
- return uuid.join('');
- };
-
- /**
- @name string#stripTags
- @public
- @function
- @return {String} A String with HTML tags removed.
- @description Strips HTML tags from a string.
- @param {String} The string to strip HTML tags from
- @param {String|Array} A String or Array containing allowed tags. e.g. "<br><p>"
- */
- this.stripTags = function(string, allowed) {
- // taken from http://phpjs.org/functions/strip_tags/
- var allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
- var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
- comments = /<!--[\s\S]*?-->/gi;
- return string.replace(comments, '').replace(tags, function ($0, $1) {
- return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
- });
- }
- })();
- module.exports = string;
|