Fixed timed event check
[ajatus.git] / js / ajatus.history.js
blob293547786721249cd05e6068d390841d3a5ff119
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             enabled: true,
20             _handler: false,
21             last_checked: ''
22         };
23         $.extend($.ajatus.history, {
24             init: function() {
25                 $.ajatus.history.enabled = typeof($.ajatus.history.handler) != 'undefined' ? true : false;
26                 
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();
32                     });
33                 }
34             },
35             add_map: function(hash, action) {
36                 $.ajatus.history._handler.add_map(hash, action);
37             },
38             add_dynamic_map: function() {
39                 var statics = [];
40                 var mods = [];
41                 var action = '';
42                 if (arguments[0]) {
43                     statics = arguments[0];
44                 }
45                 if (arguments[1]) {
46                     mods = arguments[1];
47                 }
48                 if (arguments[2]) {
49                     action = arguments[2];
50                 }
51                 $.ajatus.history._handler.add_dynamic_map(statics, mods, action);
52             },
53             update: function(hash) {
54                 $.ajatus.history._handler.update(hash);
55             },
56             check: function(hash) {             
57                 if (typeof hash == 'undefined') {
58                 var hash = $.ajatus.history._handler.get_hash();                    
59                 }
60             // $.ajatus.debug('$.ajatus.history.check('+hash+')');
61             
62                 if (   !$.browser.mozilla
63                     && $.ajatus.history.last_checked == hash)
64                 {
65                     //$.ajatus.debug('$.ajatus.history.check skipping hash '+hash);
66                     return false;
67                 }               
68                 $.ajatus.history.last_checked = hash;            
69                 
70                 if ($.ajatus.history._handler.executable(hash)) {
71                     $.ajatus.tabs.set_active_by_hash(hash);
72                     $.ajatus.history._handler.execute(hash);
73                     return true;
74                 }
75                 
76                 return false;
77             },
78             navigate: function(count) {
79                 if (typeof count == 'undefined') {
80                     return;
81                 }
82                 
83                 $.ajatus.history._handler.navigate(count);
84             }
85         });
86         
87     /**
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.
91      */
92     $.ajatus.history.handler = function(){
93         
94         var mappings = {};
95         var dyn_mappings = [];
96         
97         var RESET_EVENT = 'historyReset';
99         var _currentHash = location.hash;
100         var _intervalId = null;
101         var _observeHistory;
103         this.update = function(){}; // empty function body for graceful degradation
104         
105         this.add_map = function(hash, action) {
106             mappings[hash] = action;
107         };
108         
109         this.add_dynamic_map = function(statics, mods, action) {
110             var hash = '#';
111             $.each(statics, function(i,s){
112                 hash += s + '.';
113             });
114             var dyn_map = {
115                 'hash': hash,
116                 'statics': statics,
117                 'mods': mods,
118                 'action': action
119             };
120             dyn_mappings.push(dyn_map);
121         };
122         
123         this.is_dyn_mapping = function(hash) {
124             var ch = hash.replace('#','');
125             var hash_parts = ch.split(/\./);
127             var ret_val = false;
128             var func_args = [];
129             var added_args = [];
130             $.each(dyn_mappings, function(i,dm){                
131                 var matched = false;
132                 var matched_key = 0;
133                 $.each(dm.statics, function(k,n){
134                     if (hash_parts[k] == n) {
135                         matched = true;
136                         matched_key = k;
137                     }
138                 });
139                 
140                 $.each(hash_parts, function(hk,hp){
141                     if (   hk > matched_key
142                         && hk < dm.mods)
143                     {
144                         
145                         if ($.inArray(hp, added_args) == -1) {
146                             added_args.push(hp);
147                             func_args.push(hp);
148                         }
149                     }
150                 });
151                 
152                 if (func_args.length > dm.mods) {
153                     matched = false;
154                 }
156                 if (matched) {
157                     ret_val = {
158                         id: Number(i),
159                         args: func_args
160                     }
161                 }
162             });
163             
164             
165             
166             return ret_val;
167         };
168         
169         this.executable = function(hash){
170             if (mappings[hash]) {
171                 return true;
172             } else {
173                 var dm_data = this.is_dyn_mapping(hash);
174                 if (dm_data !== false) {
175                     return true;
176                 }
177             }
178             return false;            
179         };
180         
181         this.execute = function(hash){
182             //$.ajatus.debug('$.ajatus.extension.history.execute('+hash+')');
183             
184             if (mappings[hash]) {
185                 eval(mappings[hash]);
186                 
187                 //$.ajatus.debug('$.ajatus.extension.history.execute evaled from mappings: '+mappings[hash]);
188                 
189                 return true;
190             } else {
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);
194                     
195                     var func = eval(dyn_mappings[dm_data.id].action);
196                     func.apply(func, dm_data.args);
197                                         
198                     return true;
199                 }
200             }
201             return false;
202         };
203         
204         this.get_hash = function(){
205             var hash = '';
206             
207             if ($.browser.safari) {
208                 if (window.document.URL.indexOf('#') >= 0) {
209                     hash = '#'+window.document.URL.split('#')[1];
210                 }
211             } else {
212                 hash = location.hash;                
213             }
214             
215             return hash;
216         };
217         
218         // create custom event for state reset
219         var _defaultReset = function() {
220         };        
221         $(window.document).bind(RESET_EVENT, _defaultReset);
222         
223         if ($.browser.safari) {
225             var _backStack, _forwardStack, _addHistory; // for Safari
227             // establish back/forward stacks
228             $(function() {
229                 _backStack = [];
230                 _backStack.length = history.length;
231                 _forwardStack = [];
232             });
233             var isFirst = false, initialized = false;
234             _addHistory = function(hash) {
235                 _backStack.push(hash);
236                 _forwardStack.length = 0; // clear forwardStack (true click occured)
237                 isFirst = false;
238             };
240             this.update = function(hash) {
241                 _currentHash = hash;
242                 location.hash = hash;
243                 _addHistory(_currentHash);
244             };
246             this.navigate = function(count) {
247                 if (count < 0) {
248                     var idx = (_backStack.length + count) - 1;
249                     var newHash = _backStack[idx];
250                 } else {
251                     var idx = (_forwardStack.length - count) - 1;
252                     var newHash = _forwardStack[idx];
253                 }
254                 
255                 if (typeof newHash != 'undefined') {
256                     $.ajatus.history.update(newHash);                        
257                 }
258             }
260             _observeHistory = function() {
261                 var historyDelta = history.length - _backStack.length;
262                 if (historyDelta) { // back or forward button has been pushed
263                     isFirst = false;
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());
270                     }
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);
278                     }
279                     
280                     isFirst = true;
281                 }
282                 initialized = true;
283             };
284         } else {
285             this.update = function(hash) {
286                 if (_currentHash != hash) {
287                     location.hash = hash;
288                     _currentHash = hash;
289                 }
290             };
292             _observeHistory = function() {
293                 if (location.hash) {              
294                     if (_currentHash != location.hash) {
295                         _currentHash = location.hash;
296                         $.ajatus.history.check(_currentHash);
297                     }
298                 } else if (_currentHash) {
299                     _currentHash = '';
300                     $(window.document).trigger(RESET_EVENT);
301                 }
302             };
303             
304             this.navigate = function(count) {
305                 window.history.go(count);
306             }
307         }
308         
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);
313             }
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);
317             }
318             // start observer
319             if (_observeHistory && _intervalId == null) {
320                 _intervalId = setInterval(_observeHistory, 200); // Safari needs at least 200 ms
321             }
322         };
323     };
324     
325 })(jQuery);