View change clears "unsaved" counter
[ajatus.git] / js / ajatus.core.js
blobb749f353ffd34830f8bd71aff09744be40a1773f
1 /*
2  * This file is part of
3  *
4  * 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 Tag support [Views, service] (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 Hour reporting [Views, widget]
22     TODO Gravatar widget
23     TODO Implement arhive view and action
24     TODO Implement delete action
25     TODO Add undelete support
26     TODO Implement file attachments?
27     TODO Documentation (API, Manifesto)
28     TODO GPL Headers (2 copyrights: Nemein & MySelf)
29     TODO Auto-saving to forms (after 5 mins)
30     TODO Context syntax tags (me:, attn:, by:, for:)
33 if (console == undefined) {
34     var console = {};
35     console.log = function(str){return;};
38 (function($){
39     $.ajatus = {};
40     $.ajatus.application_content_area = null;
41     
42     $.ajatus.version = [0, 0, 6];
43     
44     $.ajatus.active_type = null;
45     
46     $.ajatus.converter = {};
47     $.ajatus.converter.parseJSON = function (json_str) {
48         try
49         {
50             return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
51                     json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
52                 eval('(' + json_str + ')');
53         }
54         catch (e)
55         {
56             return false;
57         }
58     }
59     $.ajatus.converter.toJSON = function (item,item_type) {
60         var m = {
61                 '\b': '\\b',
62                 '\t': '\\t',
63                 '\n': '\\n',
64                 '\f': '\\f',
65                 '\r': '\\r',
66                 '"' : '\\"',
67                 '\\': '\\\\'
68             },
69             s = {
70                 arr: function (x) {
71                     var a = ['['], b, f, i, l = x.length, v;
72                     for (i = 0; i < l; i += 1) {
73                         v = x[i];
74                         v = conv(v);
75                         if (typeof v == 'string') {
76                             if (b) {
77                                 a[a.length] = ',';
78                             }
79                             a[a.length] = v;
80                             b = true;
81                         }
82                     }
83                     a[a.length] = ']';
84                     return a.join('');
85                 },
86                 bool: function (x) {
87                     return String(x);
88                 },
89                 nul: function (x) {
90                     return "null";
91                 },
92                 num: function (x) {
93                     return isFinite(x) ? String(x) : 'null';
94                 },
95                 obj: function (x) {
96                     if (x) {
97                         if (x instanceof Array) {
98                             return s.arr(x);
99                         }
100                         var a = ['{'], b, f, i, v;
101                         for (i in x) {
102                             v = x[i];
103                             v = conv(v);
104                             if (typeof v == 'string') {
105                                 if (b) {
106                                     a[a.length] = ',';
107                                 }
108                                 a.push(s.str(i), ':', v);
109                                 b = true;
110                             }
111                         }
112                         a[a.length] = '}';
113                         return a.join('');
114                     }
115                     return 'null';
116                 },
117                 str: function (x) {
118                     if (/["\\\x00-\x1f]/.test(x)) {
119                         x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
120                             var c = m[b];
121                             if (c) {
122                                 return c;
123                             }
124                             c = b.charCodeAt();
125                             return '\\u00' +
126                                 Math.floor(c / 16).toString(16) +
127                                 (c % 16).toString(16);
128                         });
129                     }
130                     return '"' + x + '"';
131                 }
132             };
133             conv = function (x) {
134                 var itemtype = typeof x;
135                 switch(itemtype) {
136                         case "array":
137                           return s.arr(x);
138                           break;
139                         case "object":
140                           return s.obj(x);
141                           break;
142                         case "string":
143                           return s.str(x);
144                           break;
145                         case "number":
146                           return s.num(x);
147                           break;
148                         case "null":
149                           return s.nul(x);
150                           break;
151                         case "boolean":
152                           return s.bool(x);
153                           break;
154                 }
155             }
157         var itemtype = item_type || typeof item;
158         switch(itemtype) {
159                 case "array":
160                   return s.arr(item);
161                   break;
162                 case "object":
163                   return s.obj(item);
164                   break;
165                 case "string":
166                   return s.str(item);
167                   break;
168                 case "number":
169                   return s.num(item);
170                   break;
171                 case "null":
172                   return s.nul(item);
173                   break;
174                 case "boolean":
175                   return s.bool(item);
176                   break;                                
177                 default:
178                   throw("Unknown type for $.ajatus.converter.toJSON");
179                 }
180     }
181     
182     $.ajatus.formatter = {};
183     $.ajatus.formatter.date = {};
184     $.ajatus.formatter.date.fix_length = function(number){
185         return ((number < 10) ? '0' : '') + number;
186     };
187     $.ajatus.formatter.date.is_iso8601 = function(value) {
188         if (typeof(value) != 'string')
189         {
190             return false;
191         }
192         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})))?)?)?)?";
193         var isod = value.match(new RegExp(regexp));
194         if (isod[10])
195         {
196             return true;
197         }
198         return false;
199     };
200     $.ajatus.formatter.date.js_to_iso8601 = function(date) {
201         var str = "";
202         str += date.getFullYear();
203         str += "-" + $.ajatus.formatter.date.fix_length(date.getMonth() + 1);
204         str += "-" + $.ajatus.formatter.date.fix_length(date.getDate());
205         str += "T" + $.ajatus.formatter.date.fix_length(date.getHours());
206         str += ":" + $.ajatus.formatter.date.fix_length(date.getMinutes());
207         str += ":" + $.ajatus.formatter.date.fix_length(date.getSeconds());
208         //str += 'Z';
209         
210         return str;
211     };
212     $.ajatus.formatter.date.iso8601_to_js = function(iso_date) {
213         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})))?)?)?)?";
214         var isod = iso_date.match(new RegExp(regexp));
215         
216         if (   !isod
217             || isod.length < 11)
218         {
219             return new Date();
220         }
221         
222         var date = new Date(isod[1], 0, 1);
223         date.setMonth(isod[3] - 1);
224         date.setDate(isod[5]);
225         date.setHours(isod[7]);
226         date.setMinutes(isod[8]);
227         date.setSeconds(isod[10]);
228         
229         return date;
230     };
231     $.ajatus.formatter.date.caldate_to_iso8601 = function(caldate, format, caltime, time_format) {
232         
233         var format = format || "MDY/";
234         var time_format = time_format || "HMS:";
235         
236         var cdparts = caldate.split(format.charAt(3));
237         var fparts = format.split("");
238         var date = new Date();
239         
240         $.each(cdparts, function(i,v){
241             v = Number(v);
242             if (fparts[i] == 'M') {
243                 date.setMonth(v-1);
244             }
245             if (fparts[i] == 'D') {
246                 date.setDate(v);
247             }
248             if (fparts[i] == 'Y') {
249                 date.setFullYear(v);
250             }
251         });
252         
253         if (caltime)
254         {
255             var time_separator = time_format.charAt(time_format.length - 1);
256             var tfparts = time_format.split("");
257             var ctparts = caltime.split(time_separator);
259             $.each(ctparts, function(i,v){
260                 v = Number(v);
261                 if (tfparts[i] == 'H') {
262                     date.setHours(v);
263                 }
264                 if (tfparts[i] == 'M') {
265                     date.setMinutes(v);
266                 }
267                 if (tfparts[i] == 'S') {
268                     date.setSeconds(v);
269                 }
270             });
271         }
272         else
273         {
274             date.setHours(0);
275             date.setMinutes(0);
276             date.setSeconds(0);
277         }
278                 
279         return $.ajatus.formatter.date.js_to_iso8601(date);
280     };
281     $.ajatus.formatter.date.iso8601_to_caldate = function(iso_date, format) {
282         var format = format || "MDY/";
283         
284         var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
285         var date_str = '';
286         
287         var day = date.getDate();
288         var month = date.getMonth();
289         var year = date.getFullYear();
290         month++;
291         
292                 for (var i = 0; i < 3; i++) {
293                         date_str += format.charAt(3) +
294                         (format.charAt(i) == 'D' ? $.ajatus.formatter.date.fix_length(day) :
295                         (format.charAt(i) == 'M' ? $.ajatus.formatter.date.fix_length(month) :
296                         (format.charAt(i) == 'Y' ? year : '?')));
297                 }
298                 date_str = date_str.substring(format.charAt(3) ? 1 : 0);
299                 
300                 return date_str;
301     };
302     $.ajatus.formatter.date.iso8601_to_caltime = function(iso_date, format) {
303         var format = format || "HMS:";
304         
305         var date = $.ajatus.formatter.date.iso8601_to_js(iso_date);
306         var time_str = '';
307         
308         var idparts = {
309             H: $.ajatus.formatter.date.fix_length(date.getHours()),
310             M: $.ajatus.formatter.date.fix_length(date.getMinutes()),
311             S: $.ajatus.formatter.date.fix_length(date.getSeconds())
312         };
313                 var separator = format.charAt(format.length - 1);
314         var fparts = format.split("");
315         var utt1_parts = user_test_time1.split(utt1_seprt);
317         $.each(utt1_parts, function(i,v){
318         });
319         
320         $.each(fparts, function(i,k){
321             time_str += fparts[k] + separator;
322         });
323         time_str = time_str.substring(1);
324                 
325                 return time_str;
326     };
328     /**
329      * Following functions are taken from the jquery form plugin.
330      * Plugin can be found from http://www.malsup.com/jquery/form/
331      */
332     $.fieldValue = function(el, successful) {
333         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
334         if (typeof successful == 'undefined') successful = true;
336         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
337             (t == 'checkbox' || t == 'radio') && !el.checked ||
338             (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
339             tag == 'select' && el.selectedIndex == -1))
340                 return null;
342         if (tag == 'select') {
343             var index = el.selectedIndex;
344             if (index < 0) return null;
345             var a = [], ops = el.options;
346             var one = (t == 'select-one');
347             var max = (one ? index+1 : ops.length);
348             for(var i=(one ? index : 0); i < max; i++) {
349                 var op = ops[i];
350                 if (op.selected) {
351                     // extra pain for IE...
352                     var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
353                     if (one) return v;
354                     a.push(v);
355                 }
356             }
357             return a;
358         }
359         return el.value;
360     };
361         $.fn.formToArray = function(semantic) {
362         var a = [];
363         if (this.length == 0) return a;
365         var form = this[0];
366         var els = semantic ? form.getElementsByTagName('*') : form.elements;
367         if (!els) return a;
368         for(var i=0, max=els.length; i < max; i++) {
369             var el = els[i];
370             var n = el.name;
371             if (!n) continue;
373             if (semantic && form.clk && el.type == "image") {
374                 // handle image inputs on the fly when semantic == true
375                 if(!el.disabled && form.clk == el)
376                     a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
377                 continue;
378             }
380             var v = $.fieldValue(el, true);
381             if (v && v.constructor == Array) {
382                 for(var j=0, jmax=v.length; j < jmax; j++)
383                     a.push({name: n, value: v[j]});
384             }
385             else if (v !== null && typeof v != 'undefined')
386                 a.push({name: n, value: v});
387         }
389         if (!semantic && form.clk) {
390             // input type=='image' are not found in elements array! handle them here
391             var inputs = form.getElementsByTagName("input");
392             for(var i=0, max=inputs.length; i < max; i++) {
393                 var input = inputs[i];
394                 var n = input.name;
395                 if(n && !input.disabled && input.type == "image" && form.clk == input)
396                     a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
397             }
398         }
399         return a;
400     };
401     /**
402      * Form plugin functions end
403      */
404     
405     $.ajatus.tags = {
406         cache: []
407     };
408     $.extend($.ajatus.tags, {
409         init: function() {
410             $.ajatus.events.lock_pool.increase();
411             $.ajatus.tags.load();
412             $.ajatus.events.lock_pool.decrease();
413         },
414         load: function() {
415             var jqcouch = $.jqcouch.doc;
416             jqcouch.on_success = function(data, caller)
417             {
418                 if (data.total_rows <= 0) {
419                     return false;
420                 }
421                 
422                 $.each(data.rows, function(i,n){
423                     $.ajatus.tags.cache.push(n._id);
424                 });
425             };
427             jqcouch.get_all($.ajatus.preferences.client.application_database + '/tags');
428             console.log("All tags:");
429             console.log($.ajatus.tags.cache);
430         },
431         get: function(tag) {
432             
433         },
434         exists: function(tag) {
435             var found = $.inArray(tag, $.ajatus.tags.cache);
436             if (found != -1) {
437                 return true;
438             }
439             return false;
440         },
441         create: function(name, jqc) {
442             
443             if ($.ajatus.tags.exists(name)) {
444                 return false;
445             }
446             
447             if (jqc == undefined) {
448                 var jqc = $.jqcouch.doc;
449             }
450             
451             var db_path = $.ajatus.preferences.client.application_database + '/tags/';
452             
453             var tag = {
454                 _id: name,
455                 value: {
456                     _type: 'tag',
457                     title: {
458                         val: name,
459                         widget: {
460                             name: 'text',
461                             config: {}
462                         }
463                     }
464                 }
465             };
466             tag = new $.ajatus.document(tag);
467             
468             var now = $.ajatus.formatter.date.js_to_iso8601(new Date());
469             var new_metadata = {
470                 created: now,
471                 creator: $.ajatus.preferences.local.user,
472                 revised: now,
473                 revisor: $.ajatus.preferences.local.user
474             };
475             tag = $.ajatus.document.modify_metadata(tag, new_metadata);
476             
477             console.log("saving tag:");
478             console.log(tag);
479             // + name
480             jqc.save(db_path, tag);
481             
482             return true;            
483         },
484         remove: function(tag, jqc) {
485             
486         }
487     });
488     
489     $.ajatus.tabs = {};
490     $.ajatus.tabs.set_active_by_hash = function(hash) {
491         // console.log('set_active_by_hash: '+hash);
492         
493         var views_tab_holder = $('#tabs-views ul');
494         var app_tab_holder = $('#tabs-application ul');
495         
496         var selected_tab = jQuery('li a[@href$="' + hash + '"]', views_tab_holder).parent();
497         if (selected_tab[0] != undefined) {
498             // console.log('views selected_tab:');
499             // console.log(selected_tab);
500             jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
501             jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
502             selected_tab.addClass('tabs-selected').focus();
503         } else {
504             selected_tab = jQuery('li a[@href$="' + hash + '"]', app_tab_holder).parent();
505             // console.log('app selected_tab:');
506             // console.log(selected_tab);
507             if (selected_tab[0] != undefined) {
508                 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
509                 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();
510                 selected_tab.addClass('tabs-selected').focus();
511             } else {
512                 jQuery('li', views_tab_holder).removeClass('tabs-selected').blur();
513                 jQuery('li', app_tab_holder).removeClass('tabs-selected').blur();                
514             }            
515         }
516     };
517     $.ajatus.tabs.on_click = function(e) {        
518         var trgt = target(e);
519         jQuery("li", parent(e)).removeClass("tabs-selected").index(trgt);
520         jQuery(trgt).addClass("tabs-selected");
521         
522         var new_hash = jQuery('a', trgt)[0].hash;
523         location.hash = new_hash;
524         $.ajatus.history.update(new_hash);
525         
526         function parent(event) {
527                 var element = event.target;
528                 if (element)
529                 {
530                     if (element.tagName == "UL")
531                     {
532                         return element;
533                     }
535                         while(element.tagName != "UL")
536                         {
537                                 element = element.parentNode;
538                         }
539                 }
540                 return element;
541         };
543         function target(event) {
544                 var element = event.target;
545                 if (element)
546                 {
547                     if (element.tagName == "UL")
548                     {
549                         element = jQuery(element).find('li').eq(0);
550                         return element;
551                     }
553                         while(element.tagName != "LI")
554                         {
555                                 element = element.parentNode;
556                         }
557                 }
558                 return element;
559         };
560     };
562     $.jqcouch.on_error = function(request,caller) {
563         $.ajatus.ajax_error(request, caller);
564     };
565     $.jqcouch.on_success = function(data,caller) {
566         $.ajatus.ajax_success(data, caller);
567     };
568     
569     $.ajatus.preferences = $.ajatus.preferences || {};
570     $.ajatus.preferences.local = {};
571     $.ajatus.preferences.client = {};
572     $.ajatus.preferences.client_defaults = {
573         debug: false,
574         history: true,
575         server_url: '/',
576         application_url: '/_utils/ajatus/',
577         application_database: 'ajatus_db',
578         content_types: {},
579         custom_views: [],
580         custom_widgets: [],
581         extensions: ['history']
582     };
583             
584     $.ajatus.preferences.loader = function() {
585         this.jqcouch = $.jqcouch.doc;
586         this.loaded = false;
587         
588         this.jqcouch.on_error = function(request, caller)
589         {
590             this.loaded = false;
591             $.ajatus.events.lock_pool.decrease();
592             $.ajatus.ajax_error(request, caller);
593         }
594         this.jqcouch.on_success = function(data, caller)
595         {
596             $.ajatus.events.lock_pool.decrease();
597             $.ajatus.preferences.local = data.value;
598         }
599     }
600     $.extend($.ajatus.preferences.loader.prototype, {
601         load: function() {
602             $.ajatus.events.lock_pool.increase();
603             this.jqcouch.get($.ajatus.preferences.client.application_database + '/preferences');
604         }
605     });
606         
607         $.ajatus.security_pass = function() {
608             try {
609             netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite UniversalFileRead");
610         } catch (e) {
611             alert("Permission UniversalBrowserRead denied. with error "+e);
612         }
613         };
614         
615         $.ajatus.init = function(element, options) {
616             //$.ajatus.security_pass();
617             
618             var application_element = jQuery(element);
619             $.ajatus.application_element = application_element;
620             $.ajatus.application_content_area = $('#middle #content', application_element);
621             
622             $('#header .app_version', $.ajatus.application_element).html($.ajatus.version.join('.'));
623             
624             $.ajatus.preferences.client = $.extend($.ajatus.preferences.client_defaults, options);
625             $.jqcouch.server_url = $.ajatus.preferences.client.server_url;
626             
627         $.ajaxSetup({
628             type: "GET",
629             url: $.ajatus.preferences.client.server_url,
630             global: false,
631             cache: false,
632             dataType: "json"
633         });
634         
635         $('#top #new-item-button', application_element).bind('click', function(e){
636             $.ajatus.views.system.create.render($.ajatus.active_type);
637         });
638         
639         $.ajatus.installer.is_installed();        
640         if ($.ajatus.installer.installed == false) {
641             var status = $.ajatus.installer.install();
642             $.ajatus.debug("Installer finished with status: "+status);            
643         } else {
644             $.ajatus.start();
645         }
646         // else
647         // {
648         //     $.ajatus.installer.uninstall();
649         // }
650     };
651     
652     $.ajatus.start = function() {
653             $.ajatus.events.lock_pool.increase();
654             $.ajatus.locker = new $.ajatus.events.lock({
655                 watch: {
656                     validate: function(){return $.ajatus.events.lock_pool.count == 0;},
657                     interval: 200,
658                 safety_runs: 0
659                 },
660                 on_release: function(){
661                     $.ajatus.application_element.addClass('ajatus_initialized');
662                     
663                     var show_frontpage = true;
664                 if (   $.ajatus.history
665                     && $.ajatus.history.enabled)
666                 {
667                      show_frontpage = !$.ajatus.history.check();
668                 }
669                 
670                 if (show_frontpage) {
671                     $.ajatus.views.system.frontpage.render();                    
672                 }
673                 }
674             });
675             
676         var preference_loader = new $.ajatus.preferences.loader;
677         preference_loader.load();
678         
679         $.ajatus.extensions.init({
680             on_ready: function(){
681                 $.ajatus.tags.init();
682                 $.ajatus.widgets.init();
683                 $.ajatus.views.init();
684                 
685                 $.ajatus.active_type = $.ajatus.preferences.client.content_types['note'];
686                 
687                 // $.each($.ajatus.preferences.client.content_types, function(i,n){
688                 //     if ($.ajatus.active_type == null) {
689                 //         $.ajatus.active_type = n;
690                 //         return true;
691                 //     }
692                 // });
694                 // Release the first lock
695                 $.ajatus.events.lock_pool.decrease();
696             }
697         });
698         
699         window.onbeforeunload = function() {
700             if ($.ajatus.events.named_lock_pool.count('unsaved') > 0) {
701                 return "Reason: You have unsaved changes.";                
702             }
703         }
704     }
705     
706     $.ajatus.ajax_error = function(request, caller) {
707         $.ajatus.debug("Ajax error request.status: "+request.status);
708         if (caller != undefined) {
709             $.ajatus.debug("Caller method: "+caller.method);
710             $.ajatus.debug("Caller action:");
711             $.ajatus.debug(caller.action);
712             if (caller.args != undefined) {
713                 $.ajatus.debug("Caller args:");
714                 $.ajatus.debug(caller.args);
715             }
716         }
717         
718         if (request.responseText != '')
719         {
720             var response = eval("(" + request.responseText + ")");
721             $.ajatus.debug('response.error.reason: '+response.error.reason);            
722         }
723     };
724     $.ajatus.ajax_success = function(data, caller) {
725         $.ajatus.debug("Ajax success");
726         if (caller != undefined)
727         {
728             $.ajatus.debug("Caller method: "+caller.method);
729             $.ajatus.debug("Caller action: "+caller.action);
730             if (caller.args != undefined)
731             {
732                 $.ajatus.debug("Caller args: "+caller.args);
733             }
734         }
735         $.ajatus.debug("data: "+data);
736         
737         return false;
738     };
739     
740     $.ajatus.debug = function(msg, title, type) {
741         if ($.ajatus.preferences.client.debug == true)
742         {
743             console.log(msg);
744         }
745     };
747         $.ajatus.register_form = function(form) {
748             $.ajatus.forms.register.normal(form);
749         // jqform = jQuery(form);
750         // var a = [];
751         // jqform.bind('submit',function(e){
752         //     return false;
753         // });
754         // 
755         // $('input[@type=submit]', jqform).bind('click', function(){
756         //          $.ajatus.process_form(jqform.formToArray(false));
757         // });
758         };
759         
760         $.ajatus.process_form = function(form_data) {
761             var doc = {
762                 _id: '',
763                 _rev: '',
764                 value: {}
765             };
766             var form_values = {};
767             var form_id = '';
768             var doc_type = 'note';
769             
770             $.each(form_data, function(i,row){
771                 //console.log('i: '+i+' row.name: '+row.name+' row.value: '+row.value);
772                 
773                 if (   row.name == '_id'
774                     && (   row.value != undefined
775                         && row.value != ''))
776                 {
777                     doc['_id'] = String(row.value);
778                     form_id = doc['_id'];
779                 }
780                 else if(   row.name == '_rev'
781                         && (   row.value != undefined
782                             && row.value != ''))
783                 {
784                     doc['_rev'] = Number(row.value);
785                 } else {
786                     if (row.name != 'submit') {
787                         if (row.name == '_type') {
788                         doc_type = row.value;
789                         form_values[row.name] = row.value;
790                     }
791                     else if (   row.name.substr(0,6) != "widget"
792                              && row.name.substr(0,8) != "metadata")
793                     {
794                         var item = {};
795                         var widget = {};
796                         var prev_val = false;
797                         $.each(form_data, function(x,r){
798                             if (r.name == 'widget['+row.name+':name]') {
799                                 widget['name'] = r.value;
800                             } else if (r.name == 'widget['+row.name+':config]') {
801                                     widget['config'] = $.ajatus.converter.parseJSON(r.value);
802                                 } else if (r.name == 'widget['+row.name+':prev_val]') {
803                                     prev_val = $.ajatus.converter.parseJSON(r.value);
804                                 }
805                             });
806                             
807                             var wdgt = new $.ajatus.widget(widget['name'], widget['config']);
808                         item['val'] = wdgt.value_on_save(row.value, prev_val);
809                         item['widget'] = widget;
810                         form_values[row.name] = item;
811                     }
812                     else if (row.name.substr(0,8) == "metadata")
813                     {
814                         if (!form_values['metadata'])
815                         {
816                             form_values['metadata'] = {};
817                         }
818                         
819                         var re = /\bmetadata\[([a-z]+)\b/;
820                         var results = re.exec(row.name);
821                         var key = results[1];
823                         form_values['metadata'][key] = {
824                             val: row.value
825                         };
826                     }
827                     }
828                 }
829             });
830             doc['value'] = form_values;
831             
832             doc = new $.ajatus.document(doc);
833             
834             var now = $.ajatus.formatter.date.js_to_iso8601(new Date());
835             
836             var new_metadata = {
837                 revised: now,
838                 revisor: $.ajatus.preferences.local.user
839             };
840             if (   doc._id == undefined
841                 || doc._id == '')
842             {
843                 new_metadata['created'] = now;
844                 new_metadata['creator'] = $.ajatus.preferences.local.user;
845             }
847             doc = $.ajatus.document.modify_metadata(doc, new_metadata);
848                     
849             var jqcouch = $.jqcouch.doc;            
850         var db_path = $.ajatus.preferences.client.application_database + '/content';
851         jqcouch.on_success = function(data, caller) {
852             if (data.ok) {
853                 doc['_id'] = data._id;
854                 doc['_rev'] = data._rev;
855                 doc = new $.ajatus.document(doc);
856                 $.ajatus.views.system.item.render(doc);
857             }
858         }
859             jqcouch.save(db_path, doc);
860             
861             return false;
862         };
864     $.fn.initialize_ajatus = function(options) {
865                 if (!$(this).is(".ajatus_initialized")) {
866                     return new $.ajatus.init(this, options);
867             }
868         };
869         
870 })(jQuery);
872 function initialize_ajatus() {
873     jQuery('#application').initialize_ajatus({
874         debug: true,
875         custom_views: [
876             'test'
877         ],
878         server_url: 'http://couchdb_server:8888/'
879         //application_database: 'ajatus_stable_db'
880     });
883 jQuery(document).ready(function() {
884     initialize_ajatus();