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