/*! htmlslice - v0.1.1 - 2/17/2012 * https://github.com/cowboy/htmlslice * Copyright (c) 2012 "Cowboy" Ben Alman; Licensed MIT */ (function(exports) { // Match a tag or character in such a way that open tags, close tags, self- // closing tags or characters can be differentiated from one another. var matchComplex = /<(\/)?([^ \/>]*).*?(\/)?>|[\s\S]/; // Match a tag in such a way that self-closing tags can be differentiated from // open or close tags. var matchSimple = /<.*?(\/)?>/g; // Get the number of "characters" (including self-closing tags) in an HTML // string. function getLength(html) { return (html || '').replace(matchSimple, function(_, selfClosing) { // Self-closing tags should count as characters, but open and close tags // should not. return selfClosing ? ' ' : ''; }).length; } // Constructor function. function HtmlSlice(html) { if (!(this instanceof HtmlSlice)) { return new HtmlSlice(html); } this.html = html; this.length = getLength(html); } // Expose the constructor function. exports.HtmlSlice = HtmlSlice; // General utility. HtmlSlice.prototype.toString = function() { return this.html; }; // The HTML-aware slice function. HtmlSlice.prototype.slice = function(begin, end) { // Normalize begin to behave like String#slice. if (begin == null) { begin = 0; } else if (begin < 0) { begin += this.length; } begin = Math.min(Math.max(begin, 0), this.length); // Normalize end to behave like String#slice. if (end == null) { end = this.length; } else if (end < 0) { end += this.length; } end = Math.min(Math.max(end, 0), this.length); // Keep track of opening tags. var opentags = []; // And closing tags. var closetags = []; // The current actual character position. var pos = 0; // The current html-aware character position. var hpos = 0; // The current result string (sans any remaining closing tags). var result = ''; // Matches! var matches; while (hpos < end && (matches = this.html.slice(pos).match(matchComplex))) { // Update the actual position. pos += matches[0].length; if (matches[1]) { // Close tag. if (hpos <= begin) { opentags.pop(); } closetags.shift(); } else if (matches[2] && !matches[3]) { // Open tag. if (hpos <= begin) { opentags.push(matches[0]); } closetags.unshift(''); } else { // Character or self-closing tag. if (hpos++ === begin) { result += opentags.join(''); } } // Add matched tag or character to result if begun. if (hpos > begin) { result += matches[0]; } } return result + closetags.join(''); }; }(typeof exports === 'object' && exports || this));