Created
November 15, 2012 00:31
-
-
Save christoomey/4075843 to your computer and use it in GitHub Desktop.
Javascript utility to wrap instances of a search string within a target string
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| window.CJT or= {} | |
| class CJT.SpanWrapper | |
| STRING_NOT_FOUND: -1 | |
| constructor: (options={}) -> | |
| @tagname = options.tagname ? 'span' | |
| @classname = options.classname ? '' | |
| wrap: (target, searchString) => | |
| return target if searchString is '' | |
| normalizedSearchString = @_normalize searchString | |
| matchSegments = @_splitMatchSegments(target, normalizedSearchString) | |
| if matchSegments is @STRING_NOT_FOUND | |
| target | |
| else | |
| @_wrapMatches(matchSegments) | |
| _splitMatchSegments: (target, searchString) => | |
| rangeFromSearchStringLengthToZero = [(searchString.length - 1)..0] | |
| for index in rangeFromSearchStringLengthToZero | |
| [searchTestSegment, leftoverSearch] = @_splitAtIndex(searchString, index) | |
| if @_contains(target, searchTestSegment) | |
| splitTarget = @_splitAround(target, searchTestSegment) | |
| if leftoverSearch is '' | |
| unmatchedEnd = start: splitTarget.end, match: "" | |
| return [splitTarget].concat(unmatchedEnd) | |
| else | |
| splitEnd = @_splitMatchSegments(splitTarget.end, leftoverSearch) | |
| if splitEnd is @STRING_NOT_FOUND | |
| continue | |
| else | |
| return [splitTarget].concat(splitEnd) | |
| @STRING_NOT_FOUND | |
| _wrapMatches: (matchSegments) => | |
| wrappedTarget = '' | |
| for segment in matchSegments | |
| wrappedMatch = @_wrapped(segment.match) | |
| wrappedTarget += (segment.start + wrappedMatch) | |
| wrappedTarget | |
| _splitAtIndex: (string, zeroBasedIndex) => | |
| start = string[0..zeroBasedIndex] | |
| end = string[(zeroBasedIndex + 1)..(string.length - 1)] | |
| [start, end] | |
| _contains: (target, substring) => | |
| target.toLowerCase().indexOf(substring) >= 0 | |
| _normalize: (string) => string.toLowerCase().replace(/\s+/g, '') | |
| _wrapped: (core) => | |
| return '' if core is '' | |
| (@_openTag() + core + @_closeTag()) | |
| _openTag: => | |
| if @classname | |
| "<#{@tagname} class='#{@classname}'>" | |
| else | |
| "<#{@tagname}>" | |
| _closeTag: => "</#{@tagname}>" | |
| _splitAround: (target, substring) => | |
| downcasedTarget = target.toLowerCase() | |
| index = downcasedTarget.indexOf substring | |
| start = target.substring(0, index) | |
| match = target.substring(index, index + substring.length) | |
| end = target.substring(index + substring.length, target.length) | |
| start: start, match: match, end: end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #= require spanWrapper | |
| describe 'SpanWrapper', -> | |
| wrapper = new CJT.SpanWrapper | |
| target = 'a random target' | |
| describe '#wrap', -> | |
| it 'returns the target with the searchString wrapped in a span', -> | |
| searchString = 'random' | |
| wrapped = wrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual('a <span>random</span> target') | |
| it 'returns the target unchanged if the searchString is empty', -> | |
| searchString = '' | |
| wrapped = wrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual(target) | |
| it 'returns the target if the searchString is not in the target', -> | |
| searchString = 'XZX' | |
| wrapped = wrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual(target) | |
| it 'uses the provided tagname to wrap the searchString', -> | |
| divWrapper = new App.Util.SpanWrapper(tagname: 'div') | |
| searchString = 'random' | |
| wrapped = divWrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual('a <div>random</div> target') | |
| it 'adds the class to the tag if provided', -> | |
| wrapperWithClass = new App.Util.SpanWrapper(classname: 'highlighted') | |
| searchString = 'random' | |
| wrapped = wrapperWithClass.wrap(target, searchString) | |
| expect(wrapped).toEqual("a <span class='highlighted'>random</span> target") | |
| it 'ignores case in both target and searchString', -> | |
| caseSensitiveTarget = 'AsDf' | |
| caseSensitiveSearchString = 'aSdF' | |
| wrapped = wrapper.wrap(caseSensitiveTarget, caseSensitiveSearchString) | |
| expect(wrapped).toEqual("<span>#{caseSensitiveTarget}</span>") | |
| it 'ignores whitespace in the search string', -> | |
| searchString = 'dom t' | |
| wrapped = wrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual('a ran<span>dom</span> <span>t</span>arget') | |
| it 'returns the target with each piece of the searchString wrapped', -> | |
| searchString = 'rantar' | |
| wrapped = wrapper.wrap(target, searchString) | |
| expect(wrapped).toEqual('a <span>ran</span>dom <span>tar</span>get') | |
| it 'wraps each match segment of a discontiguous match individually', -> | |
| discontiguousTarget = 'Windows 7 License Decision' | |
| discontiguousSearchString = 'iss' | |
| wrapped = wrapper.wrap(discontiguousTarget, discontiguousSearchString) | |
| expected = 'W<span>i</span>ndow<span>s</span> 7 Licen<span>s</span>e Decision' | |
| expect(wrapped).toEqual(expected) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment