Firefox ampersand in hash
plugin
70
2009/10/28 03:47
2010/08/22 01:13
I have a form that may have ampersands in the submitted data. I encode the data, so the ampersand is properly escaped as %26.
This works perfectly in IE8 and Chrome 3, but in Firefox 3.5.3 (and I'm guessing other versions), this results in two requests being made--the first with the escaped ampersands and the second with the unescaped ampersands. This happens because Firefox tries to be smart and changes the location.hash parameter on you to the unescaped version which then triggers a history event by the history plugin.
I fixed this in my copy by checking the location.href parameter instead of the location.hash parameter.
To fix this, I changed:
var current_hash = location.hash.replace(/\?.*$/, '');
to be:
if (location.hash == '')
var current_hash = '#';
else
{
var current_hash = location.href.replace(/.*?#/, '#');
current_hash = current_hash.replace(/\?.*$/, '');
}
in two places. Here is my code in full:
/*
* jQuery history plugin
*
* sample page: http://www.mikage.to/jquery/jquery_history.html
*
* Copyright (c) 2006-2009 Taku Sano (Mikage Sawatari)
* Licensed under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
* Modified by Lincoln Cooper to add Safari support and only call the callback once during initialization
* for msie when no initial hash supplied.
*
* Modified by Joe Lencioni (http://shiftingpixel.com) to fix Firefox hash bug.
*/
jQuery.extend({
historyCurrentHash: undefined,
historyCallback: undefined,
historyIframeSrc: undefined,
historyNeedIframe: jQuery.browser.msie && (jQuery.browser.version < 8 || document.documentMode < 8),
historyInit: function(callback, src){
jQuery.historyCallback = callback;
if (src) jQuery.historyIframeSrc = src;
if (location.hash == '')
var current_hash = '#';
else
{
var current_hash = location.href.replace(/.*?#/, '#');
current_hash = current_hash.replace(/\?.*$/, '');
}
jQuery.historyCurrentHash = current_hash;
if (jQuery.historyNeedIframe) {
// To stop the callback firing twice during initilization if no hash present
if (jQuery.historyCurrentHash == '') {
jQuery.historyCurrentHash = '#';
}
// add hidden iframe for IE
jQuery("body").prepend('<iframe id="jQuery_history" style="display: none;"'+
' src="javascript:false;"></iframe>'
);
var ihistory = jQuery("#jQuery_history")[0];
var iframe = ihistory.contentWindow.document;
iframe.open();
iframe.close();
iframe.location.hash = current_hash;
}
else if (jQuery.browser.safari) {
// etablish back/forward stacks
jQuery.historyBackStack = [];
jQuery.historyBackStack.length = history.length;
jQuery.historyForwardStack = [];
jQuery.lastHistoryLength = history.length;
jQuery.isFirst = true;
}
if(current_hash)
jQuery.historyCallback(current_hash.replace(/^#/, ''));
setInterval(jQuery.historyCheck, 100);
},
historyAddHistory: function(hash) {
// This makes the looping function do something
jQuery.historyBackStack.push(hash);
jQuery.historyForwardStack.length = 0; // clear forwardStack (true click occured)
this.isFirst = true;
},
historyCheck: function(){
if (jQuery.historyNeedIframe) {
// On IE, check for location.hash of iframe
var ihistory = jQuery("#jQuery_history")[0];
var iframe = ihistory.contentDocument || ihistory.contentWindow.document;
var current_hash = iframe.location.hash.replace(/\?.*$/, '');
if(current_hash != jQuery.historyCurrentHash) {
location.hash = current_hash;
jQuery.historyCurrentHash = current_hash;
jQuery.historyCallback(current_hash.replace(/^#/, ''));
}
} else if (jQuery.browser.safari) {
if(jQuery.lastHistoryLength == history.length && jQuery.historyBackStack.length > jQuery.lastHistoryLength) {
jQuery.historyBackStack.shift();
}
if (!jQuery.dontCheck) {
var historyDelta = history.length - jQuery.historyBackStack.length;
jQuery.lastHistoryLength = history.length;
if (historyDelta) { // back or forward button has been pushed
jQuery.isFirst = false;
if (historyDelta < 0) { // back button has been pushed
// move items to forward stack
for (var i = 0; i < Math.abs(historyDelta); i++) jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop());
} else { // forward button has been pushed
// move items to back stack
for (var i = 0; i < historyDelta; i++) jQuery.historyBackStack.push(jQuery.historyForwardStack.shift());
}
var cachedHash = jQuery.historyBackStack[jQuery.historyBackStack.length - 1];
if (cachedHash != undefined) {
jQuery.historyCurrentHash = location.hash.replace(/\?.*$/, '');
jQuery.historyCallback(cachedHash);
}
} else if (jQuery.historyBackStack[jQuery.historyBackStack.length - 1] == undefined && !jQuery.isFirst) {
// back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
// document.URL doesn't change in Safari
if (location.hash) {
var current_hash = location.hash;
jQuery.historyCallback(location.hash.replace(/^#/, ''));
} else {
var current_hash = '';
jQuery.historyCallback('');
}
jQuery.isFirst = true;
}
}
} else {
// otherwise, check for location.hash
if (location.hash == '')
var current_hash = '#';
else
{
var current_hash = location.href.replace(/.*?#/, '#');
current_hash = current_hash.replace(/\?.*$/, '');
}
if(current_hash != jQuery.historyCurrentHash) {
jQuery.historyCurrentHash = current_hash;
jQuery.historyCallback(current_hash.replace(/^#/, ''));
}
}
},
historyLoad: function(hash){
var newhash;
hash = decodeURIComponent(hash.replace(/\?.*$/, ''));
if (jQuery.browser.safari) {
newhash = hash;
}
else {
newhash = '#' + hash;
location.hash = newhash;
}
jQuery.historyCurrentHash = newhash;
if (jQuery.historyNeedIframe) {
var ihistory = jQuery("#jQuery_history")[0];
var iframe = ihistory.contentWindow.document;
iframe.open();
iframe.close();
iframe.location.hash = newhash;
jQuery.lastHistoryLength = history.length;
jQuery.historyCallback(hash);
}
else if (jQuery.browser.safari) {
jQuery.dontCheck = true;
// Manually keep track of the history values for Safari
this.historyAddHistory(hash);
// Wait a while before allowing checking so that Safari has time to update the "history" object
// correctly (otherwise the check loop would detect a false change in hash).
var fn = function() {jQuery.dontCheck = false;};
window.setTimeout(fn, 200);
jQuery.historyCallback(hash);
// N.B. "location.hash=" must be the last line of code for Safari as execution stops afterwards.
// By explicitly using the "location.hash" command (instead of using a variable set to "location.hash") the
// URL in the browser and the "history" object are both updated correctly.
location.hash = newhash;
}
else {
jQuery.historyCallback(hash);
}
}
});
......
RIVABbxSeTTWatdpUpC
2010/06/27 18:06
ttPwsGrHnDfY
2010/06/27 18:52
KKMWeJYMmUKsq
2010/06/27 19:23
FEKhvvPwBxFjHkuN
2010/06/27 19:38
GLYZIqJybXmO
2010/06/27 20:10
vYikWuFsMkcWpmS
2010/06/27 20:17
kUvpbCevmIjtvRcU
2010/06/27 20:32
HhYIlgdSAddLBus
2010/06/27 21:03
yVEZFOQAuXJsoD
2010/06/27 21:34
BWJQdTPAgSOA
2010/06/27 21:50
ggiXyeMM
2010/06/27 22:21
psjxybGlEMaRSQk
2010/07/13 07:07
jsDdFhoyfWWTd
2010/08/06 04:35
HQPyPkYVIkkjzBoHJv
2010/08/13 14:32
RmGDSwgxjEcP
2010/08/13 21:10
UwPWitPFsW
2010/08/22 01:13