Fixed timed event check
[ajatus.git] / js / ajatus.core.js
blob3a09f77f160a4e66edc3f326f1f04cdab4000a01
1 /*
2  * This file is part of
3  *
4  * {@link http://ajatus.info 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  */
16     TODO Tags are links
17     TODO Additional fields [widget]
18     TODO Skype widget -bergie
19     TODO Formatter service
20     TODO XMMP & Email formatter
21     TODO Gravatar widget
22     TODO Implement arhive view
23     TODO Implement file attachments
24     TODO Documentation (API)
27 if (typeof console == 'undefined') {
28     var console = {};
29     console.log = function() {
30         return;
31     };
34 (function($){
35     $.ajatus = {};
36     $.ajatus.application_content_area = null;
37     
38     $.ajatus.version = [0, 0, 9];
39     
40     $.ajatus.active_type = null;
41     
42     /**
43      * Holds all converters available in Ajatus.
44      */
45     $.ajatus.converter = {};
47     /**
48      * Parses and evaluates JSON string to Javascript
49      * @param {String} json_str JSON String
50      * @returns Parsed JSON string or false on failure
51      */
52     $.ajatus.converter.parseJSON = function (json_str) {
53         try {
54             return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
55                     json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
56                 eval('(' + json_str + ')');
57         } catch (e) {
58             return false;
59         }
60     }
62     /**
63      * Parses Javascript to JSON
64      * @param {Mixed} item Javascript to be parsed
65      * @param {String} type of the Javascript item to be parsed (Optional)
66      * @returns JSON string
67      * @type String
68      */
69     $.ajatus.converter.toJSON = function (item,item_type) {
70         var m = {
71                 '\b': '\\b',
72                 '\t': '\\t',
73                 '\n': '\\n',
74                 '\f': '\\f',
75                 '\r': '\\r',
76                 '"' : '\\"',
77                 '\\': '\\\\'
78             },
79             s = {
80                 arr: function (x) {
81                     var a = ['['], b, f, i, l = x.length, v;
82                     for (i = 0; i < l; i += 1) {
83                         v = x[i];
84                         v = conv(v);
85                         if (typeof v == 'string') {
86                             if (b) {
87                                 a[a.length] = ',';
88                             }
89                             a[a.length] = v;
90                             b = true;
91                         }
92                     }
93                     a[a.length] = ']';
94                     return a.join('');
95                 },
96                 bool: function (x) {
97                     return String(x);
98                 },
99                 nul: function (x) {
100                     return "null";
101                 },
102                 num: function (x) {
103                     return isFinite(x) ? String(x) : 'null';
104                 },
105                 obj: function (x) {
106                     if (x) {
107                         if (x instanceof Array) {
108                             return s.arr(x);
109                         }
110                         var a = ['{'], b, f, i, v;
111                         for (i in x) {
112                             v = x[i];
113                             v = conv(v);
114                             if (typeof v == 'string') {
115                                 if (b) {
116                                     a[a.length] = ',';
117                                 }
118                                 a.push(s.str(i), ':', v);
119                                 b = true;
120                             }
121                         }
122                         a[a.length] = '}';
123                         return a.join('');
124                     }
125                     return 'null';
126                 },
127                 str: function (x) {
128                     if (/["\\\x00-\x1f]/.test(x)) {
129                         x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
130                             var c = m[b];
131                             if (c) {
132                                 return c;
133                             }
134                             c = b.charCodeAt();
135                             return '\\u00' +
136                                 Math.floor(c / 16).toString(16) +
137                                 (c % 16).toString(16);
138                         });
139                     }
140                     return '"' + x + '"';
141                 }
142             };
143             conv = function (x) {
144                 var itemtype = typeof x;
145                 switch(itemtype) {
146                     case "array":
147                       return s.arr(x);
148                       break;
149                     case "object":
150                       return s.obj(x);
151                       break;
152                     case "string":
153                       return s.str(x);
154                       break;
155                     case "number":
156                       return s.num(x);
157                       break;
158                     case "null":
159                       return s.nul(x);
160                       break;
161                     case "boolean":
162                       return s.bool(x);
163                       break;
164                 }
165             }
167         var itemtype = item_type || typeof item;
168         switch(itemtype) {
169             case "array":
170               return s.arr(item);
171               break;
172             case "object":
173               return s.obj(item);
174               break;
175             case "string":
176               return s.str(item);
177               break;
178             case "number":
179               return s.num(item);
180               break;
181             case "null":
182               return s.nul(item);
183               break;
184             case "boolean":
185               return s.bool(item);
186               break;                
187             default:
188               throw("Unknown type for $.ajatus.converter.toJSON");
189             }
190     }
191     
192     /**
193      * Holds all formatters available in Ajatus.
194      * @constructor
195      */
196     $.ajatus.formatter = {};
198     /**
199      * Holds all date formatters available in Ajatus.
200      * @constructor
201      */
202     $.ajatus.formatter.date = {};
204     /**
205      * Formats numbers < 10 to two digit numbers
206      * @param {Number} number Number to format
207      * @returns Formatted number
208      * @type String
209      */
210     $.ajatus.formatter.date.fix_length = function(number){
211         return ((number < 10) ? '0' : '') + number;
212     };
214     /**
215      * Checks if given value is ISO8601 Formatted Date
216      * @param {String} value Value to check
217      * @returns True, False if value not string or ISO8601 formatted
218      * @type Boolean
219      */
220     $.ajatus.formatter.date.is_iso8601 = function(value) {
221         if (   typeof value != 'string'
222             || value == '')
223         {
224             return false;
225         }
226         
227         var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
228         var isod = value.match(new RegExp(regexp));
229         
230         if (   typeof isod == 'object'
231             && typeof isod[10] != 'undefined')
232         {
233             return true;
234         }
235         
236         return false;
237     };
239     /**
240      * Converts JS Date to ISO8601 Formatted JS Date
241      * @param {Date} date Javascript date to convert
242      * @returns ISO8601 formatted Date
243      * @type Date
244      */
245     $.ajatus.formatter.date.js_to_iso8601 = function(date) {
246         var str = "";
247         str += date.getFullYear();
248         str += "-" + $.ajatus.formatter.date.fix_length(date.getMonth() + 1);
249         str += "-" + $.ajatus.formatter.date.fix_length(date.getDate());
250         str += "T" + $.ajatus.formatter.date.fix_length(date.getHours());
251         str += ":" + $.ajatus.formatter.date.fix_length(date.getMinutes());
252         str += ":" + $.ajatus.formatter.date.fix_length(date.getSeconds());
253         //str += 'Z';
254         
255         return str;
256     };
258     /**
259      * Converts ISO8601 Formatted JS Date to JS Date
260      * @param {Date} iso_date ISO8601 formatted date to convert
261      * @returns Javascript Date
262      * @type Date
263      */
264     $.ajatus.formatter.date.iso8601_to_js = function(iso_date) {
265         var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
266         var isod = iso_date.match(new RegExp(regexp));
267         
268         if (   !isod
269             || isod.length < 11)
270         {
271             return new Date();
272         }
273         
274         var date = new Date(isod[1], 0, 1);
275         date.setMonth(isod[3] - 1);
276         date.setDate(isod[5]);
277         date.setHours(isod[7]);
278         date.setMinutes(isod[8]);
279         date.setSeconds(isod[10]);
280         
281         return date;
282     };
283     
284     /**
285      * Converts Date string from calendar widget to ISO8601 Formatted JS Date
286      * @param {String} caldate Date value from calendar widget
287      * @param {String} format Format used in conversion (Default: "MDY/")
288      * @param {String} caltime Time value from calendar widget
289      * @param {String} time_format Format used in conversion (Default: "HMS:")
290      * @returns ISO8601 formatted Javascript Date
291      * @type Date
292      */
293     $.ajatus.formatter.date.caldate_to_iso8601 = function(caldate, format, caltime, time_format) {
294         
295         var format = format || ($.ajatus.i10n.datetime.date || "MDY/");
296         var time_format = time_format || ($.ajatus.i10n.datetime.time || "HMS:");
297         
298         var cdparts = caldate.split(format.charAt(3));
299         var fparts = format.split("");
300         var date = new Date();
301         
302         $.each(cdparts, function(i,v){
303             v = Number(v);
304             if (fparts[i] == 'M') {
305                 date.setMonth(v-1);
306             }
307             if (fparts[i] == 'D') {
308                 date.setDate(v);
309             }
310             if (fparts[i] == 'Y') {
311                 date.setFullYear(v);
312             }
313         });
314         
315         if (caltime) {
316             var time_separator = time_format.charAt(time_format.length - 1);
317             var tfparts = time_format.split("");
318             var ctparts = caltime.split(time_separator);
320             $.each(ctparts, function(i,v){
321                 v = Number(v);
322                 if (tfparts[i] == 'H') {
323                     date.setHours(v);
324                 }
325                 if (tfparts[i] == 'M') {
326                     date.setMinutes(v);
327                 }
328                 if (tfparts[i] == 'S') {
329                     date.setSeconds(v);
330                 }
331             });
332         } else {
333             date.setHours(0);
334             date.setMinutes(0);
335             date.setSeconds(0);
336         }
337                 
338         return $.ajatus.formatter.date.js_to_iso8601(date);
339     };
340     
341     /**
342      * Converts ISO8601 Formatted JS Date to calendar widget date string
343      * @param {Date} iso_date ISO8601 Formatted Date
344      * @param {String} format Format used in conversion (Default: "MDY/")
345      * @returns Calendar widget supported value
346      * @type String
347      */
348     $.ajatus.formatter.date.iso8601_to_caldate = function(iso_date, format) {
349         var format = format || ($.ajatus.i10n.datetime.date || "MDY/");
350         
351         var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
352         var date_str = '';
353         
354         var day = date.getDate();
355         var month = date.getMonth();
356         var year = date.getFullYear();
357         month++;
358         
359         for (var i = 0; i < 3; i++) {
360             date_str += format.charAt(3) +
361             (format.charAt(i) == 'D' ? $.ajatus.formatter.date.fix_length(day) :
362             (format.charAt(i) == 'M' ? $.ajatus.formatter.date.fix_length(month) :
363             (format.charAt(i) == 'Y' ? year : '?')));
364         }
365         date_str = date_str.substring(format.charAt(3) ? 1 : 0);
366         
367         return date_str;
368     };
369     
370     /**
371      * Converts ISO8601 Formatted JS Date to calendar widget time string
372      * @param {Date} iso_date ISO8601 Formatted Date
373      * @param {String} format Format used in conversion (Default: "HMS:")
374      * @returns Calendar widget supported value
375      * @type String
376      */
377     $.ajatus.formatter.date.iso8601_to_caltime = function(iso_date, format) {
378         var format = format || ($.ajatus.i10n.datetime.time || "HMS:");
379         
380         var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
381         var time_str = '';
382         
383         var idparts = {
384             H: $.ajatus.formatter.date.fix_length(date.getHours()),
385             M: $.ajatus.formatter.date.fix_length(date.getMinutes()),
386             S: $.ajatus.formatter.date.fix_length(date.getSeconds())
387         };
389         var separator = format.charAt(format.length - 1);
390         var fparts = format.split("");
391         
392         $.each(fparts, function(i,k){
393             if (typeof idparts[k] != 'undefined') {
394                 time_str += idparts[k] + separator;
395             }
396         });
397         time_str = time_str.substring((time_str.length-1), -1);
398         
399         return time_str;
400     };
401     
402     $.ajatus.tabs = {};
403     $.ajatus.tabs.prepare = function(tab) {
404         tab.bind('mouseover', function(e){
405             tab.addClass('tabs-hover');
406         });
407         tab.bind('mouseout', function(e){
408             tab.removeClass('tabs-hover');
409         });
410     };
411     $.ajatus.tabs.set_active_by_hash = function(hash) {
412         // $.ajatus.debug('$.ajatus.tabs.set_active_by_hash('+hash+')');
413         
414         var views_tab_holder = $('#tabs-views ul');
415         var app_tab_holder = $('#tabs-application ul');
416         
417         var selected_tab = $('li a[@href$="' + hash + '"]', views_tab_holder).parent();
418         if (selected_tab[0] != undefined) {
419             // $.ajatus.debug('views selected_tab:');
420             // $.ajatus.debug(selected_tab);
421             $('li', views_tab_holder).removeClass('tabs-selected').blur();
422             $('li', app_tab_holder).removeClass('tabs-selected').blur();
423             selected_tab.addClass('tabs-selected').focus();
424         } else {
425             selected_tab = $('li a[@href$="' + hash + '"]', app_tab_holder).parent();
426             // $.ajatus.debug('app selected_tab:');
427             // $.ajatus.debug(selected_tab);
428             if (selected_tab[0] != undefined) {
429                 $('li', views_tab_holder).removeClass('tabs-selected').blur();
430                 $('li', app_tab_holder).removeClass('tabs-selected').blur();
431                 selected_tab.addClass('tabs-selected').focus();
432             } else {
433                 $('li', views_tab_holder).removeClass('tabs-selected').blur();
434                 $('li', app_tab_holder).removeClass('tabs-selected').blur();                
435             }            
436         }
437     };
438     $.ajatus.tabs.on_click = function(e) {        
439         var trgt = target(e);
440         $("li", parent(e)).removeClass("tabs-selected").index(trgt);
441         $(trgt).addClass("tabs-selected");
442         
443         var new_hash = $('a', trgt)[0].hash;
444         location.hash = new_hash;
445         
446         if (   $.ajatus.history
447             && $.ajatus.history.enabled)
448         {        
449             $.ajatus.history.update(new_hash);
450         }
451         
452         $.ajatus.views.on_change(new_hash);
453         
454         function parent(event) {
455             var element = event.target;
456             if (element)
457             {
458                 if (element.tagName == "UL")
459                 {
460                     return element;
461                 }
463                 while(element.tagName != "UL")
464                 {
465                     element = element.parentNode;
466                 }
467             }
468             return element;
469         };
471         function target(event) {
472             var element = event.target;
473             if (element)
474             {
475                 if (element.tagName == "UL")
476                 {
477                     element = $(element).find('li').eq(0);
478                     return element;
479                 }
481                 while(element.tagName != "LI")
482                 {
483                     element = element.parentNode;
484                 }
485             }
486             return element;
487         };
488     };
489     
490     $.ajatus.security_pass = function() {
491         try {
492             netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
493         } catch (e) {
494             alert("Permission UniversalBrowserRead denied. with error "+e);
495         }
496     };
497     
498     $.ajatus.init = function(element, options) {
499         //$.ajatus.security_pass();
500         
501         var application_element = $(element);
502         $.ajatus.application_element = application_element;
503         $.ajatus.application_content_area = $('#middle #content', $.ajatus.application_element);
504         $.ajatus.application_dynamic_elements = $('#dynamic-elements', $.ajatus.application_element);
505         
506         $.ajatus.preferences.client.server_url = window.location.protocol + '//' + document.hostname + (window.location.port != '' ? ':'+window.location.port : '') + '/';
507         
508         $.ajatus.preferences.client = $.extend({}, $.ajatus.preferences.client_defaults, options);
509         //Force the system types to load:
510         $.ajatus.preferences.client.types.system = $.ajatus.preferences.client_defaults.types.system;
511         
512         $.jqCouch.set_defaults({
513             server_url: $.ajatus.preferences.client.server_url
514         });
515         
516         var a_db_suffix = $.ajatus.preferences.client.application_database_identifier != '' ? '_db' : 'db';
517         $.ajatus.preferences.client.application_database = 'ajatus_' + $.ajatus.preferences.client.application_database_identifier + a_db_suffix;
518         $.ajatus.preferences.client.content_database = $.ajatus.preferences.client.application_database + '_content';
519         $.ajatus.preferences.client.tags_database = $.ajatus.preferences.client.application_database + '_tags';
520         
521         $.ajaxSetup({
522             type: "GET",
523             url: $.ajatus.preferences.client.server_url,
524             global: false,
525             cache: false,
526             dataType: "json"
527         });
528         
529         $('#top #new-item-button', application_element).bind('click', function(e){
530             $.ajatus.views.system.create.render($.ajatus.active_type);
531         });
532         
533         $.ajatus.installer.is_installed();        
534         if ($.ajatus.installer.installed == false) {
536             if ($.ajatus.preferences.client.language == null) {
537                 var lang = 'en_GB';
538                 $.ajatus.preferences.client.language = 'en_GB';
539             }
540             $.ajatus.i10n.init($.ajatus.preferences.client.language, function(){
541                 var status = $.ajatus.installer.install();
542                 $.ajatus.debug("Installer finished with status: "+status);
543                 if (status) {
544                     window.location = window.location + "#view.preferences";
545                 }
546             });
547         } else {
548             $.ajatus.preload();
549         }
550         // else
551         // {
552         //     $.ajatus.installer.uninstall();
553         // }
554     };
555     
556     $.ajatus.preload = function() {
557         var preference_loader = new $.ajatus.preferences.loader;
558         preference_loader.load();
560         if ($.ajatus.preferences.client.language == null) {
561             var lang = 'en_GB';
562             if (typeof($.ajatus.preferences.local.localization) != 'undefined') {
563                 lang = $.ajatus.preferences.local.localization.language;
564             }
565             $.ajatus.preferences.client.language = lang;
566         }
567         $.ajatus.i10n.init($.ajatus.preferences.client.language, function(){
568             $.ajatus.types.init(function(){
569                 $.ajatus.start();
570             });            
571         });       
572     };
573     
574     $.ajatus.start = function() {
575         $.ajatus.debug('started', 'Ajatus start');
577             $.ajatus.events.lock_pool.increase();
578         
579         if ($.ajatus.preferences.local.layout.theme_name != 'default') {
580             $.ajatus.preferences.client.theme_url = 'themes/' + $.ajatus.preferences.local.layout.theme_name + '/';
581             $.ajatus.preferences.client.theme_icons_url = 'themes/' + $.ajatus.preferences.local.layout.icon_set + '/images/icons/';
582                         
583             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/structure.css');
584             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/common.css');
585             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/elements.css');
586         }
587         
588             $.ajatus.locker = new $.ajatus.events.lock({
589                 watch: {
590                     validate: function(){return $.ajatus.events.lock_pool.count == 0;},
591                     interval: 200,
592                 safety_runs: 0
593             },
594             on_release: function(){
595                 $.ajatus.application_element.addClass('ajatus_initialized');
596                 
597                 var show_frontpage = true;
598                 if (   $.ajatus.history
599                     && $.ajatus.history.enabled)
600                 {
601                      show_frontpage = !$.ajatus.history.check();
602                 }
603                 
604                 if (show_frontpage) {
605                     $.ajatus.views.system.frontpage.render();                    
606                 }
607             }
608         });
609         
610         $('#header .app_version', $.ajatus.application_element).html($.ajatus.version.join('.'));
611         $('#header .tagline', $.ajatus.application_element).html($.ajatus.i10n.get('Distributed CRM'));
612         $('#new-item-button').text($.ajatus.i10n.get('new'));
613         
614         $.ajatus.toolbar.init();        
615         $.ajatus.history.init();        
616         $.ajatus.tags.init();
617         $.ajatus.widgets.init();
618         $.ajatus.views.init();
619         
620         $.ajatus.extensions.init({
621             on_ready: function(){                
622                 $.ajatus.active_type = $.ajatus.preferences.client.content_types['note'];
624                 if ($.ajatus.preferences.modified) {
625                     $.ajatus.views.on_change_actions.add('$.ajatus.preferences.view.save($.ajatus.preferences.local)');
626                 }
627                 
628                 // Release the first lock
629                 $.ajatus.events.lock_pool.decrease();
630             }
631         });
632         
633         $.ajatus.elements.messages.set_holder();
634         
635         window.onbeforeunload = function() {
636             if ($.ajatus.events.named_lock_pool.count('unsaved') > 0) {
637                 return $.ajatus.i10n.get('You have unsaved changes.');
638             }
639         }
640         
641         $.ajatus.debug('ended', 'Ajatus start');
642     }
643     
644     $.ajatus.ajax_error = function(request, caller) {
645         $.ajatus.debug("Ajax error request.status: "+request.status);
646         
647         if (typeof caller != 'undefined') {
648             $.ajatus.debug("Caller method: "+caller.method);
649             $.ajatus.debug("Caller action:");
650             $.ajatus.debug(caller.action);
651             
652             if (caller.args != undefined) {
653                 $.ajatus.debug("Caller args:");
654                 $.ajatus.debug(caller.args);
655             }
656         }
657         
658         if (request.responseText != '') {
659             var response = eval("(" + request.responseText + ")");
660             $.ajatus.debug('response.error.reason: '+response.error.reason);            
661         }
662     };
663     $.ajatus.ajax_success = function(data, caller) {
664         $.ajatus.debug("Ajax success");
665         
666         if (typeof caller != 'undefined') {
667             $.ajatus.debug("Caller method: "+caller.method);
668             $.ajatus.debug("Caller action: "+caller.action);
669             
670             if (typeof caller.args != 'undefined') {
671                 $.ajatus.debug("Caller args: "+caller.args);
672             }
673         }
674         $.ajatus.debug("data: "+data);
675         
676         return false;
677     };
678     
679     $.ajatus.debug = function(msg, title, type) {
680         if ($.ajatus.preferences.client.debug == true) {
681             if (typeof(console.log) != 'undefined') {
682                 debug_console(msg, title, type);
683             } else {
684                 debug_static(msg, title, type);
685             }
686         }
688         function debug_console(msg, title, type) {
689             if (typeof type == 'undefined') {
690                 var type = 'log';
691             }
692             
693             if (typeof(console[type]) != 'undefined') {
694                 console.log(format(msg, title));
695             } else {
696                 console.log(format(msg, title));
697             }
698         }
699         
700         function debug_static(msg, title, type) {
701             // TODO: Implement Static debug handler
702         }
703         
704         function format(msg, title) {
705             var string = '';
706             
707             if (typeof title != 'undefined') {
708                 string += title += ': ';
709             }
710             
711             if (typeof msg != 'string') {
712                 string += $.ajatus.utils.pretty_print(msg);
713             } else {
714                 string += msg;
715             }
716             
717             return string;            
718         }
719     };
720     
721     $.fn.initialize_ajatus = function(options) {
722         if (! $(this).is('.ajatus_initialized')) {
723             return new $.ajatus.init(this, options);
724         }
725     };
726     
727 })(jQuery);
729 function initialize_ajatus() {
730     if (typeof ajatus_client_config == 'undefined') {
731         var ajatus_client_config = {};
732     }
733     
734     jQuery('#application').initialize_ajatus($.extend({
735         // debug: true,
736         custom_views: [
737             //'test'
738         ],
739         // types: {
740         //     in_use: [
741         //         'note', 'contact', 'event', 'expense', 'hour_report'
742         //     ]
743         // },
744         application_url: '/_utils/ajatus_dev/', // '/_utils/ajatus/'
745         application_database_identifier: 'dev' // 'stable'
746     }, ajatus_client_config));
749 $(document).ready(function() {
750     try{
751         initialize_ajatus();        
752     } catch(e) {
753         alert("could not initialize ajatus! Reason: "+e);
754     }