diff options
author | Jörn Zaefferer <joern.zaefferer@gmail.com> | 2008-10-08 13:54:11 +0000 |
---|---|---|
committer | Jörn Zaefferer <joern.zaefferer@gmail.com> | 2008-10-08 13:54:11 +0000 |
commit | 226e11cecf35cc74fa97511d187d8a9bed05680f (patch) | |
tree | 60025a67a3a6d80a2253f50bd1556d7696fa31af /demos/functional | |
parent | 8b7f927e4b3c4622025d81845b9e7f2b88c760d0 (diff) | |
download | jquery-ui-226e11cecf35cc74fa97511d187d8a9bed05680f.tar.gz jquery-ui-226e11cecf35cc74fa97511d187d8a9bed05680f.zip |
functional demos: added history_remote plugin
Diffstat (limited to 'demos/functional')
-rw-r--r-- | demos/functional/index.html | 1 | ||||
-rw-r--r-- | demos/functional/js/behaviour.functionaldemos.js | 2 | ||||
-rw-r--r-- | demos/functional/js/jquery.history_remote.js | 342 |
3 files changed, 345 insertions, 0 deletions
diff --git a/demos/functional/index.html b/demos/functional/index.html index 9207a135f..9900a539e 100644 --- a/demos/functional/index.html +++ b/demos/functional/index.html @@ -92,6 +92,7 @@ <script src="js/jquery.chili.pack.js" type="text/javascript"></script> <script src="js/chili-recipes.js" type="text/javascript"></script> + <script src="js/jquery.history_remote.js" type="text/javascript"></script> <script src="js/jquery.mousewheel.js" type="text/javascript"></script> <script type="text/javascript"> diff --git a/demos/functional/js/behaviour.functionaldemos.js b/demos/functional/js/behaviour.functionaldemos.js index 8e375db28..19393476c 100644 --- a/demos/functional/js/behaviour.functionaldemos.js +++ b/demos/functional/js/behaviour.functionaldemos.js @@ -10,4 +10,6 @@ $(document).ready(function() { loadDemo(location.hash.replace(/^#/, "")); } + $.ajaxHistory.initialize(); + });
\ No newline at end of file diff --git a/demos/functional/js/jquery.history_remote.js b/demos/functional/js/jquery.history_remote.js new file mode 100644 index 000000000..6966b5816 --- /dev/null +++ b/demos/functional/js/jquery.history_remote.js @@ -0,0 +1,342 @@ +/** + * History/Remote - jQuery plugin for enabling history support and bookmarking + * @requires jQuery v1.0.3 + * + * http://stilbuero.de/jquery/history/ + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Version: 0.2.3 + */ + +(function($) { // block scope + +/** + * Initialize the history manager. Subsequent calls will not result in additional history state change + * listeners. Should be called soonest when the DOM is ready, because in IE an iframe needs to be added + * to the body to enable history support. + * + * @example $.ajaxHistory.initialize(); + * + * @param Function callback A single function that will be executed in case there is no fragment + * identifier in the URL, for example after navigating back to the initial + * state. Use to restore such an initial application state. + * Optional. If specified it will overwrite the default action of + * emptying all containers that are used to load content into. + * @type undefined + * + * @name $.ajaxHistory.initialize() + * @cat Plugins/History + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +$.ajaxHistory = new function() { + + var RESET_EVENT = 'historyReset'; + + var _currentHash = location.hash; + var _intervalId = null; + var _observeHistory; // define outside if/else required by Opera + + this.update = function() { }; // empty function body for graceful degradation + + // create custom event for state reset + var _defaultReset = function() { + $('.remote-output').empty(); + }; + $(document).bind(RESET_EVENT, _defaultReset); + + // TODO fix for Safari 3 + // if ($.browser.msie) + // else if hash != _currentHash + // else check history length + + if ($.browser.msie) { + + var _historyIframe, initialized = false; // for IE + + // add hidden iframe + $(function() { + _historyIframe = $('<iframe style="display: none;"></iframe>').appendTo(document.body).get(0); + var iframe = _historyIframe.contentWindow.document; + // create initial history entry + iframe.open(); + iframe.close(); + if (_currentHash && _currentHash != '#') { + iframe.location.hash = _currentHash.replace('#', ''); + } + }); + + this.update = function(hash) { + _currentHash = hash; + var iframe = _historyIframe.contentWindow.document; + iframe.open(); + iframe.close(); + iframe.location.hash = hash.replace('#', ''); + }; + + _observeHistory = function() { + var iframe = _historyIframe.contentWindow.document; + var iframeHash = iframe.location.hash; + if (iframeHash != _currentHash) { + _currentHash = iframeHash; + if (iframeHash && iframeHash != '#') { + // order does matter, set location.hash after triggering the click... + $('a[@href$="' + iframeHash + '"]').click(); + location.hash = iframeHash; + } else if (initialized) { + location.hash = ''; + $(document).trigger(RESET_EVENT); + } + } + initialized = true; + }; + + } else if ($.browser.mozilla || $.browser.opera) { + + this.update = function(hash) { + _currentHash = hash; + }; + + _observeHistory = function() { + if (location.hash) { + if (_currentHash != location.hash) { + _currentHash = location.hash; + $('a[@href$="' + _currentHash + '"]').click(); + } + } else if (_currentHash) { + _currentHash = ''; + $(document).trigger(RESET_EVENT); + } + }; + + } else if ($.browser.safari) { + + var _backStack, _forwardStack, _addHistory; // for Safari + + // etablish back/forward stacks + $(function() { + _backStack = []; + _backStack.length = history.length; + _forwardStack = []; + + }); + var isFirst = false, initialized = false; + _addHistory = function(hash) { + _backStack.push(hash); + _forwardStack.length = 0; // clear forwardStack (true click occured) + isFirst = false; + }; + + this.update = function(hash) { + _currentHash = hash; + _addHistory(_currentHash); + }; + + _observeHistory = function() { + var historyDelta = history.length - _backStack.length; + if (historyDelta) { // back or forward button has been pushed + isFirst = false; + if (historyDelta < 0) { // back button has been pushed + // move items to forward stack + for (var i = 0; i < Math.abs(historyDelta); i++) _forwardStack.unshift(_backStack.pop()); + } else { // forward button has been pushed + // move items to back stack + for (var i = 0; i < historyDelta; i++) _backStack.push(_forwardStack.shift()); + } + var cachedHash = _backStack[_backStack.length - 1]; + $('a[@href$="' + cachedHash + '"]').click(); + _currentHash = location.hash; + } else if (_backStack[_backStack.length - 1] == undefined && !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 (document.URL.indexOf('#') >= 0) { + $('a[@href$="' + '#' + document.URL.split('#')[1] + '"]').click(); + } else if (initialized) { + $(document).trigger(RESET_EVENT); + } + isFirst = true; + } + initialized = true; + }; + + } + + this.initialize = function(callback) { + // custom callback to reset app state (no hash in url) + if (typeof callback == 'function') { + $(document).unbind(RESET_EVENT, _defaultReset).bind(RESET_EVENT, callback); + } + // look for hash in current URL (not Safari) + if (location.hash && typeof _addHistory == 'undefined') { + $('a[@href$="' + location.hash + '"]').trigger('click'); + } + // start observer + if (_observeHistory && _intervalId == null) { + _intervalId = setInterval(_observeHistory, 200); // Safari needs at least 200 ms + } + }; + +}; + +/** + * Implement Ajax driven links in a completely unobtrusive and accessible manner (also known as "Hijax") + * with support for the browser's back/forward navigation buttons and bookmarking. + * + * The link's href attribute gets altered to a fragment identifier, such as "#remote-1", so that the browser's + * URL gets updated on each click, whereas the former value of that attribute is used to load content via + * XmlHttpRequest from and update the specified element. If no target element is found, a new div element will be + * created and appended to the body to load the content into. The link informs the history manager of the + * state change on click and adds an entry to the browser's history. + * + * jQuery's Ajax implementation adds a custom request header of the form "X-Requested-With: XmlHttpRequest" + * to any Ajax request so that the called page can distinguish between a standard and an Ajax (XmlHttpRequest) + * request. + * + * @example $('a.remote').remote('#output'); + * @before <a class="remote" href="/path/to/content.html">Update</a> + * @result <a class="remote" href="#remote-1">Update</a> + * @desc Alter a link of the class "remote" to an Ajax-enhanced link and let it load content from + * "/path/to/content.html" via XmlHttpRequest into an element with the id "output". + * @example $('a.remote').remote('#output', {hashPrefix: 'chapter'}); + * @before <a class="remote" href="/path/to/content.html">Update</a> + * @result <a class="remote" href="#chapter-1">Update</a> + * @desc Alter a link of the class "remote" to an Ajax-enhanced link and let it load content from + * "/path/to/content.html" via XmlHttpRequest into an element with the id "output". + * + * @param String expr A string containing a CSS selector or basic XPath specifying the element to load + * content into via XmlHttpRequest. + * @param Object settings An object literal containing key/value pairs to provide optional settings. + * @option String hashPrefix A String that is used for constructing the hash the link's href attribute + * gets altered to, such as "#remote-1". Default value: "remote-". + * @param Function callback A single function that will be executed when the request is complete. + * @type jQuery + * + * @name remote + * @cat Plugins/Remote + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Implement Ajax driven links in a completely unobtrusive and accessible manner (also known as "Hijax") + * with support for the browser's back/forward navigation buttons and bookmarking. + * + * The link's href attribute gets altered to a fragment identifier, such as "#remote-1", so that the browser's + * URL gets updated on each click, whereas the former value of that attribute is used to load content via + * XmlHttpRequest from and update the specified element. If no target element is found, a new div element will be + * created and appended to the body to load the content into. The link informs the history manager of the + * state change on click and adds an entry to the browser's history. + * + * jQuery's Ajax implementation adds a custom request header of the form "X-Requested-With: XmlHttpRequest" + * to any Ajax request so that the called page can distinguish between a standard and an Ajax (XmlHttpRequest) + * request. + * + * @example $('a.remote').remote( $('#output > div')[0] ); + * @before <a class="remote" href="/path/to/content.html">Update</a> + * @result <a class="remote" href="#remote-1">Update</a> + * @desc Alter a link of the class "remote" to an Ajax-enhanced link and let it load content from + * "/path/to/content.html" via XmlHttpRequest into an element with the id "output". + * @example $('a.remote').remote('#output', {hashPrefix: 'chapter'}); + * @before <a class="remote" href="/path/to/content.html">Update</a> + * @result <a class="remote" href="#chapter-1">Update</a> + * @desc Alter a link of the class "remote" to an Ajax-enhanced link and let it load content from + * "/path/to/content.html" via XmlHttpRequest into an element with the id "output". + * + * @param Element elem A DOM element to load content into via XmlHttpRequest. + * @param Object settings An object literal containing key/value pairs to provide optional settings. + * @option String hashPrefix A String that is used for constructing the hash the link's href attribute + * gets altered to, such as "#remote-1". Default value: "remote-". + * @param Function callback A single function that will be executed when the request is complete. + * @type jQuery + * + * @name remote + * @cat Plugins/Remote + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +$.fn.remote = function(output, settings, callback) { + + callback = callback || function() {}; + if (typeof settings == 'function') { // shift arguments + callback = settings; + } + + settings = $.extend({ + hashPrefix: 'remote-' + }, settings || {}); + + var target = $(output).size() && $(output) || $('<div></div>').appendTo('body'); + target.addClass('remote-output'); + + return this.each(function(i) { + var href = this.href, hash = '#' + (this.title && this.title.replace(/\s/g, '_') || settings.hashPrefix + (i + 1)), + a = this; + this.href = hash; + $(this).click(function(e) { + // lock target to prevent double loading in Firefox + if (!target['locked']) { + // add to history only if true click occured, not a triggered click + if (e.clientX) { + $.ajaxHistory.update(hash); + } + target.load(href, function() { + target['locked'] = null; + callback.apply(a); + }); + } + }); + }); + +}; + +/** + * Provides the ability to use the back/forward navigation buttons in a DHTML application. + * A change of the application state is reflected by a change of the URL fragment identifier. + * + * The link's href attribute needs to point to a fragment identifier within the same resource, + * although that fragment id does not need to exist. On click the link changes the URL fragment + * identifier, informs the history manager of the state change and adds an entry to the browser's + * history. + * + * @param Function callback A single function that will be executed as the click handler of the + * matched element. It will be executed on click (adding an entry to + * the history) as well as in case the history manager needs to trigger + * it depending on the value of the URL fragment identifier, e.g. if its + * current value matches the href attribute of the matched element. + * + * @type jQuery + * + * @name history + * @cat Plugins/History + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +$.fn.history = function(callback) { + return this.click(function(e) { + // add to history only if true click occured, + // not a triggered click... + if (e.clientX) { + // ...and die if already active + if (this.hash == location.hash) { + return false; + } + $.ajaxHistory.update(this.hash); + } + if (typeof callback == 'function') { + callback.call(this); + } + }); +}; + +})(jQuery); + +/* +var logger; +$(function() { + logger = $('<div style="position: fixed; top: 0; overflow: hidden; border: 1px solid; padding: 3px; width: 120px; height: 150px; background: #fff; color: red;"></div>').appendTo(document.body); +}); +function log(m) { + logger.prepend(m + '<br />'); +}; +*/ + |