/*global Autolinker */ /*jshint eqnull:true, boss:true */ /** * @class Autolinker.Util * @singleton * * A few utility methods for Autolinker. */ Autolinker.Util = { /** * @property {Function} abstractMethod * * A function object which represents an abstract method. */ abstractMethod : function() { throw "abstract"; }, /** * @private * @property {RegExp} trimRegex * * The regular expression used to trim the leading and trailing whitespace * from a string. */ trimRegex : /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, /** * Assigns (shallow copies) the properties of `src` onto `dest`. * * @param {Object} dest The destination object. * @param {Object} src The source object. * @return {Object} The destination object (`dest`) */ assign : function( dest, src ) { for( var prop in src ) { if( src.hasOwnProperty( prop ) ) { dest[ prop ] = src[ prop ]; } } return dest; }, /** * Assigns (shallow copies) the properties of `src` onto `dest`, if the * corresponding property on `dest` === `undefined`. * * @param {Object} dest The destination object. * @param {Object} src The source object. * @return {Object} The destination object (`dest`) */ defaults : function( dest, src ) { for( var prop in src ) { if( src.hasOwnProperty( prop ) && dest[ prop ] === undefined ) { dest[ prop ] = src[ prop ]; } } return dest; }, /** * Extends `superclass` to create a new subclass, adding the `protoProps` to the new subclass's prototype. * * @param {Function} superclass The constructor function for the superclass. * @param {Object} protoProps The methods/properties to add to the subclass's prototype. This may contain the * special property `constructor`, which will be used as the new subclass's constructor function. * @return {Function} The new subclass function. */ extend : function( superclass, protoProps ) { var superclassProto = superclass.prototype; var F = function() {}; F.prototype = superclassProto; var subclass; if( protoProps.hasOwnProperty( 'constructor' ) ) { subclass = protoProps.constructor; } else { subclass = function() { superclassProto.constructor.apply( this, arguments ); }; } var subclassProto = subclass.prototype = new F(); // set up prototype chain subclassProto.constructor = subclass; // fix constructor property subclassProto.superclass = superclassProto; delete protoProps.constructor; // don't re-assign constructor property to the prototype, since a new function may have been created (`subclass`), which is now already there Autolinker.Util.assign( subclassProto, protoProps ); return subclass; }, /** * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the * end of the string (by default, two periods: '..'). If the `str` length does not exceed * `len`, the string will be returned unchanged. * * @param {String} str The string to truncate and add an ellipsis to. * @param {Number} truncateLen The length to truncate the string at. * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str` * when truncated. Defaults to '...' */ ellipsis : function( str, truncateLen, ellipsisChars ) { var ellipsisLength; if( str.length > truncateLen ) { if(ellipsisChars == null) { ellipsisChars = '…'; ellipsisLength = 3; } else { ellipsisLength = ellipsisChars.length; } str = str.substring( 0, truncateLen - ellipsisLength ) + ellipsisChars; } return str; }, /** * Supports `Array.prototype.indexOf()` functionality for old IE (IE8 and below). * * @param {Array} arr The array to find an element of. * @param {*} element The element to find in the array, and return the index of. * @return {Number} The index of the `element`, or -1 if it was not found. */ indexOf : function( arr, element ) { if( Array.prototype.indexOf ) { return arr.indexOf( element ); } else { for( var i = 0, len = arr.length; i < len; i++ ) { if( arr[ i ] === element ) return i; } return -1; } }, /** * Removes array elements based on a filtering function. Mutates the input * array. * * Using this instead of the ES5 Array.prototype.filter() function, to allow * Autolinker compatibility with IE8, and also to prevent creating many new * arrays in memory for filtering. * * @param {Array} arr The array to remove elements from. This array is * mutated. * @param {Function} fn A function which should return `true` to * remove an element. * @return {Array} The mutated input `arr`. */ remove : function( arr, fn ) { for( var i = arr.length - 1; i >= 0; i-- ) { if( fn( arr[ i ] ) === true ) { arr.splice( i, 1 ); } } }, /** * Performs the functionality of what modern browsers do when `String.prototype.split()` is called * with a regular expression that contains capturing parenthesis. * * For example: * * // Modern browsers: * "a,b,c".split( /(,)/ ); // --> [ 'a', ',', 'b', ',', 'c' ] * * // Old IE (including IE8): * "a,b,c".split( /(,)/ ); // --> [ 'a', 'b', 'c' ] * * This method emulates the functionality of modern browsers for the old IE case. * * @param {String} str The string to split. * @param {RegExp} splitRegex The regular expression to split the input `str` on. The splitting * character(s) will be spliced into the array, as in the "modern browsers" example in the * description of this method. * Note #1: the supplied regular expression **must** have the 'g' flag specified. * Note #2: for simplicity's sake, the regular expression does not need * to contain capturing parenthesis - it will be assumed that any match has them. * @return {String[]} The split array of strings, with the splitting character(s) included. */ splitAndCapture : function( str, splitRegex ) { // @if DEBUG if( !splitRegex.global ) throw new Error( "`splitRegex` must have the 'g' flag set" ); // @endif var result = [], lastIdx = 0, match; while( match = splitRegex.exec( str ) ) { result.push( str.substring( lastIdx, match.index ) ); result.push( match[ 0 ] ); // push the splitting char(s) lastIdx = match.index + match[ 0 ].length; } result.push( str.substring( lastIdx ) ); return result; }, /** * Trims the leading and trailing whitespace from a string. * * @param {String} str The string to trim. * @return {String} */ trim : function( str ) { return str.replace( this.trimRegex, '' ); } };