4 * Ajatus - Distributed CRM
5 * @requires jQuery v1.2.1
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
17 TODO Additional fields [widget, support]
18 TODO Skype widget -bergie
19 TODO Formatter service
20 TODO XMMP & Email formatter
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') {
31 console.log = function(str){return;};
36 $.ajatus.application_content_area = null;
38 $.ajatus.version = [0, 0, 9];
40 $.ajatus.active_type = null;
42 $.ajatus.converter = {};
43 $.ajatus.converter.parseJSON = function (json_str) {
46 return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
47 json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
48 eval('(' + json_str + ')');
55 $.ajatus.converter.toJSON = function (item,item_type) {
67 var a = ['['], b, f, i, l = x.length, v;
68 for (i = 0; i < l; i += 1) {
71 if (typeof v == 'string') {
89 return isFinite(x) ? String(x) : 'null';
93 if (x instanceof Array) {
96 var a = ['{'], b, f, i, v;
100 if (typeof v == 'string') {
104 a.push(s.str(i), ':', v);
114 if (/["\\\x00-\x1f]/.test(x)) {
115 x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
122 Math.floor(c / 16).toString(16) +
123 (c % 16).toString(16);
126 return '"' + x + '"';
129 conv = function (x) {
130 var itemtype = typeof x;
153 var itemtype = item_type || typeof item;
174 throw("Unknown type for $.ajatus.converter.toJSON");
178 $.ajatus.formatter = {};
179 $.ajatus.formatter.date = {};
180 $.ajatus.formatter.date.fix_length = function(number){
181 return ((number < 10) ? '0' : '') + number;
183 $.ajatus.formatter.date.is_iso8601 = function(value) {
184 if (typeof(value) != 'string')
188 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})))?)?)?)?";
189 var isod = value.match(new RegExp(regexp));
196 $.ajatus.formatter.date.js_to_iso8601 = function(date) {
198 str += date.getFullYear();
199 str += "-" + $.ajatus.formatter.date.fix_length(date.getMonth() + 1);
200 str += "-" + $.ajatus.formatter.date.fix_length(date.getDate());
201 str += "T" + $.ajatus.formatter.date.fix_length(date.getHours());
202 str += ":" + $.ajatus.formatter.date.fix_length(date.getMinutes());
203 str += ":" + $.ajatus.formatter.date.fix_length(date.getSeconds());
208 $.ajatus.formatter.date.iso8601_to_js = function(iso_date) {
209 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})))?)?)?)?";
210 var isod = iso_date.match(new RegExp(regexp));
218 var date = new Date(isod[1], 0, 1);
219 date.setMonth(isod[3] - 1);
220 date.setDate(isod[5]);
221 date.setHours(isod[7]);
222 date.setMinutes(isod[8]);
223 date.setSeconds(isod[10]);
227 $.ajatus.formatter.date.caldate_to_iso8601 = function(caldate, format, caltime, time_format) {
229 var format = format || "MDY/";
230 var time_format = time_format || "HMS:";
232 var cdparts = caldate.split(format.charAt(3));
233 var fparts = format.split("");
234 var date = new Date();
236 $.each(cdparts, function(i,v){
238 if (fparts[i] == 'M') {
241 if (fparts[i] == 'D') {
244 if (fparts[i] == 'Y') {
251 var time_separator = time_format.charAt(time_format.length - 1);
252 var tfparts = time_format.split("");
253 var ctparts = caltime.split(time_separator);
255 $.each(ctparts, function(i,v){
257 if (tfparts[i] == 'H') {
260 if (tfparts[i] == 'M') {
263 if (tfparts[i] == 'S') {
275 return $.ajatus.formatter.date.js_to_iso8601(date);
277 $.ajatus.formatter.date.iso8601_to_caldate = function(iso_date, format) {
278 var format = format || "MDY/";
280 var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
283 var day = date.getDate();
284 var month = date.getMonth();
285 var year = date.getFullYear();
288 for (var i = 0; i < 3; i++) {
289 date_str += format.charAt(3) +
290 (format.charAt(i) == 'D' ? $.ajatus.formatter.date.fix_length(day) :
291 (format.charAt(i) == 'M' ? $.ajatus.formatter.date.fix_length(month) :
292 (format.charAt(i) == 'Y' ? year : '?')));
294 date_str = date_str.substring(format.charAt(3) ? 1 : 0);
298 $.ajatus.formatter.date.iso8601_to_caltime = function(iso_date, format) {
299 var format = format || "HMS:";
301 var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
305 H: $.ajatus.formatter.date.fix_length(date.getHours()),
306 M: $.ajatus.formatter.date.fix_length(date.getMinutes()),
307 S: $.ajatus.formatter.date.fix_length(date.getSeconds())
309 var separator = format.charAt(format.length - 1);
310 var fparts = format.split("");
311 var utt1_parts = user_test_time1.split(utt1_seprt);
313 $.each(utt1_parts, function(i,v){
316 $.each(fparts, function(i,k){
317 time_str += fparts[k] + separator;
319 time_str = time_str.substring(1);
328 $.extend($.ajatus.tags, {
330 $.ajatus.events.lock_pool.increase();
331 $.ajatus.tags.load();
332 $.ajatus.events.lock_pool.decrease();
333 $.ajatus.tags.update_app_tag_list();
336 var jqcouch = $.jqcouch.doc;
337 jqcouch.on_success = function(data, caller)
339 if (data.total_rows <= 0) {
343 $.each(data.rows, function(i,n){
344 $.ajatus.tags.cache.push(n.id);
348 jqcouch.get_all($.ajatus.preferences.client.tags_database);
350 update_cache: function() {
351 $.ajatus.tags.update_app_tag_list();
352 if ($.ajatus.tags.active != '') {
353 $.ajatus.tags.set_active($.ajatus.tags.active);
356 set_active: function(tag) {
357 var select = $('#application-tags select');
358 $('option', select).each(function(i,o){
359 if (o.value == tag) {
360 o.selected = 'selected';
363 $.ajatus.tags.active = tag;
365 update_app_tag_list: function() {
366 var select = $('#application-tags select');
368 $.each($.ajatus.tags.cache, function(i){
369 var opt = $('<option>'+this+'</option>').attr({
372 opt.appendTo(select);
374 var def = $('<option />').attr({
377 }).html($.ajatus.i10n.get('Global Tag') + ' ');
378 def.prependTo(select);
379 select.bind('change', function(e){
380 $.ajatus.tags.set_active(e.currentTarget.value);
383 search: function(query, max_res) {
384 // console.log("Search tags with: "+query);
387 if (typeof max_res != 'number') {
391 query = $.ajatus.tags._prepare_query(query);
393 var jqcv = $.jqcouch.view;
394 jqcv.on_success = function(data, caller) {
395 if (data.total_rows == 0) {
399 $.each(data.rows, function(i,n){
400 var doc = new $.ajatus.document(n);
405 var rel_view = 'if (doc.value.title.val.match(/\\\\b'+query+'(.*?)\\\\b/) ';
406 rel_view += '&& doc.value.metadata.archived.val == "" && doc.value.metadata.deleted.val != 1){';
407 rel_view += 'map( doc._id, doc.value );}';
409 $.ajatus.preferences.client.tags_database, {
412 "$.ajatus.views.generate('"+rel_view+"')"
415 // console.log('results: ');
416 // console.log(results);
420 search_cache: function(query) {
421 // console.log("Search tags cache with: "+query);
423 query = $.ajatus.tags._prepare_query(query);
424 var re = new RegExp("\\b"+query+"(.*?)\\b");
426 $.each($.ajatus.tags.cache, function(i, tag){
427 if (tag.toString().match(re)) {
432 // console.log('results: ');
433 // console.log(results);
437 _prepare_query: function(query) {
438 query = query.replace(/[\(|\)|\.|\?|\;|\/]/, '');
439 query = query.replace('\\', '');
440 query = query.replace('*', '(.*?)');
444 get: function(tag, rev) {
447 var jqcouch = $.jqcouch.doc;
448 jqcouch.on_success = function(data, caller) {
449 var doc = new $.ajatus.document(data);
454 if (typeof rev != 'undefined') {
458 jqcouch.get($.ajatus.preferences.client.tags_database + '/' + tag, args);
462 exists: function(tag) {
463 var found = $.inArray(tag, $.ajatus.tags.cache);
469 create: function(name, jqc) {
471 if ($.ajatus.tags.exists(name)) {
475 if (typeof jqc == 'undefined') {
476 var jqc = $.jqcouch.doc;
492 tag = new $.ajatus.document(tag);
494 var now = $.ajatus.formatter.date.js_to_iso8601(new Date());
497 creator: $.ajatus.preferences.local.user.name,
499 revisor: $.ajatus.preferences.local.user.name
501 tag = $.ajatus.document.modify_metadata(tag, new_metadata);
502 jqc.save($.ajatus.preferences.client.tags_database, tag);
506 remove: function(tag, jqc) {
507 if (typeof jqc == 'undefined') {
508 var jqc = $.jqcouch.doc;
509 jqc.on_success = function(data, caller) {
510 var msg = $.ajatus.elements.messages.create(
511 $.ajatus.i10n.get('Object deleted'),
512 $.ajatus.i10n.get('Tag %s removed from Ajatus.', [tag])
517 jqc.del($.ajatus.preferences.client.tags_database + '/' + tag);
519 related: function(tags, skip_docs) {
521 var jqcv = $.jqcouch.view;
522 if (typeof skip_docs != 'object') {
525 jqcv.on_success = function(data, caller) {
526 if (data.total_rows == 0) {
530 $.each(data.rows, function(i,n){
531 if ($.ajatus.utils.array.has_match(tags, n.value.tags.val)) {
532 if (skip_docs.length > 0) {
533 if ($.inArray(n.id, skip_docs) == -1) {
534 var doc = new $.ajatus.document(n);
538 var doc = new $.ajatus.document(n);
545 var rel_view = 'if (typeof(doc.value.tags) != "undefined" && doc.value.tags.val.length > 0 ';
546 rel_view += '&& doc.value.metadata.archived.val == "" && doc.value.metadata.deleted.val != 1){';
547 rel_view += 'map( doc.value._type, {"_id": doc._id, "_rev": doc._rev, "_type": doc.value._type,';
548 rel_view += '"title": doc.value.title,';
550 $.each($.ajatus.preferences.client.content_types, function(key,type){
551 if (type['title_item']) {
552 $.each(type['title_item'], function(i,part){
553 if (type.schema[part]) {
554 rel_view += '"'+part+'": doc.value.'+part+',';
560 rel_view += '"tags": doc.value.tags })}';
563 $.ajatus.preferences.client.content_database, {},
564 "$.ajatus.views.generate('"+rel_view+"')"
572 $.ajatus.tabs.set_active_by_hash = function(hash) {
573 // console.log('set_active_by_hash: '+hash);
574 $.ajatus.views.on_change(hash);
576 var views_tab_holder = $('#tabs-views ul');
577 var app_tab_holder = $('#tabs-application ul');
579 var selected_tab = jQuery('li a[@href$="' + hash + '"]', views_tab_holder).parent();
580 if (selected_tab[0] != undefined) {
581 // console.log('views selected_tab:');
582 // console.log(selected_tab);
583 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
584 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
585 selected_tab.addClass('tabs-selected').focus();
587 selected_tab = jQuery('li a[@href$="' + hash + '"]', app_tab_holder).parent();
588 // console.log('app selected_tab:');
589 // console.log(selected_tab);
590 if (selected_tab[0] != undefined) {
591 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
592 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
593 selected_tab.addClass('tabs-selected').focus();
595 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
596 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
600 $.ajatus.tabs.on_click = function(e) {
601 var trgt = target(e);
602 jQuery("li", parent(e)).removeClass("tabs-selected").index(trgt);
603 jQuery(trgt).addClass("tabs-selected");
605 var new_hash = jQuery('a', trgt)[0].hash;
606 location.hash = new_hash;
607 $.ajatus.history.update(new_hash);
608 $.ajatus.views.on_change(new_hash);
610 function parent(event) {
611 var element = event.target;
614 if (element.tagName == "UL")
619 while(element.tagName != "UL")
621 element = element.parentNode;
627 function target(event) {
628 var element = event.target;
631 if (element.tagName == "UL")
633 element = jQuery(element).find('li').eq(0);
637 while(element.tagName != "LI")
639 element = element.parentNode;
646 $.jqcouch.on_error = function(request,caller) {
647 $.ajatus.ajax_error(request, caller);
649 $.jqcouch.on_success = function(data,caller) {
650 $.ajatus.ajax_success(data, caller);
653 $.ajatus.security_pass = function() {
655 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
657 alert("Permission UniversalBrowserRead denied. with error "+e);
661 $.ajatus.init = function(element, options) {
662 //$.ajatus.security_pass();
664 var application_element = $(element);
665 $.ajatus.application_element = application_element;
666 $.ajatus.application_content_area = $('#middle #content', $.ajatus.application_element);
667 $.ajatus.application_dynamic_elements = $('#dynamic-elements', $.ajatus.application_element);
669 $.ajatus.preferences.client.server_url = window.location.protocol + '//' + document.hostname + (window.location.port != '' ? ':'+window.location.port : '') + '/';
671 $.ajatus.preferences.client = $.extend({}, $.ajatus.preferences.client_defaults, options);
672 $.jqcouch.server_url = $.ajatus.preferences.client.server_url;
674 var a_db_suffix = $.ajatus.preferences.client.application_database_prefix != '' ? '_db' : 'db';
675 $.ajatus.preferences.client.application_database = 'ajatus_' + $.ajatus.preferences.client.application_database_prefix + a_db_suffix;
676 $.ajatus.preferences.client.content_database = $.ajatus.preferences.client.application_database + '_content';
677 $.ajatus.preferences.client.tags_database = $.ajatus.preferences.client.application_database + '_tags';
681 url: $.ajatus.preferences.client.server_url,
687 $('#top #new-item-button', application_element).bind('click', function(e){
688 $.ajatus.views.system.create.render($.ajatus.active_type);
691 $.ajatus.installer.is_installed();
692 if ($.ajatus.installer.installed == false) {
693 var status = $.ajatus.installer.install();
694 $.ajatus.debug("Installer finished with status: "+status);
700 // $.ajatus.installer.uninstall();
704 $.ajatus.start = function() {
705 $.ajatus.events.lock_pool.increase();
706 $.ajatus.locker = new $.ajatus.events.lock({
708 validate: function(){return $.ajatus.events.lock_pool.count == 0;},
712 on_release: function(){
713 $.ajatus.application_element.addClass('ajatus_initialized');
715 // var trs = $.ajatus.i10n.get('Test %s', ['one']);
716 // console.log("$.ajatus.i10n.get('Test %s', ['one']): "+trs);
717 // var pts = $.ajatus.i10n.get('%d comment', [2], 'comment');
718 // console.log("$.ajatus.i10n.get('%d comment', [2], 'comment'): "+pts);
719 // var pts = $.ajatus.i10n.get('%d comment in %d doc', [1, 3]);
720 // console.log("$.ajatus.i10n.get('%d comment in %d doc', [1, 3]): "+pts);
721 // var pts = $.ajatus.i10n.get('%d comment in %d doc', [2, 1]);
722 // console.log("$.ajatus.i10n.get('%d comment in %d doc', [2, 1]): "+pts);
724 var show_frontpage = true;
725 if ( $.ajatus.history
726 && $.ajatus.history.enabled)
728 show_frontpage = !$.ajatus.history.check();
731 if (show_frontpage) {
732 $.ajatus.views.system.frontpage.render();
737 var preference_loader = new $.ajatus.preferences.loader;
738 preference_loader.load();
740 if ($.ajatus.preferences.client.language == null) {
741 $.ajatus.preferences.client.language = $.ajatus.preferences.local.localization.language;
743 $.ajatus.i10n.init($.ajatus.preferences.client.language);
745 $('#middle', $.ajatus.application_element).css({
746 'min-height': $.ajatus.preferences.local.layout.content_height
749 $('#header .app_version', $.ajatus.application_element).html($.ajatus.version.join('.'));
750 $('#header .tagline', $.ajatus.application_element).html($.ajatus.i10n.get('Distributed CRM'));
752 $.ajatus.extensions.init({
753 on_ready: function(){
754 $.ajatus.tags.init();
755 $.ajatus.widgets.init();
756 $.ajatus.views.init();
758 $.ajatus.active_type = $.ajatus.preferences.client.content_types['note'];
760 // Release the first lock
761 $.ajatus.events.lock_pool.decrease();
765 $.ajatus.elements.messages.set_holder();
767 window.onbeforeunload = function() {
768 if ($.ajatus.events.named_lock_pool.count('unsaved') > 0) {
769 return "Reason: You have unsaved changes.";
774 $.ajatus.ajax_error = function(request, caller) {
775 $.ajatus.debug("Ajax error request.status: "+request.status);
776 if (typeof caller != 'undefined') {
777 $.ajatus.debug("Caller method: "+caller.method);
778 $.ajatus.debug("Caller action:");
779 $.ajatus.debug(caller.action);
780 if (caller.args != undefined) {
781 $.ajatus.debug("Caller args:");
782 $.ajatus.debug(caller.args);
786 if (request.responseText != '') {
787 var response = eval("(" + request.responseText + ")");
788 $.ajatus.debug('response.error.reason: '+response.error.reason);
791 $.ajatus.ajax_success = function(data, caller) {
792 $.ajatus.debug("Ajax success");
793 if (typeof caller != 'undefined')
795 $.ajatus.debug("Caller method: "+caller.method);
796 $.ajatus.debug("Caller action: "+caller.action);
797 if (typeof caller.args != 'undefined') {
798 $.ajatus.debug("Caller args: "+caller.args);
801 $.ajatus.debug("data: "+data);
806 $.ajatus.debug = function(msg, title, type) {
807 if ($.ajatus.preferences.client.debug == true)
813 $.fn.initialize_ajatus = function(options) {
814 if (!$(this).is('.ajatus_initialized')) {
815 return new $.ajatus.init(this, options);
821 function initialize_ajatus() {
822 jQuery('#application').initialize_ajatus({
828 application_url: '/_utils/ajatus/', // '/_utils/ajatus_dev/'
829 application_database_prefix: '' // 'dev'
833 jQuery(document).ready(function() {
837 alert("could not initialize ajatus! Reason: "+e);