Updated Ajatus to use the new jqCouch library
[ajatus.git] / js / ajatus.core.js
blobaef6c25d9c6f9813d24b3921fab7d26b73386d20
1 /*
2 * This file is part of
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
16 TODO Tags are links
17 TODO Additional fields [widget, support]
18 TODO Skype widget -bergie
19 TODO Formatter service
20 TODO XMMP & Email formatter
21 TODO Gravatar widget
22 TODO Implement arhive view and action
23 TODO Implement file attachments
24 TODO Documentation (API)
25 TODO Auto-saving to forms (after 5 mins)
26 TODO Context syntax tags (me:, attn:, by:, for:)
29 if (typeof console == 'undefined') {
30 var console = {};
31 console.log = function() {
32 return;
36 (function($){
37 $.ajatus = {};
38 $.ajatus.application_content_area = null;
40 $.ajatus.version = [0, 0, 9];
42 $.ajatus.active_type = null;
44 /**
45 * Holds all converters available in Ajatus.
47 $.ajatus.converter = {};
49 /**
50 * Parses and evaluates JSON string to Javascript
51 * @param {String} json_str JSON String
52 * @returns Parsed JSON string or false on failure
54 $.ajatus.converter.parseJSON = function (json_str) {
55 try {
56 return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
57 json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
58 eval('(' + json_str + ')');
59 } catch (e) {
60 return false;
64 /**
65 * Parses Javascript to JSON
66 * @param {Mixed} item Javascript to be parsed
67 * @param {String} type of the Javascript item to be parsed (Optional)
68 * @returns JSON string
69 * @type String
71 $.ajatus.converter.toJSON = function (item,item_type) {
72 var m = {
73 '\b': '\\b',
74 '\t': '\\t',
75 '\n': '\\n',
76 '\f': '\\f',
77 '\r': '\\r',
78 '"' : '\\"',
79 '\\': '\\\\'
81 s = {
82 arr: function (x) {
83 var a = ['['], b, f, i, l = x.length, v;
84 for (i = 0; i < l; i += 1) {
85 v = x[i];
86 v = conv(v);
87 if (typeof v == 'string') {
88 if (b) {
89 a[a.length] = ',';
91 a[a.length] = v;
92 b = true;
95 a[a.length] = ']';
96 return a.join('');
98 bool: function (x) {
99 return String(x);
101 nul: function (x) {
102 return "null";
104 num: function (x) {
105 return isFinite(x) ? String(x) : 'null';
107 obj: function (x) {
108 if (x) {
109 if (x instanceof Array) {
110 return s.arr(x);
112 var a = ['{'], b, f, i, v;
113 for (i in x) {
114 v = x[i];
115 v = conv(v);
116 if (typeof v == 'string') {
117 if (b) {
118 a[a.length] = ',';
120 a.push(s.str(i), ':', v);
121 b = true;
124 a[a.length] = '}';
125 return a.join('');
127 return 'null';
129 str: function (x) {
130 if (/["\\\x00-\x1f]/.test(x)) {
131 x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
132 var c = m[b];
133 if (c) {
134 return c;
136 c = b.charCodeAt();
137 return '\\u00' +
138 Math.floor(c / 16).toString(16) +
139 (c % 16).toString(16);
142 return '"' + x + '"';
145 conv = function (x) {
146 var itemtype = typeof x;
147 switch(itemtype) {
148 case "array":
149 return s.arr(x);
150 break;
151 case "object":
152 return s.obj(x);
153 break;
154 case "string":
155 return s.str(x);
156 break;
157 case "number":
158 return s.num(x);
159 break;
160 case "null":
161 return s.nul(x);
162 break;
163 case "boolean":
164 return s.bool(x);
165 break;
169 var itemtype = item_type || typeof item;
170 switch(itemtype) {
171 case "array":
172 return s.arr(item);
173 break;
174 case "object":
175 return s.obj(item);
176 break;
177 case "string":
178 return s.str(item);
179 break;
180 case "number":
181 return s.num(item);
182 break;
183 case "null":
184 return s.nul(item);
185 break;
186 case "boolean":
187 return s.bool(item);
188 break;
189 default:
190 throw("Unknown type for $.ajatus.converter.toJSON");
195 * Holds all formatters available in Ajatus.
196 * @constructor
198 $.ajatus.formatter = {};
201 * Holds all date formatters available in Ajatus.
202 * @constructor
204 $.ajatus.formatter.date = {};
207 * Formats numbers < 10 to two digit numbers
208 * @param {Number} number Number to format
209 * @returns Formatted number
210 * @type String
212 $.ajatus.formatter.date.fix_length = function(number){
213 return ((number < 10) ? '0' : '') + number;
217 * Checks if given value is ISO8601 Formatted Date
218 * @param {String} value Value to check
219 * @returns True, False if value not string or ISO8601 formatted
220 * @type Boolean
222 $.ajatus.formatter.date.is_iso8601 = function(value) {
223 if (typeof value != 'string') {
224 return false;
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 if (isod[10]) {
229 return true;
231 return false;
235 * Converts JS Date to ISO8601 Formatted JS Date
236 * @param {Date} date Javascript date to convert
237 * @returns ISO8601 formatted Date
238 * @type Date
240 $.ajatus.formatter.date.js_to_iso8601 = function(date) {
241 var str = "";
242 str += date.getFullYear();
243 str += "-" + $.ajatus.formatter.date.fix_length(date.getMonth() + 1);
244 str += "-" + $.ajatus.formatter.date.fix_length(date.getDate());
245 str += "T" + $.ajatus.formatter.date.fix_length(date.getHours());
246 str += ":" + $.ajatus.formatter.date.fix_length(date.getMinutes());
247 str += ":" + $.ajatus.formatter.date.fix_length(date.getSeconds());
248 //str += 'Z';
250 return str;
254 * Converts ISO8601 Formatted JS Date to JS Date
255 * @param {Date} iso_date ISO8601 formatted date to convert
256 * @returns Javascript Date
257 * @type Date
259 $.ajatus.formatter.date.iso8601_to_js = function(iso_date) {
260 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})))?)?)?)?";
261 var isod = iso_date.match(new RegExp(regexp));
263 if ( !isod
264 || isod.length < 11)
266 return new Date();
269 var date = new Date(isod[1], 0, 1);
270 date.setMonth(isod[3] - 1);
271 date.setDate(isod[5]);
272 date.setHours(isod[7]);
273 date.setMinutes(isod[8]);
274 date.setSeconds(isod[10]);
276 return date;
280 * Converts Date string from calendar widget to ISO8601 Formatted JS Date
281 * @param {String} caldate Date value from calendar widget
282 * @param {String} format Format used in conversion (Default: "MDY/")
283 * @param {String} caltime Time value from calendar widget
284 * @param {String} time_format Format used in conversion (Default: "HMS:")
285 * @returns ISO8601 formated Javascript Date
286 * @type Date
288 $.ajatus.formatter.date.caldate_to_iso8601 = function(caldate, format, caltime, time_format) {
290 var format = format || ($.ajatus.i10n.datetime.date || "MDY/");
291 var time_format = time_format || ($.ajatus.i10n.datetime.time || "HMS:");
293 var cdparts = caldate.split(format.charAt(3));
294 var fparts = format.split("");
295 var date = new Date();
297 $.each(cdparts, function(i,v){
298 v = Number(v);
299 if (fparts[i] == 'M') {
300 date.setMonth(v-1);
302 if (fparts[i] == 'D') {
303 date.setDate(v);
305 if (fparts[i] == 'Y') {
306 date.setFullYear(v);
310 if (caltime) {
311 var time_separator = time_format.charAt(time_format.length - 1);
312 var tfparts = time_format.split("");
313 var ctparts = caltime.split(time_separator);
315 $.each(ctparts, function(i,v){
316 v = Number(v);
317 if (tfparts[i] == 'H') {
318 date.setHours(v);
320 if (tfparts[i] == 'M') {
321 date.setMinutes(v);
323 if (tfparts[i] == 'S') {
324 date.setSeconds(v);
327 } else {
328 date.setHours(0);
329 date.setMinutes(0);
330 date.setSeconds(0);
333 return $.ajatus.formatter.date.js_to_iso8601(date);
337 * Converts ISO8601 Formatted JS Date to calendar widget date string
338 * @param {Date} iso_date ISO8601 Fromatted Date
339 * @param {String} format Format used in conversion (Default: "MDY/")
340 * @returns Calendar widget supported value
341 * @type String
343 $.ajatus.formatter.date.iso8601_to_caldate = function(iso_date, format) {
344 var format = format || ($.ajatus.i10n.datetime.date || "MDY/");
346 var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
347 var date_str = '';
349 var day = date.getDate();
350 var month = date.getMonth();
351 var year = date.getFullYear();
352 month++;
354 for (var i = 0; i < 3; i++) {
355 date_str += format.charAt(3) +
356 (format.charAt(i) == 'D' ? $.ajatus.formatter.date.fix_length(day) :
357 (format.charAt(i) == 'M' ? $.ajatus.formatter.date.fix_length(month) :
358 (format.charAt(i) == 'Y' ? year : '?')));
360 date_str = date_str.substring(format.charAt(3) ? 1 : 0);
362 return date_str;
366 * Converts ISO8601 Formatted JS Date to calendar widget time string
367 * @param {Date} iso_date ISO8601 Fromatted Date
368 * @param {String} format Format used in conversion (Default: "HMS:")
369 * @returns Calendar widget supported value
370 * @type String
372 $.ajatus.formatter.date.iso8601_to_caltime = function(iso_date, format) {
373 var format = format || ($.ajatus.i10n.datetime.time || "HMS:");
375 var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
376 var time_str = '';
378 var idparts = {
379 H: $.ajatus.formatter.date.fix_length(date.getHours()),
380 M: $.ajatus.formatter.date.fix_length(date.getMinutes()),
381 S: $.ajatus.formatter.date.fix_length(date.getSeconds())
383 var separator = format.charAt(format.length - 1);
384 var fparts = format.split("");
386 $.each(fparts, function(i,k){
387 time_str += fparts[k] + separator;
389 time_str = time_str.substring(1);
391 return time_str;
394 $.ajatus.tabs = {};
395 $.ajatus.tabs.prepare = function(tab) {
396 tab.bind('mouseover', function(e){
397 tab.addClass('tabs-hover');
399 tab.bind('mouseout', function(e){
400 tab.removeClass('tabs-hover');
403 $.ajatus.tabs.set_active_by_hash = function(hash) {
404 // $.ajatus.debug('$.ajatus.tabs.set_active_by_hash('+hash+')');
406 $.ajatus.views.on_change(hash);
408 var views_tab_holder = $('#tabs-views ul');
409 var app_tab_holder = $('#tabs-application ul');
411 var selected_tab = jQuery('li a[@href$="' + hash + '"]', views_tab_holder).parent();
412 if (selected_tab[0] != undefined) {
413 // $.ajatus.debug('views selected_tab:');
414 // $.ajatus.debug(selected_tab);
415 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
416 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
417 selected_tab.addClass('tabs-selected').focus();
418 } else {
419 selected_tab = jQuery('li a[@href$="' + hash + '"]', app_tab_holder).parent();
420 // $.ajatus.debug('app selected_tab:');
421 // $.ajatus.debug(selected_tab);
422 if (selected_tab[0] != undefined) {
423 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
424 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
425 selected_tab.addClass('tabs-selected').focus();
426 } else {
427 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
428 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
432 $.ajatus.tabs.on_click = function(e) {
433 var trgt = target(e);
434 jQuery("li", parent(e)).removeClass("tabs-selected").index(trgt);
435 jQuery(trgt).addClass("tabs-selected");
437 var new_hash = jQuery('a', trgt)[0].hash;
438 location.hash = new_hash;
440 if ( $.ajatus.history
441 && $.ajatus.history.enabled)
443 $.ajatus.history.update(new_hash);
446 $.ajatus.views.on_change(new_hash);
448 function parent(event) {
449 var element = event.target;
450 if (element)
452 if (element.tagName == "UL")
454 return element;
457 while(element.tagName != "UL")
459 element = element.parentNode;
462 return element;
465 function target(event) {
466 var element = event.target;
467 if (element)
469 if (element.tagName == "UL")
471 element = jQuery(element).find('li').eq(0);
472 return element;
475 while(element.tagName != "LI")
477 element = element.parentNode;
480 return element;
484 $.ajatus.security_pass = function() {
485 try {
486 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
487 } catch (e) {
488 alert("Permission UniversalBrowserRead denied. with error "+e);
492 $.ajatus.init = function(element, options) {
493 //$.ajatus.security_pass();
495 var application_element = $(element);
496 $.ajatus.application_element = application_element;
497 $.ajatus.application_content_area = $('#middle #content', $.ajatus.application_element);
498 $.ajatus.application_dynamic_elements = $('#dynamic-elements', $.ajatus.application_element);
500 $.ajatus.preferences.client.server_url = window.location.protocol + '//' + document.hostname + (window.location.port != '' ? ':'+window.location.port : '') + '/';
502 $.ajatus.preferences.client = $.extend({}, $.ajatus.preferences.client_defaults, options);
503 //Force the system types to load:
504 $.ajatus.preferences.client.types.system = $.ajatus.preferences.client_defaults.types.system;
506 $.jqCouch.set_defaults({
507 server_url: $.ajatus.preferences.client.server_url
510 var a_db_suffix = $.ajatus.preferences.client.application_database_identifier != '' ? '_db' : 'db';
511 $.ajatus.preferences.client.application_database = 'ajatus_' + $.ajatus.preferences.client.application_database_identifier + a_db_suffix;
512 $.ajatus.preferences.client.content_database = $.ajatus.preferences.client.application_database + '_content';
513 $.ajatus.preferences.client.tags_database = $.ajatus.preferences.client.application_database + '_tags';
515 $.ajaxSetup({
516 type: "GET",
517 url: $.ajatus.preferences.client.server_url,
518 global: false,
519 cache: false,
520 dataType: "json"
523 $('#top #new-item-button', application_element).bind('click', function(e){
524 $.ajatus.views.system.create.render($.ajatus.active_type);
527 $.ajatus.installer.is_installed();
528 if ($.ajatus.installer.installed == false) {
529 var status = $.ajatus.installer.install();
530 $.ajatus.debug("Installer finished with status: "+status);
531 if (status) {
532 window.location = window.location + "#view.preferences";
534 } else {
535 $.ajatus.preload();
537 // else
538 // {
539 // $.ajatus.installer.uninstall();
540 // }
543 $.ajatus.preload = function() {
544 var preference_loader = new $.ajatus.preferences.loader;
545 preference_loader.load();
547 $.ajatus.types.init(function(){
548 $.ajatus.start();
549 });
552 $.ajatus.start = function() {
553 $.ajatus.debug('started', 'Ajatus start');
555 $.ajatus.events.lock_pool.increase();
557 if ($.ajatus.preferences.client.language == null) {
558 var lang = 'en_GB';
559 if (typeof($.ajatus.preferences.local.localization) != 'undefined') {
560 lang = $.ajatus.preferences.local.localization.language;
562 $.ajatus.preferences.client.language = lang;
564 $.ajatus.i10n.init($.ajatus.preferences.client.language);
566 if ($.ajatus.preferences.local.layout.theme != 'default') {
567 $.ajatus.preferences.client.theme_url = 'themes/' + $.ajatus.preferences.local.layout.theme + '/';
569 $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/structure.css');
570 $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/common.css');
571 $.ajatus.layout.styles.load($.ajatus.preferences.client.theme_url + 'css/elements.css');
574 $.ajatus.locker = new $.ajatus.events.lock({
575 watch: {
576 validate: function(){return $.ajatus.events.lock_pool.count == 0;},
577 interval: 200,
578 safety_runs: 0
580 on_release: function(){
581 $.ajatus.application_element.addClass('ajatus_initialized');
583 var show_frontpage = true;
584 if ( $.ajatus.history
585 && $.ajatus.history.enabled)
587 show_frontpage = !$.ajatus.history.check();
590 if (show_frontpage) {
591 $.ajatus.views.system.frontpage.render();
596 $('#header .app_version', $.ajatus.application_element).html($.ajatus.version.join('.'));
597 $('#header .tagline', $.ajatus.application_element).html($.ajatus.i10n.get('Distributed CRM'));
598 $('#new-item-button').text($.ajatus.i10n.get('new'));
600 $.ajatus.toolbar.init();
601 $.ajatus.history.init();
602 $.ajatus.tags.init();
603 $.ajatus.widgets.init();
604 $.ajatus.views.init();
606 $.ajatus.extensions.init({
607 on_ready: function(){
608 $.ajatus.active_type = $.ajatus.preferences.client.content_types['note'];
610 if ($.ajatus.preferences.modified) {
611 $.ajatus.views.on_change_actions.add('$.ajatus.preferences.view.save($.ajatus.preferences.local)');
614 // Release the first lock
615 $.ajatus.events.lock_pool.decrease();
619 $.ajatus.elements.messages.set_holder();
621 window.onbeforeunload = function() {
622 if ($.ajatus.events.named_lock_pool.count('unsaved') > 0) {
623 return $.ajatus.i10n.get('You have unsaved changes.');
627 $.ajatus.debug('ended', 'Ajatus start');
630 $.ajatus.ajax_error = function(request, caller) {
631 $.ajatus.debug("Ajax error request.status: "+request.status);
633 if (typeof caller != 'undefined') {
634 $.ajatus.debug("Caller method: "+caller.method);
635 $.ajatus.debug("Caller action:");
636 $.ajatus.debug(caller.action);
638 if (caller.args != undefined) {
639 $.ajatus.debug("Caller args:");
640 $.ajatus.debug(caller.args);
644 if (request.responseText != '') {
645 var response = eval("(" + request.responseText + ")");
646 $.ajatus.debug('response.error.reason: '+response.error.reason);
649 $.ajatus.ajax_success = function(data, caller) {
650 $.ajatus.debug("Ajax success");
652 if (typeof caller != 'undefined') {
653 $.ajatus.debug("Caller method: "+caller.method);
654 $.ajatus.debug("Caller action: "+caller.action);
656 if (typeof caller.args != 'undefined') {
657 $.ajatus.debug("Caller args: "+caller.args);
660 $.ajatus.debug("data: "+data);
662 return false;
665 $.ajatus.debug = function(msg, title, type) {
666 if ($.ajatus.preferences.client.debug == true) {
667 if (typeof(console.log) != 'undefined') {
668 debug_console(msg, title, type);
669 } else {
670 debug_static(msg, title, type);
674 function debug_console(msg, title, type) {
675 if (typeof type == 'undefined') {
676 var type = 'log';
679 if (typeof(console[type]) != 'undefined') {
680 console.log(format(msg, title));
681 } else {
682 console.log(format(msg, title));
686 function debug_static(msg, title, type) {
687 // TODO: Implement Static debug handler
690 function format(msg, title) {
691 var string = '';
692 if (typeof title != 'undefined') {
693 string += title += ': ';
695 string += msg;
697 return string;
701 $.fn.initialize_ajatus = function(options) {
702 if (! $(this).is('.ajatus_initialized')) {
703 return new $.ajatus.init(this, options);
707 })(jQuery);
709 function initialize_ajatus() {
710 if (typeof ajatus_client_config == 'undefined') {
711 var ajatus_client_config = {};
714 jQuery('#application').initialize_ajatus($.extend({
715 // debug: true,
716 custom_views: [
717 //'test'
719 // types: {
720 // in_use: [
721 // 'note', 'contact', 'event', 'expense', 'hour_report'
722 // ]
723 // },
724 application_url: '/_utils/ajatus_dev/', // '/_utils/ajatus/'
725 application_database_identifier: 'dev' // 'stable'
726 }, ajatus_client_config));
729 jQuery(document).ready(function() {
730 try{
731 initialize_ajatus();
732 } catch(e) {
733 alert("could not initialize ajatus! Reason: "+e);