From 65867145dfe02624c4f6bb825524c296a744a562 Mon Sep 17 00:00:00 2001 From: Rob Garrison Date: Sun, 8 Nov 2015 20:47:30 -0600 Subject: [PATCH] Sort2Hash: Add 2 utility functions to simplify hash processing --- dist/js/widgets/widget-sort2Hash.min.js | 2 +- docs/example-widget-sort-to-hash.html | 138 +++++++++++++++--------- js/widgets/widget-sort2Hash.js | 64 +++++++---- 3 files changed, 134 insertions(+), 70 deletions(-) diff --git a/dist/js/widgets/widget-sort2Hash.min.js b/dist/js/widgets/widget-sort2Hash.min.js index f6174a68..490c59f5 100644 --- a/dist/js/widgets/widget-sort2Hash.min.js +++ b/dist/js/widgets/widget-sort2Hash.min.js @@ -1,2 +1,2 @@ /*! Widget: sort2Hash (BETA) - updated 11/8/2015 (v2.24.4) */ -!function(a){"use strict";var b=a.tablesorter||{},c=b.sort2Hash={init:function(d,e){var f,g,h,i,j=d.table,k=d.pager,l=b.hasWidget(j,"saveSort"),m=c.decodeHash(d,e,"sort");(m&&!l||m&&l&&e.sort2Hash_overrideSaveSort)&&c.convertString2Sort(d,e,m),b.hasWidget(d.table,"pager")&&(g=parseInt(c.decodeHash(d,e,"page"),10),h=k.page=(0>g?0:g>k.totalPages?k.totalPages-1:g)+1,i=k.size=parseInt(c.decodeHash(d,e,"size"),10)),b.hasWidget(j,"filter")?(f=c.decodeHash(d,e,"filter"),f&&(f=f.split(e.sort2Hash_separator),d.$table.one("tablesorter-ready",function(){setTimeout(function(){d.$table.one("filterEnd",function(){a(this).trigger("pageAndSize",[h,i])}),a.tablesorter.setFilters(j,f,!0)},100)}))):d.$table.trigger("pageAndSize",[h,i]),d.$table.on("sortEnd.sort2hash filterEnd.sort2hash pagerComplete.sort2Hash",function(){this.hasInitialized&&c.setHash(this.config,this.config.widgetOptions)})},getTableId:function(b,c){return c.sort2Hash_tableId||b.table.id||"table"+a("table").index(b.$table)},regexEscape:function(a){return a.replace(/([\.\^\$\*\+\-\?\(\)\[\]\{\}\\\|])/g,"\\$1")},convertString2Sort:function(a,b,d){for(var e,f,g,h,i,j,k=d.split(b.sort2Hash_separator),l=0,m=k.length,n=[];m>l;){if(f=k[l++],h=parseInt(f,10),isNaN(h)||h>a.columns)for(e=new RegExp("("+c.regexEscape(f)+")","i"),i=0;i-1?1:0),n.push([f,g]))}n.length&&(a.sortList=n)},convertSort2String:function(b,c){var d,e,f,g,h=[],i=b.sortList||[],j=i.length;for(d=0;j>d;d++)f=i[d][0],e=a.trim(b.$headerIndexed[f].attr(c.sort2Hash_headerTextAttr)),h.push(""!==e?encodeURIComponent(e):f),g=c.sort2Hash_directionText[i[d][1]],h.push(g);return h.join(c.sort2Hash_separator)},convertFilter2String:function(b,c){var d,e,f,g,h=[],i=b.sortList||[],j=i.length;for(d=0;j>d;d++)f=i[d][0],e=a.trim(b.$headerIndexed[f].attr(c.sort2Hash_headerTextAttr)),f="undefined"!=typeof e?encodeURIComponent(e):f,h.push(f),g=c.sort2Hash_directionText[i[d][1]],h.push(g);return h.join(c.sort2Hash_separator)},encodeHash:function(a,b,d,e,f){var g=!1,h=c.getTableId(a,b);return"function"==typeof b.sort2Hash_encodeHash&&(g=b.sort2Hash_encodeHash(a,h,d,e,f||e)),g===!1&&(g="&"+d+"["+h+"]="+e),g},decodeHash:function(a,b,d){var e,f=!1,g=c.getTableId(a,b);return"function"==typeof b.sort2Hash_decodeHash&&(f=b.sort2Hash_decodeHash(a,g,d)),f===!1&&(e=new RegExp("[\\#&]"+d+"\\["+c.regexEscape(g)+"\\]=([^&]*)"),f=e.exec(window.location.hash)),f?decodeURIComponent(f[1]):""},cleanHash:function(a,b,d,e){var f,g,h,i,j=!1,k=c.getTableId(a,b);if("function"==typeof b.sort2Hash_cleanHash&&(j=b.sort2Hash_cleanHash(a,k,d,e)),j===!1)for(j=[],h=(e||"").slice(1).split("&"),g=h.length,i=new RegExp(d+"\\["+c.regexEscape(k)+"\\]=([^&]*)"),f=0;g>f;f++)i.test(h[f])||j.push(h[f]);return j.length?"#"+j.join("&"):""},setHash:function(d,e){var f="",g=window.location.hash,h=b.hasWidget(d.table,"pager"),i=b.hasWidget(d.table,"filter"),j=c.convertSort2String(d,e),k=i&&""!==d.lastSearch.join("")?d.lastSearch:[],l=encodeURIComponent(k.join(d.widgetOptions.sort2Hash_separator)),m={sort:j?c.encodeHash(d,e,"sort",j,d.sortList):"",page:h?c.encodeHash(d,e,"page",d.pager.page+1):"",size:h?c.encodeHash(d,e,"size",d.pager.size):"",filter:l?c.encodeHash(d,e,"filter",l,k):""};a.each(m,function(a,b){g=c.cleanHash(d,e,a,g),f+=b}),window.location.hash=((window.location.hash||"").replace("#","").length?g:e.sort2Hash_hash)+f}};b.addWidget({id:"sort2Hash",priority:60,options:{sort2Hash_hash:"#",sort2Hash_separator:"-",sort2Hash_headerTextAttr:"data-header",sort2Hash_directionText:[0,1],sort2Hash_overrideSaveSort:!1},init:function(a,b,d,e){c.init(d,e)},remove:function(a,b){b.$table.off(".sort2hash")}})}(jQuery); \ No newline at end of file +!function(a){"use strict";var b=a.tablesorter||{},c=b.sort2Hash={init:function(d,e){var f,g,h,i,j=d.table,k=d.pager,l=b.hasWidget(j,"saveSort"),m=c.decodeHash(d,e,"sort");(m&&!l||m&&l&&e.sort2Hash_overrideSaveSort)&&c.convertString2Sort(d,e,m),b.hasWidget(d.table,"pager")&&(g=parseInt(c.decodeHash(d,e,"page"),10),h=k.page=0>g?0:g>k.totalPages?k.totalPages-1:g,i=k.size=parseInt(c.decodeHash(d,e,"size"),10)),b.hasWidget(j,"filter")&&(f=c.decodeHash(d,e,"filter"),f&&(f=f.split(e.sort2Hash_separator),d.$table.one("tablesorter-ready",function(){setTimeout(function(){d.$table.one("filterEnd",function(){a(this).trigger("pageAndSize",[h,i])}),a.tablesorter.setFilters(j,f,!0)},100)}))),f||d.$table.one("tablesorter-ready",function(){d.$table.trigger("pageAndSize",[h,i])}),d.$table.on("sortEnd.sort2hash filterEnd.sort2hash pagerComplete.sort2Hash",function(){this.hasInitialized&&c.setHash(this.config,this.config.widgetOptions)})},getTableId:function(b,c){return c.sort2Hash_tableId||b.table.id||"table"+a("table").index(b.$table)},regexEscape:function(a){return a.replace(/([\.\^\$\*\+\-\?\(\)\[\]\{\}\\\|])/g,"\\$1")},convertString2Sort:function(a,b,d){for(var e,f,g,h,i,j,k=d.split(b.sort2Hash_separator),l=0,m=k.length,n=[];m>l;){if(f=k[l++],h=parseInt(f,10),isNaN(h)||h>a.columns)for(e=new RegExp("("+c.regexEscape(f)+")","i"),i=0;i-1?1:0),n.push([f,g]))}n.length&&(a.sortList=n)},convertSort2String:function(b,c){var d,e,f,g,h=[],i=b.sortList||[],j=i.length;for(d=0;j>d;d++)f=i[d][0],e=a.trim(b.$headerIndexed[f].attr(c.sort2Hash_headerTextAttr)),h.push(""!==e?encodeURIComponent(e):f),g=c.sort2Hash_directionText[i[d][1]],h.push(g);return h.join(c.sort2Hash_separator)},convertFilter2String:function(b,c){var d,e,f,g,h=[],i=b.sortList||[],j=i.length;for(d=0;j>d;d++)f=i[d][0],e=a.trim(b.$headerIndexed[f].attr(c.sort2Hash_headerTextAttr)),f="undefined"!=typeof e?encodeURIComponent(e):f,h.push(f),g=c.sort2Hash_directionText[i[d][1]],h.push(g);return h.join(c.sort2Hash_separator)},getParam:function(a,b,d){b||(b=window.location.hash);var e=new RegExp("[\\?&]"+c.regexEscape(a)+"=([^&#]*)"),f=e.exec(b);return d?e:null===f?"":decodeURIComponent(f[1])},removeParam:function(a,b){b||(b=window.location.hash);var d,e=c.getParam(a,b,!0),f=[],g=b.split("&"),h=g.length;for(d=0;h>d;d++)e.test("&"+g[d])||f.push(g[d]);return f.length?f.join("&"):""},encodeHash:function(a,b,d,e,f){var g=!1,h=c.getTableId(a,b);return"function"==typeof b.sort2Hash_encodeHash&&(g=b.sort2Hash_encodeHash(a,h,d,e,f||e)),g===!1&&(g="&"+d+"["+h+"]="+e),g},decodeHash:function(a,b,d){var e=!1,f=c.getTableId(a,b);return"function"==typeof b.sort2Hash_decodeHash&&(e=b.sort2Hash_decodeHash(a,f,d)),e===!1&&(e=c.getParam(d+"["+f+"]")),e||""},cleanHash:function(a,b,d,e){var f=!1,g=c.getTableId(a,b);return"function"==typeof b.sort2Hash_cleanHash&&(f=b.sort2Hash_cleanHash(a,g,d,e)),f===!1&&(f=c.removeParam(d+"["+g+"]",e)),f||""},setHash:function(d,e){var f="",g=window.location.hash,h=b.hasWidget(d.table,"pager"),i=b.hasWidget(d.table,"filter"),j=c.convertSort2String(d,e),k=i&&""!==d.lastSearch.join("")?d.lastSearch:[],l=encodeURIComponent(k.join(d.widgetOptions.sort2Hash_separator)),m={sort:j?c.encodeHash(d,e,"sort",j,d.sortList):"",page:h?c.encodeHash(d,e,"page",d.pager.page+1):"",size:h?c.encodeHash(d,e,"size",d.pager.size):"",filter:l?c.encodeHash(d,e,"filter",l,k):""};a.each(m,function(a,b){g=c.cleanHash(d,e,a,g),f+=b}),window.location.hash=((window.location.hash||"").replace("#","").length?g:e.sort2Hash_hash)+f}};b.addWidget({id:"sort2Hash",priority:60,options:{sort2Hash_hash:"#",sort2Hash_separator:"-",sort2Hash_headerTextAttr:"data-header",sort2Hash_directionText:[0,1],sort2Hash_overrideSaveSort:!1},init:function(a,b,d,e){c.init(d,e)},remove:function(a,b){b.$table.off(".sort2hash")}})}(jQuery); \ No newline at end of file diff --git a/docs/example-widget-sort-to-hash.html b/docs/example-widget-sort-to-hash.html index 32eee5a0..6632aa90 100644 --- a/docs/example-widget-sort-to-hash.html +++ b/docs/example-widget-sort-to-hash.html @@ -61,28 +61,23 @@ // tableId = processed table ID // component: 'sort', 'page', 'size' or 'filter' // return false to let the widget decode the hash - var regex = new RegExp( '[\\#&]' + component + '\\[' + tableId + '\\]=([^&]*)' ), - result = regex.exec( window.location.hash ); - return result ? decodeURIComponent( result[1] ) : ''; + + // $.tablesorter.sort2Hash.getParam( parameter, url ); function added in v2.24.4 + // parameter = desired parameter to extract + // url (optional) = hash or href string to extract the parameter value from + return $.tablesorter.sort2Hash.getParam( component + '[' + tableId + ']' ) || ''; } */ sort2Hash_cleanHash : null, /* sort2Hash_cleanHash : function( config, tableId, component, hash ) { - var index, - result = [], - regex = new RegExp( component + '\\[' + tableId + '\\]=([^&]*)' ); - parts = ( hash || '' ).slice(1).split( '&' ), - len = parts.length, - for ( index = 0; index < len; index++ ) { - if ( !regex.test( parts[ index ] ) ) { - result.push( parts[ index ] ); - } - } - // if we still have something left, join the components back together - // and return it so the next component can be processed - // we don't update the window.location.hash, or the page jumps to the top! - return result.length ? c.widgetOptions.sort2Hash_hash + result.join( '&' ) : ''; + // config = table.config settings + // tableId = processed table ID + // component: 'sort', 'page', 'size' or 'filter' + // return false to let the widget decode the hash + + // removeParam function added v2.24.4 + return $.tablesorter.sort2Hash.removeParam( component + '[' + tableId + ']' ); } */ pager_output : '{startRow:input} to {endRow} ({filteredRows})', @@ -109,6 +104,7 @@

