├── README.md
├── jquery
└── jquery.hash.js
└── Hash.js
/README.md:
--------------------------------------------------------------------------------
1 | # Hash handler
2 |
3 | ## Information
4 |
5 | A JavaScript library that keeps track of the history of changes to the hash
6 | part in the address bar.
7 |
8 | A callback function, "handler", is used to handle changes. The function will
9 | be called once when the library is initialized (usually when the page has
10 | finished loading) and then whenever the hash changes.
11 |
12 | **Warning** for Internet Explorer 7 and below: If an element on the page has
13 | the same ID as the hash used, the history will get messed up.
14 |
15 | Does not support history in Safari 2 and below.
16 |
17 | ## Example
18 |
19 | function handler(newHash, initial) {
20 | if (initial)
21 | alert('Hash is "' + newHash + '"');
22 | else
23 | alert('Hash changed to "' + newHash + '"');
24 | }
25 | Hash.init(handler);
26 | Hash.go('abc123');
27 |
28 | The `initial` argument is a boolean that indicates whether the handler was
29 | called for initial state by `Hash.init` (value will be `true`), or due to
30 | an actual change to the hash (`false`).
31 |
32 | ### jQuery plugin
33 |
34 | Also included is a jQuery plugin that simplifies the use of the Hash library.
35 |
36 | #### Example
37 |
38 | // Add events before calling init to make sure they are triggered for
39 | // initial hash value.
40 | $('div#log').hashchange(function (e, newHash) {
41 | $(this).prepend('
New hash: "' + newHash + '"
');
42 | });
43 | // Initialize.
44 | $.hash.init();
45 | $.hash.go('abc123');
46 | // Changes hash when the anchor is clicked. Also automatically sets the
47 | // href attribute to "#def456", unless a second argument with a false
48 | // value is supplied.
49 | $('a#my-anchor').hash('def456');
50 |
51 | ## MIT license
52 |
53 | This project is licensed under an MIT license.
54 |
55 | Copyright (c) 2009-2010 Andreas Blixt
56 |
57 | http://www.opensource.org/licenses/mit-license.php
58 |
--------------------------------------------------------------------------------
/jquery/jquery.hash.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) 2009-2011 Andreas Blixt
3 | * Contributors: Simon Chester
4 | * http://github.com/blixt/js-hash
5 | * MIT License: http://www.opensource.org/licenses/mit-license.php
6 | *
7 | * jQuery hash plugin (Depends on jQuery, Hash)
8 | * Plugin for detecting changes to the hash and for adding history support for
9 | * hashes to certain browsers.
10 | */
11 | /* Example:
12 | * // Add events before calling init to make sure they are triggered for
13 | * // initial hash value.
14 | * $('div#log').hashchange(function (e, newHash) {
15 | * $(this).prepend('New hash: "' + newHash + '"
');
16 | * });
17 | * // Initialize.
18 | * $.hash.init();
19 | * $.hash.go('abc123');
20 | * // Changes hash when the anchor is clicked. Also automatically sets the
21 | * // href attribute to "#def456", unless a second argument with a false
22 | * // value is supplied.
23 | * $('a#my-anchor').hash('def456');
24 | *
25 | * WARNING for Internet Explorer 7 and below:
26 | * If an element on the page has the same ID as the hash used, the history will
27 | * get messed up.
28 | *
29 | * Does not support history in Safari 2 and below.
30 | *
31 | *
32 | * Updated by Simon Chester (simonches@gmail.com) on 2011-05-16:
33 | * - Updated to reflect Hash.js no longer needing iframe argument
34 | */
35 |
36 | (function (jQuery, Hash) {
37 | var
38 | // Plugin settings
39 | eventName = 'hashchange',
40 | eventDataName = 'hash.fn',
41 | init,
42 | // Import globals
43 | window = this,
44 | documentMode = document.documentMode,
45 |
46 | // Called whenever the hash changes.
47 | callback = function (newHash) {
48 | jQuery.event.trigger(eventName, [newHash]);
49 | };
50 |
51 | jQuery.hash = {
52 | init: function () {
53 | // init can only be called once.
54 | if (init) return;
55 | init = 1;
56 |
57 | Hash.init(callback);
58 | },
59 |
60 | go: Hash.go
61 | };
62 |
63 | jQuery.fn.hash = function (newHash, changeHref) {
64 | var fn = this.data(eventDataName);
65 | if (fn) this.unbind('click', fn);
66 |
67 | if (typeof newHash == 'string') {
68 | fn = function () { Hash.go(newHash); return false; };
69 | this.data(eventDataName, fn);
70 | this.click(fn);
71 | if (changeHref || changeHref === undefined)
72 | this.attr('href', '#' + newHash);
73 | }
74 |
75 | return this;
76 | };
77 |
78 | jQuery.fn[eventName] = function (fn) {
79 | return this.bind(eventName, fn);
80 | };
81 | })(jQuery, Hash);
82 |
--------------------------------------------------------------------------------
/Hash.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) 2009-2011 Andreas Blixt
3 | * Contributors: Aaron Ogle ,
4 | * Matti Virkkunen ,
5 | * Simon Chester
6 | * http://github.com/blixt/js-hash
7 | * MIT License: http://www.opensource.org/licenses/mit-license.php
8 | *
9 | * Hash handler
10 | * Keeps track of the history of changes to the hash part in the address bar.
11 | */
12 | /* WARNING for Internet Explorer 7 and below:
13 | * If an element on the page has the same ID as the hash used, the history will
14 | * get messed up.
15 | *
16 | * Does not support history in Safari 2 and below.
17 | *
18 | * Example:
19 | * function handler(newHash, initial) {
20 | * if (initial)
21 | * alert('Hash is "' + newHash + '"');
22 | * else
23 | * alert('Hash changed to "' + newHash + '"');
24 | * }
25 | * Hash.init(handler);
26 | * Hash.go('abc123');
27 | *
28 | *
29 | * Updated by Simon Chester (simonches@gmail.com) on 2011-05-16:
30 | * - Removed the need for blank.html and the iframe argument by creating the
31 | * iframe on initialization.
32 | *
33 | * Updated by Matti Virkkunen (mvirkkunen@gmail.com) on 2009-11-16:
34 | * - Added second argument to callback that indicates whether the callback is
35 | * due to initial state (true) or due to an actual change to the hash
36 | * (false).
37 | *
38 | * Updated by Aaron Ogle (aogle@avencia.com) on 2009-08-11:
39 | * - Fixed bug where Firefox automatically unescapes location.hash but no
40 | * other browsers do. Always get the hash by parsing location.href and
41 | * never use location.hash.
42 | */
43 |
44 | var Hash = (function () {
45 | var
46 | // Import globals
47 | window = this,
48 | documentMode = document.documentMode,
49 | history = window.history,
50 | // Plugin variables
51 | callback, hash,
52 | // IE-specific
53 | iframe,
54 |
55 | getHash = function () {
56 | // Internet Explorer 6 (and possibly other browsers) extracts the query
57 | // string out of the location.hash property into the location.search
58 | // property, so we can't rely on it. The location.search property can't be
59 | // relied on either, since if the URL contains a real query string, that's
60 | // what it will be set to. The only way to get the whole hash is to parse
61 | // it from the location.href property.
62 | //
63 | // Another thing to note is that in Internet Explorer 6 and 7 (and possibly
64 | // other browsers), subsequent hashes are removed from the location.href
65 | // (and location.hash) property if the location.search property is set.
66 | //
67 | // Via Aaron: Firefox 3.5 (and below?) always unescape location.hash which
68 | // causes poll to fire the hashchange event twice on escaped hashes. This
69 | // is because the hash variable (escaped) will not match location.hash
70 | // (unescaped.) The only consistent option is to rely completely on
71 | // location.href.
72 | var index = window.location.href.indexOf('#');
73 | return (index == -1 ? '' : window.location.href.substr(index + 1));
74 | },
75 |
76 | // Used by all browsers except Internet Explorer 7 and below.
77 | poll = function () {
78 | var curHash = getHash();
79 | if (curHash != hash) {
80 | hash = curHash;
81 | callback(curHash, false);
82 | }
83 | },
84 |
85 | // From:
86 | // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
87 | isHashChangeSupported = function() {
88 | var eventName = 'onhashchange';
89 | var isSupported = (eventName in document.body);
90 | if (!isSupported) {
91 | document.body.setAttribute(eventName, 'return;');
92 | isSupported = typeof document.body[eventName] == 'function';
93 | }
94 |
95 | // documentMode logic from YUI to filter out IE8 Compat Mode (which
96 | // generates false positives).
97 | return isSupported && (document.documentMode === undefined ||
98 | document.documentMode > 7);
99 | },
100 |
101 | createIframe = function () {
102 | var tempEl = document.createElement();
103 | tempEl.innerHTML = '';
105 | var frame = tempEl.childNodes[0];
106 | document.body.appendChild(frame);
107 | return frame;
108 | },
109 |
110 | // Used to create a history entry with a value in the iframe.
111 | setIframe = function (newHash) {
112 | try {
113 | var doc = iframe.contentWindow.document;
114 | doc.open();
115 | doc.write('' + newHash + '');
116 | doc.close();
117 | hash = newHash;
118 | } catch (e) {
119 | setTimeout(function () { setIframe(newHash); }, 10);
120 | }
121 | },
122 |
123 | // Used by Internet Explorer 7 and below to set up an iframe that keeps track
124 | // of history changes.
125 | setUpIframe = function () {
126 | // Don't run until access to the body is allowed.
127 | try {
128 | iframe = createIframe();
129 | } catch (e) {
130 | setTimeout(setUpIframe, 10);
131 | return;
132 | }
133 |
134 | // Create a history entry for the initial state.
135 | setIframe(hash);
136 | var data = hash;
137 |
138 | setInterval(function () {
139 | var curData, curHash;
140 |
141 | try {
142 | curData = iframe.contentWindow.document.body.innerText;
143 | if (curData != data) {
144 | data = curData;
145 | window.location.hash = hash = curData;
146 | callback(curData, true);
147 | } else {
148 | curHash = getHash();
149 | if (curHash != hash) setIframe(curHash);
150 | }
151 | } catch (e) {
152 | }
153 | }, 50);
154 | };
155 |
156 | return {
157 | init: function (cb) {
158 | // init can only be called once.
159 | if (callback) return;
160 |
161 | callback = cb;
162 |
163 | // Keep track of the hash value.
164 | hash = getHash();
165 | cb(hash, true);
166 |
167 | if (isHashChangeSupported()) {
168 | if (window.addEventListener){
169 | window.addEventListener('hashchange', poll, false);
170 | } else if (window.attachEvent){
171 | window.attachEvent('onhashchange', poll);
172 | }
173 | } else {
174 | // Run specific code for Internet Explorer.
175 | if (window.ActiveXObject) {
176 | if (!documentMode || documentMode < 8) {
177 | // Internet Explorer 5.5/6/7 need an iframe for history
178 | // support.
179 | setUpIframe();
180 | }
181 | } else {
182 | // Change Opera navigation mode to improve history support.
183 | if (history.navigationMode) {
184 | history.navigationMode = 'compatible';
185 | }
186 |
187 | setInterval(poll, 50);
188 | }
189 | }
190 | },
191 |
192 | go: function (newHash) {
193 | // Cancel if the new hash is the same as the current one, since there
194 | // is no cross-browser way to keep track of navigation to the exact
195 | // same hash multiple times in a row. A wrapper can handle this by
196 | // adding an incrementing counter to the end of the hash.
197 | if (newHash == hash) return;
198 | if (iframe) {
199 | setIframe(newHash);
200 | } else {
201 | window.location.hash = hash = newHash;
202 | callback(newHash, false);
203 | }
204 | },
205 |
206 | getHash: getHash
207 | };
208 | })();
209 |
--------------------------------------------------------------------------------