diff --git a/docs/example-widget-mark.html b/docs/example-widget-mark.html new file mode 100644 index 00000000..a96a0621 --- /dev/null +++ b/docs/example-widget-mark.html @@ -0,0 +1,421 @@ + + + + + jQuery tablesorter 2.0 - Mark Widget + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+
+ +

Notes

+
+
    +
  • Added v2.27.0 for use with the mark.js plugin.
  • +
  • Notable Issues: +
      +
    • - Some rows won't be highlighted because the filter finds fuzzy matches across rows, while the mark widget is targeting separate cell content.
    • +
    • - Using a regular expression search will not include diacritics; use an " OR " search instead .
      + I may fix this in future updates. +
    • +
    • or - When a regular expression matches everything, the regex is ignored by the mark widget otherwise, mark.js does bad things; but this regex is okay: .
    • +
    • or - Operators & ranges are not fully supported (these are not supported at all by the filter widget in an "all" columns match - see the documentation on any match limitations).
    • +
    • - There is nothing to highlight for not matches.
    • +
    +
  • +
  • The mark function is used for the following filter queries: +
      +
    • Plain text
    • +
    • Logical AND ( and  or  && ) & logical OR ( or  or |) searches.
    • +
    • Invalid regex (e.g. /(|)/).
    • +
    • Operator, range, exact and not searches.
    • +
    +
  • +
  • The markRegExp function is used when the filter contains: +
      +
    • A "valid" regular expression
    • +
    • Fuzzy search (~)
    • +
    • Wild card ? or * search
    • +
    +
  • + +
  • Try out the jSFiddle Playground.
  • +
+
+ +

Options

+
+

Mark widget defaults (added inside of tablesorter widgetOptions)

+
The widgetOptions are essentially Mark.js settings with a mark_ prefix. All options are dynamically updated.
+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
"markUpdate"This option sets the event that can be triggered on the table to update the marks. +
+
+ Use it as follows: +
$('table').tablesorter({
+  widgets: ['mark'],
+  widgetOptions: {
+    mark_tsUpdate: 'markFu'
+  }
+});
+
+$('button.update').click(function(){
+  // update the marks in the table
+  $('table').trigger('markFu');
+});
+
+
Mark.js settings - see official documentation
mark_element"mark"HTML element to wrap matches.
mark_className""A class name added to the element.
mark_exclude[]An array with exclusion selectors. Matches inside these elements will be ignored.
mark_separateWordSearchtrueWhether to search for each word separated by a blank.
mark_accuracy"partially"Use "partially", "complementary", "exactly" or object - see markjs docs for details.
mark_diacriticstrueIf true, diacritic characters should be matched.
mark_synonyms{}An object with synonyms..
mark_iframesfalseWhether to search also inside iframes.
mark_eachfunction(element){}A callback for each marked element.
function(parameters){ return true; }A callback to filter or limit matches. +
+
+ When the mark function is called, the function parameters are: +
function(node, keyword, matches, totalMatches) { return true; }
+ When the markRegExp function is called, the function parameters are: +
function(node, match, totalMatches) { return true; }
+

In both cases, a return true is needed to highlight the match.

+ Note See the notes section above to know when which function is called. +
+
function(keyword){}A callback function that will be called when there are no matches. +
+
+ When the mark function is called, the keyword parameter is a string. +

When the markRegExp function is called, the keyword parameter is the regular expression.

+ Note See the notes section above to know when which function is called. +
+
mark_donefunction(totalMatches){}A callback function after all marks are done.
mark_debugfalseIf true, messages will be logged.
mark_logconsoleLog message to this object.
+
+ +
+

+ +

Demo: With the Filter Widget

