(function(exportTo) {
"use strict";
var selfClosingTags = ["area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"];
/**
* Truncates a given HTML string to the specified length.
* @param {string} text This is the HTMl string to be truncated
* @param {object} options An options object defining how to truncate
* Default values:
* {
* TruncateBy : 'words', // Options are 'words', 'characters' or 'paragraphs'
* TruncateLength : 50, // The count to be used with TruncatedBy
* StripHTML : false, // Whether or not the truncated text should contain HTML tags
* Strict : true, // When set to false the truncated text finish at the end of the word
* Suffix : '...' // Text to be appended to the end of the truncated text
* }
* @return {string} This returns the provided string truncated to the
* length provided by the options. HTML tags may be stripped based
* on the given options.
*/
var truncatise = function(text,options) {
var options = options || {},
text = (text || "").trim(),
truncatedText = "",
currentState = 0,
isEndOfWord = false,
isTagOpen = false,
currentTag = "",
tagStack = [],
nextChar = "";
//Counters
var charCounter = 0,
wordCounter = 0,
paragraphCounter = 0;
//currentState values
var NOT_TAG = 0,
TAG_START = 1,
TAG_ATTRIBUTES = 2;
//Set default values
options.TruncateBy = (options.TruncateBy === undefined
|| typeof options.TruncateBy !== "string"
|| !options.TruncateBy.match(/(word(s)?|character(s)?|paragraph(s)?)/))
? 'words'
: options.TruncateBy.toLowerCase();
options.TruncateLength = (options.TruncateLength === undefined
|| typeof options.TruncateLength !== "number")
? 50
: options.TruncateLength;
options.StripHTML = (options.StripHTML === undefined
|| typeof options.StripHTML !== "boolean")
? false
: options.StripHTML;
options.Strict = (options.Strict === undefined
|| typeof options.Strict !== "boolean")
? true
: options.Strict;
options.Suffix = (options.Suffix === undefined
|| typeof options.Suffix !== "string")
? '...'
: options.Suffix;
if(text === "" || (text.length <= options.TruncateLength && options.StripHTML === false)){
return text;
}
if(options.StripHTML) {
text = String(text).replace(/
/gi, ' ');
}
//If not splitting on paragraphs we can quickly remove tags using regex
if(options.StripHTML && !options.TruncateBy.match(/(paragraph(s)?)/)){
text = String(text).replace(//gm, '').replace(/<\/?[^>]+>/gi, '');
}
//Remove newline seperating paragraphs
text = String(text).replace(/<\/p>(\r?\n)+
/gm, '
'); //Replace double newlines with paragraphs if(options.StripHTML && String(text).match(/\r?\n\r?\n/)){ text = String(text).replace(/((.+)(\r?\n\r?\n|$))/gi, "
$2
"); } for (var pointer = 0; pointer < text.length; pointer++ ) { var currentChar = text[pointer]; switch(currentChar){ case "<": if(currentState === NOT_TAG){ currentState = TAG_START; currentTag = ""; } if(!options.StripHTML){ truncatedText += currentChar; } break; case ">": if(currentState === TAG_START || currentState === TAG_ATTRIBUTES){ currentState = NOT_TAG; currentTag = currentTag.toLowerCase(); if(currentTag === "/p"){ paragraphCounter++; if(options.StripHTML){ truncatedText += " "; } } // Ignore self-closing tags. if ((selfClosingTags.indexOf(currentTag) === -1) && (selfClosingTags.indexOf(currentTag + '/') === -1)) { if(currentTag.indexOf("/") >= 0){ tagStack.pop(); } else { tagStack.push(currentTag); } } } if(!options.StripHTML){ truncatedText += currentChar; } break; case " ": if(currentState === TAG_START){ currentState = TAG_ATTRIBUTES; } if(currentState === NOT_TAG){ wordCounter++; charCounter++; } if(currentState === NOT_TAG || !options.StripHTML){ truncatedText += currentChar; } break; default: if(currentState === NOT_TAG){ charCounter++; } if(currentState === TAG_START){ currentTag += currentChar; } if(currentState === NOT_TAG || !options.StripHTML){ truncatedText += currentChar; } break; } nextChar = text[pointer + 1] || ""; isEndOfWord = options.Strict ? true : (!currentChar.match(/[a-zA-ZÇ-Ü']/i) || !nextChar.match(/[a-zA-ZÇ-Ü']/i)); if(options.TruncateBy.match(/word(s)?/i) && options.TruncateLength <= wordCounter){ truncatedText = truncatedText.replace(/\s+$/, ''); break; } if(options.TruncateBy.match(/character(s)?/i) && options.TruncateLength <= charCounter && isEndOfWord){ break; } if(options.TruncateBy.match(/paragraph(s)?/i) && options.TruncateLength === paragraphCounter){ break; } } if(!options.StripHTML && tagStack.length > 0){ while(tagStack.length > 0){ var tag = tagStack.pop(); if(tag!=="!--"){ truncatedText += ""+tag+">"; } } } if(pointer < text.length - 1) { if(truncatedText.match(/<\/p>$/gi)){ truncatedText = truncatedText.replace(/(<\/p>)$/gi, options.Suffix + "$1"); }else{ truncatedText = truncatedText + options.Suffix; } } return truncatedText.trim(); }; // Export to node if (typeof module !== 'undefined' && module.exports){ return module.exports = truncatise; } // Nope, export to the browser instead. exportTo.truncatise = truncatise; }(this));