Sort2Hash: Add 2 utility functions to simplify hash processing

This commit is contained in:
Rob Garrison 2015-11-08 20:47:30 -06:00
parent f3449a8e7f
commit 65867145df
3 changed files with 134 additions and 70 deletions

View File

@ -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<a.columns;i++)j=a.$headerIndexed[i],e.test(j.attr(b.sort2Hash_headerTextAttr))&&(f=i,i=a.columns);g=k[l++],"undefined"!=typeof f&&"undefined"!=typeof g&&(isNaN(g)&&(g=g.indexOf(b.sort2Hash_directionText[1])>-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);
!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<a.columns;i++)j=a.$headerIndexed[i],e.test(j.attr(b.sort2Hash_headerTextAttr))&&(f=i,i=a.columns);g=k[l++],"undefined"!=typeof f&&"undefined"!=typeof g&&(isNaN(g)&&(g=g.indexOf(b.sort2Hash_directionText[1])>-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);

View File

@ -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 @@
<h3><a href="#">Notes</a></h3>
<div>
<ul>
<li>In <span class="version">v2.24.4</span>, added a <code>getParam</code> (get URL parameter) &amp; a <code>removeParam</code> utility function to help deal with parameters in the URL hash. See the "Functions" section below for more details.</li>
<li>In <span class="version">v2.24.0</span>, lots of changes were made:
<ul>
<li>Removed <code class="alert">sort2Hash_useHeaderText</code> and <code class="alert">sort2Hash_processHeaderText</code> options.</li>
@ -212,7 +208,7 @@
<td><a href="#" class="permalink">sort2Hash_encodeHash</a></td>
<td><code>null</code></td>
<td>
Add a function to create a custom hash.
Add a function to create a custom hash (<span class="version updated">v2.24.4</span>).
<div class="collapsible">
<br>
The following example is a duplicate of the internal code that encodes the hash. Adapt to fit your needs:
@ -245,7 +241,7 @@
<td><a href="#" class="permalink">sort2Hash_decodeHash</a></td>
<td><code>null</code></td>
<td>
Add a function to process a custom hash.
Add a function to process a custom hash (<span class="version updated">v2.24.4</span>).
<div class="collapsible">
<br>
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 + ']' ) || '';
}</pre>
Return the component value or an empty string.
<p>If this function returns <code>false</code>, then the internal code will attempt to decode the hash for that component. This was done in case only one parameter needs special handling:</p>
<pre class="prettyprint lang-js">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;
}</pre>
@ -279,18 +273,33 @@
<td><a href="#" class="permalink">sort2Hash_cleanHash</a></td>
<td><code>null</code></td>
<td>
Add a function to remove a custom hash
Add a function to remove a custom hash (<span class="version updated">v2.24.4</span>).
<div class="collapsible">
<br>
The following example is a duplicate of the internal code that removes unused values in the hash. Adapt to fit your needs:
<pre class="prettyprint lang-js">sort2Hash_cleanHash : function( config, tableId, component, hash ) {
// removeParam function added v2.24.4
return $.tablesorter.sort2Hash.removeParam( component + '[' + tableId + ']' );
}</pre>
Return a string of the remaining components or an empty string.
<p>If this function returns <code>false</code>, then the internal code will attempt to clean the hash for that component. This was done in case only one parameter needs special handling:</p>
<pre class="prettyprint lang-js">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( component + '\\[' + tableId + '\\]=([^&]*)' );
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...
@ -303,22 +312,7 @@
// 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( '&' ) : '';
}</pre>
Return a string of the remaining components or an empty string.
<p>If this function returns <code>false</code>, then the internal code will attempt to clean the hash for that component. This was done in case only one parameter needs special handling:</p>
<pre class="prettyprint lang-js">sort2Hash_cleanHash : function( config, tableId, component, hash ) {
if ( component === 'filter' ) {
var index,
result = [],
regex = new RegExp( tableId + 'filter=([^&]*)' );
parts = ( hash || '' ).slice(1).split( '&' ),
len = parts.length,
for ( index = 0; index < len; index++ ) {
if ( !regex.test( parts[ index ] ) ) {
result.push( parts[ index ] );
}
}
return result.length ? c.widgetOptions.sort2Hash_hash + result.join( '&' ) : '';
*/
}
return false;
}</pre>
@ -376,6 +370,54 @@
</table>
</div>
<h3><a href="#">Functions</a></h3>
<div>
<h3>Sort2Hash utility functions (added to <code>$.tablesorter.sort2Hash</code>)</h3>
<div class="tip">
<span class="label label-info">TIP!</span> Click on the link in the function column to reveal full details (or <a href="#" class="toggleAll">toggle</a>|<a href="#" class="showAll">show</a>|<a href="#" class="hideAll">hide</a> all) or double click to update the browser location.
</div>
<table class="tablesorter-blue options">
<thead>
<tr><th>Function</th><th class="sorter-false">Description</th></tr>
</thead>
<tbody>
<tr id="getparam">
<td><a href="#" class="permalink">getParam</a></td>
<td>
Get URL Parameter (getParam) function
<div class="collapsible">
<p>This function will extract the value of the name passed to the function. Use it as follows:</p>
<pre class="prettyprint lang-js">// $.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";</pre>
</div>
</td>
</tr>
<tr id="removeparam">
<td><a href="#" class="permalink">removeParam</a></td>
<td>
Remove parameter from hash
<div class="collapsible">
<p>This function will remove the key &amp; value from the passed hash. Use it as follows:</p>
<pre class="prettyprint lang-js">// $.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";</pre>
<span class="label warning">*NOTE*</span> This function does not update <code>window.location.hash</code>.
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h1>Demo</h1>

View File

@ -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 ) {