+
+ Search/Highlight all columns: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankFirst NameLast NameAgeTotalDiscountDate
1Philip Aaron WongJohnson Sr Esq25$5.9522%Jun 26, 2004 7:22 AM
11AáronHibert12$2.995%Aug 21, 2009 12:21 PM
12Brandon ClarkHenry Jr51$42.2918%Oct 13, 2000 1:15 PM
111PeterPárker28$9.9920%Jul 6, 2006 8:14 AM
21JohnHood33$19.9925%Dec 10, 2002 5:14 AM
013ClarkKènt Sr.18$15.8944%Jan 12, 2003 11:14 AM
005BruceAlmighty Esq45$153.1944%Jan 18, 2021 9:12 AM
10AlexDumāss13$5.294%Jan 8, 2012 5:11 PM
16JimFranco24$14.1914%Jan 14, 2004 11:23 AM
166Brüce LeeEvans22$13.1911%Jan 18, 2007 9:12 AM
100Brenda DexterMcMasters18$55.2015%Feb 12, 2010 7:23 PM
55DennísBronson65$123.0032%Jan 20, 2001 1:12 PM
9MarthadelFuego25$22.0917%Jun 11, 2011 10:55 AM
+ +

Demo: Without the Filter Widget

+
+ Search/Highlight all columns: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankFirst NameLast NameAgeTotalDiscountDate
1Philip Aaron WongJohnson Sr Esq25$5.9522%Jun 26, 2004 7:22 AM
11AáronHibert12$2.995%Aug 21, 2009 12:21 PM
12Brandon ClarkHenry Jr51$42.2918%Oct 13, 2000 1:15 PM
111PeterPárker28$9.9920%Jul 6, 2006 8:14 AM
21JohnHood33$19.9925%Dec 10, 2002 5:14 AM
013ClarkKènt Sr.18$15.8944%Jan 12, 2003 11:14 AM
005BruceAlmighty Esq45$153.1944%Jan 18, 2021 9:12 AM
10AlexDumāss13$5.294%Jan 8, 2012 5:11 PM
16JimFranco24$14.1914%Jan 14, 2004 11:23 AM
166Brüce LeeEvans22$13.1911%Jan 18, 2007 9:12 AM
100Brenda DexterMcMasters18$55.2015%Feb 12, 2010 7:23 PM
55DennísBronson65$123.0032%Jan 20, 2001 1:12 PM
9MarthadelFuego25$22.0917%Jun 11, 2011 10:55 AM
+ +

Page Header

+
+
<!-- blue theme stylesheet -->
+<link rel="stylesheet" href="css/theme.blue.css">
+<!-- tablesorter plugin -->
+<script src="js/jquery-latest.min.js"></script>
+<script src="js/jquery.tablesorter.js"></script>
+<script src="js/jquery.tablesorter.widgets.js"></script>
+
+<!-- jquery.mark.js & tablesorter mark widget loaded after the plugin -->
+<script src="js/jquery.mark.js"></script>
+<script src="js/widget-mark.js"></script>
+
+ +

CSS

+
+

+	
+ +

Javascript

+
+

