add AJATUS_ROOT to include path since autoloading is not used everywhere, some places...
[ajatus.git] / js / ajatus.history.js
blob59fde869980833ef6457570b3cb368fdee9d424f
1 /*
2  * This file is part of
3  *
4  * Ajatus - Distributed CRM
5  * @requires jQuery v1.2.1
6  * 
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
12  * 
13  */
15 (function($){
16     $.ajatus = $.ajatus || {};
17     
18         $.ajatus.history = {
19             _handler: false,
20             last_checked: ''
21         };
22         $.extend($.ajatus.history, {
23             init: function() {          
24             $.ajatus.history._handler = new $.ajatus.history.handler;
25             $.ajatus.history._handler.initialize(function(){
26                 $.ajatus.tabs.set_active_by_hash('#view.frontpage');
27                 $.ajatus.views.system.frontpage.render();
28             });
29             },
30             add_map: function(hash, action) {
31                 $.ajatus.history._handler.add_map(hash, action);
32             },
33             add_dynamic_map: function() {
34                 var statics = [];
35                 var mods = [];
36                 var action = '';
37                 if (arguments[0]) {
38                     statics = arguments[0];
39                 }
40                 if (arguments[1]) {
41                     mods = arguments[1];
42                 }
43                 if (arguments[2]) {
44                     action = arguments[2];
45                 }
46                 $.ajatus.history._handler.add_dynamic_map(statics, mods, action);
47             },
48             update: function(hash) {
49                 $.ajatus.history._handler.update(hash);
50             },
51             check: function(hash) {             
52                 if (typeof hash == 'undefined') {
53                 var hash = $.ajatus.history._handler.get_hash();                    
54                 }
55             // $.ajatus.debug('$.ajatus.history.check('+hash+')');
56             
57                 if (   !$.browser.mozilla
58                     && $.ajatus.history.last_checked == hash)
59                 {
60                     //$.ajatus.debug('$.ajatus.history.check skipping hash '+hash);
61                     return false;
62                 }               
63                 $.ajatus.history.last_checked = hash;            
64                 
65                 if ($.ajatus.history._handler.executable(hash)) {
66                     $.ajatus.tabs.set_active_by_hash(hash);
67                     $.ajatus.history._handler.execute(hash);
68                     return true;
69                 }
70                 
71                 return false;
72             },
73             navigate: function(count) {
74                 if (typeof count == 'undefined') {
75                     return;
76                 }
77                 
78                 $.ajatus.history._handler.navigate(count);
79             }
80         });
81         
82     /**
83      * Base idea is taken from $.ajaxHistory plugin by Klaus Hartl
84      * Difference in this is that we evaluate predefined actions
85      * that are mapped against hashes.
86      */
87     $.ajatus.history.handler = function(){
88         
89         var mappings = {};
90         var dyn_mappings = [];
91         
92         var RESET_EVENT = 'historyReset';
94         var _currentHash = location.hash;
95         var _intervalId = null;
96         var _observeHistory;
98         this.update = function(){}; // empty function body for graceful degradation
99         
100         this.add_map = function(hash, action) {
101             mappings[hash] = action;
102         };
103         
104         this.add_dynamic_map = function(statics, mods, action) {
105             var hash = '#';
106             $.each(statics, function(i,s){
107                 hash += s + '.';
108             });
109             var dyn_map = {
110                 'hash': hash,
111                 'statics': statics,
112                 'mods': mods,
113                 'action': action
114             };
115             dyn_mappings.push(dyn_map);
116         };
117         
118         this.is_dyn_mapping = function(hash) {
119             var ch = hash.replace('#','');
120             var hash_parts = ch.split(/\./);
122             var ret_val = false;
123             var func_args = [];
124             var added_args = [];
125             $.each(dyn_mappings, function(i,dm){                
126                 var matched = false;
127                 var matched_key = 0;
128                 $.each(dm.statics, function(k,n){
129                     if (hash_parts[k] == n) {
130                         matched = true;
131                         matched_key = k;
132                     }
133                 });
134                 
135                 $.each(hash_parts, function(hk,hp){
136                     if (   hk > matched_key
137                         && hk < dm.mods)
138                     {
139                         
140                         if ($.inArray(hp, added_args) == -1) {
141                             added_args.push(hp);
142                             func_args.push(hp);
143                         }
144                     }
145                 });
146                 
147                 if (func_args.length > dm.mods) {
148                     matched = false;
149                 }
151                 if (matched) {
152                     ret_val = {
153                         id: Number(i),
154                         args: func_args
155                     }
156                 }
157             });
158             
159             
160             
161             return ret_val;
162         };
163         
164         this.executable = function(hash){
165             if (mappings[hash]) {
166                 return true;
167             } else {
168                 var dm_data = this.is_dyn_mapping(hash);
169                 if (dm_data !== false) {
170                     return true;
171                 }
172             }
173             return false;            
174         };
175         
176         this.execute = function(hash){
177             //$.ajatus.debug('$.ajatus.extension.history.execute('+hash+')');
178             
179             if (mappings[hash]) {
180                 eval(mappings[hash]);
181                 
182                 //$.ajatus.debug('$.ajatus.extension.history.execute evaled from mappings: '+mappings[hash]);
183                 
184                 return true;
185             } else {
186                 var dm_data = this.is_dyn_mapping(hash);
187                 if (dm_data !== false) {
188                     //$.ajatus.debug('$.ajatus.extension.history.execute hash is dynamic mapping. Evaling: '+dyn_mappings[dm_data.id].action);
189                     
190                     var func = eval(dyn_mappings[dm_data.id].action);
191                     func.apply(func, dm_data.args);
192                                         
193                     return true;
194                 }
195             }
196             return false;
197         };
198         
199         this.get_hash = function(){
200             var hash = '';
201             
202             if ($.browser.safari) {
203                 if (window.document.URL.indexOf('#') >= 0) {
204                     hash = '#'+window.document.URL.split('#')[1];
205                 }
206             } else {
207                 hash = location.hash;                
208             }
209             
210             return hash;
211         };
212         
213         // create custom event for state reset
214         var _defaultReset = function() {
215         };        
216         $(window.document).bind(RESET_EVENT, _defaultReset);
217         
218         if ($.browser.safari) {
220             var _backStack, _forwardStack, _addHistory; // for Safari
222             // establish back/forward stacks
223             $(function() {
224                 _backStack = [];
225                 _backStack.length = history.length;
226                 _forwardStack = [];
227             });
228             var isFirst = false, initialized = false;
229             _addHistory = function(hash) {
230                 _backStack.push(hash);
231                 _forwardStack.length = 0; // clear forwardStack (true click occured)
232                 isFirst = false;
233             };
235             this.update = function(hash) {
236                 _currentHash = hash;
237                 location.hash = hash;
238                 _addHistory(_currentHash);
239             };
241             this.navigate = function(count) {
242                 if (count < 0) {
243                     var idx = (_backStack.length + count) - 1;
244                     var newHash = _backStack[idx];
245                 } else {
246                     var idx = (_forwardStack.length - count) - 1;
247                     var newHash = _forwardStack[idx];
248                 }
249                 
250                 if (typeof newHash != 'undefined') {
251                     $.ajatus.history.update(newHash);                        
252                 }
253             }
255             _observeHistory = function() {
256                 var historyDelta = history.length - _backStack.length;
257                 if (historyDelta) { // back or forward button has been pushed
258                     isFirst = false;
259                     if (historyDelta < 0) { // back button has been pushed
260                         // move items to forward stack
261                         for (var i = 0; i < Math.abs(historyDelta); i++) _forwardStack.unshift(_backStack.pop());
262                     } else { // forward button has been pushed
263                         // move items to back stack
264                         for (var i = 0; i < historyDelta; i++) _backStack.push(_forwardStack.shift());
265                     }
266                     var cachedHash = _backStack[_backStack.length - 1];
267                     _currentHash = location.hash;
269                     $.ajatus.history.check(_currentHash);
270                 } else if (typeof(_backStack[_backStack.length - 1]) == 'undefined' && !isFirst) {                    
271                     if (location.hash == '') {
272                         $(window.document).trigger(RESET_EVENT);
273                     }
274                     
275                     isFirst = true;
276                 }
277                 initialized = true;
278             };
279         } else {
280             this.update = function(hash) {
281                 if (_currentHash != hash) {
282                     location.hash = hash;
283                     _currentHash = hash;
284                 }
285             };
287             _observeHistory = function() {
288                 if (location.hash) {              
289                     if (_currentHash != location.hash) {
290                         _currentHash = location.hash;
291                         $.ajatus.history.check(_currentHash);
292                     }
293                 } else if (_currentHash) {
294                     _currentHash = '';
295                     $(window.document).trigger(RESET_EVENT);
296                 }
297             };
298             
299             this.navigate = function(count) {
300                 window.history.go(count);
301             }
302         }
303         
304         this.initialize = function(callback) {
305             // custom callback to reset app state (no hash in url)
306             if (typeof callback == 'function') {
307                 $(window.document).unbind(RESET_EVENT, _defaultReset).bind(RESET_EVENT, callback);
308             }
309             // look for hash in current URL (not Safari) and execute predefined action if one is found
310             if (location.hash && typeof _addHistory == 'undefined') {                
311                 $.ajatus.history.check(location.hash);
312             }
313             // start observer
314             if (_observeHistory && _intervalId == null) {
315                 _intervalId = setInterval(_observeHistory, 200); // Safari needs at least 200 ms
316             }
317         };
318     };
319     
320 })(jQuery);