4 * Ajatus - Distributed CRM
5 * @requires jQuery v1.2.1
7 * Copyright (c) 2007 Jerry Jalava <jerry.jalava@gmail.com>
8 * Copyright (c) 2007 Nemein Oy <http://nemein.com>
9 * Website: http://ajatus.info
10 * Licensed under the GPL license
11 * http://www.gnu.org/licenses/gpl.html
16 $.ajatus = $.ajatus || {};
23 $.extend($.ajatus.history, {
25 $.ajatus.history.enabled = typeof($.ajatus.history.handler) != 'undefined' ? true : false;
27 if ($.ajatus.history.enabled) {
28 $.ajatus.history._handler = new $.ajatus.history.handler;
29 $.ajatus.history._handler.initialize(function(){
30 $.ajatus.tabs.set_active_by_hash('#view.frontpage');
31 $.ajatus.views.system.frontpage.render();
35 add_map: function(hash, action) {
36 $.ajatus.history._handler.add_map(hash, action);
38 add_dynamic_map: function() {
43 statics = arguments[0];
49 action = arguments[2];
51 $.ajatus.history._handler.add_dynamic_map(statics, mods, action);
53 update: function(hash) {
54 $.ajatus.history._handler.update(hash);
56 check: function(hash) {
57 if (typeof hash == 'undefined') {
58 var hash = $.ajatus.history._handler.get_hash();
60 // $.ajatus.debug('$.ajatus.history.check('+hash+')');
62 if ( !$.browser.mozilla
63 && $.ajatus.history.last_checked == hash)
65 //$.ajatus.debug('$.ajatus.history.check skipping hash '+hash);
68 $.ajatus.history.last_checked = hash;
70 if ($.ajatus.history._handler.executable(hash)) {
71 $.ajatus.tabs.set_active_by_hash(hash);
72 $.ajatus.history._handler.execute(hash);
78 navigate: function(count) {
79 if (typeof count == 'undefined') {
83 $.ajatus.history._handler.navigate(count);
88 * Base idea is taken from $.ajaxHistory plugin by Klaus Hartl
89 * Difference in this is that we evaluate predefined actions
90 * that are mapped against hashes.
92 $.ajatus.history.handler = function(){
95 var dyn_mappings = [];
97 var RESET_EVENT = 'historyReset';
99 var _currentHash = location.hash;
100 var _intervalId = null;
103 this.update = function(){}; // empty function body for graceful degradation
105 this.add_map = function(hash, action) {
106 mappings[hash] = action;
109 this.add_dynamic_map = function(statics, mods, action) {
111 $.each(statics, function(i,s){
120 dyn_mappings.push(dyn_map);
123 this.is_dyn_mapping = function(hash) {
124 var ch = hash.replace('#','');
125 var hash_parts = ch.split(/\./);
130 $.each(dyn_mappings, function(i,dm){
133 $.each(dm.statics, function(k,n){
134 if (hash_parts[k] == n) {
140 $.each(hash_parts, function(hk,hp){
141 if ( hk > matched_key
145 if ($.inArray(hp, added_args) == -1) {
152 if (func_args.length > dm.mods) {
169 this.executable = function(hash){
170 if (mappings[hash]) {
173 var dm_data = this.is_dyn_mapping(hash);
174 if (dm_data !== false) {
181 this.execute = function(hash){
182 //$.ajatus.debug('$.ajatus.extension.history.execute('+hash+')');
184 if (mappings[hash]) {
185 eval(mappings[hash]);
187 //$.ajatus.debug('$.ajatus.extension.history.execute evaled from mappings: '+mappings[hash]);
191 var dm_data = this.is_dyn_mapping(hash);
192 if (dm_data !== false) {
193 //$.ajatus.debug('$.ajatus.extension.history.execute hash is dynamic mapping. Evaling: '+dyn_mappings[dm_data.id].action);
195 var func = eval(dyn_mappings[dm_data.id].action);
196 func.apply(func, dm_data.args);
204 this.get_hash = function(){
207 if ($.browser.safari) {
208 if (window.document.URL.indexOf('#') >= 0) {
209 hash = '#'+window.document.URL.split('#')[1];
212 hash = location.hash;
218 // create custom event for state reset
219 var _defaultReset = function() {
221 $(window.document).bind(RESET_EVENT, _defaultReset);
223 if ($.browser.safari) {
225 var _backStack, _forwardStack, _addHistory; // for Safari
227 // establish back/forward stacks
230 _backStack.length = history.length;
233 var isFirst = false, initialized = false;
234 _addHistory = function(hash) {
235 _backStack.push(hash);
236 _forwardStack.length = 0; // clear forwardStack (true click occured)
240 this.update = function(hash) {
242 location.hash = hash;
243 _addHistory(_currentHash);
246 this.navigate = function(count) {
248 var idx = (_backStack.length + count) - 1;
249 var newHash = _backStack[idx];
251 var idx = (_forwardStack.length - count) - 1;
252 var newHash = _forwardStack[idx];
255 if (typeof newHash != 'undefined') {
256 $.ajatus.history.update(newHash);
260 _observeHistory = function() {
261 var historyDelta = history.length - _backStack.length;
262 if (historyDelta) { // back or forward button has been pushed
264 if (historyDelta < 0) { // back button has been pushed
265 // move items to forward stack
266 for (var i = 0; i < Math.abs(historyDelta); i++) _forwardStack.unshift(_backStack.pop());
267 } else { // forward button has been pushed
268 // move items to back stack
269 for (var i = 0; i < historyDelta; i++) _backStack.push(_forwardStack.shift());
271 var cachedHash = _backStack[_backStack.length - 1];
272 _currentHash = location.hash;
274 $.ajatus.history.check(_currentHash);
275 } else if (typeof(_backStack[_backStack.length - 1]) == 'undefined' && !isFirst) {
276 if (location.hash == '') {
277 $(window.document).trigger(RESET_EVENT);
285 this.update = function(hash) {
286 if (_currentHash != hash) {
287 location.hash = hash;
292 _observeHistory = function() {
294 if (_currentHash != location.hash) {
295 _currentHash = location.hash;
296 $.ajatus.history.check(_currentHash);
298 } else if (_currentHash) {
300 $(window.document).trigger(RESET_EVENT);
304 this.navigate = function(count) {
305 window.history.go(count);
309 this.initialize = function(callback) {
310 // custom callback to reset app state (no hash in url)
311 if (typeof callback == 'function') {
312 $(window.document).unbind(RESET_EVENT, _defaultReset).bind(RESET_EVENT, callback);
314 // look for hash in current URL (not Safari) and execute predefined action if one is found
315 if (location.hash && typeof _addHistory == 'undefined') {
316 $.ajatus.history.check(location.hash);
319 if (_observeHistory && _intervalId == null) {
320 _intervalId = setInterval(_observeHistory, 200); // Safari needs at least 200 ms