now adding / editng entries to cache list instead of debitList or creditList
[Bookkeeping.git] / webkell / javascript / bkeeping.js
blob20f00b4f2c23bb1223f4c5ee8a02c045d3b52316
1 /* bkeeping */
3 if (!window.console)
5         var console = {};
6         console.log = alert;
9 var BLOCK_PARSE_REGEX = /\$\{(\w+)\}/igm;
10 // hack for mock data testing:
11 var _LAST_PARSED_DICT = null;
12 String.prototype.parse_vars = function(dataDict)
14         _LAST_PARSED_DICT = dataDict;
15         return this.replace(BLOCK_PARSE_REGEX, function(match, param, offset, orig)
16         {
17                 if (!dataDict) { return ""; }
18                 return (dataDict[param] || (dataDict[param] == 0)) ? (dataDict[param]) : ("");
19         });
22 /**
23  * The "bind()" function extension from Prototype.js, extracted for general use
24  *
25  * @author Richard Harrison, http://www.pluggable.co.uk
26  * @author Sam Stephenson (Modified from Prototype Javascript framework)
27  * @license MIT-style license @see http://www.prototypejs.org/
28  */
29 Function.prototype.bind = function()
31     var _$A = function(a)
32     {
33         return Array.prototype.slice.call(a);
34     }
35     if ((arguments.length < 2) && (typeof arguments[0] == "undefined"))
36     {
37         return this;
38     }
39     var __method = this, args = _$A(arguments), object = args.shift();
40     return function()
41     {
42         return __method.apply(object, args.concat(_$A(arguments)));
43     }
46 /* settings */
47 var DEBUG = true;
48 var MOCK_DATA = false;
49 var MOCK_DATA_FAILURES_MODE = false;
50 /* ******** */
52 /* commands */
53 var LOGIN_COMMAND = "login (user -username ${username} -password ${password});";
54 var REGISTER_COMMAND = "var someVariable = add ( " +
55                                        "(load (`/system[ @id='main.system']`)) " +
56                                           "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}'>" +
57 "<allowedActions id='${username}.allowedActions' xmlns='com/interrupt/bookkeeping/cc/bkell/aauth' >" + 
58 "<command id='command.create' name='create' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
59 "<command id='command.add' name='add' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
60 "<command id='command.remove' name='remove' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
61 "<command id='command.reverse' name='reverse' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
62 "<command id='command.find' name='find' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
63 "<command id='command.load' name='load' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
64 "<command id='command.list' name='list' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
65 "<command id='command.print' name='print' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
66 "<command id='command.commit' name='commit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
67 "<command id='command.login' name='login' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
68 "<command id='command.logout' name='logout' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
69 "<command id='command.exit' name='exit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" + 
70 "</allowedActions>" + 
71                                             "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" + 
72                               "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" + 
73                               "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" + 
74                                           "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" + 
75                               "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" + 
76                               "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='defaultCurrency' name='defaultCurrency' value='${currency}' />" + 
77                             "</profileDetails>" + 
78                           "</user> , user -returninput true" + 
79                                         ") ; "; 
80 var GETPROFILE_COMMAND = "load (`/system[@id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']`);";
81 var UPDATEPROFILE_COMMAND = "var someVariable = update ( " +
82                                                      "(`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) " +
83                                            "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
84                                                    "<profileDetail name='first.name' value='${firstname}'/>" +
85                                                    "<profileDetail name='last.name' value='${lastname}'/>" +
86                                                    "<profileDetail name='email' value='${email}'/>" +
87                                                    "<profileDetail name='country' value='${country}'/>" +
88                                                 "</profileDetails>" +
89                                                 ");commit ((`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) @someVariable);";
91 var LISTACCOUNTS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`);";
92 var LISTJOURNALS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']`);";
93 var LISTENTRIES_COMMAND =  "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${journal}']/entries[@id='main.entries']`);";
94 var LISTENTRY_COMMAND =  "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${journal}']/entries[@id='main.entries']/entry[@id='${entry}']`);";
96 var LOADDEFAULTCURRENCY_COMMAND =  "load ( `/system[ @id='main.system' ]/aauthentication[ @id='main.authentication' ]/users[ @id='aauth.users' ]/user[ @id='${username}' ]/profileDetails[ @id='user.details' ]/profileDetail[ @id='defaultCurrency' ] `);";
99 /**** 
100  *              ACCOUNT Commands 
101  */
102 var ADDACCOUNT_COMMAND = "var addedAccount = add((load(`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`))" +
103                 "<account xmlns='com/interrupt/bookkeeping/account' type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\" currency='CDN' /> );"+ 
104                 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @addedAccount);"; 
106 var REMOVEACCOUNT_COMMAND = "var removedAccount = remove ((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) account -id ${aid});" +
107                 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @removedAccount);";
108 var UPDATEACCOUNT_COMMAND = "var updatedAccount = update((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']/account[@id='${id}']`)" + 
109                 "<account xmlns=\"com/interrupt/bookkeeping/account\" type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\"/>);" + 
110                 "commit( (`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']/account[@id='${id}']`) @updatedAccount);"; 
112 /**** 
113  *              JOURNAL Commands 
114  */
115 var ADDJOURNAL_COMMAND = "var addedJournal = add((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`)" +
116                 "<journal:journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'/>" +
117                 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @addedJournal);";
118 var REMOVEJOURNAL_COMMAND = "var removedJournal = remove ((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) journal -id ${jid});" +
119                 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @removedJournal);";
120 var UPDATEJOURNAL_COMMAND = "var updatedJournal = update((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}]`)" +
121                 "<journal xmlns=\"com/interrupt/bookkeeping/account\" id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'/>" +
122                 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}']`) @updatedJournal);";
125 /**** 
126  *              JOURNAL ENTRY Commands 
127  */
128 var ADDENTRY_COMMAND = "var addedEntry = add((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`)" +
129                 "<entry xmlns='com/interrupt/bookkeeping/journal' id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
130                         "${debitsANDcredits}" + 
131                   "</entry> ); " + 
132                 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`) @addedEntry);";
134 var REMOVEENTRY_COMMAND = "var removedEntry = remove ((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`) entry -id ${eid});" +
135                 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`) @removedEntry);";
137 var UPDATEENTRY_COMMAND = "var updatedEntry = update( (load(`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']/entry[@id='${id}']`))" +
138                 "<entry xmlns=\"com/interrupt/bookkeeping/journal\" id='${id}' entrynum='' state='' journalid='${jid}' date='${date}' currency='${cc}'>" +
139                         "${debitsANDcredits}" + 
140                 "</entry> );" + 
141                 "commit( (`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}']/entries[@id='main.entries']/entry[@id='${id}']`) @updatedEntry);";
142                 
143                 
144 /* ******** */
146 /* templates */
147 // acl_rows
148 var ACCOUNT_ROW = '<div id="${rowid}" class="acl_row row">' +
149                                         '<div class="edit_column left_column"><a href="javascript:bkeeping.editAccount(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
150                                         '<div class="name_column left_column">${aname}</div>' +
151                                         '<div class="category_column left_column">${category}</div>' +
152                                         '<div class="balance_column left_column">${balance}</div>' +
153                                         '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteAccount(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
154                                    '</div>';
155 // js_rows
156 var JOURNAL_ROW = '<div id="${rowid}" class="js_row row">' +
157                                         '<div class="edit_column left_column"><a href="javascript:bkeeping.editJournal(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
158                                         '<div class="large_name_column left_column"><a onclick="bkeeping.showJournal(\'${rowid}\')">${jname}</a></div>' +
159                                         '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteJournal(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
160                                  '</div>';
162 // j_rows
163 var ENTRY_ROW = '<div id="${rowid}" class="j_row row">' +
164                                         
165                                         '<div class="edit_column left_column"><a href="javascript:bkeeping.editEntry(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
166                                         
167                                         '<div class="date_column left_column" style="margin:0 50px;" >${date}</div>' +
168                                         '<div class="entry_column left_column" style="margin:0 50px;" >${entry}</div>' +
169                                         '<div class="balance_column left_column" style="margin:0 50px;" >${balance}</div>' +
170                                         
171                                         '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteEntry(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
172                                         
173                                   '</div>';
177 var TENTRY_ROW =        '<div id="jentry_rows" class="j_rows rows">' + 
178                                         
179                                         '       <div class="edit_column left_column"><a href="javascript:bkeeping.addEditEntryPart(\'${rowid}\', \'${eid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' + 
180                                         
181                                         '       <div style="float:left; width: 18%; margin-left: 90px; ">${dname}</div><div style="float:left; width: 20%; ">${damount}</div>' + 
182                                         '       <div style="float:left; width: 20%; ">${cname}</div><div style="float:left; width: 20%; ">${camount}</div>' + 
183                                         
184                                         '       <div class="delete_column left_column"><a href="javascript:bkeeping.zzz(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' + 
185                                         
186                                         '</div>'; 
187                                         
188                                         
189 var AE_ACCOUNT = ["Add/Edit Account", '<table class="formtable">' +
190                                                 '<tr>' +
191                                                         '<td>' +
192                                                                 'Name' +
193                                                         '</td>' +
194                                                         '<td>' +
195                                                                 '<input type="text" name="account_name" id="account_name" class="data_field" value="${aname}" />' +
196                                                         '</td>' +
197                                                 '</tr>' +
198                                                 '<tr>' +
199                                                         '<td>' +
200                                                                 'Type' +
201                                                         '</td>' +
202                                                         '<td>' +
203                                                                 '<select name="account_type" id="account_type" class="data_field">' +
204                                                                         '${accounts}'+
205                                                                 '</select>' +
206                                                         '</td>' +
207                                                 '</tr>' +
208                                         '</table>'];
210 var AE_JOURNAL = ["Add/Edit Journal", '<table class="formtable">' +
211                                                 '<tr>' +
212                                                         '<td>' +
213                                                                 'Name' +
214                                                         '</td>' +
215                                                         '<td>' +
216                                                                 '<input type="text" name="journal_name" id="journal_name" class="data_field" />' +
217                                                         '</td>' +
218                                                 '</tr>' +
219                                                 '<tr>' +
220                                                         '<td>' +
221                                                                 'Type' +
222                                                         '</td>' +
223                                                         '<td>' +
224                                                                 '<select name="journal_type" id="journal_type" class="data_field">' +
225                                                                         '<option>default</option>' +
226                                                                 '</select>' +
227                                                         '</td>' +
228                                                 '</tr>' +
229                                                 '<tr>' +
230                                                         '<td>' +
231                                                                 'Balance' +
232                                                         '</td>' +
233                                                         '<td>' +
234                                                                 '<input type="text" name="journal_balance" id="journal_balance" class="data_field" />' +
235                                                         '</td>' +
236                                                 '</tr>' +
237                                         '</table>']; 
239 var AE_ENTRY = ["Add/Edit Entry", '<table class="formtable">' + 
240                                                 '<input type="hidden" name="entryid" value="${entryid}" />' + 
241                                                 '<tr>' +
242                                                         '<td>' +
243                                                                 
244                                                                 '<span class="smallformtext">Currency:</span>&nbsp;' +
245 '<select name="entry_cc" id="entry_cc" class="data_field">' +
246         '<option value="CDN" ${curr_cdn} >Canadian Dollar</option>' +
247         '<option value="USD" ${curr_usd} >US Dollar</option>' +
248         '<option value="BP"  ${curr_bp} >British Pound</option>' +
249         '<option value="EUR" ${curr_eur} >European Euro</option>' +
250         '<option value="JPN" ${curr_jpn} >Japanese Yen</option>' +
251 '</select>' +
252                                                         '</td>' +
253                                                 '</tr>' +
254                                                 '<tr>' +
255                                                         '<td>' + 
256 '<span class="smallformtext">Amount:</span>&nbsp;<input type="text" size="6" name="entry_ecamount" id="entry_ecamount" class="data_field" value="${evalue}" >&nbsp;' +
257                                                         '</td>' +
258                                                 '</tr>' +
259                                                 '<tr>' +
260                                                         '<td>' + 
261                                                                 '<span class="smallformtext">Account:</span>&nbsp;' +
262                                                                 '<select name="entry_caid" id="entry_caid" class="data_field">' +
263                                                                         '${accounts}' +
264                                                                 '</select>' +
265                                                         '</td>' +
266                                                 '</tr>' +
267                                                 '<tr>' +
268                                                         '<td>' + 
269                                                                 '<span class="smallformtext">Type:</span>&nbsp;' +
270 '<select name="entry_ctype" id="entry_ctype" class="data_field">' +
271         '<option value="debit" ${entry_dtype} >debit</option><option value="credit" ${entry_ctype} >credit</option>' +
272 '</select>' +
273                                                         '</td>' +
274                                                 '</tr>' +
275                                         '</table>'];
278 /* ********* */
280 var DIALOG_HOLDER = "<div id='bkeeping_dialog' class='dialog'>Dialog default text.</div>";
281 var _DIALOG_FIRST = true;
283 // top namespace
284 var bkeeping = { 
285         
286         
287         // session
288         ACTIVE_SESSION: null,
289         DEFAULT_CURRENCY: null, 
290         
291         
292         //** initialise entry UI with debit & credit lists 
293         debitList: [], 
294         creditList: [], 
295         
296         cache:
297         {
298                 user: null,
299                 accounts: [],
300                 journals: [],
301                 entries: []
302         },
303         
304         /* logging */
305         debug: function(message)
306         {
307                 if (DEBUG)
308                 {
309                         if (window.console)
310                         {
311                                 console.log(message);
312                         }
313                         else
314                         {
315                                 alert(message);
316                         }
317                 }
318         },
320         log: function(message)
321         {
322                 if (window.console)
323                 {
324                         console.log(message);
325                 }
326                 else
327                 {
328                         // do nothing (yet)
329                 }
330         },
332         raise: function(message)
333         {
334                 alert(message);
335         },
336         /* ******* */
338         modalDialogScreen: function(showhide)
339         {
340                 var m = $("#modal_screen");
341                 if (showhide == "show")
342                 {
343                         m.css('display', 'block');
344                 }
345                 else
346                 {
347                         m.css('display', 'none');                       
348                 }
349         },
351         showModalDialog: function()
352         {
353                 bkeeping.modalDialogScreen("show");
354                 bkeeping.showDialog.apply(this, arguments);
355         },
357         showDialog: function()
358         {
359                 bkeeping.setDialogContent.apply(this, arguments);
360                 bkeeping.openDialog();
361         },
363         setDialogContent: function()
364         {
365                 var args = Array.prototype.slice.call(arguments);
366                 var content = args.shift();
367                 if (content instanceof Function)
368                 {
369                         $("#bkeeping_dialog").html(content.apply(this, args));
370                 }
371                 else
372                 {
373                         $("#bkeeping_dialog").html(content);                    
374                 }
375         },
377         openDialog: function()
378         {
379                 if (_DIALOG_FIRST)
380                 {
381                         _DIALOG_FIRST = false;
382                         $("#bkeeping_dialog").dialog({ close: bkeeping.modalDialogScreen.bind(this, "hide") });
383                 }
384                 else
385                 {
386                         $("#bkeeping_dialog").dialog("open");
387                 }
388         },
390         _a_open: true,
391         toggleAccountsOpen: function()
392         {
393                 var newleft = "0px";
394                 if (bkeeping._a_open)
395                 {
396                         bkeeping._a_open = false;
397                         newleft = "-373px";
398                 }
399                 else
400                 {
401                         bkeeping._a_open = true;
402                 }
403                 
404                 $("#accounts_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
405                 
406         },
408         _j_open: true,
409         toggleJournalsOpen: function()
410         {
411                 var newleft = "436px";
412                 if (bkeeping._j_open)
413                 {
414                         bkeeping._j_open = false;
415                         newleft = "809px";
416                 }
417                 else
418                 {
419                         bkeeping._j_open = true;
420                 }               
421                 $("#journals_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
422         },
424         /* state */
425         /* cookie functions: http://techpatterns.com/downloads/javascript_cookies.php */
426         setCookie: function(name, value, expires, path, domain, secure)
427         {
428                 // set time, it's in milliseconds
429                 var today = new Date();
430                 today.setTime(today.getTime());
432                 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
433                 path = path || "/";
435                 /*
436                     {expires} days
437                 */
438                 if (expires)
439                 {
440                         expires = expires * 1000 * 60 * 60 * 24;
441                 }
442                 var expires_date = new Date(today.getTime() + (expires));
443                 document.cookie = name + "=" + escape(value) +
444                         ((expires) ? ";expires=" + expires_date.toGMTString() : "") +
445                         ((path) ? ";path=" + path : "") +
446                         ((domain) ? ";domain=" + domain : "") +
447                         ((secure) ? ";secure" : "" );
448         },
450         getCookie: function(name)
451         {
452                 // first we'll split this cookie up into name/value pairs
453                 // note: document.cookie only returns name=value, not the other components
454                 var a_all_cookies = document.cookie.split(';');
455                 var a_temp_cookie = '';
456                 var cookie_name = '';
457                 var cookie_value = '';
458                 var b_cookie_found = false; // set boolean t/f default f
460                 for (var i = 0; i < a_all_cookies.length; i++)
461                 {
462                         // now we'll split apart each name=value pair
463                         a_temp_cookie = a_all_cookies[i].split('=');
465                         // and trim left/right whitespace while we're at it
466                         cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
468                         // if the extracted name matches passed name
469                         if (cookie_name == name)
470                         {
471                                 b_cookie_found = true;
472                                 // we need to handle case where cookie has no value but exists (no = sign, that is):
473                                 if (a_temp_cookie.length > 1)
474                                 {
475                                         cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ''));
476                                 }
477                                 // note that in cases where cookie is initialized but no value, null is returned
478                                 return cookie_value;
479                                 break;
480                         }
481                         a_temp_cookie = null;
482                         cookie_name = '';
483                 }
484                 if (!b_cookie_found)
485                 {
486                         return null;
487                 }
488         },
490         deleteCookie: function(name, path, domain)
491         {
492                 if (this.getCookie(name))
493                 {
494                         // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
495                         path = path || "/";
496                         document.cookie = name + "=" +
497                                 ((path) ? ";path=" + path : "") +
498                                 ((domain) ? ";domain=" + domain : "" ) +
499                                 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
500                 }
501         },
503         Session: function(sessionId, loggedInUserid, loggedInGroup)
504         {
505                 this.cookieName = "_bk_session_js";
506                 this.loaded = false;
507                 this.sessionId = sessionId || null;
508                 this.loggedInUserid = loggedInUserid || null;
509                 this.loggedInGroup = loggedInGroup || null;
511                 this.update = function(sessionId, loggedInUserid, loggedInGroup)
512                 {
513                         this.sessionId = sessionId || null;
514                         this.loggedInUserid = loggedInUserid || null;
515                         this.loggedInGroup = loggedInGroup || null;
516                         return this;
517                 }
519                 this.clear = function()
520                 {
521                         this.loaded = false;
522                         this.sessionId = null;
523                         this.loggedInUserid = null;
524                         this.loggedInGroup = null;
525                         bkeeping.deleteCookie(this.cookieName);
526                         return this;
527                 }
529                 function saveToSessionCookie()
530                 {
531                         bkeeping.setCookie(this.cookieName, this.sessionId + "," + this.loggedInUserid + "," + this.loggedInGroup);
532                         return this;
533                 }
534                 this.save = saveToSessionCookie.bind(this);
536                 function loadFromSessionCookie()
537                 {
538                         var cookievalues = bkeeping.getCookie(this.cookieName);
539                         if (cookievalues)
540                         {
541                                 var vals = cookievalues.split(',');
542                                 this.sessionId = vals[0];
543                                 this.loggedInUserid = vals[1];
544                                 this.loggedInGroup = vals[2];
545                                 return true;
546                         }
547                         return false;
548                 }
550                 if (!this.sessionId)
551                 {
552                         // try to load from cookie:
553                         this.loaded = loadFromSessionCookie.call(this); // true if there was a session cookie, false otherwise
554                 }
556                 return this;
557         },
558         /* ***** */
560         /* data objects */
561         User: function(root)
562         {
563                 this.root = root;
565                 this.getProfileDetailsList = function()
566                 {
567                         return this.root["children"][0]["profileDetails"]["children"];
568                 }
570                 this.getUserid = function()
571                 {
572                         return this.root["id"];
573                 }
575                 this.getUsername = function()
576                 {
577                         return this.root["username"];
578                 }
580                 this.getPassword = function()
581                 {
582                         return this.root["password"];
583                 }
585                 this.getFirstname = function()
586                 {
587                         return this.root["children"][0]["profileDetails"]["children"][0]["profileDetail"].value;
588                 }
590                 this.getLastname = function()
591                 {
592                         return this.root["children"][0]["profileDetails"]["children"][1]["profileDetail"].value;
593                 }
595                 this.getEmail = function()
596                 {
597                         return this.root["children"][0]["profileDetails"]["children"][2]["profileDetail"].value;
598                 }
600                 this.getCountry = function()
601                 {
602                         return this.root["children"][0]["profileDetails"]["children"][3]["profileDetail"].value;
603                 }
604         },
605         
606         UserSession: function(root)
607         {
608                 this.root = root;
609                 this.getSessionId =  function()
610                 {
611                         return this.root["id"];
612                 }
614                 this.getGroup = function()
615                 {
616                         return this.root["groupid"];
617                 }
619                 this.getUser = function()
620                 {
621                         return this.root["userid"];
622                 }
624                 return this;
625         },
626         
627         DefaultCurrency: function(root)
628         {
629                 this.root = root;
630                 this.getDefaultCurrency = this.lister.bind(this, "profileDetail", bkeeping.DefaultCurrency);
631                 this.getDefaultCurrencyRaw = this.rawlister.bind(this); 
632         },
633         
634         lister: function(nodename, _class)
635         {       
636                 if( this.root["children"] != null ) { 
637                         
638                         var c = this.root["children"];
639                         if (c.length > 0)
640                         {
641                                 var a = new Array();
642                                 for (var i = 0; i < c.length; i++) { 
643                                         
644                                         if( c[i][nodename] != undefined ) { 
645                                                 a.push(new _class(c[i][nodename])); 
646                                         }
647                                         else if( c[i]["debit"] != undefined ) { 
648                                                 a.push( new bkeeping.Subentry( c[i]["debit"], "debit" ) ); 
649                                         }
650                                         else if( c[i]["credit"] != undefined ) { 
651                                                 a.push( new bkeeping.Subentry( c[i]["credit"], "credit" ) ); 
652                                         }
653                                 }
654                                 return a;
655                         }
656                 }
657         }, 
658         
659         rawlister: function()
660         {
661                 return this.root["children"];
662         },
663         
664         Lister: function()
665         {
666                 this.lister = bkeeping.lister;
667                 this.rawlister = bkeeping.rawlister;
668         },
669         
670         AccountList: function(root)
671         {
672                 this.root = root;
673                 this.getAccounts = this.lister.bind(this, "account", bkeeping.Account);
674                 this.getAsList = this.getAccounts;
675                 this.getAccountsRaw = this.rawlister.bind(this);
676                 this.getAsRawList = this.getAccountsRaw;
677         },
678         
679         Account: function(root)
680         {
681                 this.root = root;
683                 this.getType = function()
684                 {
685                         return this.root['type'];
686                 }
688                 this.getId = function()
689                 {
690                         return this.root['id'];
691                 }
693                 this.getName = function()
694                 {
695                         return this.root['name'];
696                 }
698                 this.getCounterWeight = function()
699                 {
700                         return this.root['counterWeight'];
701                 },
703                 this.getBalance = function()
704                 {
705                         // XXX balance not in account structure, HELP
706                         return "xxxhelpxxx";
707                 }
708         },
709         
710         JournalList: function(root)
711         {
712                 this.root = root;
713                 this.getJournals = this.lister.bind(this, "journal", bkeeping.Journal);
714                 this.getAsList = this.getJournals;
715                 this.getJournalsRaw = this.rawlister.bind(this);
716                 this.getAsRawList = this.getJournalsRaw;
717         },
718         
719         Journal: function(root)
720         {
721                 this.root = root;
723                 this.getId = function()
724                 {
725                         return this.root["id"];
726                 }
728                 this.getName = function()
729                 {
730                         return this.root["name"];
731                 }
733                 this.getType = function()
734                 {
735                         return this.root["type"];
736                 }
738                 this.getBalance = function()
739                 {
740                         return this.root["balance"];
741                 }
742         },
744         EntryList: function(root)
745         {
746                 this.root = root;
747                 this.getEntries = this.lister.bind(this, "entry", bkeeping.Entry);
748                 this.getAsRawList = this.rawlister.bind(this);
749         },
751         Entry: function(root)
752         {
753                 this.root = root;
755                 this.getId = function()
756                 {
757                         return this.root["id"];
758                 }
760                 this.getEntrynum = function()
761                 {
762                         return this.root["entrynum"];
763                 }
765                 this.getState = function()
766                 {
767                         return this.root["state"];
768                 }
770                 this.getJournalid = function()
771                 {
772                         return this.root["journalid"];
773                 }
775                 this.getDate = function()
776                 {
777                         return this.root["date"];
778                 }
780                 this.getCurrency = function()
781                 {
782                         return this.root["currency"];
783                 }
785                 this.getSubentries = function(type, raw)
786                 {
787                         var d = this.root["children"];
788                         var a = new Array();
789                         for (var i = 0; i < d.length; i++)
790                         {
791                                 if (d[i][type])
792                                 {
793                                         if (raw)
794                                         {
795                                                 a.push(d[i]);
796                                         }
797                                         else
798                                         {
799                                                 a.push(new bkeeping.Subentry(d[i], type));
800                                         }
801                                 }
802                         }
803                         return a;
804                 }
806                 this.getDebits = function()
807                 {
808                         return this.getSubentries("debit");
809                 }
811                 this.getCredits = function()
812                 {
813                         return this.getSubentries("credit");
814                 }
816                 this.getDebitsRaw = function()
817                 {
818                         return this.getSubentries("debit", true);
819                 }
821                 this.getCreditsRaw = function()
822                 {
823                         return this.getSubentries("credit", true);
824                 }
825         },
827         Subentry: function(root, type)
828         {
829                 this.root = root;
830                 this.type = type;
831                 
832                 this.getId = function()
833                 {
834                         return this.root["id"];
835                 }
836                 
837                 this.getAmount = function()
838                 {
839                         return this.root["amount"];
840                 }
841                 
842                 this.getEntryid = function()
843                 {
844                         return this.root["entryid"];
845                 }
846                 
847                 this.getAccountid = function()
848                 {
849                         return this.root["accountid"];
850                 }
851                 
852                 this.getAccount = function()
853                 {
854                         return this.root["account"];
855                 }
856                 
857                 this.getCurrency = function()
858                 {
859                         return this.root["currency"];
860                 }
861         },
862         /* ************ */
864         /* commands */
865         login: function()
866         {
867                 var u = $("#u").val();
868                 var p = $("#p").val();
869                 var ret = new this.LoginCommand(u, p).run();
870         },
872         logout: function()
873         {
874                 this.ACTIVE_SESSION.clear();
875                 setTimeout('document.location.href = "index.html";', 2000);
876         },
878         register: function()
879         {
880                 var username = $("#username").val();
881                 var password = $("#password").val();
882                 var firstname = $("#firstname").val();
883                 var lastname = $("#lastname").val();
884                 var email = $("#email").val();
885                 var country = $("#country").val(); 
886                 var currency = $("#currency").val(); 
887                 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency).run();
888         },
890         editprofile: function()
891         {
892                 var username = $("#username").val();
893                 var password = $("#password").val();
894                 var firstname = $("#firstname").val();
895                 var lastname = $("#lastname").val();
896                 var email = $("#email").val();
897                 var country = $("#country").val(); 
898                 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency, true).run();              
899         },
901         deleteThing: function(thingtype, id)
902         {
903                 var goAhead = function(id)
904                 {
905                         $('#bkeeping_dialog').dialog("close");                  
906                         var ret = new this.RemoveCommand(thingtype, function(id) { alert(thingtype + " " + id + " removed"); }.bind(this, id), {'id': id}).run();
907                 }.bind(this, id);
908                 bkeeping.showDialog("Are you sure you want to delete " + thingtype + " " + id + "?");
909                 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "DELETE": goAhead });
910                 return true;            
911         },
913         addedEdited: function(jsonData)
914         {
915                 bkeeping.debug("addedEdited: returned jsonData:");
916                 bkeeping.debug(jsonData);
917                 alert('Recored added/edited.  Reloading UI to display updates.');
918                 var loc = document.location.href;
919                 if (loc.indexOf('?') != -1)
920                 {
921                         loc = loc.substring(0, loc.indexOf('?'));
922                 }
923                 if (!DEBUG)
924                 {
925                         document.location.href = loc + '?timestamp=' + new Date().getTime();
926                 }
927                 
928                 
929                 //** closing entry window  
930                 $("#entry_ui").css("display", "none"); 
931                 
932                 
933                 //** reloading journal window
934                 bkeeping.showJournal(bkeeping._displayed_journal); 
935                 
936         },
938         wrapFormAsObj: function()
939         {
940                 var pars = {};
941                 var fields = $(".data_field");
942                 for (var i = 0; i < fields.length; i++)
943                 {
944                         var field = fields[i];
945                         var n = field.id.substring(field.id.lastIndexOf('_') + 1);
946                         var v = $(field).val();
947                         bkeeping.debug('wrapFormAsObj: found ' + n + '/' + v);
948                         pars[n] = v;
949                 }
950                 return pars;
951         },
953         getAccountsAsOptionsList: function(atype)
954         {
955                 // we have to assume they're cached, coz this is a synchronous call
956                 var html = "";
957                 var accounts = bkeeping.cache.accounts;
958                 for (var i = 0; i < accounts.length; i++)
959                 {
960                         var id = accounts[i].account.id; 
961                         var nam = accounts[i].account.name; 
962                         var type = accounts[i].account.type; 
963                         if (id)
964                         {
965                                 if( atype == type ) { 
966                                         html += '<option value="' + id + '" selected="selected" >' + nam + '</option>'; 
967                                 }
968                                 else { 
969                                         html += '<option value="' + id + '">' + nam + '</option>'; 
970                                 }
971                         }
972                 }
973                 return html;
974         }, 
975         getAccountTypesAsOptionsList: function(atype)
976         {
977                 // we have to assume they're cached, coz this is a synchronous call 
978                 var html = ""; 
979                 var thingy = {  asset: '<option ${selected}>asset</option>', 
980                                                 expense: '<option ${selected}>expense</option>', 
981                                                 liability: '<option ${selected}>liability</option>', 
982                                                 revenue: '<option ${selected}>revenue</option>' 
983                                          }; 
984                 
985                 for( property in thingy ) { 
986                         
987                         if ( property == atype ) { 
988                                 
989                                 html += thingy[property].parse_vars( { selected:"selected='selected'" } ); 
990                         }
991                         else { 
992                                 
993                                 html += thingy[property].parse_vars(); 
994                         }
995                 }
996                 return html;
997         }, 
998         
999         getCounterWeight: function(type)
1000         {
1001                 switch (type)
1002                 {
1003                         case "asset":
1004                                 return "debit";
1005                         case "expense":
1006                                 return "debit";
1007                         case "liability":
1008                                 return "credit";
1009                         case "revenue":
1010                                 return "credit";
1011                         default:
1012                                 return "credit";
1013                 }
1014         },
1016         getCurrentDate: function()
1017         {
1018                 var d = new Date();
1019                 var day = d.getDate();
1020                 var month = d.getMonth() + 1;
1021                 var year = d.getFullYear();
1022                 if (day < 10) { day = '0' + day; }
1023                 if (month < 10) { month = '0' + month; }
1024                 return day + '' + month + '' + year;
1025         },
1026         
1027         getAccountNameForId: function(aid) { 
1028                 
1029                 var accounts = bkeeping.cache.accounts;
1030                 for (var i = 0; i < accounts.length; i++)
1031                 {
1032                         var id = accounts[i].account.id;
1033                         var nam = accounts[i].account.name;
1034                         if (id == aid ) 
1035                         {
1036                                 return nam;
1037                         }
1038                 }
1039                 return null; 
1040                 
1041         }, 
1042         
1043         goAccountPart: function(thingtype, id) 
1044         {
1045                 
1046                 var type = id ? this.EditCommand : this.AddCommand; 
1047                 var pars = this.wrapFormAsObj(); 
1048                 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"]; 
1049                 pars["id"] = id; 
1050                 
1051                 
1052                 if( (pars["type"] == "asset") || (pars["type"] == "expense") ) {        //** if asset or expense - debit 
1053                         pars["cw"] = "debit"; 
1054                 }
1055                 else {          //** otherwise - credit 
1056                         pars["cw"] = "credit"; 
1057                 }
1058                 
1059                 $('#bkeeping_dialog').dialog("close"); 
1060                 
1061                 
1062                 var ret = new type(thingtype, this.addedEdited, pars).run(); 
1063         }, 
1064         
1065         goJournalEntryPart: function(thingtype, id) 
1066         {
1067                         
1068                         var type = id ? this.EditCommand : this.AddCommand; 
1069                         var pars = this.wrapFormAsObj(); 
1070                         
1071                         //** put in data mapping
1072                         
1073                         
1074                         //** TODO - i) take ID and update entry or ii) put a new entry with ID of 'new' 
1075                         var entry = null; 
1076                         if( (id != null) && (id.length > 0) ) { 
1077                                 
1078                                 entry = bkeeping.findEntry(id); 
1079                         }
1080                         else if( bkeeping.findEntry("new") != null ) { 
1081                                 
1082                                 entry = bkeeping.findEntry("new"); 
1083                         }
1084                         else { 
1085                                 
1086                                 entry = new bkeeping.Entry(); 
1087                                 entry["children"] = []; 
1088                                 
1089                                 entry["id"] = "new"; 
1090                                 entry["currency"] = pars["cc"]; 
1091                                 entry["date"] = ""; 
1092                                 entry["entrynum"] = null; 
1093                                 entry["journalid"] = ""; 
1094                                 entry["state"] = null; 
1095                                 entry["xmlns"] = "com/interrupt/bookkeeping/journal"; 
1096                                 
1097                                 //bkeeping.cache.entries[ bkeeping.cache.entries.length ] =
1098                                 bkeeping.cache.entries.push( { entry: entry } ); 
1099                                 
1100                         }
1101                         
1102                         //** set the entry currency 
1103                         if( pars["cc"] == null ) { 
1104                                 if( entry != null ) { 
1105                                         pars["cc"] = entry["currency"]; 
1106                                 }
1107                         }
1108                         
1109                         var obj = null; 
1110                         if( pars["ctype"] == "debit" ) {
1111                                 
1112                                 var debit = {   
1113                                                 account: bkeeping.getAccountNameForId( pars["caid"] ), 
1114                                                 accountid: pars["caid"], 
1115                                                 amount: pars["ecamount"], 
1116                                                 currency: pars["cc"], 
1117                                                 entryid: "", 
1118                                                 id: id, 
1119                                                 xmlns: "com/interrupt/bookkeeping/account" 
1120                                 }
1121                                 obj = { 
1122                                         dname: bkeeping.getAccountNameForId( pars["caid"] ), 
1123                                         daid: pars["caid"], 
1124                                         damount: pars["ecamount"], 
1125                                         cname: "&nbsp;", 
1126                                         caid: "&nbsp;", 
1127                                         camount: "&nbsp;" 
1128                                 } 
1129                                 
1130                                 entry.children.push( { debit: debit } ); 
1131                                 
1132                         }
1133                         else { 
1134                                 
1135                                 var credit = {  
1136                                                 account: bkeeping.getAccountNameForId( pars["caid"] ), 
1137                                                 accountid: pars["caid"], 
1138                                                 amount: pars["ecamount"], 
1139                                                 currency: pars["cc"], 
1140                                                 entryid: "", 
1141                                                 id: id, 
1142                                                 xmlns: "com/interrupt/bookkeeping/account" 
1143                                 }
1144                                 obj = {
1145                                         dname: "&nbsp;",
1146                                         daid: "&nbsp;",
1147                                         damount: "&nbsp;",
1148                                         cname: bkeeping.getAccountNameForId( pars["caid"] ),
1149                                         caid: pars["caid"],
1150                                         camount: pars["ecamount"]
1151                                 } 
1152                                 
1153                                 
1154                                 entry.children.push( { credit: credit } ); 
1155                                 
1156                         }
1157                         
1158                         
1159                         var entryPart = TENTRY_ROW.parse_vars( obj ); 
1160                         var thing = $("#jentry_rows"); 
1161                         $("#jentry_rows").append( entryPart ); 
1162                         
1163                         $('#bkeeping_dialog').dialog("close"); 
1164                         
1165                         //var ret = new type(thingtype, this.addedEdited, pars).run(); 
1166                         
1167         }, 
1168         
1169         findAccountName: function(aid) { 
1170                 
1171                 var returnName = null; 
1172                 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) { 
1173                         
1174                         var eachAccount = bkeeping.cache.accounts[i]["account"]; 
1175                         if( eachAccount["id"] == aid ) { 
1176                                 
1177                                 returnName = eachAccount["name"]; 
1178                                 break; 
1179                         }
1180                 }
1181                 
1182                 return returnName; 
1183                 
1184         }, 
1185         findAccountType: function(aid) { 
1186                 
1187                 var returnType = null; 
1188                 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) { 
1189                         
1190                         var eachAccount = bkeeping.cache.accounts[i]["account"]; 
1191                         if( eachAccount["id"] == aid ) { 
1192                                 
1193                                 returnType = eachAccount["type"]; 
1194                                 break; 
1195                         }
1196                 }
1197                 
1198                 return returnType; 
1199                 
1200         }, 
1201         findEntry: function(eid) { 
1202                 
1203                 var entry = null; 
1204                 for( var i = 0; i < bkeeping.cache.entries.length; i++ ) { 
1205                         
1206                         var eachEntry = bkeeping.cache.entries[i]["entry"]; 
1207                         if( eachEntry["id"] == eid ) { 
1208                                 
1209                                 entry = eachEntry; 
1210                                 break; 
1211                         }
1212                 }
1213                 
1214                 return entry; 
1215                 
1216         }, 
1217         
1218         addEditJournal: function(thingtype, id, returnHandler) // id is null on adds
1219         {
1220                 
1221                 // raise correct form
1222                 var x = eval("AE_" + thingtype.toUpperCase());
1223                 var form = x[1]; 
1224                 
1225                 var accountName = bkeeping.findAccountName(id); 
1226                 var accountType = bkeeping.findAccountType(id); 
1227                 
1228                 // could be vars: 
1229                 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } ); 
1230                 bkeeping.showModalDialog(form); 
1231                 $('#bkeeping_dialog').dialog('option', 'title', x[0]); 
1232                 
1233                 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler }); 
1234                 
1235                 return true; 
1236         },
1237         
1238         addEditAccount: function(thingtype, id, returnHandler) // id is null on adds
1239         {
1240                 
1241                 // raise correct form
1242                 var x = eval("AE_" + thingtype.toUpperCase());
1243                 var form = x[1]; 
1244                 
1245                 var accountName = bkeeping.findAccountName(id); 
1246                 var accountType = bkeeping.findAccountType(id); 
1247                 
1248                 // could be vars: 
1249                 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } ); 
1250                 bkeeping.showModalDialog(form); 
1251                 $('#bkeeping_dialog').dialog('option', 'title', x[0]); 
1252                 
1253                 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler }); 
1254                 
1255                 return true; 
1256         },
1257         
1258         addEditEntry: function(thingtype, id, eid, returnHandler) // id is null on adds
1259         {
1260                 
1261                 // raise correct form 
1262                 var x = eval("AE_" + thingtype.toUpperCase()); 
1263                 var form = x[1]; 
1264                 var obj = {}; 
1265                 if( (eid != undefined) && (eid != null) ) { 
1266                         obj["entryid"] = eid
1267                 }
1268                 
1269                 var entryF = bkeeping.findEntry(eid); 
1270                 
1271                 // find entry part 
1272                 if( entryF != null ) { 
1273                 
1274                         var entryPartF = null; 
1275                         for( var i=0; i < entryF.children.length; i++ ) { 
1276                                 
1277                                 var eachC = entryF.children[i]; 
1278                                 var thing = null; 
1279                                 
1280                                 if( eachC["credit"] || eachC["debit"] ) { 
1281                                         
1282                                         if( eachC["credit"] ) { 
1283                                                 
1284                                                 obj["entry_ctype"] = " selected='selected' "; 
1285                                                 thing = eachC["credit"]; 
1286                                         }
1287                                         else if( eachC["debit"] ) { 
1288                                                 
1289                                                 obj["entry_dtype"] = " selected='selected' "; 
1290                                                 thing = eachC["debit"]; 
1291                                         }
1292                                         
1293                                         var accountType = bkeeping.findAccountType( thing["accountid"] ); 
1294                                         obj["accounts"] = bkeeping.getAccountsAsOptionsList(accountType); 
1295                                         obj["evalue"] = thing["amount"]; 
1296                                         
1297                                         if( thing["currency"] == "CDN" ) { 
1298                                                 obj["curr_cdn"] = " selected='selected' "; 
1299                                         }
1300                                         else if( thing["currency"] == "USD" ) { 
1301                                                 obj["curr_usd"] = " selected='selected' "; 
1302                                         }
1303                                         else if( thing["currency"] == "BP" ) { 
1304                                                 obj["curr_bp"] = " selected='selected' "; 
1305                                         }
1306                                         else if( thing["currency"] == "EUR" ) { 
1307                                                 obj["curr_eur"] = " selected='selected' "; 
1308                                         }
1309                                         else if( thing["currency"] == "JPN" ) { 
1310                                                 obj["curr_jpn"] = " selected='selected' "; 
1311                                         }
1312                                 
1313                                 }
1314                         
1315                         }
1316                 }
1317                 else { 
1318                         
1319                         obj["accounts"] = bkeeping.getAccountsAsOptionsList(null); 
1320                 }
1321                 
1322                 
1323                 // could be vars: 
1324                 form = form.parse_vars( obj ); 
1325                 
1326                 bkeeping.showModalDialog(form); 
1327                 $('#bkeeping_dialog').dialog('option', 'title', x[0]); 
1328                 
1329                 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler }); 
1330                 
1331                 return true; 
1332         },
1333         
1334         saveEntry: function(id) 
1335         {
1336                 
1337                 var type = id ? this.EditCommand : this.AddCommand;
1338                 var pars = this.wrapFormAsObj();
1339                 pars['date'] = bkeeping.getCurrentDate();
1340                 pars['id'] = id;
1341                 pars['jid'] = bkeeping._displayed_journal;
1342                 pars['username'] = bkeeping.ACTIVE_SESSION["loggedInUserid"]; 
1343                 
1344                 
1345                 //** TODO [done] 
1346                 //** 1. findEntry 'entryid' or 'new' 
1347                 var entry = null; 
1348                 if( id.length > 0 ) { 
1349                         
1350                         entry = bkeeping.findEntry(id); 
1351                 }
1352                 else { 
1353                         
1354                         entry = bkeeping.findEntry("new"); 
1355                         entry["id"] = ""; 
1356                 }
1357                 
1358                 if( entry != null ) { 
1359                         pars["cc"] = entry["currency"]; 
1360                 }
1361                 
1362                 
1363                 //** 2. ensure balances 
1364                 var dsumDEBIT = 0; 
1365                 var csumDEBIT = 0; 
1366                 
1367                 var dsumCREDIT = 0; 
1368                 var csumCREDIT = 0; 
1369                 
1370                 for( var i = 0; i < entry["children"].length; i++ ) {  //** sum up debits & credits 
1371                         
1372                         
1373                         //** on debit side ( asset / expense )
1374                         //** on credit side ( liability / revenue )
1375                         if( entry["children"][i]["credit"] ) { 
1376                                 
1377                                 var thing = entry["children"][i]["credit"]; 
1378                                 
1379                                 // check account type
1380                                 var atype = bkeeping.findAccountType( thing["accountid"] ); 
1381                                 if( atype == "asset" || atype == "expense" ) { 
1382                                         
1383                                         csumDEBIT += parseFloat( thing["amount"] ); 
1384                                 }
1385                                 else if( atype == "liability" || atype == "revenue" ) { 
1386                                         
1387                                         csumCREDIT += parseFloat( thing["amount"] ); 
1388                                 }
1389                                 
1390                         }
1391                         else if( entry["children"][i]["debit"] ) { 
1392                                 
1393                                 var thing = entry["children"][i]["debit"]; 
1394                                 
1395                                 // check account type
1396                                 var atype = bkeeping.findAccountType( thing["accountid"] ); 
1397                                 if( atype == "asset" || atype == "expense" ) { 
1398                                         
1399                                         dsumDEBIT += parseFloat( thing["amount"] ); 
1400                                 }
1401                                 else if( atype == "liability" || atype == "revenue" ) { 
1402                                         
1403                                         dsumCREDIT += parseFloat( thing["amount"] ); 
1404                                 }
1405                                 
1406                         }
1407                         
1408                 }
1409                 
1410                 
1411                 /**
1412                  * check that debits and credots are balanced 
1413                  */
1414                 var lhs = csumDEBIT + dsumCREDIT; 
1415                 var rhs = csumCREDIT + dsumDEBIT; 
1416                 if( lhs != rhs ) { 
1417                         
1418                         alert("debit and credits are not equal"); 
1419                         return false; 
1420                 }
1421                 
1422                 
1423                 /**
1424                  * 
1425                  * 3. build  debit and credit XML parts 
1426                  */
1427                 pars['debitsANDcredits'] = "";
1428                 for( var i = 0; i < entry["children"].length; i++ ) {  //** sum up debits & credits 
1429                         
1430                         
1431                         if( entry["children"][i]["credit"] ) { 
1432                                 
1433                                 var cthing = entry["children"][i]["credit"]; 
1434                                 var cthingXML = "<credit xmlns='com/interrupt/bookkeeping/account' id='"+ cthing["id"] +"' amount='"+ 
1435                                         cthing["amount"] +"' entryid='"+ cthing["entryid"] +"' accountid='"+ cthing["accountid"] +"' account='"+ 
1436                                         cthing["account"] +"' currency='"+ cthing["currency"] +"'/>"; 
1437                                 
1438                                 pars['debitsANDcredits'] += cthingXML; 
1439                                 
1440                         }
1441                         else if( entry["children"][i]["debit"] ) { 
1442                                 
1443                                 var dthing = entry["children"][i]["debit"]; 
1444                                 var dthingXML = "<debit xmlns='com/interrupt/bookkeeping/account' id='"+ dthing["id"] +"' amount='"+ 
1445                                         dthing["amount"] +"' entryid='"+ dthing["entryid"] +"' accountid='"+ dthing["accountid"] +"' account='"+ 
1446                                         dthing["account"] +"' currency='"+ dthing["currency"] +"'/>"; 
1447                                 
1448                                 pars['debitsANDcredits'] += dthingXML; 
1449                                 
1450                         }
1451                         
1452                 }
1453                 
1454                 
1455                 //** 4. refresh the entries list at the end of 'run'
1456                 var ret = new type("entry", this.addedEdited, pars).run(); 
1457                 
1458         }, 
1459         
1460         addAccount: function()
1461         {       
1462                 var returnHandler = bkeeping.goAccountPart.bind(this, "account", null); 
1463                 bkeeping.addEditAccount("account", null, returnHandler);
1464         },
1465         
1466         editAccount: function(aid)
1467         {
1468                 var returnHandler = bkeeping.goAccountPart.bind(this, "account", aid); 
1469                 bkeeping.addEditAccount("account", aid, returnHandler);
1470         },
1472         deleteAccount: function()
1473         {
1474                 
1475         },
1477         addJournal: function()
1478         {       
1479                 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "journal", null); 
1480                 return bkeeping.addEditJournal("journal", null, returnHandler);
1481         },
1482         
1483         editJournal: function(jid)
1484         {
1485                 
1486                 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "journal", jid); 
1487                 bkeeping.addEditJournal("journal", jid, returnHandler); 
1488                 
1489                 var a = new bkeeping.GetCommand("journal", jid, function(journalObj)
1490                 {
1491                         $("#journal_name").val(journalObj.getName());
1492                         $("#journal_type").val(journalObj.getType());
1493                         $("#journal_balance").val(journalObj.getBalance());
1494                 }).run();
1495         }, 
1497         deleteJournal: function()
1498         {
1499                 
1500         },
1501         
1502         addEntry: function()
1503         {
1504                 return bkeeping.showJournalEntry(null); 
1505         },
1506         
1507         addEditEntryPart: function(id, eid)
1508         { 
1509                 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "entry", null); 
1510                 bkeeping.addEditEntry("entry", id, eid, returnHandler); 
1511         }, 
1512         
1513         editEntry: function(eid)
1514         {
1515                 bkeeping.showJournalEntry( eid );
1516                 var a = new bkeeping.GetCommand("entry", eid, function(entryObj)
1517                 {
1518                         $("#entry_caid").val(entryObj.getCaid());
1519                         $("#entry_daid").val(entryObj.getDaid());
1520                         $("#entry_ecamount").val(entryObj.getEcamount());
1521                         $("#entry_edamount").val(entryObj.getEdamount());
1522                         $("#entry_cc").val(entryObj.getCurrency());
1523                 }).run();
1524         },
1525         
1526         deleteEntry: function()
1527         {
1528                 
1529         },
1530         
1531         _displayed_journal: null,
1532         showJournal: function(id)
1533         {
1534                 
1535                 //** clear previous content 
1536                 var jrows = document.getElementById( "j_rows" ); 
1537                 jrows["innerHTML"] = ""; 
1538                 
1539                 
1540                 //** show journal UI 
1541                 var journalUI = $("#journal_ui"); 
1542                 journalUI.css("display", "block"); 
1543                 
1544                 
1545                 bkeeping._displayed_journal = id;
1546                 $("#_journal_id").html(id);
1547                 
1548                 new bkeeping.ListCommand("entries", bkeeping.displayEntries, id).run();
1549         },
1550         
1551         showJournalEntry: function(id)
1552         {
1553                 
1554                 //** clear previous content 
1555                 var erows = document.getElementById( "jentry_rows" ); 
1556                 erows["innerHTML"] = ""; 
1557                 
1558                 
1559                 var eui = $("#entry_ui"); 
1560                 eui.css("display", "block"); 
1561                 
1562                 var eui2 = document.getElementById( "entry_ui" );       // trying to put the entryid in the template 
1563                 
1564                 eui2["innerHTML"].parse_vars = String.prototype.parse_vars; 
1565                 eui2["innerHTML"] = eui2["innerHTML"].parse_vars( { entryid: id } ); 
1566                 
1567                 
1568                 $("#_journalentry_id").html(id);
1569                 
1570                 if(id) { 
1571                         new bkeeping.ListCommand("entry", bkeeping.displayEntry, id).run(); 
1572                 }
1573         },
1574         
1575         getGenericFailureReason: function(jsonData)
1576         {
1577                 var r;
1578                 try 
1579                 {
1580                         r = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1581                 }
1582                 catch (e)
1583                 {
1584                         bkeeping.debug(e);
1585                         r = "Unknown.";
1586                 }
1587                 return r;
1588         },
1590         loadProfile: function(data)
1591         {
1592                 for (var i = 0; i < data.length; i++)
1593                 {
1594                         var o = data[i]["profileDetail"];
1595                         $("#" + o.id).val(o.value);
1596                 }
1597         },
1599         GetprofileCommand: function(callback)
1600         {
1601                 this.callback = callback;
1603                 function isSuccess(jsonData)
1604                 {
1605                         return (jsonData && jsonData["user"]);
1606                 }
1608                 var getFailureReason = bkeeping.getGenericFailureReason;
1610                 function handleResponse(jsonData, textStatus)
1611                 {
1612                         if (isSuccess(jsonData))
1613                         {
1614                                 var u = new bkeeping.User(jsonData["user"]);
1615                                 setTimeout(callback.bind(this, u.getProfileDetailsList()), 1000);
1616                         }
1617                         else
1618                         {
1619                                 var reason = getFailureReason(jsonData);
1620                                 alert("Failed to get profile.  Reason: " + reason);
1621                         }
1622                 }
1623                 
1624                 this.run = function()
1625                 {
1626                         bkeeping.send_bkexpr("getprofile", GETPROFILE_COMMAND.parse_vars({username:bkeeping.ACTIVE_SESSION.loggedInUserid}), handleResponse.bind(this));
1627                 }
1628         },
1630         RegisterCommand: function(username, password, firstname, lastname, email, country, currency, updating)
1631         {
1632                 this.username = username;
1633                 this.password = password;
1634                 this.firstname = firstname;
1635                 this.lastname = lastname;
1636                 this.email = email;
1637                 this.country = country; 
1638                 this.currency = currency; 
1639                 this.updating = updating;
1641                 function isSuccess(jsonData)
1642                 {
1643                         return (jsonData && jsonData["user"]);
1644                 }
1646                 var getFailureReason = bkeeping.getGenericFailureReason;
1648                 function handleResponse(jsonData, textStatus)
1649                 {
1650                         // on successful register, user is redirected to the home page and messaged about his ability to login or that his changes were saved
1651                         if (isSuccess(jsonData))
1652                         {
1653                                 var flag = "canlogin";
1654                                 if (this.updating)
1655                                 {
1656                                         flag = "changessaved";
1657                                 }
1658                                 setTimeout("document.location.href='accounts.html';", 0);
1659                         }
1660                         else
1661                         {
1662                                 // show a UI for registration failure:
1663                                 var reason = getFailureReason(jsonData);
1664                                 var verb = "register";
1665                                 if (this.updating)
1666                                 {
1667                                         verb = "save changes"
1668                                 }
1669                                 alert("Failed to " + verb + ".  Reason: " + reason);
1670                         }
1671                 }
1673                 this.run = function()
1674                 {
1675                         var command = REGISTER_COMMAND;
1676                         var cname = "register";
1677                         if (this.updating)
1678                         {
1679                                 command = UPDATEPROFILE_COMMAND;
1680                                 cname = "updateprofile";
1681                         } 
1682                         bkeeping.send_bkexpr(cname, command.parse_vars({username: this.username, password: this.password, firstname: this.firstname, lastname: this.lastname, email: this.email, country: this.country, currency: this.currency}), handleResponse.bind(this));
1683                 }
1684         },
1686         LoginCommand: function(username, password)
1687         {
1688                 this.username = username;
1689                 this.password = password;
1691                 function isSuccess(jsonData)
1692                 {
1693                         return (jsonData && jsonData["userSession"]);
1694                 }
1696                 var getFailureReason = bkeeping.getGenericFailureReason;
1698                 function handleResponse(jsonData, textStatus)
1699                 {
1700                         // on successful login, user is redirected to the accounts page (accounts.html)
1701                         if (isSuccess(jsonData))
1702                         {
1703                                 // set the session
1704                                 var us = new bkeeping.UserSession(jsonData["userSession"]);
1705                                 bkeeping.ACTIVE_SESSION.update(us.getSessionId(), us.getUser(), us.getGroup()).save();
1706                                 // go to the accounts page
1707                                 setTimeout("document.location.href='accounts.html';", 2000);
1708                         }
1709                         else
1710                         {
1711                                 // show a UI for login failure:
1712                                 var reason = getFailureReason(jsonData);
1713                                 alert("Failed to login.  Reason: " + reason);
1714                         }
1715                 }
1717                 this.run = function()
1718                 {
1719                         bkeeping.send_bkexpr("login", LOGIN_COMMAND.parse_vars({username: this.username, password: this.password}), handleResponse.bind(this));
1720                 }
1721         },
1723         displayAccounts: function(accountListObj)
1724         {
1725                 var as = accountListObj.getAccounts();
1726                 if( as ) { 
1727                         
1728                         var lister = $("#acl_rows");
1729                         var html = ""; 
1730                         for (var i = 0; i < as.length; i++)
1731                         {
1732                                 var a = as[i];
1733                                 if( a["root"] ) { 
1734                                         var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType(), balance: a.getBalance() };
1735                                         html += ACCOUNT_ROW.parse_vars(obj);
1736                                 }
1737                         }
1738                         lister.html(html); 
1739                 }
1740         },
1742         displayJournals: function(journalListObj)
1743         {
1744                 var js = journalListObj.getJournals();
1745                 if( js ) { 
1746                         var lister = $("#js_rows");
1747                         var html = ""; 
1748                         for (var i = 0; i < js.length; i++)
1749                         {
1750                                 var j = js[i];
1751                                 if( j["root"] ) { 
1752                                         var obj = { rowid: j.getId(), jname: j.getName() };
1753                                         html += JOURNAL_ROW.parse_vars(obj); 
1754                                 }
1755                         }
1756                         lister.html(html); 
1757                 }
1758         },
1759         
1760         //** display all the entries in a journal 
1761         displayEntries: function(entryListObj)
1762         {
1763                 var es = entryListObj.getEntries();
1764                 var lister = $("#j_rows");
1765                 var html = ""; 
1766                 if(es) { 
1767                         for (var i = 0; i < es.length; i++)
1768                         {
1769                                 var e = es[i];
1770                                 
1771                                 // ??? xxx not sure what data to put here
1772                                 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1773                                 
1774                                 if( e["root"] != undefined ) { 
1775                                         
1776                                         var obj = { 
1777                                                 rowid: e.getId(), 
1778                                                 date: (e.getDate() || "<none>"), 
1779                                                 entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), 
1780                                                 balance: ( "0.00" ) 
1781                                         };
1782                                         
1783                                         
1784                                         html += ENTRY_ROW.parse_vars(obj); 
1785                                 }
1786                                 
1787                         }
1788                 }
1789                 lister.html(html);
1790         },
1791         
1792         //** display the debit / credits in a single entry 
1793         displayEntry: function(entryListObj)
1794         {
1795                 var es = entryListObj.getEntries();
1796                 var lister = $("#jentry_rows");
1797                 var html = ""; 
1798                 if(es) { 
1799                         for (var i = 0; i < es.length; i++)
1800                         {
1801                                 var e = es[i]; 
1802                                 
1803                                 // ??? xxx not sure what data to put here 
1804                                 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1805                                 
1806                                 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: ( "0.00" ) };
1807                                 
1808                                 var obj = {}; 
1809                                 if( e["type"] == "debit" ) { 
1810                                         obj = { 
1811                                                 dname: e.getAccount(), 
1812                                                 damount: e.getAmount(), 
1813                                                 cname: "&nbsp;", 
1814                                                 camount: "&nbsp;" 
1815                                         }; 
1816                                 }
1817                                 else if( e["type"] == "credit" ) { 
1818                                         obj = {
1819                                                 dname: "&nbsp;", 
1820                                                 damount: "&nbsp;", 
1821                                                 cname: e.getAccount(), 
1822                                                 camount: e.getAmount() 
1823                                         }; 
1824                                 }
1825                                 
1826                                 obj["rowid"] = e.getId(); 
1827                                 obj["eid"] = entryListObj["root"]["id"]; 
1828                                 
1829                                 html += TENTRY_ROW.parse_vars(obj); 
1830                                 
1831                         }
1832                 }
1833                 lister.html(html);
1834         },
1835         
1836         objectIsOfType: function(obj, single_or_plural_type)
1837         {
1838                 var t = (single_or_plural_type.charAt(single_or_plural_type.length - 1) == 's') ? (single_or_plural_type.substring(0, single_or_plural_type.length - 1)) : single_or_plural_type;
1839                 if (t == 'entrie') { t = "entry"; }
1840                 if (obj && obj[t])
1841                 {
1842                         return true;
1843                 }
1844                 return false;
1845         },
1846         
1847         addToCacheAsList: function(plural_type, listObj)
1848         {
1849                 var list = listObj.getAsRawList();
1850                 bkeeping.debug('AddToCacheAsList: listing (' + plural_type + '/' + list.length + ')');
1851                 bkeeping.debug(list);
1852                 if (list && (list.length > 0))
1853                 {
1854                         for (var i = 0; i < list.length; i++)
1855                         {
1856                                 if (bkeeping.objectIsOfType(list[i], plural_type))
1857                                 {
1858                                         bkeeping.addToCache(plural_type, null, list[i]);
1859                                 }
1860                         }
1861                 }
1862         },
1864         addToCache: function(plural_type, id, jsonData)
1865         {
1866                 var found = false;
1867                 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
1868                 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1869                 {
1870                         var item = bkeeping.cache[plural_type][i];
1871                         bkeeping.debug('addToCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1872                         bkeeping.debug(item);
1873                         if (item && (item[single_type].id == id))
1874                         {
1875                                 // item could be changed, so re-add
1876                                 found = true;
1877                                 bkeeping.cache[plural_type][i] = jsonData;
1878                         }
1879                 }
1880                 if (!found)
1881                 {
1882                         bkeeping.debug('addToCache: pushing jsonData onto cache');
1883                         bkeeping.debug(jsonData);
1884                         bkeeping.cache[plural_type].push(jsonData);
1885                 }
1886         },
1888         getFromCache: function(plural_type, id)
1889         {
1890                 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
1891                 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1892                 {
1893                         var item = bkeeping.cache[plural_type][i];
1894                         bkeeping.debug('getFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1895                         bkeeping.debug(item);
1896                         if (item && (item[single_type].id == id))
1897                         {
1898                                 return item;
1899                         }
1900                 }
1901         },
1903         deleteFromCache: function(single_type, id)
1904         {
1905                 var plural_type = (single_type == 'entry') ? 'entries' : (single_type + 's');
1906                 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1907                 {
1908                         var item = bkeeping.cache[plural_type][i];
1909                         bkeeping.debug('deleteFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1910                         bkeeping.debug(item);
1911                         if (item && (item[single_type].id == id))
1912                         {
1913                                 delete bkeeping.cache[plural_type][i];
1914                                 break;
1915                         }
1916                 }               
1917         },
1919         GetCommand: function(calltype, id, callback)
1920         {
1921                 this.calltype = calltype;
1922                 this.calltype_list = (this.calltype == 'entry') ? 'entries' : (this.calltype + 's');
1923                 this.id = id;
1924                 this.callback = callback;
1925                 this.command = "";
1926                 this.type = "";
1927                 var u = bkeeping.ACTIVE_SESSION.loggedInUserid;
1928                 var g = (u == 'root') ? "webkell" : (u + '.group');
1929                 this.pars = {username: u, group: g, id: this.id};
1930                 switch (this.calltype)
1931                 {
1932                         case "account":
1933                                 this.command = GETACCOUNT_COMMAND;
1934                                 this.type = bkeeping.Account;
1935                                 break;
1936                         case "journal":
1937                                 this.command = GETJOURNAL_COMMAND;
1938                                 this.type = bkeeping.Journal;
1939                                 break;
1940                         case "entry":
1941                                 this.command = GETENTRY_COMMAND;
1942                                 this.type = bkeeping.Entry;
1943                                 this.pars['jid'] = bkeeping._displayed_journal;
1944                                 break;
1945                         default:
1946                                 alert("Unknown calltype: " + this.calltype);
1947                                 break;
1948                 }
1950                 function isSuccess(jsonData)
1951                 {
1952                         return (jsonData && (jsonData[this.calltype]) || jsonData[this.calltype_list]);
1953                 }
1955                 var getFailureReason = bkeeping.getGenericFailureReason;
1957                 function handleResponse(jsonData, textStatus, cached)
1958                 {
1959                         if (isSuccess.call(this, jsonData))
1960                         {
1961                                 if (!cached || (cached != 'true'))
1962                                 {
1963                                         bkeeping.addToCache(this.calltype_list, this.id, jsonData);
1964                                 }
1965                                 return this.callback(new this.type(jsonData[this.calltype]));
1966                         }
1967                         else
1968                         {
1969                                 // show a UI for login failure:
1970                                 var reason = getFailureReason(jsonData);
1971                                 alert("Failed to get list " + this.calltype + ".  Reason: " + reason);
1972                         }
1973                 }
1975                 this.run = function()
1976                 {
1977                         var item = bkeeping.getFromCache(this.calltype_list, this.id);
1978                         if (item)
1979                         {
1980                                 bkeeping.debug('handling call type ' + this.calltype + '/' + this.id + ' from cache');
1981                                 handleResponse.call(this, item, 'success', 'true');
1982                         }
1983                         else
1984                         {
1985                                 bkeeping.debug('handling call type ' + this.calltype + ' from ajax');
1986                                 var t = "get" + this.calltype;
1987                                 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
1988                         }
1989                 }
1990         },
1991         
1992         
1993         setDefaultCurrency: function(profileDetailObj)
1994         {
1995                 
1996                 bkeeping.DEFAULT_CURRENCY = profileDetailObj["root"]["value"]; 
1997                 
1998         }, 
1999         
2000         ListCommand: function(listtype, callback, optJournalid)
2001         {
2002                 this.listtype = listtype;
2003                 this.callback = callback;
2004                 this.command = "";
2005                 this.type = "";
2006                 this.pars = { username: bkeeping.ACTIVE_SESSION.loggedInUserid }; 
2007                 switch (this.listtype)
2008                 {
2009                         case "accounts":
2010                                 this.command = LISTACCOUNTS_COMMAND;
2011                                 this.type = bkeeping.AccountList;
2012                                 break;
2013                         case "journals":
2014                                 this.command = LISTJOURNALS_COMMAND; 
2015                                 this.type = bkeeping.JournalList; 
2016                                 this.pars["journal"] = optJournalid; 
2017                                 break;
2018                         case "entries": 
2019                                 this.command = LISTENTRIES_COMMAND; 
2020                                 this.type = bkeeping.EntryList; 
2021                                 this.pars["journal"] = bkeeping["_displayed_journal"]; 
2022                                 break; 
2023                         case "entry": 
2024                                 this.command = LISTENTRY_COMMAND; 
2025                                 this.type = bkeeping.EntryList; 
2026                                 this.pars["journal"] = bkeeping["_displayed_journal"]; 
2027                                 this.pars["entry"] = optJournalid; 
2028                                 break; 
2029                         default:
2030                                 alert("Unknown list: " + this.listtype); 
2031                                 break; 
2032                 }
2033                 
2034                 function isSuccess(jsonData)
2035                 {
2036                         return (jsonData && jsonData[this.listtype]);
2037                 }
2039                 var getFailureReason = bkeeping.getGenericFailureReason;
2040                 
2041                 
2042                 function handleResponse(jsonData, textStatus, cached)
2043                 {
2044                         if (cached || isSuccess.call(this, jsonData))
2045                         {
2046                                 var r;
2047                                 if (cached && (cached == 'true'))
2048                                 {
2049                                         r = new this.type({ "children" : jsonData });
2050                                 }
2051                                 else
2052                                 {
2053                                         r = new this.type(jsonData[this.listtype]);
2054                                         bkeeping.debug("Add to cache as list in ListCommand: " + this.listtype);
2055                                         bkeeping.debug(r);
2056                                         bkeeping.addToCacheAsList(this.listtype, r);
2057                                 }
2058                                 return this.callback(r);
2059                         }
2060                         else
2061                         {
2062                                 // show a UI for login failure:
2063                                 var reason = getFailureReason(jsonData);
2064                                 alert("Failed to get list " + this.listtype + ".  Reason: " + reason);
2065                         }
2066                 }
2068                 this.run = function()
2069                 {
2070                         if ( (bkeeping.cache[this.listtype] != undefined) && (bkeeping.cache[this.listtype].length > 0) )
2071                         {
2072                                 bkeeping.debug('handling list type ' + this.listtype + ' from CACHE');
2073                                 handleResponse.call(this, bkeeping.cache[this.listtype], 'success', 'true');
2074                         }
2075                         else
2076                         {
2077                                 bkeeping.debug('handling list type ' + this.listtype + ' from AJAX');
2078                                 var t = "list" + this.listtype;
2079                                 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2080                         }
2081                 }
2082                 
2083         }, 
2084         
2085         LoadCommand: function(listtype, callback, optJournalid)
2086         {
2087                 this.listtype = listtype;
2088                 this.callback = callback;
2089                 this.command = "";
2090                 this.type = "";
2091                 this.pars = {username: bkeeping.ACTIVE_SESSION.loggedInUserid};
2092                 switch (this.listtype)
2093                 {
2094                         case "profileDetail":
2095                                 this.command = LOADDEFAULTCURRENCY_COMMAND;
2096                                 this.type = bkeeping.DefaultCurrency;
2097                                 break;
2098                         default:
2099                                 alert("Unknown list: " + this.listtype);
2100                                 break;
2101                 }
2103                 function isSuccess(jsonData)
2104                 {
2105                         return (jsonData && jsonData[this.listtype]);
2106                 }
2108                 var getFailureReason = bkeeping.getGenericFailureReason;
2110                 function handleResponse(jsonData, textStatus)
2111                 {
2112                         if (isSuccess.call(this, jsonData))
2113                         {
2114                                 this.callback(new this.type(jsonData[this.listtype])); 
2115                         }
2116                         else
2117                         { 
2118                                 // show a UI for login failure: 
2119                                 var reason = getFailureReason(jsonData);
2120                                 alert("Failed to get list " + this.listtype + ".  Reason: " + reason);
2121                         }
2122                 }
2124                 this.run = function()
2125                 {
2126                         var t = "list" + this.listtype;
2127                         bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2128                 }
2129         },
2130         
2131         
2132         /* pars is an object containing the data to be added */
2133         AddCommand: function(type, callback, pars)
2134         {
2135                 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2136                 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2137                 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2138                 this.type = type;
2139                 this.callback = callback;
2140                 this.command = "";
2141                 this.pars = pars;
2142                 switch (this.type)
2143                 {
2144                         case "account":
2145                                 this.command = ADDACCOUNT_COMMAND; 
2146                                 
2147                                 this.type = "accounts";         // special case where what's returned will be an 'accounts' 
2148                                 break;
2149                         case "journals":
2150                                 this.command = ADDJOURNAL_COMMAND;
2151                                 break;
2152                         case "entry":
2153                                 this.command = ADDENTRY_COMMAND;
2154                                 break;
2155                         default:
2156                                 alert("Unknown add: " + this.type);
2157                                 break;
2158                 }
2160                 function isSuccess(jsonData)
2161                 {
2162                         return (jsonData && jsonData[this.type]);
2163                 }
2165                 var getFailureReason = bkeeping.getGenericFailureReason;
2167                 function handleResponse(jsonData, textStatus)
2168                 {
2169                         if (isSuccess.call(this, jsonData))
2170                         {
2171                                 return this.callback(jsonData);
2172                         }
2173                         else
2174                         {
2175                                 // show a UI for login failure:
2176                                 var reason = getFailureReason(jsonData);
2177                                 alert("Failed to add " + this.type + ".  Reason: " + reason);
2178                         }
2179                 }
2181                 this.run = function()
2182                 {
2183                         bkeeping.send_bkexpr("add" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2184                 }
2185         },
2187         RemoveCommand: function(type, callback)
2188         {
2189                 // pars for account: ${aid}
2190                 // pars for journal: ${jid}
2191                 // pars for entry: ${eid}
2192                 this.type = type;
2193                 this.callback = callback;
2194                 this.command = "";
2195                 switch (this.type)
2196                 {
2197                         case "account":
2198                                 this.command = REMOVEACCOUNT_COMMAND;
2199                                 break;
2200                         case "journals":
2201                                 this.command = REMOVEJOURNAL_COMMAND;
2202                                 break;
2203                         case "entries":
2204                                 this.command = REMOVEENTRY_COMMAND;
2205                                 break;
2206                         default:
2207                                 alert("Unknown remove: " + this.type);
2208                                 break;
2209                 }
2211                 function isSuccess(jsonData)
2212                 {
2213                         return (jsonData && jsonData[this.type]);
2214                 }
2216                 var getFailureReason = bkeeping.getGenericFailureReason;
2218                 function handleResponse(jsonData, textStatus)
2219                 {
2220                         if (isSuccess.call(this, jsonData))
2221                         {
2222                                 return this.callback(jsonData);
2223                         }
2224                         else
2225                         {
2226                                 // show a UI for login failure:
2227                                 var reason = getFailureReason(jsonData);
2228                                 alert("Failed to add " + this.type + ".  Reason: " + reason);
2229                         }
2230                 }
2232                 this.run = function()
2233                 {
2234                         bkeeping.send_bkexpr("remove" + this.type, this.command.parse_vars({}), handleResponse.bind(this));
2235                 }
2236         },
2238         /* pars is an object containing the data to be added */
2239         EditCommand: function(type, callback, pars)
2240         {
2241                 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2242                 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2243                 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2244                 this.type = type;
2245                 this.callback = callback;
2246                 this.command = "";
2247                 this.pars = pars;
2248                 switch (this.type)
2249                 {
2250                         case "account":
2251                                 this.command = UPDATEACCOUNT_COMMAND;
2252                                 break;
2253                         case "journals":
2254                                 this.command = EDITJOURNAL_COMMAND;
2255                                 break;
2256                         case "entry":
2257                                 this.command = UPDATEENTRY_COMMAND;
2258                                 break;
2259                         default:
2260                                 alert("Unknown edit: " + this.type);
2261                                 break;
2262                 }
2264                 function isSuccess(jsonData)
2265                 {
2266                         return (jsonData && jsonData[this.type]);
2267                 }
2269                 var getFailureReason = bkeeping.getGenericFailureReason;
2271                 function handleResponse(jsonData, textStatus)
2272                 {
2273                         if (isSuccess.call(this, jsonData))
2274                         {
2275                                 return this.callback(jsonData);
2276                         }
2277                         else
2278                         {
2279                                 // show a UI for login failure:
2280                                 var reason = getFailureReason(jsonData);
2281                                 alert("Failed to add " + this.type + ".  Reason: " + reason);
2282                         }
2283                 }
2285                 this.run = function()
2286                 {
2287                         bkeeping.send_bkexpr("edit" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2288                 }
2289         },
2290         /* ******** */
2292         /* ajax */
2293         error: function(xmlHttpRequest, textStatus, errorThrown)
2294         {
2295                 bkeeping.raise('There was a problem with the server request: ' + textStatus + '/' + errorThrown);
2296         },
2298         success: function(data, textStatus)
2299         {
2300                 bkeeping.log('genericSuccess');
2301         },
2303         complete: function(xmlHttRequest, textStatus)
2304         {
2305                 bkeeping.log('genericComplete');
2306         },
2308         // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2309         xmlRecurse: function(node, context)
2310         {
2311                 // squeeze the attributes and add to the context
2312                 var attributes = node.attributes;
2313                 if (attributes)
2314                 {
2315                         for (var i = 0; i < attributes.length; i++)
2316                         {
2317                                 var n = attributes[i].name;
2318                                 var v = attributes[i].nodeValue;
2319                                 context[n] = v;
2320                         }
2321                 }
2322                 // recurse:
2323                 var children = node.childNodes;
2324                 if (children)
2325                 {
2326                         if (children.length > 0)
2327                         {
2328                                 if ((children.length == 1) && (children[0].nodeType == 3)) // 3 is text node
2329                                 {
2330                                         context["value"] = children[0].textContent;
2331                                 }
2332                                 else
2333                                 {
2334                                         if (!context["children"])
2335                                         {
2336                                                 context["children"] = [];
2337                                         }
2338                                         for (var j = 0; j < children.length; j++)
2339                                         {
2340                                                 var p = {};
2341                                                 p[children[j].nodeName] = {};
2342                                                 context["children"].push(p);
2343                                                 this.xmlRecurse(children[j], p[children[j].nodeName]);
2344                                         }
2345                                 }
2346                         }
2347                 }
2348                 return context;
2349         },
2351         // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2352         xmlToJson: function(data, type)
2353         {
2354                 if (!data)
2355                 {
2356                         return "";
2357                 }
2358                 var xmlDoc;
2359                 var json = {};
2360                 try //Internet Explorer
2361                 {
2362                         xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
2363                         xmlDoc.async = "false";
2364                         xmlDoc.loadXML(data);
2365                 }
2366                 catch(e)
2367                 {
2368                         var parser = new DOMParser();
2369                         //xmlDoc = parser.parseFromString(data, "text/xml");
2370                         xmlDoc = data;
2371                 }
2373                 // convert to json (simplified greatly)
2374                 // we'll take advantage of the fact that all the data being passed around is linked to attributes
2375                 // and thus we'll go ahead and make our json like this:
2376                 // <name1 attr1=val attr2=val...>
2377                 //   <name2 attr3=val attr4=val...>
2378                 //   <name2 attr5=val attr6=val...>
2379                 //   <name3 attr7=val attr8=val...>
2380                 // </name1>
2381                 //
2382                 // becomes this:
2383                 // { 
2384                 //   "name1":
2385                 //   {
2386                 //      "attr1": val,
2387                 //      "attr2": val,
2388                 //      "value": null,  // special value -- the text node child of this ele, exclusive with children
2389                 //      "children":
2390                 //      [
2391                 //         {
2392                 //            "name2":
2393                 //            {
2394                 //               "attr3": val,
2395                 //               "attr4": val
2396                 //            }
2397                 //         },
2398                 //         {
2399                 //            "name2":
2400                 //            {
2401                 //               "attr5": val,
2402                 //               "attr6": val
2403                 //            }
2404                 //         },
2405                 //         {
2406                 //            "name3":
2407                 //            {
2408                 //               "attr7": val,
2409                 //               "attr8": val
2410                 //            }
2411                 //         }
2412                 //      ]
2413                 //   }
2414                 // }
2415                 var root = xmlDoc.firstChild;
2416                 json[root.nodeName] = {};
2417                 //json[root.nodeName] = this.xmlRecurse(root, json[root.nodeName]);
2418                 json[root.nodeName] = bkeeping.xmlRecurse(root, json[root.nodeName]);
2419                 return json;
2420         },
2422                                         zzz: function (xhr, ajaxOptions, thrownError) {
2423                                                         alert(xhr.status);
2424                                                         alert(thrownError);
2425                                         }, 
2426         send_bkexpr: function(bkexprname, expr, callback, optErrorCallback, optCompleteCallback)
2427         {
2428                 if ( MOCK_DATA == "true" || MOCK_DATA == true )
2429                 {
2430                         var r;
2431                         if (MOCK_DATA_FAILURES_MODE)
2432                         {
2433                                 r = MOCK_DATA["failures"][bkexprname];
2434                         }
2435                         else
2436                         {
2437                                 r = MOCK_DATA["successes"][bkexprname];
2438                         }
2439                         var response = r.response.parse_vars(_LAST_PARSED_DICT);
2440                         var type = r.type;
2441                         var textStatus = r.textStatus;
2442                         var sanitizedData = this.xmlToJson(response, type);
2443                         callback(sanitizedData, textStatus);
2444                 }
2445                 else
2446                 {
2447                         var url = "/webkell/webkell";
2448                         if (bkexprname == "register")
2449                         {
2450                                 url = "/webkell/authenticate";
2451                         }
2452                         $.ajax(
2453                                 {
2454                                         success: callback || bkeeping.success,
2455                                         error: optErrorCallback || bkeeping.error,
2456                     complete: optCompleteCallback || bkeeping.complete,
2457                                         data: {"bkexpr": expr},
2458                                         dataFilter: this.xmlToJson,
2459                                         dataType: "xml",
2460                                         type: "POST",
2461                                         url: url
2462                                 }
2463                         );
2464                 }
2465         },
2466         /* **** */
2468         /* initialization */
2469         displayLoginBlurb: function()
2470         {
2471                 $("#loggedinnote").css('display', "block");
2472                 var template = $("#loggedinnote").html();
2473                 $("#loggedinnote").html(template.parse_vars({"name": bkeeping.ACTIVE_SESSION.loggedInUserid}));
2474                 try
2475                 {
2476                         $("#loginholder").css("visibility", "hidden");
2477                 }
2478                 catch(e)
2479                 {
2480                         // element not there
2481                 }
2482         },
2484         page_loader_init: function()
2485         {
2486                 if (window.page_loader)
2487                 {
2488                         page_loader(); // defined in e.g. accounts.html, or any other UI page
2489                 }
2490         },
2492         page_init: function()
2493         {
2494                 var path = document.location.href.match(/^http:\/\/[^\/]+\/(.*)$/);
2495                 if (path)
2496                 {
2497                         path = path[1];
2498                         // cut off querystring and hash
2499                         if (path.indexOf("?") > -1)
2500                         {
2501                                 path = path.substring(0, path.indexOf("?"));
2502                         }
2503                         if (path.indexOf("#") > -1)
2504                         {
2505                                 path = path.substring(0, path.indexOf("#"));
2506                         }
2507                         // if it's under webkell, remove that:
2508                         if (path.indexOf("webkell") == 0)
2509                         {
2510                                 path = path.substring(8);
2511                         }
2512                 }
2513                 else
2514                 {
2515                         path = "home";
2516                 }
2518                 if ((path == "") || (path == "/") || (path == "index.html"))
2519                 {
2520                         path = "home";
2521                 }
2523                 switch (path)
2524                 {
2525                         case "home":
2526                                 // this is not exactly efficient, but we'll do this til we're on a proper server-side template engine
2527                                 var mode = document.location.href.substring(document.location.href.indexOf("mode=") + 5).match(/^(\w+)(.*)$/);
2528                                 if (!mode)
2529                                 {
2530                                         mode = "home";
2531                                 }
2532                                 else
2533                                 {
2534                                         mode = mode[1];
2535                                 }
2536                                 $("#contentbox").load(mode + ".html");
2537                                 if (mode == "userprofile")
2538                                 {
2539                                         var u = new bkeeping.GetprofileCommand(bkeeping.loadProfile).run();
2540                                 }
2541                                 break;;
2542                         case "accounts.html":
2543                                 // do nothing, this UI is generated dynamically
2544                                 break;;
2545                         default:
2546                                 bkeeping.debug("Not a known page: " + path);
2547                                 break;;
2548                 }
2549                 if (bkeeping.ACTIVE_SESSION.loaded)
2550                 {
2551                         bkeeping.displayLoginBlurb();
2552                 }
2554                 var flag = document.location.href.indexOf("flag=");
2555                 if (flag != -1)
2556                 {
2557                         var value = document.location.href.substring(flag);
2558                         if (value.indexOf("canlogin") != -1)
2559                         {
2560                                 bkeeping.showDialog("You have registered successfully.  You can now log in.")
2561                         }
2562                         if (value.indexOf("changessaved") != -1)
2563                         {
2564                                 bkeeping.showDialog("Your changes have been saved.");
2565                         }
2566                 }
2567         },
2569         init: function()
2570         {
2571                 bkeeping.ACTIVE_SESSION = new bkeeping.Session(); // load from cookie if present
2572                 if (!$("#bkeeping_dialog").length)
2573                 {
2574                         $("body").append(DIALOG_HOLDER);
2575                 }
2576                 //bkeeping.page_init(); // initializations related to the page we're on
2577                 //bkeeping.page_loader_init(); // initializations defined on the page we're on
2578         },
2580         unload: function()
2581         {
2582                 
2583         }
2584         /* ************ */
2587 /* prototypes */
2588 bkeeping.AccountList.prototype = new bkeeping.Lister(); 
2589 bkeeping.JournalList.prototype = new bkeeping.Lister(); 
2590 bkeeping.EntryList.prototype = new bkeeping.Lister(); 
2592 bkeeping.DefaultCurrency.prototype = new bkeeping.Lister(); 
2595 /* ********** */
2597 $(document).ready(bkeeping.init);
2598 $(window).unload(bkeeping.unload);
2600 var MOCK_DATA =
2602         successes:
2603         {
2604                 login:
2605                 {
2606                         response: "<userSession xmlns='com/interrupt/bookkeeping/users' id='c4e2e5344b91c6c1-1c82792712270d305b2-7ffd' groupid='${username}' userid='${username}' />",
2607                         type: "xml",
2608                         textStatus: "OK"
2609                 },
2611                 register:
2612                 {
2613                         response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2614                                                 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2615                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2616                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2617                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2618                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2619                                                 "</profileDetails>" +
2620                                           "</user>",
2621                         type: "xml",
2622                         textStatus: "OK"
2623                 },
2625                 getprofile:
2626                 {
2627                         response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2628                                                 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2629                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='ed'/>" +
2630                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='foo'/>" +
2631                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='Eddie'/>" +
2632                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='Abrams'/>" +
2633                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='ed@abra.ms'/>" +
2634                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='Canada'/>" +
2635                                                 "</profileDetails>" +
2636                                           "</user>",
2637                         type: "xml",
2638                         textStatus: "OK"
2639                 },
2641                 updateprofile:
2642                 {
2643                         response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2644                                                 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2645                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='${username}'/>" +
2646                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='${password}'/>" +
2647                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2648                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2649                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2650                                                   "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2651                                                 "</profileDetails>" +
2652                                           "</user>",
2653                         type: "xml",
2654                         textStatus: "OK"
2655                 },
2657                 listaccounts:
2658                 {
2659                         response: "<accounts id='main.accounts'>" +
2660                                                 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
2661                                                 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
2662                                           "</accounts>",
2663                         type: "xml",
2664                         textStatus: "OK"
2665                 },
2667                 listjournals:
2668                 {
2669                         response: "<journals xmlns:journal='com/interrupt/bookkeeping/journal' id='main.journals'>" +
2670                                                 "<journal id='generalledger' name='generalledger' type='' balance=''>" +
2671                                                         "<entries id='main.entries'>" +
2672                                                                 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2673                                                                         "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2674                                                                         "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2675                                                                 "</entry>" +
2676                                                         "</entries>" +
2677                                                 "</journal>" +
2678                                                 "<journal id='otherledger' name='otherledger' type='' balance=''>" +
2679                                                         "<entries id='main.entries'>" +
2680                                                                 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2681                                                                         "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2682                                                                         "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2683                                                                 "</entry>" +
2684                                                         "</entries>" +
2685                                                 "</journal>" +
2686                                         "</journals>",
2687                         type: "xml",
2688                         textStatus: "OK"                        
2689                 },
2691                 listentries:
2692                 {
2693                         response: "<entries id='main.entries'>" +
2694                                                 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2695                                                         "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2696                                                         "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2697                                                 "</entry>" +
2698                                           "</entries>",
2699                         type: "xml",
2700                         textStatus: "OK"
2701                 },
2703                 addaccount:
2704                 {
2705                         response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acn}' currency='${cc}'/>",
2706                         type: "xml",
2707                         textStatus: "OK"
2708                 },
2710                 removeaccount:
2711                 {
2712                         response: "",
2713                         type: "xml",
2714                         textStatus: "OK"
2715                 },
2717                 editaccount:
2718                 {
2719                         response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acw}' currency='${cc}'/>",
2720                         type: "xml",
2721                         textStatus: "OK"
2722                 },
2724                 addjournal:
2725                 {
2726                         response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2727 //                                                      "<entries id='main.entries'>" +
2728 //                                                              "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2729 //                                                                      "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2730 //                                                                      "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2731 //                                                              "</entry>" +
2732 //                                                      "</entries>" +
2733                                           "</journal>",
2734                         type: "xml",
2735                         textStatus: "OK"
2736                 },
2738                 removejournal:
2739                 {
2740                         response: "",
2741                         type: "xml",
2742                         textStatus: "OK"
2743                 },
2745                 editjournal:
2746                 {
2747                         response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2748 //                                                      "<entries id='main.entries'>" +
2749 //                                                              "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2750 //                                                                      "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2751 //                                                                      "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2752 //                                                              "</entry>" +
2753 //                                                      "</entries>" +
2754                                           "</journal>",
2755                         type: "xml",
2756                         textStatus: "OK"
2757                 },
2759                 addentry:
2760                 {
2761                         response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2762                                                         "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2763                                                         "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2764                                           "</entry>",
2765                         type: "xml",
2766                         textStatus: "OK"
2767                 },
2769                 removeentry:
2770                 {
2771                         response: "",
2772                         type: "xml",
2773                         textStatus: "OK"
2774                 },
2776                 editentry:
2777                 {
2778                         response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2779                                                         "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2780                                                         "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2781                                           "</entry>",
2782                         type: "xml",
2783                         textStatus: "OK"
2784                 }
2785         },
2786         failures:
2787         {
2788                 login:
2789                 {
2790                         response: "<logs>" +
2791                                                 "<log level=''>" +
2792                                                         "<logMessages>" +
2793                                                                 "<logMessage>Failed to login.</logMessage>" +
2794                                                         "</logMessages>" +
2795                                                 "</log>" +
2796                                           "</logs>",
2797                         type: "xml",
2798                         textStatus: "OK"
2799                 },
2801                 register:
2802                 {
2803                         response: "<logs>" +
2804                                                 "<log level=''>" +
2805                                                         "<logMessages>" +
2806                                                                 "<logMessage>Failed to register.</logMessage>" +
2807                                                         "</logMessages>" +
2808                                                 "</log>" +
2809                                           "</logs>",
2810                         type: "xml",
2811                         textStatus: "OK"
2812                 },
2814                 getprofile:
2815                 {
2816                         response: "<logs>" +
2817                                                 "<log level=''>" +
2818                                                         "<logMessages>" +
2819                                                                 "<logMessage>Failed to get user profile.</logMessage>" +
2820                                                         "</logMessages>" +
2821                                                 "</log>" +
2822                                           "</logs>",
2823                         type: "xml",
2824                         textStatus: "OK"
2825                 },
2827                 updateprofile:
2828                 {
2829                         response: "<logs>" +
2830                                                 "<log level=''>" +
2831                                                         "<logMessages>" +
2832                                                                 "<logMessage>Failed to update user profile.</logMessage>" +
2833                                                         "</logMessages>" +
2834                                                 "</log>" +
2835                                           "</logs>",
2836                         type: "xml",
2837                         textStatus: "OK"
2838                 },
2840                 listaccounts:
2841                 {
2842                         response: "<logs>" +
2843                                                 "<log level=''>" +
2844                                                         "<logMessages>" +
2845                                                                 "<logMessage>Failed to get accounts list.</logMessage>" +
2846                                                         "</logMessages>" +
2847                                                 "</log>" +
2848                                           "</logs>",
2849                         type: "xml",
2850                         textStatus: "OK"                        
2851                 },
2853                 listjournals:
2854                 {
2855                         response: "<logs>" +
2856                                                 "<log level=''>" +
2857                                                         "<logMessages>" +
2858                                                                 "<logMessage>Failed to get journals list.</logMessage>" +
2859                                                         "</logMessages>" +
2860                                                 "</log>" +
2861                                           "</logs>",
2862                         type: "xml",
2863                         textStatus: "OK"                        
2864                 },
2866                 listentries:
2867                 {
2868                         response: "<logs>" +
2869                                                 "<log level=''>" +
2870                                                         "<logMessages>" +
2871                                                                 "<logMessage>Failed to get entries list.</logMessage>" +
2872                                                         "</logMessages>" +
2873                                                 "</log>" +
2874                                           "</logs>",
2875                         type: "xml",
2876                         textStatus: "OK"                        
2877                 },
2879                 addaccount:
2880                 {
2881                         response: "<logs>" +
2882                                                 "<log level=''>" +
2883                                                         "<logMessages>" +
2884                                                                 "<logMessage>Failed to add account.</logMessage>" +
2885                                                         "</logMessages>" +
2886                                                 "</log>" +
2887                                           "</logs>",
2888                         type: "xml",
2889                         textStatus: "OK"
2890                 },
2892                 removeaccount:
2893                 {
2894                         response: "<logs>" +
2895                                                 "<log level=''>" +
2896                                                         "<logMessages>" +
2897                                                                 "<logMessage>Failed to remove account.</logMessage>" +
2898                                                         "</logMessages>" +
2899                                                 "</log>" +
2900                                           "</logs>",
2901                         type: "xml",
2902                         textStatus: "OK"
2903                 },
2905                 editaccount:
2906                 {
2907                         response: "<logs>" +
2908                                                 "<log level=''>" +
2909                                                         "<logMessages>" +
2910                                                                 "<logMessage>Failed to edit account.</logMessage>" +
2911                                                         "</logMessages>" +
2912                                                 "</log>" +
2913                                           "</logs>",
2914                         type: "xml",
2915                         textStatus: "OK"
2916                 },
2918                 addjournal:
2919                 {
2920                         response: "<logs>" +
2921                                                 "<log level=''>" +
2922                                                         "<logMessages>" +
2923                                                                 "<logMessage>Failed to add journal.</logMessage>" +
2924                                                         "</logMessages>" +
2925                                                 "</log>" +
2926                                           "</logs>",
2927                         type: "xml",
2928                         textStatus: "OK"
2929                 },
2931                 removejournal:
2932                 {
2933                         response: "<logs>" +
2934                                                 "<log level=''>" +
2935                                                         "<logMessages>" +
2936                                                                 "<logMessage>Failed to remove journal.</logMessage>" +
2937                                                         "</logMessages>" +
2938                                                 "</log>" +
2939                                           "</logs>",
2940                         type: "xml",
2941                         textStatus: "OK"
2942                 },
2944                 editjournal:
2945                 {
2946                         response: "<logs>" +
2947                                                 "<log level=''>" +
2948                                                         "<logMessages>" +
2949                                                                 "<logMessage>Failed to edit journal.</logMessage>" +
2950                                                         "</logMessages>" +
2951                                                 "</log>" +
2952                                           "</logs>",
2953                         type: "xml",
2954                         textStatus: "OK"
2955                 },
2957                 addentry:
2958                 {
2959                         response: "<logs>" +
2960                                                 "<log level=''>" +
2961                                                         "<logMessages>" +
2962                                                                 "<logMessage>Failed to add entry.</logMessage>" +
2963                                                         "</logMessages>" +
2964                                                 "</log>" +
2965                                           "</logs>",
2966                         type: "xml",
2967                         textStatus: "OK"
2968                 },
2970                 removeentry:
2971                 {
2972                         response: "<logs>" +
2973                                                 "<log level=''>" +
2974                                                         "<logMessages>" +
2975                                                                 "<logMessage>Failed to remove entry.</logMessage>" +
2976                                                         "</logMessages>" +
2977                                                 "</log>" +
2978                                           "</logs>",
2979                         type: "xml",
2980                         textStatus: "OK"
2981                 },
2983                 editentry:
2984                 {
2985                         response: "<logs>" +
2986                                                 "<log level=''>" +
2987                                                         "<logMessages>" +
2988                                                                 "<logMessage>Failed to edit entry.</logMessage>" +
2989                                                         "</logMessages>" +
2990                                                 "</log>" +
2991                                           "</logs>",
2992                         type: "xml",
2993                         textStatus: "OK"
2994                 }
2995         }
2998 var _TEST_AC = "<accounts id='main.accounts'>" +
2999                                                 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
3000                                                 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
3001                                           "</accounts>";
3002 var _TEST_XML = "<bookkeeping xmlns='com/interrupt/bookkeeping' xmlns:account='com/interrupt/bookkeeping/account' xmlns:journal='com/interrupt/bookkeeping/journal' xmlns:currency='com/interrupt/bookkeeping/currency' id='main.bookkeeping'>" +
3003                                         "<currency:currencies id='main.currencies' default='CDN'>" +
3004                                                 "<currency:currency id='CDN' name='Canadian Dollar'/>" +
3005                                                 "<currency:currency id='USD' name='US Dollar'/>" +
3006                                                 "<currency:currency id='BP' name='British Pound'/>" +
3007                                                 "<currency:currency id='EUR' name='Euoropean Euro'/>" +
3008                                                 "<currency:currency id='JPN' name='Japanese Yen'/>" +
3009                                         "</currency:currencies>" +
3010                                     "<accounts xmlns='com/interrupt/bookkeeping/account' id='main.accounts'>" +
3011                                             "<account xmlns='com/interrupt/bookkeeping/account' type='asset' id='' name='' counterWeight='debit'/>" +
3012                                         "</accounts>" +
3013                                         "<journal:journals id='main.journals'>" +
3014                                                 "<journal:journal id='generalledger' name='generalledger' type='' balance=''>" +
3015                                                         "<journal:entries id='main.entries'>" +
3016                                                                 "<journal:entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
3017                                                                           "<debit xmlns='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
3018                                                                           "<credit xmlns='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
3019                                                                 "</journal:entry>" +
3020                                                         "</journal:entries>" +
3021                                                 "</journal:journal>" +
3022                                         "</journal:journals>" +
3023                                 "</bookkeeping>";