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, Manifesto)
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
,
499 revisor
: $.ajatus
.preferences
.local
.user
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
.preferences
= $.ajatus
.preferences
|| {};
654 $.ajatus
.preferences
.local
= {};
655 $.ajatus
.preferences
.local_defaults
= {
663 content_height
: '460px'
667 $.ajatus
.preferences
.client
= {};
668 $.ajatus
.preferences
.client_defaults
= {
672 application_url
: '/_utils/ajatus/',
673 application_database
: 'ajatus_db',
674 application_path
: '/usr/local/share/couchdb/www/ajatus',
678 extensions
: ['history']
681 $.ajatus
.preferences
.loader = function() {
682 this.jqcouch
= $.jqcouch
.doc
;
685 this.jqcouch
.on_error = function(request
, caller
)
688 $.ajatus
.events
.lock_pool
.decrease();
689 $.ajatus
.ajax_error(request
, caller
);
691 this.jqcouch
.on_success = function(data
, caller
)
693 $.ajatus
.events
.lock_pool
.decrease();
694 $.ajatus
.preferences
.local
= $.extend($.ajatus
.preferences
.local_defaults
, data
.value
);
697 $.extend($.ajatus
.preferences
.loader
.prototype, {
699 $.ajatus
.events
.lock_pool
.increase();
700 this.jqcouch
.get($.ajatus
.preferences
.client
.application_database
+ '/preferences');
704 $.ajatus
.security_pass = function() {
706 netscape
.security
.PrivilegeManager
.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
708 alert("Permission UniversalBrowserRead denied. with error "+e
);
712 $.ajatus
.init = function(element
, options
) {
713 //$.ajatus.security_pass();
715 var application_element
= $(element
);
716 $.ajatus
.application_element
= application_element
;
717 $.ajatus
.application_content_area
= $('#middle #content', $.ajatus
.application_element
);
718 $.ajatus
.application_dynamic_elements
= $('#dynamic-elements', $.ajatus
.application_element
);
720 $.ajatus
.preferences
.client
= $.extend($.ajatus
.preferences
.client_defaults
, options
);
721 $.jqcouch
.server_url
= $.ajatus
.preferences
.client
.server_url
;
722 $.ajatus
.preferences
.client
.content_database
= $.ajatus
.preferences
.client
.application_database
+ '_content';
723 $.ajatus
.preferences
.client
.tags_database
= $.ajatus
.preferences
.client
.application_database
+ '_tags';
727 url
: $.ajatus
.preferences
.client
.server_url
,
733 $.ajatus
.i10n
.init($.ajatus
.preferences
.client
.language
);
735 $('#top #new-item-button', application_element
).bind('click', function(e
){
736 $.ajatus
.views
.system
.create
.render($.ajatus
.active_type
);
739 $.ajatus
.installer
.is_installed();
740 if ($.ajatus
.installer
.installed
== false) {
741 var status
= $.ajatus
.installer
.install();
742 $.ajatus
.debug("Installer finished with status: "+status
);
748 // $.ajatus.installer.uninstall();
752 $.ajatus
.start = function() {
753 $.ajatus
.events
.lock_pool
.increase();
754 $.ajatus
.locker
= new $.ajatus
.events
.lock({
756 validate: function(){return $.ajatus
.events
.lock_pool
.count
== 0;},
760 on_release: function(){
761 $.ajatus
.application_element
.addClass('ajatus_initialized');
763 // var trs = $.ajatus.i10n.get('Test %s', ['one']);
764 // console.log("$.ajatus.i10n.get('Test %s', ['one']): "+trs);
765 // var pts = $.ajatus.i10n.get('%d comment', [2], 'comment');
766 // console.log("$.ajatus.i10n.get('%d comment', [2], 'comment'): "+pts);
767 // var pts = $.ajatus.i10n.get('%d comment in %d doc', [1, 3]);
768 // console.log("$.ajatus.i10n.get('%d comment in %d doc', [1, 3]): "+pts);
769 // var pts = $.ajatus.i10n.get('%d comment in %d doc', [2, 1]);
770 // console.log("$.ajatus.i10n.get('%d comment in %d doc', [2, 1]): "+pts);
772 var show_frontpage
= true;
773 if ( $.ajatus
.history
774 && $.ajatus
.history
.enabled
)
776 show_frontpage
= !$.ajatus
.history
.check();
779 if (show_frontpage
) {
780 $.ajatus
.views
.system
.frontpage
.render();
785 $('#header .app_version', $.ajatus
.application_element
).html($.ajatus
.version
.join('.'));
786 $('#header .tagline', $.ajatus
.application_element
).html($.ajatus
.i10n
.get('Distributed CRM'));
788 var preference_loader
= new $.ajatus
.preferences
.loader
;
789 preference_loader
.load();
791 $('#middle', $.ajatus
.application_element
).css({
792 height
: $.ajatus
.preferences
.local
.layout
.content_height
795 $.ajatus
.extensions
.init({
796 on_ready: function(){
797 $.ajatus
.tags
.init();
798 $.ajatus
.widgets
.init();
799 $.ajatus
.views
.init();
801 $.ajatus
.active_type
= $.ajatus
.preferences
.client
.content_types
['note'];
803 // Release the first lock
804 $.ajatus
.events
.lock_pool
.decrease();
808 $.ajatus
.elements
.messages
.set_holder();
810 window
.onbeforeunload = function() {
811 if ($.ajatus
.events
.named_lock_pool
.count('unsaved') > 0) {
812 return "Reason: You have unsaved changes.";
817 $.ajatus
.ajax_error = function(request
, caller
) {
818 $.ajatus
.debug("Ajax error request.status: "+request
.status
);
819 if (typeof caller
!= 'undefined') {
820 $.ajatus
.debug("Caller method: "+caller
.method
);
821 $.ajatus
.debug("Caller action:");
822 $.ajatus
.debug(caller
.action
);
823 if (caller
.args
!= undefined) {
824 $.ajatus
.debug("Caller args:");
825 $.ajatus
.debug(caller
.args
);
829 if (request
.responseText
!= '') {
830 var response
= eval("(" + request
.responseText
+ ")");
831 $.ajatus
.debug('response.error.reason: '+response
.error
.reason
);
834 $.ajatus
.ajax_success = function(data
, caller
) {
835 $.ajatus
.debug("Ajax success");
836 if (typeof caller
!= 'undefined')
838 $.ajatus
.debug("Caller method: "+caller
.method
);
839 $.ajatus
.debug("Caller action: "+caller
.action
);
840 if (typeof caller
.args
!= 'undefined') {
841 $.ajatus
.debug("Caller args: "+caller
.args
);
844 $.ajatus
.debug("data: "+data
);
849 $.ajatus
.debug = function(msg
, title
, type
) {
850 if ($.ajatus
.preferences
.client
.debug
== true)
856 $.fn
.initialize_ajatus = function(options
) {
857 if (!$(this).is('.ajatus_initialized')) {
858 return new $.ajatus
.init(this, options
);
864 function initialize_ajatus() {
865 jQuery('#application').initialize_ajatus({
867 // language: 'fi_FI',
871 server_url
: 'http://couchdb_server:8888/'
872 //application_database: 'ajatus_stable_db'
876 jQuery(document
).ready(function() {
880 alert("could not initialize ajatus! Reason: "+e
);