├── .gitignore ├── .gitattributes ├── .jshintrc ├── bower.json ├── package.json ├── LICENSE ├── Gruntfile.js ├── dist ├── jquery.searchable-1.0.0.min.js ├── jquery.searchable-1.1.0.min.js └── jquery.searchable-ie-1.1.0.min.js ├── jquery.searchable.js ├── README.md └── jquery.searchable-ie.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_STORE 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js eol=lf 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "browser": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "eqnull": true, 7 | "expr": true, 8 | "evil": true, 9 | "newcap": true, 10 | "noarg": true, 11 | "undef": true, 12 | "unused": true, 13 | "onevar": true, 14 | "immed": true, 15 | "quotmark": true, 16 | "trailing": true, 17 | "jquery": true 18 | } 19 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-searchable", 3 | "version": "1.1.0", 4 | "homepage": "https://github.com/stidges/jquery-searchable", 5 | "authors": [ 6 | "Stidges " 7 | ], 8 | "description": "Tiny, fast jQuery plugin to search through elements as you type.", 9 | "main": [ 10 | "dist/jquery.searchable-1.1.0.min.js", 11 | "dist/jquery.searchable-ie-1.1.0.min.js", 12 | "jquery.searchable.js", 13 | "jquery.searchable-ie.js", 14 | ], 15 | "keywords": [ 16 | "jquery", 17 | "javascript", 18 | "search", 19 | "searchable" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules" 25 | ], 26 | "dependencies": { 27 | "jquery": ">= 1.9.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.searchable", 3 | "title": "jQuery Searchable", 4 | "version": "1.1.0", 5 | "description": "Tiny, fast jQuery plugin to search through elements as you type.", 6 | "main": "jquery.searchable.js", 7 | "scripts": {}, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/stidges/jquery-searchable.git" 11 | }, 12 | "keywords": [ 13 | "javascript", 14 | "jquery", 15 | "search", 16 | "searchable" 17 | ], 18 | "author": { 19 | "name": "Stidges", 20 | "url": "http://twitter.com/stidges" 21 | }, 22 | "license": "MIT", 23 | "dependencies": {}, 24 | "devDependencies": { 25 | "grunt": "~0.4.2", 26 | "grunt-contrib-jshint": "~0.8.0", 27 | "grunt-contrib-uglify": "~0.2.7", 28 | "grunt-contrib-watch": "~0.5.3", 29 | "grunt-jscs-checker": "~0.3.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Stidges 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = function (grunt) { 6 | 7 | grunt.initConfig({ 8 | pkg: grunt.file.readJSON('package.json'), 9 | 10 | jshint: { 11 | files: [ 12 | 'jquery.searchable.js', 13 | 'jquery.searchable-ie.js', 14 | 'Gruntfile.js' 15 | ], 16 | options: { 17 | jshintrc: true 18 | } 19 | }, 20 | 21 | uglify: { 22 | options: { 23 | banner: '/*! <%= pkg.title %> v<%= pkg.version %> ' + 24 | 'by <%= pkg.author.name %> (<%= pkg.author.url %>) ' + 25 | '| <%= pkg.license %> */\n' 26 | }, 27 | min: { 28 | files: { 29 | 'dist/jquery.searchable-<%= pkg.version %>.min.js': 'jquery.searchable.js', 30 | 'dist/jquery.searchable-ie-<%= pkg.version %>.min.js': 'jquery.searchable-ie.js' 31 | } 32 | } 33 | }, 34 | 35 | jscs: { 36 | all: { 37 | options: { 38 | preset: 'jquery' 39 | }, 40 | src: [ 'jquery.searchable.js', 'jquery.searchable-ie.js' ] 41 | } 42 | }, 43 | 44 | watch: { 45 | files: [ 'jquery.searchable.js', 'jquery.searchable-ie.js' ], 46 | tasks: 'default' 47 | } 48 | }); 49 | 50 | grunt.loadNpmTasks('grunt-contrib-jshint'); 51 | grunt.loadNpmTasks('grunt-contrib-uglify'); 52 | grunt.loadNpmTasks('grunt-contrib-watch'); 53 | grunt.loadNpmTasks('grunt-jscs-checker'); 54 | 55 | grunt.registerTask('default', [ 'jscs', 'jshint', 'uglify' ]); 56 | }; 57 | -------------------------------------------------------------------------------- /dist/jquery.searchable-1.0.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Searchable v1.0.0 by Stidges (http://twitter.com/stidges) | MIT */ 2 | !function(a){function b(b,c){this.$element=a(b),this.settings=a.extend({},d,c),this.init()}var c="searchable",d={selector:"tbody tr",childSelector:"td",searchField:"#search",striped:!1,oddRow:{},evenRow:{},hide:function(a){a.hide()},show:function(a){a.show()},searchType:"default"};b.prototype={init:function(){this.$searchElems=a(this.settings.selector,this.$element),this.$search=a(this.settings.searchField),this.matcherFunc=this.getMatcherFunction(this.settings.searchType),this.bindEvents(),this.updateStriping()},bindEvents:function(){var b=this;this.$search.on("change keyup",function(){b.search(a(this).val()),b.updateStriping()}),""!==this.$search.val()&&this.$search.trigger("change")},updateStriping:function(){var b=this,c=["oddRow","evenRow"],d=this.settings.selector+":visible";this.settings.striped&&a(d,this.$element).each(function(d,e){a(e).css(b.settings[c[d%2]])})},search:function(b){var c,d,e,f,g,h,i,j;if(0===a.trim(b).length)return this.$searchElems.css("display",""),void this.updateStriping();for(d=this.$searchElems.length,c=this.matcherFunc(b),i=0;d>i;i++){for(h=a(this.$searchElems[i]),e=h.find(this.settings.childSelector),f=e.length,g=!0,j=0;f>j;j++)if(c(a(e[j]).text())){g=!1;break}g===!0?this.settings.hide(h):this.settings.show(h)}},getMatcherFunction:function(a){return"fuzzy"===a?this.getFuzzyMatcher:"strict"===a?this.getStrictMatcher:this.getDefaultMatcher},getFuzzyMatcher:function(a){var b,c=a.split("").reduce(function(a,b){return a+"[^"+b+"]*"+b});return b=new RegExp(c,"gi"),function(a){return b.test(a)}},getStrictMatcher:function(b){return b=a.trim(b),function(a){return-1!==a.indexOf(b)}},getDefaultMatcher:function(b){return b=a.trim(b).toLowerCase(),function(a){return-1!==a.toLowerCase().indexOf(b)}}},a.fn[c]=function(d){return this.each(function(){a.data(this,"plugin_"+c)||a.data(this,"plugin_"+c,new b(this,d))})}}(jQuery,window,document); -------------------------------------------------------------------------------- /dist/jquery.searchable-1.1.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Searchable v1.1.0 by Stidges (http://twitter.com/stidges) | MIT */ 2 | !function(a){function b(a){return"function"==typeof a}function c(b,c){this.$element=a(b),this.settings=a.extend({},e,c),this.init()}var d="searchable",e={selector:"tbody tr",childSelector:"td",searchField:"#search",striped:!1,oddRow:{},evenRow:{},hide:function(a){a.hide()},show:function(a){a.show()},searchType:"default",onSearchActive:!1,onSearchEmpty:!1,onSearchFocus:!1,onSearchBlur:!1,clearOnLoad:!1},f=!1,g=!1,h=!1,i=!1;c.prototype={init:function(){this.$searchElems=a(this.settings.selector,this.$element),this.$search=a(this.settings.searchField),this.matcherFunc=this.getMatcherFunction(this.settings.searchType),this.determineCallbacks(),this.bindEvents(),this.updateStriping()},determineCallbacks:function(){f=b(this.settings.onSearchActive),g=b(this.settings.onSearchEmpty),h=b(this.settings.onSearchFocus),i=b(this.settings.onSearchBlur)},bindEvents:function(){var b=this;this.$search.on("change keyup",function(){b.search(a(this).val()),b.updateStriping()}),h&&this.$search.on("focus",this.settings.onSearchFocus),i&&this.$search.on("blur",this.settings.onSearchBlur),this.settings.clearOnLoad===!0&&(this.$search.val(""),this.$search.trigger("change")),""!==this.$search.val()&&this.$search.trigger("change")},updateStriping:function(){var b=this,c=["oddRow","evenRow"],d=this.settings.selector+":visible";this.settings.striped&&a(d,this.$element).each(function(d,e){a(e).css(b.settings[c[d%2]])})},search:function(b){var c,d,e,h,i,j,k,l;if(0===a.trim(b).length)return this.$searchElems.css("display",""),this.updateStriping(),void(g&&this.settings.onSearchEmpty(this.$element));for(f&&this.settings.onSearchActive(this.$element,b),d=this.$searchElems.length,c=this.matcherFunc(b),k=0;d>k;k++){for(j=a(this.$searchElems[k]),e=j.find(this.settings.childSelector),h=e.length,i=!0,l=0;h>l;l++)if(c(a(e[l]).text())){i=!1;break}i===!0?this.settings.hide(j):this.settings.show(j)}},getMatcherFunction:function(a){return"fuzzy"===a?this.getFuzzyMatcher:"strict"===a?this.getStrictMatcher:this.getDefaultMatcher},getFuzzyMatcher:function(a){var b,c=a.split("").reduce(function(a,b){return a+"[^"+b+"]*"+b});return b=new RegExp(c,"gi"),function(a){return b.test(a)}},getStrictMatcher:function(b){return b=a.trim(b),function(a){return-1!==a.indexOf(b)}},getDefaultMatcher:function(b){return b=a.trim(b).toLowerCase(),function(a){return-1!==a.toLowerCase().indexOf(b)}}},a.fn[d]=function(b){return this.each(function(){a.data(this,"plugin_"+d)||a.data(this,"plugin_"+d,new c(this,b))})}}(jQuery,window,document); -------------------------------------------------------------------------------- /dist/jquery.searchable-ie-1.1.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Searchable v1.1.0 by Stidges (http://twitter.com/stidges) | MIT */ 2 | !function(a){function b(a){return"function"==typeof a}function c(b,c){this.$element=a(b),this.settings=a.extend({},e,c),this.init()}var d="searchable",e={selector:"tbody tr",childSelector:"td",searchField:"#search",striped:!1,oddRow:{},evenRow:{},hide:function(a){a.hide()},show:function(a){a.show()},searchType:"default",onSearchActive:!1,onSearchEmpty:!1,onSearchFocus:!1,onSearchBlur:!1,clearOnLoad:!1},f=!1,g=!1,h=!1,i=!1;"function"!=typeof Array.prototype.reduce&&(Array.prototype.reduce=function(a,b){"use strict";if(null===this||"undefined"==typeof this)throw new TypeError("Array.prototype.reduce called on null or undefined");if("function"!=typeof a)throw new TypeError(a+" is not a function");var c,d,e=this.length>>>0,f=!1;for(1c;++c)this.hasOwnProperty(c)&&(f?d=a(d,this[c],c,this):(d=this[c],f=!0));if(!f)throw new TypeError("Reduce of empty array with no initial value");return d}),c.prototype={init:function(){this.$searchElems=a(this.settings.selector,this.$element),this.$search=a(this.settings.searchField),this.matcherFunc=this.getMatcherFunction(this.settings.searchType),this.determineCallbacks(),this.bindEvents(),this.updateStriping()},determineCallbacks:function(){f=b(this.settings.onSearchActive),g=b(this.settings.onSearchEmpty),h=b(this.settings.onSearchFocus),i=b(this.settings.onSearchBlur)},bindEvents:function(){var b=this;this.$search.on("change keyup",function(){b.search(a(this).val()),b.updateStriping()}),h&&this.$search.on("focus",this.settings.onSearchFocus),i&&this.$search.on("blur",this.settings.onSearchBlur),this.settings.clearOnLoad===!0&&(this.$search.val(""),this.$search.trigger("change")),""!==this.$search.val()&&this.$search.trigger("change")},updateStriping:function(){var b=this,c=["oddRow","evenRow"],d=this.settings.selector+":visible";this.settings.striped&&a(d,this.$element).each(function(d,e){a(e).css(b.settings[c[d%2]])})},search:function(b){var c,d,e,h,i,j,k,l;if(0===a.trim(b).length)return this.$searchElems.css("display",""),this.updateStriping(),void(g&&this.settings.onSearchEmpty(this.$element));for(f&&this.settings.onSearchActive(this.$element,b),d=this.$searchElems.length,c=this.matcherFunc(b),k=0;d>k;k++){for(j=a(this.$searchElems[k]),e=j.find(this.settings.childSelector),h=e.length,i=!0,l=0;h>l;l++)if(c(a(e[l]).text())){i=!1;break}i===!0?this.settings.hide(j):this.settings.show(j)}},getMatcherFunction:function(a){return"fuzzy"===a?this.getFuzzyMatcher:"strict"===a?this.getStrictMatcher:this.getDefaultMatcher},getFuzzyMatcher:function(a){var b,c=a.split("").reduce(function(a,b){return a+"[^"+b+"]*"+b});return b=new RegExp(c,"gi"),function(a){return b.test(a)}},getStrictMatcher:function(b){return b=a.trim(b),function(a){return-1!==a.indexOf(b)}},getDefaultMatcher:function(b){return b=a.trim(b).toLowerCase(),function(a){return-1!==a.toLowerCase().indexOf(b)}}},a.fn[d]=function(b){return this.each(function(){a.data(this,"plugin_"+d)||a.data(this,"plugin_"+d,new c(this,b))})}}(jQuery,window,document); -------------------------------------------------------------------------------- /jquery.searchable.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Searchable Plugin v1.0.0 3 | * https://github.com/stidges/jquery-searchable 4 | * 5 | * Copyright 2014 Stidges 6 | * Released under the MIT license 7 | */ 8 | ;(function( $, window, document, undefined ) { 9 | 10 | var pluginName = 'searchable', 11 | defaults = { 12 | selector: 'tbody tr', 13 | childSelector: 'td', 14 | searchField: '#search', 15 | striped: false, 16 | oddRow: { }, 17 | evenRow: { }, 18 | hide: function( elem ) { elem.hide(); }, 19 | show: function( elem ) { elem.show(); }, 20 | searchType: 'default', 21 | onSearchActive: false, 22 | onSearchEmpty: false, 23 | onSearchFocus: false, 24 | onSearchBlur: false, 25 | clearOnLoad: false 26 | }, 27 | searchActiveCallback = false, 28 | searchEmptyCallback = false, 29 | searchFocusCallback = false, 30 | searchBlurCallback = false; 31 | 32 | function isFunction(value) { 33 | return typeof value === 'function'; 34 | } 35 | 36 | function Plugin( element, options ) { 37 | this.$element = $( element ); 38 | this.settings = $.extend( {}, defaults, options ); 39 | 40 | this.init(); 41 | } 42 | 43 | Plugin.prototype = { 44 | init: function() { 45 | this.$searchElems = $( this.settings.selector, this.$element ); 46 | this.$search = $( this.settings.searchField ); 47 | this.matcherFunc = this.getMatcherFunction( this.settings.searchType ); 48 | 49 | this.determineCallbacks(); 50 | this.bindEvents(); 51 | this.updateStriping(); 52 | }, 53 | 54 | determineCallbacks: function() { 55 | searchActiveCallback = isFunction( this.settings.onSearchActive ); 56 | searchEmptyCallback = isFunction( this.settings.onSearchEmpty ); 57 | searchFocusCallback = isFunction( this.settings.onSearchFocus ); 58 | searchBlurCallback = isFunction( this.settings.onSearchBlur ); 59 | }, 60 | 61 | bindEvents: function() { 62 | var that = this; 63 | 64 | this.$search.on( 'change keyup', function() { 65 | that.search( $( this ).val() ); 66 | 67 | that.updateStriping(); 68 | }); 69 | 70 | if ( searchFocusCallback ) { 71 | this.$search.on( 'focus', this.settings.onSearchFocus ); 72 | } 73 | 74 | if ( searchBlurCallback ) { 75 | this.$search.on( 'blur', this.settings.onSearchBlur ); 76 | } 77 | 78 | if ( this.settings.clearOnLoad === true ) { 79 | this.$search.val( '' ); 80 | this.$search.trigger( 'change' ); 81 | } 82 | 83 | if ( this.$search.val() !== '' ) { 84 | this.$search.trigger( 'change' ); 85 | } 86 | }, 87 | 88 | updateStriping: function() { 89 | var that = this, 90 | styles = [ 'oddRow', 'evenRow' ], 91 | selector = this.settings.selector + ':visible'; 92 | 93 | if ( !this.settings.striped ) { 94 | return; 95 | } 96 | 97 | $( selector, this.$element ).each( function( i, row ) { 98 | $( row ).css( that.settings[ styles[ i % 2 ] ] ); 99 | }); 100 | }, 101 | 102 | search: function( term ) { 103 | var matcher, elemCount, children, childCount, hide, $elem, i, x; 104 | 105 | if ( $.trim( term ).length === 0 ) { 106 | this.$searchElems.css( 'display', '' ); 107 | this.updateStriping(); 108 | 109 | if ( searchEmptyCallback ) { 110 | this.settings.onSearchEmpty( this.$element ); 111 | } 112 | 113 | return; 114 | } else if ( searchActiveCallback ) { 115 | this.settings.onSearchActive( this.$element, term ); 116 | } 117 | 118 | elemCount = this.$searchElems.length; 119 | matcher = this.matcherFunc( term ); 120 | 121 | for ( i = 0; i < elemCount; i++ ) { 122 | $elem = $( this.$searchElems[ i ] ); 123 | children = $elem.find( this.settings.childSelector ); 124 | childCount = children.length; 125 | hide = true; 126 | 127 | for ( x = 0; x < childCount; x++ ) { 128 | if ( matcher( $( children[ x ] ).text() ) ) { 129 | hide = false; 130 | break; 131 | } 132 | } 133 | 134 | if ( hide === true ) { 135 | this.settings.hide( $elem ); 136 | } else { 137 | this.settings.show( $elem ); 138 | } 139 | } 140 | }, 141 | 142 | getMatcherFunction: function( type ) { 143 | if ( type === 'fuzzy' ) { 144 | return this.getFuzzyMatcher; 145 | } else if ( type === 'strict' ) { 146 | return this.getStrictMatcher; 147 | } 148 | 149 | return this.getDefaultMatcher; 150 | }, 151 | 152 | getFuzzyMatcher: function( term ) { 153 | var regexMatcher, 154 | pattern = term.split( '' ).reduce( function( a, b ) { 155 | return a + '[^' + b + ']*' + b; 156 | }); 157 | 158 | regexMatcher = new RegExp( pattern, 'gi' ); 159 | 160 | return function( s ) { 161 | return regexMatcher.test( s ); 162 | }; 163 | }, 164 | 165 | getStrictMatcher: function( term ) { 166 | term = $.trim( term ); 167 | 168 | return function( s ) { 169 | return ( s.indexOf( term ) !== -1 ); 170 | }; 171 | }, 172 | 173 | getDefaultMatcher: function( term ) { 174 | term = $.trim( term ).toLowerCase(); 175 | 176 | return function( s ) { 177 | return ( s.toLowerCase().indexOf( term ) !== -1 ); 178 | }; 179 | } 180 | }; 181 | 182 | $.fn[ pluginName ] = function( options ) { 183 | return this.each( function() { 184 | if ( !$.data( this, 'plugin_' + pluginName ) ) { 185 | $.data( this, 'plugin_' + pluginName, new Plugin(this, options) ); 186 | } 187 | }); 188 | }; 189 | 190 | })( jQuery, window, document ); 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Searchable Plugin 2 | ======================== 3 | 4 | *Latest version: v1.1.0* (View the [changelog](#changelog)) 5 | 6 | Tiny, fast jQuery plugin to search through elements as you type. This plugin is created and maintained by **Stidges** ( [Twitter](http://twitter.com/stidges) | [Github](http://github.com/stidges) ). 7 | 8 | ## Features 9 | 10 | - **Lightweight**. This plugin is only ~1kB minified and gzipped! 11 | - **Fast**. This plugin is optimized for fast, lagless searching even through large element sets. 12 | - **Multiple search types**. This plugin provides three different search types out-of-the-box! Fuzzy matching, strict (case sensitive) matching and default (case insensitive) matching. 13 | - **Automatic row striping**. When searching through a table, rows get hidden when they don't match. When using a CSS framework like [Bootstrap](http://getbootstrap.com) this would mess up your table striping. This plugin makes allows you to define the CSS to be applied to odd and even rows, and updates them while searching. 14 | - **Custom show/hide**. You can define custom functions for showing and hiding the elements while searching. 15 | - **Search anything**. This plugin isn't restricted to use on tables, any set of elements that has 'rows' with 'columns' inside them can be used. 16 | 17 | ## Demo 18 | 19 | [Click here](http://bootsnipp.com/snippets/93XX) to view a demo of this plugin in action (Hosted on [Bootsnipp](http://bootsnipp.com)) 20 | 21 | ## Getting started 22 | 23 | ### Basic usage 24 | 25 | After downloading this plugin, include it in your HTML file after loading jQuery: 26 | 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | **Note**: If you want to support older browsers like >> 0, 52 | isValueSet = false; 53 | 54 | if ( 1 < arguments.length ) { 55 | value = opt_initialValue; 56 | isValueSet = true; 57 | } 58 | 59 | for ( index = 0; length > index; ++index ) { 60 | if ( this.hasOwnProperty( index ) ) { 61 | if ( isValueSet ) { 62 | value = callback( value, this[ index ], index, this ); 63 | } else { 64 | value = this[ index ]; 65 | isValueSet = true; 66 | } 67 | } 68 | } 69 | 70 | if ( !isValueSet ) { 71 | throw new TypeError('Reduce of empty array with no initial value'); 72 | } 73 | 74 | return value; 75 | }; 76 | } 77 | 78 | function Plugin( element, options ) { 79 | this.$element = $( element ); 80 | this.settings = $.extend( {}, defaults, options ); 81 | 82 | this.init(); 83 | } 84 | 85 | Plugin.prototype = { 86 | init: function() { 87 | this.$searchElems = $( this.settings.selector, this.$element ); 88 | this.$search = $( this.settings.searchField ); 89 | this.matcherFunc = this.getMatcherFunction( this.settings.searchType ); 90 | 91 | this.determineCallbacks(); 92 | this.bindEvents(); 93 | this.updateStriping(); 94 | }, 95 | 96 | determineCallbacks: function() { 97 | searchActiveCallback = isFunction( this.settings.onSearchActive ); 98 | searchEmptyCallback = isFunction( this.settings.onSearchEmpty ); 99 | searchFocusCallback = isFunction( this.settings.onSearchFocus ); 100 | searchBlurCallback = isFunction( this.settings.onSearchBlur ); 101 | }, 102 | 103 | bindEvents: function() { 104 | var that = this; 105 | 106 | this.$search.on( 'change keyup', function() { 107 | that.search( $( this ).val() ); 108 | 109 | that.updateStriping(); 110 | }); 111 | 112 | if ( searchFocusCallback ) { 113 | this.$search.on( 'focus', this.settings.onSearchFocus ); 114 | } 115 | 116 | if ( searchBlurCallback ) { 117 | this.$search.on( 'blur', this.settings.onSearchBlur ); 118 | } 119 | 120 | if ( this.settings.clearOnLoad === true ) { 121 | this.$search.val( '' ); 122 | this.$search.trigger( 'change' ); 123 | } 124 | 125 | if ( this.$search.val() !== '' ) { 126 | this.$search.trigger( 'change' ); 127 | } 128 | }, 129 | 130 | updateStriping: function() { 131 | var that = this, 132 | styles = [ 'oddRow', 'evenRow' ], 133 | selector = this.settings.selector + ':visible'; 134 | 135 | if ( !this.settings.striped ) { 136 | return; 137 | } 138 | 139 | $( selector, this.$element ).each( function( i, row ) { 140 | $( row ).css( that.settings[ styles[ i % 2 ] ] ); 141 | }); 142 | }, 143 | 144 | search: function( term ) { 145 | var matcher, elemCount, children, childCount, hide, $elem, i, x; 146 | 147 | if ( $.trim( term ).length === 0 ) { 148 | this.$searchElems.css( 'display', '' ); 149 | this.updateStriping(); 150 | 151 | if ( searchEmptyCallback ) { 152 | this.settings.onSearchEmpty( this.$element ); 153 | } 154 | 155 | return; 156 | } else if ( searchActiveCallback ) { 157 | this.settings.onSearchActive( this.$element, term ); 158 | } 159 | 160 | elemCount = this.$searchElems.length; 161 | matcher = this.matcherFunc( term ); 162 | 163 | for ( i = 0; i < elemCount; i++ ) { 164 | $elem = $( this.$searchElems[ i ] ); 165 | children = $elem.find( this.settings.childSelector ); 166 | childCount = children.length; 167 | hide = true; 168 | 169 | for ( x = 0; x < childCount; x++ ) { 170 | if ( matcher( $( children[ x ] ).text() ) ) { 171 | hide = false; 172 | break; 173 | } 174 | } 175 | 176 | if ( hide === true ) { 177 | this.settings.hide( $elem ); 178 | } else { 179 | this.settings.show( $elem ); 180 | } 181 | } 182 | }, 183 | 184 | getMatcherFunction: function( type ) { 185 | if ( type === 'fuzzy' ) { 186 | return this.getFuzzyMatcher; 187 | } else if ( type === 'strict' ) { 188 | return this.getStrictMatcher; 189 | } 190 | 191 | return this.getDefaultMatcher; 192 | }, 193 | 194 | getFuzzyMatcher: function( term ) { 195 | var regexMatcher, 196 | pattern = term.split( '' ).reduce( function( a, b ) { 197 | return a + '[^' + b + ']*' + b; 198 | }); 199 | 200 | regexMatcher = new RegExp( pattern, 'gi' ); 201 | 202 | return function( s ) { 203 | return regexMatcher.test( s ); 204 | }; 205 | }, 206 | 207 | getStrictMatcher: function( term ) { 208 | term = $.trim( term ); 209 | 210 | return function( s ) { 211 | return ( s.indexOf( term ) !== -1 ); 212 | }; 213 | }, 214 | 215 | getDefaultMatcher: function( term ) { 216 | term = $.trim( term ).toLowerCase(); 217 | 218 | return function( s ) { 219 | return ( s.toLowerCase().indexOf( term ) !== -1 ); 220 | }; 221 | } 222 | }; 223 | 224 | $.fn[ pluginName ] = function( options ) { 225 | return this.each( function() { 226 | if ( !$.data( this, 'plugin_' + pluginName ) ) { 227 | $.data( this, 'plugin_' + pluginName, new Plugin(this, options) ); 228 | } 229 | }); 230 | }; 231 | 232 | })( jQuery, window, document ); 233 | --------------------------------------------------------------------------------