+	
+ +
+ + + diff --git a/docs/index.html b/docs/index.html index c8b4d226..67998e4b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -484,6 +484,7 @@
  • Header titles widget (v2.15.6; v2.24.4).
  • Beta Lazyload widget (v2.24.0; v2.25.7).
  • +
  • Mark widget (v2.27.0).
  • Math widget (v2.16; v2.25.5).
  • Output widget (v2.16; v2.25.2). diff --git a/js/widgets/widget-mark.js b/js/widgets/widget-mark.js new file mode 100644 index 00000000..5ce1a1b9 --- /dev/null +++ b/js/widgets/widget-mark.js @@ -0,0 +1,135 @@ +/*! Widget: mark.js - updated 7/27/2016 (v2.27.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter; + + ts.mark = { + init : function( c, wo ) { + if ( typeof $.fn.mark === 'function' ) { + var tmp, + update = c.widgetOptions.mark_tsUpdate; + c.$table.on( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ), function( e, filters ) { + // filterEnd passes "config" as the param + ts.mark.update( c, e.type === 'filterEnd' ? '' : filters ); + }); + // Regex to split up a query + tmp = '(?:<|=|>|\\||\"|' + "\\'|" + + '\\s+(?:&&|-|' + + ( ts.language.and || 'and' ) + '|' + + ( ts.language.or || 'or' ) + '|' + + ( ts.language.to || 'to' ) + ')\\s+)'; + ts.mark.regex.filter = new RegExp(tmp, 'gim'); + } else { + console.warn('Widget-mark not initialized: missing "jquery.mark.js"'); + } + }, + regex : { + mark : /^mark_(.+)$/, + // test for regex (e.g. "/(lorem|ipsum)/gi") + pure : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/ + }, + checkRegex : function( regex ) { + if ( regex instanceof RegExp ) { + // prevent lock up of mark.js (see https://github.com/julmot/mark.js/issues/55) + var result = '\u0001\u0002\u0003\u0004\u0005'.match( regex ); + return result === null || result.length < 5; + } + return false; + }, + cleanMatches : function( matches ) { + var results = [], + indx = matches && matches.length || 0; + while ( indx-- ) { + if ( matches[indx] !== "" ) { + results[ results.length ] = matches[ indx ]; + } + } + return results; + }, + update : function( c, filters ) { + var options = {}, + regex = ts.mark.regex, + $rows = c.$table + .find( 'tbody tr' ) + .unmark() + .not( '.' + ( c.widgetOptions.filter_filteredRow || 'filtered' ) ), + filters = filters || $.tablesorter.getFilters( c.$table ); + // extract & save mark options from widgetOptions (prefixed with "mark_") + // update dynamically + $.each( filters, function( indx, filter ) { + if ( filter ) { + var testRegex = null, + matches = filter, + useRegex = false, + col = indx === c.columns ? '' : ':nth-child(' + ( indx + 1 ) + ')'; + // regular expression entered + if ( regex.pure.test( filter ) ) { + matches = regex.pure.exec( filter ); + // ignore "all" matches (i.e. /.*/) + if (matches[1] === '.*') { + matches[1] = ''; + } + try { + // make sure to include global flag when testing regex + testRegex = new RegExp( matches[ 1 ], 'gim' ); + matches = new RegExp( matches[ 1 ], matches[ 2 ] ); + } catch (err) { + matches = null; + } + if ( ts.mark.checkRegex( testRegex ) ) { + $rows.children( col ).markRegExp( matches, options ); + } + // matches is either null, invalid, or done my markRegExp + return; + } + // all special querys (or, and, wild cards & fuzzy) + // regex seems to not be consistent here; so use string indexOf + // fuzzy or wild card matches + if ( filter.indexOf( '~' ) === 0 ) { + useRegex = true; + // fuzzy search separate each letter + matches = filter.replace( /~/g, '' ).split( '' ); + } else { + // wild card matching + if ( filter.indexOf( '?' ) > -1 ) { + useRegex = true; + filter = filter.replace( /\?/g, '\\S{1}' ); + } + if ( filter.indexOf( '*' ) > -1 ) { + useRegex = true; + filter = filter.replace( /\*/g, '\\S*' ); + } + matches = filter.split( regex.filter ); + } + if ( useRegex && matches && matches.length ) { + matches = new RegExp( ts.mark.cleanMatches( matches ).join( '.*' ), 'gim' ); + if ( ts.mark.checkRegex( matches ) ) { + $rows.children( col ).markRegExp( matches, options ); + } + } else { + // pass an array of matches + $rows.children( col ).mark( ts.mark.cleanMatches( matches ), options ); + } + } + }); + } + }; + + ts.addWidget({ + id: 'mark', + options: { + mark_tsUpdate : "markUpdate" + }, + init : function( table, thisWidget, c, wo ) { + ts.mark.init( c, wo ); + }, + remove : function( table, c ) { + var update = c.widgetOptions.mark_tsUpdate; + c.$table.off( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ) ); + } + }); + +})( jQuery );