Fixed and enhanced creation and updating
[ajatus.git] / js / ajatus.utils.js
blob9acb0b7e2e47fdf7fb85c251bd0c208a8233241b
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  */
15 (function($){
16     $.ajatus = $.ajatus || {};
17     
18     $.ajatus.utils = {        
19     };
20     
21     $.ajatus.utils.merge_configs = function(a,b) {
22         var c = {};
23         
24         if (typeof a == 'undefined') {
25             return c;
26         }        
27         if (typeof b == 'undefined') {
28             var b = {};
29         }
30         
31         for (var ak in a) {
32             if (typeof a[ak] != 'object') {
33                 c[ak] = a[ak];
34                 if (   typeof b[ak] != 'undefined'
35                     && typeof b[ak] != 'object')
36                 {
37                     c[ak] = b[ak];
38                 }
39             } else {
40                 if (typeof b[ak] == 'undefined') {
41                     c[ak] = $.ajatus.utils.merge_configs(a[ak], {});
42                 } else {
43                     c[ak] = $.ajatus.utils.merge_configs(a[ak], b[ak]);
44                 }                
45             }
46         }
47         
48         return c;
49     };
50     
51     $.ajatus.utils.version = {
52         differs: function(v1, v2) {
53             var differs = false;
54             for (var i=0; i<3; i++) {
55                 if (v1[i] != v2[i]) {
56                     differs = true;
57                 }
58             }
59             
60             return differs;
61         }
62     };
63     
64     $.ajatus.utils.to_boolean = function(value) {
65         if (typeof value == 'boolean') {
66             return value;
67         }
68         
69         if (typeof value == 'string') {
70             if (value === 'true') {
71                 return true;
72             } else if (value === '1') {
73                 return true;
74             }
75             return false;
76         }
78         if (typeof value == 'number') {
79             if (value === 1) {
80                 return true;
81             }
82             return false;
83         }
84         
85         return false;
86     };
88     /**
89      * Renders pretty printed version from given value
90      * Original pretty_print function by Damien Katz <damien_katz@yahoo.com>
91      * Modified to work with Ajatus by Jerry Jalava <jerry.jalava@gmail.com>
92      * @param {Mixed} val Value to render
93      * @param {Number} indent Current indent level (Default: 4)
94      * @param {String} linesep Line break to be used (Default: "\n")
95      * @param {Number} depth Current line depth (Default: 1)
96      * @returns Pretty printed value
97      * @type String
98      */
99     $.ajatus.utils.pretty_print = function(val, indent, linesep, depth) {
100         var indent = typeof indent != 'undefined' ? indent : 4;
101         var linesep = typeof linesep != 'undefined' ? linesep : "\n";
102         var depth = typeof depth != 'undefined' ? depth : 1;
103         
104         var propsep = linesep.length ? "," + linesep : ", ";
106         var tab = [];
108         for (var i = 0; i < indent * depth; i++) {
109             tab.push("")
110         };
111         tab = tab.join(" ");
113         switch (typeof val) {
114             case "boolean":
115             case "number":
116             case "string":
117                 return $.ajatus.converter.toJSON(val);
118             case "object":
119                 if (val === null) {
120                     return "null";
121                 }
122                 if (val.constructor == Date) {
123                     return $.ajatus.converter.toJSON(val);
124                 }
126                 var buf = [];
127                 if (val.constructor == Array) {
128                     buf.push("[");
129                     for (var index = 0; index < val.length; index++) {
130                         buf.push(index > 0 ? propsep : linesep);
131                         buf.push(
132                             tab, $.ajatus.utils.pretty_print(val[index], indent, linesep, depth + 1)
133                         );
134                     }
135                     
136                     if (index >= 0) {
137                         buf.push(linesep, tab.substr(indent))
138                     };
139                     
140                     buf.push("]");
141                 } else {
142                     buf.push("{");
143                     var index = 0;
144                     for (var key in val) {                        
145                         if (! val.hasOwnProperty(key)) {
146                             continue;
147                         };
148                         
149                         buf.push(index > 0 ? propsep : linesep);
150                         buf.push(
151                             tab, $.ajatus.converter.toJSON(key), ": ",
152                             $.ajatus.utils.pretty_print(val[key], indent, linesep, depth + 1)
153                         );
154                         index++;
155                     }
156                     
157                     if (index >= 0) {
158                         buf.push(linesep, tab.substr(indent));
159                     };
160                     
161                     buf.push("}");
162                 }
163                 
164                 return buf.join("");
165             break;
166         }
167     };
169     /**
170      * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
171      * Digest Algorithm, as defined in RFC 1321.
172      * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
173      * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
174      * Distributed under the BSD License
175      * See http://pajhome.org.uk/crypt/md5 for more info.
176      *
177      * Modified to work with Ajatus by Jerry Jalava <jerry.jalava@gmail.com>
178      * @param {Mixed} val Value to render
179      * @param {Number} indent Current indent level (Default: 4)
180      * @param {String} linesep Line break to be used (Default: "\n")
181      * @param {Number} depth Current line depth (Default: 1)
182      * @returns Pretty printed value
183      * @type String
184      */
185     $.ajatus.utils.md5 = {
186         _hexcase: 0,  /* hex output format. 0 - lowercase; 1 - uppercase */
187         _b64pad: "", /* base-64 pad character. "=" for strict RFC compliance */
188         _chrsz: 8,  /* bits per input character. 8 - ASCII; 16 - Unicode */
189         
190         /*
191          * Calculate the MD5 of an array of little-endian words, and a bit length
192          */
193         _core_md5: function(x, len) {
194             /* append padding */
195             x[len >> 5] |= 0x80 << ((len) % 32);
196             x[(((len + 64) >>> 9) << 4) + 14] = len;
198             var a =  1732584193;
199             var b = -271733879;
200             var c = -1732584194;
201             var d =  271733878;
203             for (var i = 0; i < x.length; i += 16) {
204                 var olda = a;
205                 var oldb = b;
206                 var oldc = c;
207                 var oldd = d;
209                 a = $.ajatus.utils.md5._md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
210                 d = $.ajatus.utils.md5._md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
211                 c = $.ajatus.utils.md5._md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
212                 b = $.ajatus.utils.md5._md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
213                 a = $.ajatus.utils.md5._md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
214                 d = $.ajatus.utils.md5._md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
215                 c = $.ajatus.utils.md5._md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
216                 b = $.ajatus.utils.md5._md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
217                 a = $.ajatus.utils.md5._md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
218                 d = $.ajatus.utils.md5._md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
219                 c = $.ajatus.utils.md5._md5_ff(c, d, a, b, x[i+10], 17, -42063);
220                 b = $.ajatus.utils.md5._md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
221                 a = $.ajatus.utils.md5._md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
222                 d = $.ajatus.utils.md5._md5_ff(d, a, b, c, x[i+13], 12, -40341101);
223                 c = $.ajatus.utils.md5._md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
224                 b = $.ajatus.utils.md5._md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
226                 a = $.ajatus.utils.md5._md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
227                 d = $.ajatus.utils.md5._md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
228                 c = $.ajatus.utils.md5._md5_gg(c, d, a, b, x[i+11], 14,  643717713);
229                 b = $.ajatus.utils.md5._md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
230                 a = $.ajatus.utils.md5._md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
231                 d = $.ajatus.utils.md5._md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
232                 c = $.ajatus.utils.md5._md5_gg(c, d, a, b, x[i+15], 14, -660478335);
233                 b = $.ajatus.utils.md5._md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
234                 a = $.ajatus.utils.md5._md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
235                 d = $.ajatus.utils.md5._md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
236                 c = $.ajatus.utils.md5._md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
237                 b = $.ajatus.utils.md5._md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
238                 a = $.ajatus.utils.md5._md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
239                 d = $.ajatus.utils.md5._md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
240                 c = $.ajatus.utils.md5._md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
241                 b = $.ajatus.utils.md5._md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
243                 a = $.ajatus.utils.md5._md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
244                 d = $.ajatus.utils.md5._md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
245                 c = $.ajatus.utils.md5._md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
246                 b = $.ajatus.utils.md5._md5_hh(b, c, d, a, x[i+14], 23, -35309556);
247                 a = $.ajatus.utils.md5._md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
248                 d = $.ajatus.utils.md5._md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
249                 c = $.ajatus.utils.md5._md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
250                 b = $.ajatus.utils.md5._md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
251                 a = $.ajatus.utils.md5._md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
252                 d = $.ajatus.utils.md5._md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
253                 c = $.ajatus.utils.md5._md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
254                 b = $.ajatus.utils.md5._md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
255                 a = $.ajatus.utils.md5._md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
256                 d = $.ajatus.utils.md5._md5_hh(d, a, b, c, x[i+12], 11, -421815835);
257                 c = $.ajatus.utils.md5._md5_hh(c, d, a, b, x[i+15], 16,  530742520);
258                 b = $.ajatus.utils.md5._md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
260                 a = $.ajatus.utils.md5._md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
261                 d = $.ajatus.utils.md5._md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
262                 c = $.ajatus.utils.md5._md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
263                 b = $.ajatus.utils.md5._md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
264                 a = $.ajatus.utils.md5._md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
265                 d = $.ajatus.utils.md5._md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
266                 c = $.ajatus.utils.md5._md5_ii(c, d, a, b, x[i+10], 15, -1051523);
267                 b = $.ajatus.utils.md5._md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
268                 a = $.ajatus.utils.md5._md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
269                 d = $.ajatus.utils.md5._md5_ii(d, a, b, c, x[i+15], 10, -30611744);
270                 c = $.ajatus.utils.md5._md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
271                 b = $.ajatus.utils.md5._md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
272                 a = $.ajatus.utils.md5._md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
273                 d = $.ajatus.utils.md5._md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
274                 c = $.ajatus.utils.md5._md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
275                 b = $.ajatus.utils.md5._md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
277                 a = $.ajatus.utils.md5._safe_add(a, olda);
278                 b = $.ajatus.utils.md5._safe_add(b, oldb);
279                 c = $.ajatus.utils.md5._safe_add(c, oldc);
280                 d = $.ajatus.utils.md5._safe_add(d, oldd);
281             }
282           
283             return Array(a, b, c, d);
284         },
285         
286         /*
287          * These functions implement the four basic operations the algorithm uses.
288          */
289         _md5_cmn: function(q, a, b, x, s, t) {
290             return $.ajatus.utils.md5._safe_add($.ajatus.utils.md5._bit_rol($.ajatus.utils.md5._safe_add($.ajatus.utils.md5._safe_add(a, q), $.ajatus.utils.md5._safe_add(x, t)), s),b);
291         },
292         _md5_ff: function(a, b, c, d, x, s, t) {
293             return $.ajatus.utils.md5._md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
294         },
295         _md5_gg: function(a, b, c, d, x, s, t) {
296             return $.ajatus.utils.md5._md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
297         },
298         _md5_hh: function(a, b, c, d, x, s, t) {
299             return $.ajatus.utils.md5._md5_cmn(b ^ c ^ d, a, b, x, s, t);
300         },
301         _md5_ii: function(a, b, c, d, x, s, t) {
302             return $.ajatus.utils.md5._md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
303         },
304         
305         /*
306          * Calculate the HMAC-MD5, of a key and some data
307          */
308         _core_hmac_md5: function(key, data) {
309             var bkey = $.ajatus.utils.md5._str2binl(key);
310             if(bkey.length > 16) bkey = $.ajatus.utils.md5._core_md5(bkey, key.length * $.ajatus.utils.md5._chrsz);
312             var ipad = Array(16), opad = Array(16);
313             for(var i = 0; i < 16; i++) {
314                 ipad[i] = bkey[i] ^ 0x36363636;
315                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
316             }
318             var hash = $.ajatus.utils.md5._core_md5(ipad.concat($.ajatus.utils.md5._str2binl(data)), 512 + data.length * $.ajatus.utils.md5._chrsz);
320             return $.ajatus.utils.md5._core_md5(opad.concat(hash), 512 + 128);
321         },
322         
323         /*
324          * Add integers, wrapping at 2^32. This uses 16-bit operations internally
325          * to work around bugs in some JS interpreters.
326          */
327         _safe_add: function(x, y) {
328             var lsw = (x & 0xFFFF) + (y & 0xFFFF);
329             var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
330             return (msw << 16) | (lsw & 0xFFFF);
331         },
333         /*
334          * Bitwise rotate a 32-bit number to the left.
335          */
336         _bit_rol: function(num, cnt) {
337             return (num << cnt) | (num >>> (32 - cnt));
338         },
340         /*
341          * Convert a string to an array of little-endian words
342          * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
343          */
344         _str2binl: function(str) {
345             var bin = Array();
346             var mask = (1 << $.ajatus.utils.md5._chrsz) - 1;
347             for (var i = 0; i < str.length * $.ajatus.utils.md5._chrsz; i += $.ajatus.utils.md5._chrsz) {
348                 bin[i>>5] |= (str.charCodeAt(i / $.ajatus.utils.md5._chrsz) & mask) << (i%32);
349             }
350             return bin;
351         },
353         /*
354          * Convert an array of little-endian words to a string
355          */
356         _binl2str: function(bin) {
357             var str = "";
358             var mask = (1 << $.ajatus.utils.md5._chrsz) - 1;
359             for(var i = 0; i < bin.length * 32; i += $.ajatus.utils.md5._chrsz) {
360                 str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);                
361             }
362             return str;
363         },
365         /*
366          * Convert an array of little-endian words to a hex string.
367          */
368         _binl2hex: function(binarray) {
369             var hex_tab = $.ajatus.utils.md5._hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
370             var str = "";
371             for (var i = 0; i < binarray.length * 4; i++) {
372                 str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
373             }
374             return str;
375         },
377         /*
378          * Convert an array of little-endian words to a base-64 string
379          */
380         _binl2b64: function(binarray) {
381             var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
382             var str = "";
383             for (var i = 0; i < binarray.length * 4; i += 3) {
384                 var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
385                 for (var j = 0; j < 4; j++) {
386                     if (i * 8 + j * 6 > binarray.length * 32) {
387                         str += $.ajatus.utils.md5._b64pad;
388                     } else {
389                         str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
390                     }
391                 }
392             }
393             return str;
394         },
395         
396         _hex_md5: function(s) {
397             return $.ajatus.utils.md5._binl2hex($.ajatus.utils.md5._core_md5($.ajatus.utils.md5._str2binl(s), s.length * $.ajatus.utils.md5._chrsz));
398         },
399         _b64_md5: function(s) {
400             return $.ajatus.utils.md5._binl2b64($.ajatus.utils.md5._core_md5($.ajatus.utils.md5._str2binl(s), s.length * $.ajatus.utils.md5._chrsz));
401         },
402         _str_md5: function(s) {
403             return $.ajatus.utils.md5._binl2str($.ajatus.utils.md5._core_md5($.ajatus.utils.md5._str2binl(s), s.length * $.ajatus.utils.md5._chrsz));
404         },
405         _hex_hmac_md5: function(key, data) {
406             return $.ajatus.utils.md5._binl2hex($.ajatus.utils.md5._core_hmac_md5(key, data));
407         },
408         _b64_hmac_md5: function(key, data) {
409             return $.ajatus.utils.md5._binl2b64($.ajatus.utils.md5._core_hmac_md5(key, data));
410         },
411         _str_hmac_md5: function(key, data) {
412             return $.ajatus.utils.md5._binl2str($.ajatus.utils.md5._core_hmac_md5(key, data));
413         },
414         
415         encode: function(string, encoding, data) {
416             var encoded_string = '';
417             if (typeof encoding == 'undefined') {
418                 var encoding = 'hex';
419             }
420             
421             switch(encoding) {
422                 case 'b64':
423                     encoded_string = $.ajatus.utils.md5._b64_md5(string);
424                 break;
425                 case 'str':
426                     encoded_string = $.ajatus.utils.md5._str_md5(string);
427                 break;
428                 case 'hex_hmac':
429                     encoded_string = $.ajatus.utils.md5._hex_hmac_md5(string, data);
430                 break;
431                 case 'b64_hmac':
432                     encoded_string = $.ajatus.utils.md5._b64_hmac_md5(string, data);
433                 break;
434                 case 'str_hmac':
435                     encoded_string = $.ajatus.utils.md5._str_hmac_md5(string, data);
436                 break;
437                 case 'hex':
438                 default:
439                     encoded_string = $.ajatus.utils.md5._hex_md5(string);
440                 break;
441             }
442             
443             return encoded_string;
444         },
445         match: function(string, md5, encoding) {
446             var encoded_string = $.ajatus.utils.md5.encode(string, encoding);
447             
448             if (encoded_string === md5) {
449                 return true;
450             }
451             
452             return false;
453         }
454     };
455     
456     $.ajatus.utils.base64 = {
457         END_OF_INPUT: -1,
458         base64Chars: [
459             'A','B','C','D','E','F','G','H',
460             'I','J','K','L','M','N','O','P',
461             'Q','R','S','T','U','V','W','X',
462             'Y','Z','a','b','c','d','e','f',
463             'g','h','i','j','k','l','m','n',
464             'o','p','q','r','s','t','u','v',
465             'w','x','y','z','0','1','2','3',
466             '4','5','6','7','8','9','+','/'
467         ],
468         
469         reverseBase64Chars: null,
471         encode: function(value) {
472             var base64Str;
473             var base64Count;
474             
475             function setBase64Str(str) {
476                 base64Str = str;
477                 base64Count = 0;
478             }
479             
480             function readBase64() {
481                 if (! base64Str) {
482                     return $.ajatus.utils.base64.END_OF_INPUT;
483                 }
484                 if (base64Count >= base64Str.length) {
485                     return $.ajatus.utils.base64.END_OF_INPUT;
486                 }
487                 var c = base64Str.charCodeAt(base64Count) & 0xff;
488                 base64Count++;
489                 
490                 return c;
491             }
493             setBase64Str(value);
494             var result = '';
495             var inBuffer = new Array(3);
496             var lineCount = 0;
497             var done = false;
498             while (!done && (inBuffer[0] = readBase64()) != $.ajatus.utils.base64.END_OF_INPUT) {
499                 inBuffer[1] = readBase64();
500                 inBuffer[2] = readBase64();
501                 result += ($.ajatus.utils.base64.base64Chars[ inBuffer[0] >> 2 ]);
502                 if (inBuffer[1] != $.ajatus.utils.base64.END_OF_INPUT) {
503                     result += ($.ajatus.utils.base64.base64Chars[((inBuffer[0] << 4) & 0x30) | (inBuffer[1] >> 4)]);
504                     if (inBuffer[2] != $.ajatus.utils.base64.END_OF_INPUT) {
505                         result += ($.ajatus.utils.base64.base64Chars[((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]);
506                         result += ($.ajatus.utils.base64.base64Chars[inBuffer[2] & 0x3F]);
507                     } else {
508                         result += ($.ajatus.utils.base64.base64Chars[((inBuffer[1] << 2) & 0x3c)]);
509                         result += ('=');
510                         done = true;
511                     }
512                 } else {
513                     result += ($.ajatus.utils.base64.base64Chars[((inBuffer[0] << 4) & 0x30)]);
514                     result += ('=');
515                     result += ('=');
516                     done = true;
517                 }
518                 lineCount += 4;
519                 if (lineCount >= 76) {
520                     result += ('\n');
521                     lineCount = 0;
522                 }
523             }
524             
525             return result;
526         },
527         decode: function(value) {
528             var base64Str;
529             var base64Count;
530             
531             if ($.ajatus.utils.base64.reverseBase64Chars == null) {
532                 $.ajatus.utils.base64.reverseBase64Chars = [];
533                 for (var i=0; i < $.ajatus.utils.base64.base64Chars.length; i++) {
534                     $.ajatus.utils.base64.reverseBase64Chars[$.ajatus.utils.base64.base64Chars[i]] = i;
535                 }                
536             }
537             
538             function setBase64Str(str) {
539                 base64Str = str;
540                 base64Count = 0;
541             }
543             function readReverseBase64() {   
544                 if (! base64Str) {
545                     return $.ajatus.utils.base64.END_OF_INPUT;
546                 }
547                 
548                 while (true) {
549                     if (base64Count >= base64Str.length) {
550                         return $.ajatus.utils.base64.END_OF_INPUT;
551                     }
552                     var nextCharacter = base64Str.charAt(base64Count);
553                     base64Count++;
554                     if ($.ajatus.utils.base64.reverseBase64Chars[nextCharacter]){
555                         return $.ajatus.utils.base64.reverseBase64Chars[nextCharacter];
556                     }
557                     if (nextCharacter == 'A') {
558                         return 0;
559                     }
560                 }
561                 return $.ajatus.utils.base64.END_OF_INPUT;
562             }
564             function ntos(n) {
565                 n = n.toString(16);
566                 if (n.length == 1) {
567                     n = "0" + n;
568                 }
569                 n = "%" + n;
570                 
571                 return unescape(n);
572             }
573             
574             setBase64Str(value);
575             var result = "";
576             var inBuffer = new Array(4);
577             var done = false;
578             while (!done && (inBuffer[0] = readReverseBase64()) != $.ajatus.utils.base64.END_OF_INPUT
579                 && (inBuffer[1] = readReverseBase64()) != $.ajatus.utils.base64.END_OF_INPUT)
580             {
581                 inBuffer[2] = readReverseBase64();
582                 inBuffer[3] = readReverseBase64();
583                 result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
584                 if (inBuffer[2] != $.ajatus.utils.base64.END_OF_INPUT){
585                     result +=  ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
586                     if (inBuffer[3] != $.ajatus.utils.base64.END_OF_INPUT){
587                         result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
588                     } else {
589                         done = true;
590                     }
591                 } else {
592                     done = true;
593                 }
594             }
595             
596             return result;            
597         }
598     };
599     
600     /**
601      *
602      * UTF-8 data encode / decode
603      * http://www.webtoolkit.info/
604      *
605      * @returns Encoded/Decoded string
606      * @type String
607      */
608     $.ajatus.utils.utf8 = {
609         encode: function(string) {
610             string = string.replace(/\r\n/g,"\n");
611             var utftext = "";
613             for (var n = 0; n < string.length; n++) {
614                 var c = string.charCodeAt(n);
616                 if (c < 128) {
617                     utftext += String.fromCharCode(c);
618                 } else if (   (c > 127)
619                            && (c < 2048))
620                 {
621                     utftext += String.fromCharCode((c >> 6) | 192);
622                     utftext += String.fromCharCode((c & 63) | 128);
623                 } else {
624                     utftext += String.fromCharCode((c >> 12) | 224);
625                     utftext += String.fromCharCode(((c >> 6) & 63) | 128);
626                     utftext += String.fromCharCode((c & 63) | 128);
627                 }
628             }
630             return utftext;
631         },
632         decode: function(utftext) {
633             var string = "";
634             var i = 0;
635             var c = c1 = c2 = 0;
637             while ( i < utftext.length ) {
638                 c = utftext.charCodeAt(i);
640                 if (c < 128) {
641                     string += String.fromCharCode(c);
642                     i++;
643                 } else if (   (c > 191)
644                            && (c < 224))
645                 {
646                     c2 = utftext.charCodeAt(i+1);
647                     string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
648                     i += 2;
649                 } else {
650                     c2 = utftext.charCodeAt(i+1);
651                     c3 = utftext.charCodeAt(i+2);
652                     string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
653                     i += 3;
654                 }
655             }
657             return string;
658         }
659     };
660     
661     $.ajatus.utils.array = {
662         has_match: function(needles, haystack) {
663             if (   typeof haystack != 'object'
664                 || haystack.length <= 0)
665             {
666                 return false;
667             }
669             if (typeof needles == 'object') {
670                 var matched = false;
671                 $.each(needles, function(i,needle){
672                     if ($.inArray(needle, haystack) != -1) {
673                         matched = true;
674                     }
675                     // if (typeof(haystack[needle]) != 'undefined') {
676                     //                         matched = true;
677                     //                     }
678                 });
679                 return matched;
680             } else if (typeof needles == 'string') {
681                 var matched = false;
682                 if ($.inArray(needles, haystack) != -1) {
683                     matched = true;
684                 }
685                 return matched;
686             }
687             
688             return false;
689         },
690         copy: function(source) {
691             var cp = [];
692             
693             for (var k in source) {
694                 cp [k] = source[k];
695             }
696             
697             return cp;
698         }
699     };
700     
701     $.ajatus.utils.object = {
702         clone: function(obj) {
703             if(obj == null || typeof(obj) != 'object') {
704                 return obj;                
705             }
706             var temp = {};
707             for(var key in obj) {
708                 temp[key] = $.ajatus.utils.object.clone(obj[key]);                
709             }
711             return temp;
712         }
713     };
714     
715     $.ajatus.utils.pause = function(ms) {
716         var date = new Date();
717         var currDate = null;
719         do { currDate = new Date(); }
720         while(currDate - date < ms);
721     };
722         
723     $.ajatus.utils._random_key = 0;
724     $.ajatus.utils.generate_id = function() {
725         $.ajatus.utils._random_key += 1;
726         
727         var date = new Date();
728         var random_key = Math.floor(Math.random()*4013);
729         var random_key2 = Math.floor(Math.random()*3104);
730         
731         return $.ajatus.utils.md5.encode(Math.floor(((date.getTime()/1000)+random_key2) + (10016486 + (random_key * 22423)) * random_key / random_key2).toString() + "_" + $.ajatus.utils._random_key).toString().substr(0,8);
732     };
733     
734     $.ajatus.utils.get_url_arg = function(name) {
735         var value = null;
736         
737         name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
738         var exp = "[\\?&]"+name+"=([^&#]*)";
739         var regex = new RegExp( exp );
740         var results = regex.exec(window.location.href);
741         if (results != null) {
742             value = results[1];
743         }
744         
745         return value;
746     };
747     
748     $.ajatus.utils.load_script = function(url, callback, cb_args) {
749         $('head').append('<script type="text/javascript" charset="utf-8" src="'+url+'"></script>');
750         if (typeof callback == 'string') {
751             if (   typeof cb_args == 'undefined'
752                 || typeof cb_args != 'object')
753             {
754                 var cb_args = [];
755             }
756             
757             setTimeout('eval("var fn = eval('+callback+'); fn.apply(fn, [\''+cb_args.join("','")+'\']);")', 300);
758         }
759     };
760     
761     $.ajatus.utils.doc_generator = function(prefix, count, type, data, value_tpl) {
762         var doc = {
763             _id: '',
764             value: {
765             }
766         };
767         if (typeof value_tpl != 'undefined') {
768             doc.value = value_tpl;
769         }
770         if (typeof type == 'undefined') {
771             var type = $.ajatus.preferences.client.content_types['note'];
772         }
773         if (typeof data == 'undefined') {
774             var data = {
775             }
776         }
777         
778         if (   typeof type == 'string'
779             && typeof $.ajatus.preferences.client.content_types[type] != 'undefined')
780         {
781             type = $.ajatus.preferences.client.content_types[type];
782         }
783         
784         doc.value._type = type.name;
785                 
786         $.each(type.original_schema, function(i,n){
787             doc.value[i] = {
788                 val: n.def_val,
789                 widget: n.widget
790             };
791         });
792         
793         var docs = [];
794         
795         if (typeof prefix == 'undefined') {
796             var prefix = 'ajatus';
797         }
798         if (typeof count == 'undefined') {
799             count = 10;
800         }
801         
802         function prepare_title(title, id) {
803             var full_title = title + " ";
804             full_title += (id).toString();
805             return full_title;
806         }
807         
808         for (var i=0; i<count; i++) {
809             var new_doc = new $.ajatus.document(doc);
810             
811             if (   typeof data.title == 'undefined'
812                 && typeof new_doc.value['title'] != 'undefined')
813             {
814                 new_doc.value['title'].val = $.ajatus.i10n.get("Generated doc title (%s)", [prefix]) + " " + i;
815             }
816             
817             $.each(data, function(x,n){
818                 if (typeof new_doc.value[x] != 'undefined') {
819                     new_doc.value[x].val = n;
820                 }
821             });
823             var now = $.ajatus.formatter.date.js_to_iso8601(new Date());
825             var new_metadata = {
826                 created: now,
827                 creator: $.ajatus.preferences.local.user.email,
828                 revised: now,
829                 revisor: $.ajatus.preferences.local.user.email
830             };
832             new_doc = $.ajatus.document.modify_metadata(new_doc, new_metadata);
833             
834             new_doc._id = prefix + "_gen_doc_" + (i).toString();
835             docs.push($.ajatus.utils.object.clone(new_doc));
836         }
837         
838         return docs;
839     };
841     // Put focus on given form element (0 == first) (skips hidden fields)
842     $.fn.set_focus_on = function(eid) {
843         var elem = $('input:visible:not(:hidden)', this).get(eid);
844         var select = $('select:visible', this).get(eid);
846         if (select && elem) {
847             if (select.offsetTop < elem.offsetTop) {
848                 elem = select;
849             }
850         }
852         var textarea = $('textarea:visible', this).get(eid);
853         if (textarea && elem) {
854             if (textarea.offsetTop < elem.offsetTop) {
855                 elem = textarea;
856             }
857         }
859         if (elem) {
860             elem.focus();
861         }
863         return this;
864     };
865     
866     /**
867      * Following functions are taken from the jquery form plugin.
868      * Plugin can be found from http://www.malsup.com/jquery/form/
869      */
870     $.fieldValue = function(el, successful) {
871         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
872         if (typeof successful == 'undefined') successful = true;
874         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
875             (t == 'checkbox' || t == 'radio') && !el.checked ||
876             (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
877             tag == 'select' && el.selectedIndex == -1))
878                 return null;
880         if (tag == 'select') {
881             var index = el.selectedIndex;
882             if (index < 0) return null;
883             var a = [], ops = el.options;
884             var one = (t == 'select-one');
885             var max = (one ? index+1 : ops.length);
886             for(var i=(one ? index : 0); i < max; i++) {
887                 var op = ops[i];
888                 if (op.selected) {
889                     // extra pain for IE...
890                     var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
891                     if (one) return v;
892                     a.push(v);
893                 }
894             }
895             return a;
896         }
897         return el.value;
898     };
899         $.fn.formToArray = function(semantic) {
900         var a = [];
901         if (this.length == 0) return a;
903         var form = this[0];
904         var els = semantic ? form.getElementsByTagName('*') : form.elements;
905         if (!els) return a;
906         for(var i=0, max=els.length; i < max; i++) {
907             var el = els[i];
908             var n = el.name;
909             if (!n) continue;
911             if (semantic && form.clk && el.type == "image") {
912                 // handle image inputs on the fly when semantic == true
913                 if(!el.disabled && form.clk == el)
914                     a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
915                 continue;
916             }
918             var v = $.fieldValue(el, true);
919             if (v && v.constructor == Array) {
920                 for(var j=0, jmax=v.length; j < jmax; j++)
921                     a.push({name: n, value: v[j]});
922             }
923             else if (v !== null && typeof v != 'undefined')
924                 a.push({name: n, value: v});
925         }
927         if (!semantic && form.clk) {
928             // input type=='image' are not found in elements array! handle them here
929             var inputs = form.getElementsByTagName("input");
930             for(var i=0, max=inputs.length; i < max; i++) {
931                 var input = inputs[i];
932                 var n = input.name;
933                 if(n && !input.disabled && input.type == "image" && form.clk == input)
934                     a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
935             }
936         }
937         return a;
938     };
939     /**
940      * Form plugin functions end
941      */
942     
943 })(jQuery);