whole_func_string: function makePossibleRangesHTML(source, tokens, offset) {
  		offset = offset || 0;
  		var range = require('range');
  		var result = [];
  		var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName;
  		_.each(tokens, function(tok) {
  			switch (tok.type) {
  				case 'tag':
  					tagName = source.substring(tok.start, tok.end);
  					if (/^<[\w\:\-]/.test(tagName)) {
  						// add tag name
  						result.push(range.create({
  							start: tok.start + 1, 
  							end: tok.end
  						}));
  					}
  					break;
  				case 'attribute':
  					attrStart = tok.start;
  					attrName = source.substring(tok.start, tok.end);
  					break;
  					
  				case 'string':
  					// attribute value
  					// push full attribute first
  					 result.push(range.create(attrStart, tok.end - attrStart));
  					 
  					 attrValueRange = range.create(tok);
  					 attrValue = attrValueRange.substring(source);
  					 
  					 // is this a quoted attribute?
  					 if (isQuote(attrValue.charAt(0)))
  						 attrValueRange.start++;
  					 
  					 if (isQuote(attrValue.charAt(attrValue.length - 1)))
  						 attrValueRange.end--;
  					 
  					 result.push(attrValueRange);
  					 
  					 if (attrName == 'class') {
  						 result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start));
  					 }
  					 
  					break;
  			}
  		});
  		
  		// offset ranges
  		_.each(result, function(r) {
  			r.shift(offset);
  		});
  		
  		return _.chain(result)
  			.filter(function(item) {        // remove empty
  				return !!item.length();
  			})
  			.uniq(false, function(item) {   // remove duplicates
  				return item.toString();
  			})
  			.value();
  	}
func_code_string: function makePossibleRangesHTML(source, tokens, offset) {
  		offset = offset || 0;
  		var range = require('range');
  		var result = [];
  		var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName;
  		_.each(tokens, function(tok) {
  			switch (tok.type) {
  				case 'tag':
  					tagName = source.substring(tok.start, tok.end);
  					if (/^<[\w\:\-]/.test(tagName)) {
  						// add tag name
  						result.push(range.create({
  							start: tok.start + 1, 
  							end: tok.end
  						}));
  					}
  					break;
  				case 'attribute':
  					attrStart = tok.start;
  					attrName = source.substring(tok.start, tok.end);
  					break;
  					
  				case 'string':
  					// attribute value
  					// push full attribute first
  					 result.push(range.create(attrStart, tok.end - attrStart));
  					 
  					 attrValueRange = range.create(tok);
  					 attrValue = attrValueRange.substring(source);
  					 
  					 // is this a quoted attribute?
  					 if (isQuote(attrValue.charAt(0)))
  						 attrValueRange.start++;
  					 
  					 if (isQuote(attrValue.charAt(attrValue.length - 1)))
  						 attrValueRange.end--;
  					 
  					 result.push(attrValueRange);
  					 
  					 if (attrName == 'class') {
  						 result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start));
  					 }
  					 
  					break;
  			}
  		});
  		
  		// offset ranges
  		_.each(result, function(r) {
  			r.shift(offset);
  		});
  		
  		return _.chain(result)
  			.filter(function(item) {        // remove empty
  				return !!item.length();
  			})
  			.uniq(false, function(item) {   // remove duplicates
  				return item.toString();
  			})
  			.value();
  	}
func_documentation_string: Creates possible selection ranges for HTML tag
  @param {String} source Original HTML source for tokens
  @param {Array} tokens List of HTML tokens
  @returns {Array}