Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

highlight-base.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('highlight-base', function(Y) {
  9. /**
  10. Provides methods for highlighting strings within other strings by wrapping
  11. them in HTML.
  12. @module highlight
  13. @submodule highlight-base
  14. @main
  15. @since 3.3.0
  16. **/
  17. /**
  18. Provides methods for highlighting strings within other strings by wrapping
  19. them in HTML.
  20. The highlight methods first escape any special HTML characters in the input
  21. strings and then highlight the appropriate substrings by wrapping them in a
  22. `<b class="yui3-highlight"></b>` element. The `<b>` element is used rather than
  23. `<strong>` in accordance with HTML5's definition of `<b>` as being purely
  24. presentational, which is exactly what highlighting is.
  25. @class Highlight
  26. @static
  27. **/
  28. var YArray = Y.Array,
  29. Escape = Y.Escape,
  30. WordBreak = Y.Text.WordBreak,
  31. isArray = Y.Lang.isArray,
  32. EMPTY_OBJECT = {},
  33. // Regex string that captures zero or one unclosed HTML entities. Used in
  34. // the static regex template properties below. The entity matching is
  35. // intentionally loose here, since there's a world of complexity involved in
  36. // doing strict matching for this use case.
  37. UNCLOSED_ENTITY = '(&[^;\\s]*)?',
  38. Highlight = {
  39. // -- Protected Static Properties ------------------------------------------
  40. /**
  41. Regular expression template for highlighting a match that occurs anywhere
  42. in a string. The placeholder `%needles` will be replaced with a list of
  43. needles to match, joined by `|` characters.
  44. This regex should have two capturing subpatterns:
  45. 1. Zero or one unclosed HTML entity (e.g. "&amp" without a ";" at the
  46. end).
  47. 2. The `%needles` placeholder.
  48. The first subpattern match is used to emulate a negative lookbehind
  49. assertion in order to prevent highlighting inside HTML entities.
  50. @property _REGEX
  51. @type String
  52. @protected
  53. @static
  54. @final
  55. **/
  56. _REGEX: UNCLOSED_ENTITY + '(%needles)',
  57. /**
  58. Regex replacer function or string for normal matches.
  59. @property _REPLACER
  60. @type Function|String
  61. @protected
  62. @static
  63. @final
  64. **/
  65. _REPLACER: function (match, p1, p2) {
  66. // Mimicking a negative lookbehind assertion to prevent matches inside
  67. // HTML entities. Hat tip to Steven Levithan for the technique:
  68. // http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript
  69. return p1 && !(/\s/).test(p2) ? match :
  70. Highlight._TEMPLATE.replace(/\{s\}/g, p2);
  71. },
  72. /**
  73. Regular expression template for highlighting start-of-string matches
  74. (i.e., only matches that occur at the beginning of a string). The
  75. placeholder `%needles` will be replaced with a list of needles to match,
  76. joined by `|` characters.
  77. See `_REGEX` for a description of the capturing subpatterns this regex
  78. string should contain.
  79. @property _START_REGEX
  80. @type String
  81. @protected
  82. @static
  83. @final
  84. */
  85. _START_REGEX: '^' + UNCLOSED_ENTITY + '(%needles)',
  86. /**
  87. Highlight template which will be used as a replacement for matched
  88. substrings. The placeholder `{s}` will be replaced with the matched
  89. substring.
  90. @property _TEMPLATE
  91. @type String
  92. @default '<b class="yui3-highlight">{s}</b>'
  93. @protected
  94. @static
  95. @final
  96. **/
  97. _TEMPLATE: '<b class="' + Y.ClassNameManager.getClassName('highlight') + '">{s}</b>',
  98. // -- Public Static Methods ------------------------------------------------
  99. /**
  100. Highlights all occurrences in the _haystack_ string of the items in the
  101. _needles_ array, regardless of where they occur. The returned string will
  102. have all HTML characters escaped except for the highlighting markup.
  103. @method all
  104. @param {String} haystack String to apply highlighting to.
  105. @param {String|String[]} needles String or array of strings that should be
  106. highlighted.
  107. @param {Object} [options] Options object.
  108. @param {Boolean} [options.caseSensitive=false] If `true`, matching will
  109. be case-sensitive.
  110. @param {Boolean} [options.startsWith=false] If `true`, matches must be
  111. anchored to the beginning of the string.
  112. @return {String} Escaped and highlighted copy of _haystack_.
  113. @static
  114. **/
  115. all: function (haystack, needles, options) {
  116. var validNeedles = [],
  117. esc, i, len, needle, regex, replacer;
  118. if (!options) {
  119. options = EMPTY_OBJECT;
  120. }
  121. // TODO: document options.replacer
  122. esc = options.escapeHTML !== false;
  123. regex = options.startsWith ? Highlight._START_REGEX : Highlight._REGEX;
  124. replacer = options.replacer || Highlight._REPLACER;
  125. needles = isArray(needles) ? needles : [needles];
  126. // Escape HTML characters and special regular expression characters in
  127. // the needles so they can be used in a regex and matched against the
  128. // escaped haystack.
  129. for (i = 0, len = needles.length; i < len; ++i) {
  130. needle = needles[i];
  131. if (needle) {
  132. validNeedles.push(Escape.regex(esc ? Escape.html(needle) : needle));
  133. }
  134. }
  135. // Escape HTML characters in the haystack to prevent HTML injection.
  136. if (esc) {
  137. haystack = Escape.html(haystack);
  138. }
  139. // No point continuing if there are no needles.
  140. if (!validNeedles.length) {
  141. return haystack;
  142. }
  143. return haystack.replace(
  144. new RegExp(
  145. regex.replace('%needles', validNeedles.join('|')),
  146. options.caseSensitive ? 'g' : 'gi'
  147. ),
  148. replacer
  149. );
  150. },
  151. /**
  152. Same as `all()`, but case-sensitive by default.
  153. @method allCase
  154. @param {String} haystack String to apply highlighting to.
  155. @param {String|String[]} needles String or array of strings that should be
  156. highlighted.
  157. @param {Object} [options] Options object. See `all()` for details.
  158. @return {String} Escaped and highlighted copy of _haystack_.
  159. @static
  160. **/
  161. allCase: function (haystack, needles, options) {
  162. return Highlight.all(haystack, needles,
  163. Y.merge(options || EMPTY_OBJECT, {caseSensitive: true}));
  164. },
  165. /**
  166. Highlights _needles_ that occur at the start of _haystack_. The returned
  167. string will have all HTML characters escaped except for the highlighting
  168. markup.
  169. @method start
  170. @param {String} haystack String to apply highlighting to.
  171. @param {String|String[]} needles String or array of strings that should be
  172. highlighted.
  173. @param {Object} [options] Options object.
  174. @param {Boolean} [options.caseSensitive=false] If `true`, matching will
  175. be case-sensitive.
  176. @return {String} Escaped and highlighted copy of _haystack_.
  177. @static
  178. **/
  179. start: function (haystack, needles, options) {
  180. return Highlight.all(haystack, needles,
  181. Y.merge(options || EMPTY_OBJECT, {startsWith: true}));
  182. },
  183. /**
  184. Same as `start()`, but case-sensitive by default.
  185. @method startCase
  186. @param {String} haystack String to apply highlighting to.
  187. @param {String|String[]} needles String or array of strings that should be
  188. highlighted.
  189. @return {String} Escaped and highlighted copy of _haystack_.
  190. @static
  191. **/
  192. startCase: function (haystack, needles) {
  193. // No options passthru for now, since it would be redundant. If start()
  194. // ever supports more options than caseSensitive, then we'll start
  195. // passing the options through.
  196. return Highlight.start(haystack, needles, {caseSensitive: true});
  197. },
  198. /**
  199. Highlights complete words in the _haystack_ string that are also in the
  200. _needles_ array. The returned string will have all HTML characters escaped
  201. except for the highlighting markup.
  202. @method words
  203. @param {String} haystack String to apply highlighting to.
  204. @param {String|String[]} needles String or array of strings containing words
  205. that should be highlighted. If a string is passed, it will be split
  206. into words; if an array is passed, it is assumed to have already been
  207. split.
  208. @param {Object} [options] Options object.
  209. @param {Boolean} [options.caseSensitive=false] If `true`, matching will
  210. be case-sensitive.
  211. @return {String} Escaped and highlighted copy of _haystack_.
  212. @static
  213. **/
  214. words: function (haystack, needles, options) {
  215. var caseSensitive,
  216. mapper,
  217. template = Highlight._TEMPLATE,
  218. words;
  219. if (!options) {
  220. options = EMPTY_OBJECT;
  221. }
  222. caseSensitive = !!options.caseSensitive;
  223. // Convert needles to a hash for faster lookups.
  224. needles = YArray.hash(
  225. isArray(needles) ? needles : WordBreak.getUniqueWords(needles, {
  226. ignoreCase: !caseSensitive
  227. })
  228. );
  229. // The default word mapping function can be overridden with a custom
  230. // one. This is used to implement accent-folded highlighting in the
  231. // highlight-accentfold module.
  232. mapper = options.mapper || function (word, needles) {
  233. if (needles.hasOwnProperty(caseSensitive ? word : word.toLowerCase())) {
  234. return template.replace(/\{s\}/g, Escape.html(word));
  235. }
  236. return Escape.html(word);
  237. };
  238. // Split the haystack into an array of words, including punctuation and
  239. // whitespace so we can rebuild the string later.
  240. words = WordBreak.getWords(haystack, {
  241. includePunctuation: true,
  242. includeWhitespace : true
  243. });
  244. return YArray.map(words, function (word) {
  245. return mapper(word, needles);
  246. }).join('');
  247. },
  248. /**
  249. Same as `words()`, but case-sensitive by default.
  250. @method wordsCase
  251. @param {String} haystack String to apply highlighting to.
  252. @param {String|String[]} needles String or array of strings containing words
  253. that should be highlighted. If a string is passed, it will be split
  254. into words; if an array is passed, it is assumed to have already been
  255. split.
  256. @return {String} Escaped and highlighted copy of _haystack_.
  257. @static
  258. **/
  259. wordsCase: function (haystack, needles) {
  260. // No options passthru for now, since it would be redundant. If words()
  261. // ever supports more options than caseSensitive, then we'll start
  262. // passing the options through.
  263. return Highlight.words(haystack, needles, {caseSensitive: true});
  264. }
  265. };
  266. Y.Highlight = Highlight;
  267. }, '3.4.0' ,{requires:['array-extras', 'classnamemanager', 'escape', 'text-wordbreak']});