Removed history enabled checks as it is required by core
[ajatus.git] / js / ajatus.core.js
blob561bad5fa403ea58188501d5fd42ac5f864151e4
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         $.ajatus.history.update(new_hash);
447         
448         $.ajatus.views.on_change(new_hash);
449         
450         function parent(event) {
451             var element = event.target;
452             if (element)
453             {
454                 if (element.tagName == "UL")
455                 {
456                     return element;
457                 }
459                 while(element.tagName != "UL")
460                 {
461                     element = element.parentNode;
462                 }
463             }
464             return element;
465         };
467         function target(event) {
468             var element = event.target;
469             if (element)
470             {
471                 if (element.tagName == "UL")
472                 {
473                     element = $(element).find('li').eq(0);
474                     return element;
475                 }
477                 while(element.tagName != "LI")
478                 {
479                     element = element.parentNode;
480                 }
481             }
482             return element;
483         };
484     };
485     
486     $.ajatus.security_pass = function() {
487         try {
488             netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
489         } catch (e) {
490             alert("Permission UniversalBrowserRead denied. with error "+e);
491         }
492     };
493     
494     $.ajatus.init = function(element, options) {
495         //$.ajatus.security_pass();
496         
497         var application_element = $(element);
498         $.ajatus.application_element = application_element;
499         $.ajatus.application_content_area = $('#middle #content', $.ajatus.application_element);
500         $.ajatus.application_dynamic_elements = $('#dynamic-elements', $.ajatus.application_element);
501         
502         $.ajatus.preferences.client.server_url = window.location.protocol + '//' + document.hostname + (window.location.port != '' ? ':'+window.location.port : '') + '/';
503         
504         $.ajatus.preferences.client = $.extend({}, $.ajatus.preferences.client_defaults, options);
505         //Force the system types to load:
506         $.ajatus.preferences.client.types.system = $.ajatus.preferences.client_defaults.types.system;
507         
508         $.jqCouch.set_defaults({
509             server_url: $.ajatus.preferences.client.server_url
510         });
511         
512         var a_db_suffix = $.ajatus.preferences.client.application_database_identifier != '' ? '_db' : 'db';
513         $.ajatus.preferences.client.application_database = 'ajatus_' + $.ajatus.preferences.client.application_database_identifier + a_db_suffix;
514         $.ajatus.preferences.client.content_database = $.ajatus.preferences.client.application_database + '_content';
515         
516         $.ajaxSetup({
517             type: "GET",
518             url: $.ajatus.preferences.client.server_url,
519             global: false,
520             cache: false,
521             dataType: "json"
522         });
523         
524         $('#top #new-item-button', application_element).bind('click', function(e){
525             $.ajatus.views.system.create.render($.ajatus.active_type);
526         });
527         
528         $.ajatus.installer.is_installed();        
529         if ($.ajatus.installer.installed == false) {
531             if ($.ajatus.preferences.client.language == null) {
532                 var lang = 'en_GB';
533                 $.ajatus.preferences.client.language = 'en_GB';
534             }
535             $.ajatus.i10n.init($.ajatus.preferences.client.language, function(){
536                 var status = $.ajatus.installer.install();
537                 $.ajatus.debug("Installer finished with status: "+status);
538                 if (status) {
539                     window.location = window.location + "#view.preferences";
540                 }
541             });
542         } else {
543             $.ajatus.preload();
544         }
545         // else
546         // {
547         //     $.ajatus.installer.uninstall();
548         // }
549     };
550     
551     $.ajatus.preload = function() {
552         var preference_loader = new $.ajatus.preferences.loader;
553         preference_loader.load();
555         if ($.ajatus.preferences.client.language == null) {
556             var lang = 'en_GB';
557             if (typeof($.ajatus.preferences.local.localization) != 'undefined') {
558                 lang = $.ajatus.preferences.local.localization.language;
559             }
560             $.ajatus.preferences.client.language = lang;
561         }
562         $.ajatus.i10n.init($.ajatus.preferences.client.language, function(){
563             $.ajatus.types.init(function(){
564                 $.ajatus.start();
565             });            
566         });       
567     };
568     
569     $.ajatus.start = function() {
570         $.ajatus.debug('started', 'Ajatus start');
572             $.ajatus.events.lock_pool.increase();
573         
574         if ($.ajatus.preferences.local.layout.theme_name != 'default') {
575             $.ajatus.preferences.client.theme_url = 'themes/' + $.ajatus.preferences.local.layout.theme_name + '/';
576             $.ajatus.preferences.client.theme_icons_url = 'themes/' + $.ajatus.preferences.local.layout.icon_set + '/images/icons/';
577                         
578             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/structure.css');
579             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/common.css');
580             $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/elements.css');
581         }
582         
583             $.ajatus.locker = new $.ajatus.events.lock({
584                 watch: {
585                     validate: function(){return $.ajatus.events.lock_pool.count == 0;},
586                     interval: 200,
587                 safety_runs: 0
588             },
589             on_release: function(){
590                 $.ajatus.application_element.addClass('ajatus_initialized');
591                 
592                 var show_frontpage = !$.ajatus.history.check();
593                 
594                 if (show_frontpage) {
595                     $.ajatus.views.system.frontpage.render();                    
596                 }
597             }
598         });
599         
600         $('#header .app_version', $.ajatus.application_element).html($.ajatus.version.join('.'));
601         $('#header .tagline', $.ajatus.application_element).html($.ajatus.i10n.get('Distributed CRM'));
602         $('#new-item-button').text($.ajatus.i10n.get('new'));
603         
604         $.ajatus.toolbar.init();
605         $.ajatus.history.init();        
606         $.ajatus.tags.init();
607         $.ajatus.widgets.init();
608         $.ajatus.views.init();
609         
610         $.ajatus.extensions.init({
611             on_ready: function(){                
612                 $.ajatus.active_type = $.ajatus.preferences.client.content_types['note'];
614                 if ($.ajatus.preferences.modified) {
615                     $.ajatus.views.on_change_actions.add('$.ajatus.preferences.view.save($.ajatus.preferences.local)');
616                 }
617                                 
618                 // var gen_docs = $.ajatus.utils.doc_generator('notes', 200);
619                 // $.jqCouch.connection('doc').bulk_save($.ajatus.preferences.client.content_database, gen_docs);
620                 
621                 // Release the first lock
622                 $.ajatus.events.lock_pool.decrease();
623             }
624         });
625         
626         $.ajatus.elements.messages.set_holder();
627         
628         window.onbeforeunload = function() {
629             if ($.ajatus.events.named_lock_pool.count('unsaved') > 0) {
630                 return $.ajatus.i10n.get('You have unsaved changes.');
631             }
632         }
633         
634         $.ajatus.debug('ended', 'Ajatus start');
635     }
636     
637     $.ajatus.ajax_error = function(request, caller) {
638         $.ajatus.debug("Ajax error request.status: "+request.status);
639         
640         if (typeof caller != 'undefined') {
641             $.ajatus.debug("Caller method: "+caller.method);
642             $.ajatus.debug("Caller action:");
643             $.ajatus.debug(caller.action);
644             
645             if (caller.args != undefined) {
646                 $.ajatus.debug("Caller args:");
647                 $.ajatus.debug(caller.args);
648             }
649         }
650         
651         if (request.responseText != '') {
652             var response = eval("(" + request.responseText + ")");
653             $.ajatus.debug('response.error.reason: '+response.error.reason);            
654         }
655     };
656     $.ajatus.ajax_success = function(data, caller) {
657         $.ajatus.debug("Ajax success");
658         
659         if (typeof caller != 'undefined') {
660             $.ajatus.debug("Caller method: "+caller.method);
661             $.ajatus.debug("Caller action: "+caller.action);
662             
663             if (typeof caller.args != 'undefined') {
664                 $.ajatus.debug("Caller args: "+caller.args);
665             }
666         }
667         $.ajatus.debug("data: "+data);
668         
669         return false;
670     };
671     
672     $.ajatus.debug = function(msg, title, type) {
673         if ($.ajatus.preferences.client.debug == true) {
674             if (typeof(console.log) != 'undefined') {
675                 debug_console(msg, title, type);
676             } else {
677                 debug_static(msg, title, type);
678             }
679         }
681         function debug_console(msg, title, type) {
682             if (typeof type == 'undefined') {
683                 var type = 'log';
684             }
685             
686             if (typeof(console[type]) != 'undefined') {
687                 console.log(format(msg, title));
688             } else {
689                 console.log(format(msg, title));
690             }
691         }
692         
693         function debug_static(msg, title, type) {
694             // TODO: Implement Static debug handler
695         }
696         
697         function format(msg, title) {
698             var string = '';
699             
700             if (typeof title != 'undefined') {
701                 string += title += ': ';
702             }
703             
704             if (typeof msg != 'string') {
705                 string += $.ajatus.utils.pretty_print(msg);
706             } else {
707                 string += msg;
708             }
709             
710             return string;            
711         }
712     };
713     
714     $.fn.initialize_ajatus = function(options) {
715         if (! $(this).is('.ajatus_initialized')) {
716             return new $.ajatus.init(this, options);
717         }
718     };
719     
720 })(jQuery);
722 function initialize_ajatus() {
723     if (typeof ajatus_client_config == 'undefined') {
724         var ajatus_client_config = {};
725     }
726     
727     jQuery('#application').initialize_ajatus($.extend({
728         // debug: true,
729         custom_views: [
730             //'test'
731         ],
732         // types: {
733         //     in_use: [
734         //         'note', 'contact', 'event', 'expense', 'hour_report'
735         //     ]
736         // },
737         application_url: '/_utils/ajatus_dev/', // '/_utils/ajatus/'
738         application_database_identifier: 'dev' // 'stable'
739     }, ajatus_client_config));
742 $(document).ready(function() {
743     try{
744         initialize_ajatus();        
745     } catch(e) {
746         alert("could not initialize ajatus! Reason: "+e);
747     }