| @@ -1,50 +1,81 @@ | ||
| 1 | -(function($) { | |
| 1 | +(function ($) { | |
| 2 | 2 | |
| 3 | - var sel = function(ptr, context) { | |
| 4 | - if($.isFunction(ptr)) | |
| 3 | + var timer = 0; | |
| 4 | + | |
| 5 | + var sel = function (ptr, context) { | |
| 6 | + if ($.isFunction(ptr)) | |
| 5 | 7 | return ptr.apply(context); |
| 6 | 8 | return typeof ptr == "string" ? context.find(ptr) : ptr; |
| 7 | 9 | }; |
| 8 | 10 | |
| 9 | - var filter = function(obj, search, opts) { | |
| 10 | - var re = new RegExp(search.replace(/[.\\\\+*?\[\]\{\}\(\)-]/g, "\\$&"), | |
| 11 | + // Writes text into box, wrapping the first match of re in the highlight element. | |
| 12 | + // Text is only ever inserted as text nodes so names are never parsed as HTML. | |
| 13 | + var highlight = function (box, text, re, opts) { | |
| 14 | + var m = re ? text.match(re) : null; | |
| 15 | + if (!m || !m[0].length) { | |
| 16 | + box.text(text); | |
| 17 | + return; | |
| 18 | + } | |
| 19 | + box.empty().append( | |
| 20 | + document.createTextNode(text.slice(0, m.index)), | |
| 21 | + $(opts.highlight).text(m[0]), | |
| 22 | + document.createTextNode(text.slice(m.index + m[0].length)) | |
| 23 | + ); | |
| 24 | + }; | |
| 25 | + | |
| 26 | + var filter = function (obj, search, opts) { | |
| 27 | + var re = new RegExp(search.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&"), | |
| 11 | 28 | opts.caseSensitive ? "" : "i"); |
| 12 | 29 | var ctx = sel(opts.element, obj); |
| 30 | + var n = 0; | |
| 31 | + var size = opts.stepSize || 100; | |
| 13 | 32 | |
| 14 | - ctx.each(function() { | |
| 15 | - var e = $(this); | |
| 33 | + var step = function () { | |
| 34 | + for (var i = 0; i < size; i++) { | |
| 16 | 35 | |
| 17 | - var box = opts.subject ? sel(opts.subject, e) : this; | |
| 18 | - var val = box.text(); | |
| 36 | + if (n >= ctx.length) { | |
| 37 | + if (opts.after) | |
| 38 | + opts.after.apply(ctx); | |
| 39 | + return; | |
| 40 | + } | |
| 19 | 41 | |
| 20 | - if(!search.length) { | |
| 21 | - e.show(); | |
| 22 | - if(opts.highlight) { | |
| 23 | - box.html(val); | |
| 42 | + var e = $(ctx[n++]); | |
| 43 | + | |
| 44 | + var box = opts.subject ? sel(opts.subject, e) : this; | |
| 45 | + var val = box.text(); | |
| 46 | + | |
| 47 | + if (!e.hasClass('hidden')) { | |
| 48 | + if (!search.length) { | |
| 49 | + e.show(); | |
| 50 | + if (opts.highlight) { | |
| 51 | + highlight(box, val, null, opts); | |
| 52 | + } | |
| 53 | + } else if (val.match(re)) { | |
| 54 | + e.show(); | |
| 55 | + if (opts.highlight) { | |
| 56 | + highlight(box, val, re, opts); | |
| 57 | + } | |
| 58 | + } else { | |
| 59 | + e.hide(); | |
| 60 | + } | |
| 24 | 61 | } |
| 25 | - } else if(val.match(re)) { | |
| 26 | - e.show(); | |
| 27 | - if(opts.highlight) { | |
| 28 | - box.html(val.replace(re, opts.highlightRe)); | |
| 29 | - } | |
| 30 | - } else { | |
| 31 | - e.hide(); | |
| 32 | 62 | } |
| 63 | + timer = setTimeout(step, 1); | |
| 64 | + }; | |
| 33 | 65 | |
| 34 | - }); | |
| 66 | + clearTimeout(timer); | |
| 67 | + step(); | |
| 35 | 68 | |
| 36 | - if(opts.after) | |
| 37 | - opts.after.apply(ctx); | |
| 38 | - | |
| 39 | 69 | }; |
| 40 | 70 | |
| 41 | - $.fn.searchFilter = function(opts) { | |
| 42 | - if(opts.highlight) { | |
| 43 | - opts.highlightRe = $("<div>@</div>").wrapInner(opts.highlight).html().replace(/@/g, "$$&"); | |
| 44 | - } | |
| 45 | - $(this).keyup(function() { filter(this, this.value, opts) }); | |
| 46 | - $(this).change(function() { filter(this, this.value, opts) }); | |
| 71 | + $.fn.searchFilter = function (opts) { | |
| 72 | + $(this).on("keyup", function () { | |
| 73 | + filter(this, this.value, opts) | |
| 74 | + }); | |
| 75 | + $(this).on("change", function () { | |
| 76 | + filter(this, this.value, opts) | |
| 77 | + }); | |
| 47 | 78 | } |
| 48 | 79 | |
| 49 | 80 | |
| 50 | -})(window.jQuery); | |
| 81 | +})(window.jQuery); | |