Notes

    +
  • In v2.24.4, added a getParam (get URL parameter) & a removeParam utility function to help deal with parameters in the URL hash. See the "Functions" section below for more details.
  • In v2.24.0, lots of changes were made:
    • Removed sort2Hash_useHeaderText and sort2Hash_processHeaderText options.
    • @@ -212,7 +208,7 @@ null - Add a function to create a custom hash. + Add a function to create a custom hash (v2.24.4).

      The following example is a duplicate of the internal code that encodes the hash. Adapt to fit your needs: @@ -245,7 +241,7 @@ null - Add a function to process a custom hash. + Add a function to process a custom hash (v2.24.4).

      The following example is a duplicate of the internal code that decodes the hash. Adapt to fit your needs: @@ -254,20 +250,18 @@ // tableId = processed table ID // component: 'sort', 'page', 'size' or 'filter' // return false to let the widget decode the hash - var regex = new RegExp( '[\\#&]' + component + '\\[' + tableId + '\\]=([^&]*)' ), - result = regex.exec( window.location.hash ); - return result ? result[1] : ''; + + // $.tablesorter.sort2Hash.getParam( parameter, url ); function added in v2.24.4 + // parameter = desired parameter to extract + // url (optional) = hash or href string to extract the parameter value from + return $.tablesorter.sort2Hash.getParam( component + '[' + tableId + ']' ) || ''; } Return the component value or an empty string.

      If this function returns false, then the internal code will attempt to decode the hash for that component. This was done in case only one parameter needs special handling:

      sort2Hash_decodeHash : function( config, tableId, component ) {
         // only the filter gets special handling
      -  var result, regex;
         if ( component === 'filter' ) {
      -    regex = new RegExp( '[\\#&]' + tableId + 'filter=([^&]*)' );
      -    result = regex.exec( window.location.hash );
      -    // make sure to use decodeURIComponent on the result
      -    return result ? decodeURIComponent( result[1] ) : '';
      +    return $.tablesorter.sort2Hash.getParam( tableId + 'filter' ) || '';
         }
         return false;
       }
      @@ -279,46 +273,46 @@ null - Add a function to remove a custom hash + Add a function to remove a custom hash (v2.24.4).

      The following example is a duplicate of the internal code that removes unused values in the hash. Adapt to fit your needs:
      sort2Hash_cleanHash : function( config, tableId, component, hash ) {
      -  var index,
      -    result = [],
      -    // regular expression to match the component & value
      -    regex = new RegExp( component + '\\[' + tableId + '\\]=([^&]*)' );
      -    // split hash into component parts
      -    parts = ( hash || '' ).slice(1).split( '&' ),
      -    len = parts.length,
      -  // cycle through each component part
      -  for ( index = 0; index < len; index++ ) {
      -    // if the component doesn't match the regular expression...
      -    if ( !regex.test( parts[ index ] ) ) {
      -      // then save it
      -      result.push( parts[ index ] );
      -    }
      -  }
      -  // if we still have something left, join the components back together
      -  // and return it so the next component can be processed
      -  // we don't update the window.location.hash, or the page jumps to the top!
      -  return result.length ? c.widgetOptions.sort2Hash_hash + result.join( '&' ) : '';
      +  // removeParam function added v2.24.4
      +  return $.tablesorter.sort2Hash.removeParam( component + '[' + tableId + ']' );
       }
      Return a string of the remaining components or an empty string.

      If this function returns false, then the internal code will attempt to clean the hash for that component. This was done in case only one parameter needs special handling:

      sort2Hash_cleanHash : function( config, tableId, component, hash ) {
      +  // config = table.config settings
      +  // tableId = processed table ID
      +  // component: 'sort', 'page', 'size' or 'filter'
      +  // return false to let the widget decode the hash
      +
         if ( component === 'filter' ) {
      +    // removeParam function added v2.24.4
      +    return $.tablesorter.sort2Hash.removeParam( tableId + 'filter' );
      +    /* ORIGINAL METHOD, use if the "getParam" function doesn't work on your custom parameter
           var index,
             result = [],
      +      // regular expression to match the component & value
             regex = new RegExp( tableId + 'filter=([^&]*)' );
      +      // split hash into component parts
             parts = ( hash || '' ).slice(1).split( '&' ),
      -      len = parts.length,
      +      len = parts.length;
      +    // cycle through each component part
           for ( index = 0; index < len; index++ ) {
      +      // if the component doesn't match the regular expression...
             if ( !regex.test( parts[ index ] ) ) {
      +        // then save it
               result.push( parts[ index ] );
             }
           }
      +    // if we still have something left, join the components back together
      +    // and return it so the next component can be processed
      +    // we don't update the window.location.hash, or the page jumps to the top!
           return result.length ? c.widgetOptions.sort2Hash_hash + result.join( '&' ) : '';
      +    */
         }
         return false;
       }
      @@ -376,6 +370,54 @@
      +

      Functions

      +
      +

      Sort2Hash utility functions (added to $.tablesorter.sort2Hash)

      +
      + 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. +
      + + + + + + + + + + + + + + + + +
      FunctionDescription
      + Get URL Parameter (getParam) function +
      +

      This function will extract the value of the name passed to the function. Use it as follows:

      +
      // $.tablesorter.sort2Hash.getParam( name, hash );
      +// name = parameter name (key)
      +// hash = (optional) if undefined, it will get the string value from window.location.hash
      +var hash = '&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10&filter[table-users][user_name]=ad',
      +	result = $.tablesorter.sort2Hash.getParam( 'filter[table-users][user_name]', hash );
      +// result = "ad";
      +
      +
      + Remove parameter from hash +
      +

      This function will remove the key & value from the passed hash. Use it as follows:

      +
      // $.tablesorter.sort2Hash.removeParam( name, hash );
      +// name = parameter name (key)
      +// hash = (optional) if undefined, it will get the string value from window.location.hash
      +var hash = '&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10&filter[table-users][user_name]=ad',
      +	result = $.tablesorter.sort2Hash.removeParam( 'filter[table-users][user_name]', hash );
      +// result = "&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10";
      + *NOTE* This function does not update window.location.hash. +
      +
      +
      +

      Demo

      diff --git a/js/widgets/widget-sort2Hash.js b/js/widgets/widget-sort2Hash.js index 645e78d3..6d114892 100644 --- a/js/widgets/widget-sort2Hash.js +++ b/js/widgets/widget-sort2Hash.js @@ -17,7 +17,7 @@ } if ( ts.hasWidget( c.table, 'pager' ) ) { temp = parseInt( s2h.decodeHash( c, wo, 'page' ), 10 ); - page = pager.page = ( temp < 0 ? 0 : ( temp > pager.totalPages ? pager.totalPages - 1 : temp ) ) + 1; + page = pager.page = ( temp < 0 ? 0 : ( temp > pager.totalPages ? pager.totalPages - 1 : temp ) ); size = pager.size = parseInt( s2h.decodeHash( c, wo, 'size' ), 10 ); } if ( ts.hasWidget( table, 'filter' ) ) { @@ -33,9 +33,13 @@ }, 100 ); }); } - } else { - c.$table.trigger( 'pageAndSize', [ page, size ] ); } + if ( !filter ) { + c.$table.one( 'tablesorter-ready', function() { + c.$table.trigger( 'pageAndSize', [ page, size ] ); + }); + } + c.$table.on( 'sortEnd.sort2hash filterEnd.sort2hash pagerComplete.sort2Hash', function() { if ( this.hasInitialized ) { s2h.setHash( this.config, this.config.widgetOptions ); @@ -125,6 +129,33 @@ return sort.join( wo.sort2Hash_separator ); }, + // Get URL Parameters (getParam) + // modified from http://www.netlobo.com/url_query_string_javascript.html + getParam : function ( name, hash, returnRegex ) { + if ( !hash ) { hash = window.location.hash; } + var regex = new RegExp( '[\\?&]' + s2h.regexEscape( name ) + '=([^&#]*)' ), + match = regex.exec( hash ); + if ( returnRegex ) { return regex; } + return match === null ? '' : decodeURIComponent( match[ 1 ] ); + }, + + // remove parameter from hash + removeParam : function( name, hash ) { + if ( !hash ) { hash = window.location.hash; } + var index, + regex = s2h.getParam( name, hash, true ), + result = [], + parts = hash.split( '&' ), + len = parts.length; + for ( index = 0; index < len; index++ ) { + // regex expects a leading '&'... + if ( !regex.test( '&' + parts[ index ] ) ) { + result.push( parts[ index ] ); + } + } + return result.length ? result.join( '&' ) : ''; + }, + encodeHash : function( c, wo, component, value, rawValue ) { var result = false, tableId = s2h.getTableId( c, wo ); @@ -138,39 +169,30 @@ }, decodeHash : function( c, wo, component ) { - var regex, - result = false, + var result = false, tableId = s2h.getTableId( c, wo ); if ( typeof wo.sort2Hash_decodeHash === 'function' ) { + // return a string result = wo.sort2Hash_decodeHash( c, tableId, component ); } if ( result === false ) { - regex = new RegExp( '[\\#&]' + component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ), - /*jshint -W030 */ - result = regex.exec( window.location.hash ); + result = s2h.getParam( component + '[' + tableId + ']' ); } - return result ? decodeURIComponent( result[ 1 ] ) : ''; + return result || ''; }, cleanHash : function( c, wo, component, hash ) { - var index, len, parts, regex, - result = false, + var result = false, tableId = s2h.getTableId( c, wo ); if ( typeof wo.sort2Hash_cleanHash === 'function' ) { + // can return an array or string result = wo.sort2Hash_cleanHash( c, tableId, component, hash ); } if ( result === false ) { - result = []; - parts = ( hash || '' ).slice(1).split( '&' ); - len = parts.length; - regex = new RegExp( component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ); - for ( index = 0; index < len; index++ ) { - if ( !regex.test( parts[ index ] ) ) { - result.push( parts[ index ] ); - } - } + // parameter example: 'sort[table0]=0,0' + result = s2h.removeParam( component + '[' + tableId + ']', hash ); } - return result.length ? '#' + result.join( '&' ) : ''; + return result || ''; }, setHash : function( c, wo ) {