]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Emails/javascript/EmailUICompose.js
Release 6.5.11
[Github/sugarcrm.git] / modules / Emails / javascript / EmailUICompose.js
1 /*********************************************************************************
2  * SugarCRM Community Edition is a customer relationship management program developed by
3  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Affero General Public License version 3 as published by the
7  * Free Software Foundation with the addition of the following permission added
8  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
9  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
10  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
11  * 
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
15  * details.
16  * 
17  * You should have received a copy of the GNU Affero General Public License along with
18  * this program; if not, see http://www.gnu.org/licenses or write to the Free
19  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA.
21  * 
22  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
23  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
24  * 
25  * The interactive user interfaces in modified source and object code versions
26  * of this program must display Appropriate Legal Notices, as required under
27  * Section 5 of the GNU Affero General Public License version 3.
28  * 
29  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
30  * these Appropriate Legal Notices must retain the display of the "Powered by
31  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
32  * technical reasons, the Appropriate Legal Notices must display the words
33  * "Powered by SugarCRM".
34  ********************************************************************************/
35
36  (function() {
37         var sw = YAHOO.SUGAR,
38                 Event = YAHOO.util.Event,
39                 Connect = YAHOO.util.Connect,
40             Dom = YAHOO.util.Dom
41             SE = SUGAR.email2;
42
43 ///////////////////////////////////////////////////////////////////////////////
44 ////    ADDRESS BOOK
45 SE.addressBook = {
46     _contactCache : new Array(), // cache of contacts
47     _dd : new Array(), // filtered list, same format as _contactCache
48     _ddLists : new Array(), // list of Lists
49     _dd_mlUsed : new Array(), // contacts in mailing list edit view column1
50     _dd_mlAvailable : new Array(), // contacts in mailing list edit view column2
51     clickBubble : true, // hack to get around onclick event bubbling
52         relatedBeanId : '',
53         relatedBeanType : '',
54         idx : 0,
55
56     itemSpacing : 'white-space:nowrap; padding:2px;',
57     reGUID : SE.reGUID,
58
59
60
61     /**
62     *  YUI bug fix 2527707.  Causes nested datatable's in <tables> to cause 404 errors whens earching.
63     */
64     initFixForDatatableSort: function () {
65         //Workaround for YUI bug 2527707: http://yuilibrary.com/projects/yui2/ticket/913efafad48ce433199f3e72e4847b18, should be removed when YUI 2.8+ is used
66         YAHOO.widget.DataTable.prototype.getColumn = function(column) {
67             var oColumn = this._oColumnSet.getColumn(column);
68
69             if(!oColumn) {
70                 // Validate TD element
71                 var elCell = column.nodeName.toLowerCase() != "th" ? this.getTdEl(column) : false;
72                 if(elCell) {
73                     oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
74                 }
75                 // Validate TH element
76                 else {
77                     elCell = this.getThEl(column);
78                     if(elCell) {
79                         // Find by TH el ID
80                         var allColumns = this._oColumnSet.flat;
81                         for(var i=0, len=allColumns.length; i<len; i++) {
82                             if(allColumns[i].getThEl().id === elCell.id) {
83                                 oColumn = allColumns[i];
84                             }
85                         }
86                     }
87                 }
88             }
89
90             return oColumn;
91         };
92     },
93
94     cancelEdit : function() {
95         if(this.editContactDialog)
96             this.editContactDialog.hide();
97         if(this.editMailingListDialog)
98             this.editMailingListDialog.hide();
99     },
100
101     /**
102      * Clears filter form
103      */
104     clear : function() {
105         var t = document.getElementById('contactsFilter');
106         t.value = '';
107         this.filter(t);
108     },
109
110     /**
111      * handle context-menu Compose-to call
112      * @param string type 'contacts' or 'lists'
113      */
114     composeTo : function(type, waited) {
115         var activePanel = SUGAR.email2.innerLayout.get("activeTab").get("id")
116         if (activePanel.substring(0, 10) != "composeTab") {
117             SE.composeLayout.c0_composeNewEmail();
118             setTimeout("SE.addressBook.composeTo('" + type + "', true);");
119                 SE.contextMenus.contactsContextMenu.hide();
120             return;
121         }
122         var idx = activePanel.substring(10);
123         var rows = [ ];
124         var id = '';
125         // determine if we have a selection to work with
126         if(type == 'contacts') {
127             var ids = SE.contactView.getSelectedRows();
128             for (var i in ids) {
129                 rows[i] = SE.contactView.getRecord(ids[i]);
130             }
131             removeHiddenNodes(rows, SE.contactView);
132         }
133                 else { return; }
134
135         if(rows.length > 0) {
136             SE.composeLayout.handleDrop(
137                 (type == 'contacts') ? SE.contactView : SE.emailListsView,
138                 null, rows, 'addressTO' + idx );
139         } else {
140             alert(app_strings.LBL_EMAIL_MENU_MAKE_SELECTION);
141         }
142     },
143
144     editContact : function() {
145         SE.contextMenus.contactsContextMenu.hide();
146         var element = SE.contactView.getSelectedNodes()[0];
147         var elementId = "";
148         if (element.className.indexOf('address-contact') > -1) {
149             elementId = element.id;
150         } else if (element.className.indexOf('address-exp-contact') > -1) {
151             elementId = element.id.substring(2);
152         }
153     },
154
155
156     /**
157      * Filters contact entries based on user input
158      */
159     filter : function(inputEl) {
160         var ret = new Object();
161         var re = new RegExp(inputEl.value, "gi");
162
163         for(var i in this._contactCache) {
164             if(this._contactCache[i].name.match(re)) {
165                 ret[i] = this._contactCache[i];
166             }
167         }
168
169         this.buildContactList(ret);
170     },
171
172     fullForm : function(id, module) {
173         document.location = "index.php?return_module=Emails&return_action=index&module=" + module + "&action=EditView&record=" + id;
174     },
175
176     /**
177      * returns a formatted email address from the addressBook cache
178      */
179     getFormattedAddress : function(id) {
180         var o = this._contactCache[id];
181         var primaryEmail = '';
182
183         for(var i=0; i<o.email.length; i++) {
184             var currentEmail = o.email[i].email_address;
185
186             if(o.email[i].primary_address == 1) {
187                 primaryEmail = o.email[i].email_address;
188             }
189         }
190
191         var finalEmail = (primaryEmail == "") ? currentEmail : primaryEmail;
192         var name = new String(o.name);
193         var finalName = name.replace(/(<([^>]+)>)/ig, "").replace(/&#039;/gi,'\'');
194         var ret = finalName + " <" + finalEmail.replace(/&#039;/gi,'\'') + ">";
195
196         return ret;
197     },
198
199     /**
200      * Sets up async call to query for matching contacts, users, etc.
201      */
202     searchContacts : function() {
203         var fn = document.getElementById('input_searchField').value;
204         var pe = document.getElementById('input_searchPerson').value;
205
206         var rb = document.getElementById('hasRelatedBean').checked;
207         if (rb) {
208                         var idx = this.idx;
209                 var relatedBeanId = document.getElementById('data_parent_id' + idx).value;
210                 var relatedBeanType = document.getElementById('data_parent_type' + idx).value;
211                 this.addressBookDataModel.params['related_bean_id'] = relatedBeanId;
212                 this.addressBookDataModel.params['related_bean_type'] = relatedBeanType;
213         } else {
214                 this.addressBookDataModel.params['related_bean_id'] = '';
215         }
216
217         this.addressBookDataModel.params['search_field'] = fn;
218         this.addressBookDataModel.params['person'] = pe;
219         this.addressBookDataModel.params['emailUIAction'] = 'getAddressSearchResults';
220         this.grid._oDataSource = this.addressBookDataModel;
221         this.grid.getDataSource().sendRequest(SUGAR.util.paramsToUrl(this.addressBookDataModel.params),  this.grid.onDataReturnInitializeTable, this.grid);
222     },
223
224     /**
225      * Clear Search Crieteria For Addressbook
226      */
227     clearAddressBookSearch : function() {
228         document.getElementById('input_searchField').value = "";
229         document.getElementById('input_searchPerson').selectedIndex = 0;
230     },
231
232     /**
233      * Opens modal select window to add contacts to addressbook
234      */
235     selectContactsDialogue : function(destId) {
236         if(!this.contactsDialogue) {
237                 var dlg = this.contactsDialogue = new YAHOO.widget.Dialog("contactsDialogue", {
238                 modal:true,
239                 visible:false,
240                 draggable: false,
241                 constraintoviewport: true,
242                 width   : 980,
243                 buttons : [{text: app_strings.LBL_EMAIL_ADDRESS_BOOK_ADD, isDefault: true, handler: this.populateEmailAddressFieldsFromResultTable},
244                            {text: app_strings.LBL_EMAIL_ADDRESS_BOOK_CLEAR, isDefault: true, handler: this.clearAllEmailAddressFieldsFromResultTable} ]
245             });
246                 dlg.setHeader(app_strings.LBL_EMAIL_ADDRESS_BOOK_SELECT_TITLE);
247
248                 var body = SUGAR.util.getAndRemove("contactsDialogueHTML");
249                 dlg.setBody(body.innerHTML);
250                 dlg.renderEvent.subscribe(function() {
251                 var iev = YAHOO.util.Dom.get("contactsDialogueBody");
252                 if (iev && !SUGAR.isIE) {
253                         this.body.style.width = "950px";
254                 }
255             }, dlg);
256
257
258                 dlg.beforeRenderEvent.subscribe(function() {
259                         var dd = new YAHOO.util.DDProxy(dlg.element);
260                         dd.setHandleElId(dlg.header);
261                         dd.on('endDragEvent', function() {
262                                 dlg.show();
263                         });
264                 }, dlg, true);
265                 dlg.render();
266
267                 var tp = new YAHOO.widget.TabView("contactsSearchTabs");
268
269                 var tabContent = SUGAR.util.getAndRemove("searchForm");
270                 tp.addTab(new YAHOO.widget.Tab({
271                                 label: app_strings.LBL_EMAIL_ADDRESS_BOOK_TITLE,
272                                 scroll : true,
273                                 content : tabContent.innerHTML,
274                                 id : "addressSearchTab",
275                                 active : true
276                         }));
277
278                 var addListenerFields = ['input_searchPerson','input_searchField' ]
279                 YAHOO.util.Event.addListener(addListenerFields,"keydown", function(e){
280                         if (e.keyCode == 13) {
281                                 YAHOO.util.Event.stopEvent(e);
282                                 SUGAR.email2.addressBook.searchContacts();
283                         }
284                 });
285
286                 this.contactsDialogue.render();
287                 dlg.center();
288         }
289         //Quick Compose does not have an innerLayout component and will always be referenced with ix 0.
290         if (typeof(SUGAR.email2.innerLayout) == 'undefined')
291             var idx = 0;
292         else
293         {
294             var activePanel = SUGAR.email2.innerLayout.get("activeTab").get("id");
295             var idx = activePanel.substring(10);
296         }
297         SE.addressBook.idx = idx;
298
299                 var relatedBeanId;
300         if ((hasRelatedBeanId = document.getElementById('data_parent_id' + idx).value) != '') {
301                 document.getElementById('relatedBeanColumn').style.display = '';
302                 var relatedBeanName = document.getElementById('data_parent_name' + idx).value;
303                         var relatedBeanType = document.getElementById('data_parent_type' + idx).value;
304                         relatedBeanId = document.getElementById('data_parent_id' + idx).value;
305                         document.getElementById('relatedBeanInfo').innerHTML = ' ' + relatedBeanType + ' <b>' + relatedBeanName + '</b>';
306                         SE.addressBook.relatedBeanType = relatedBeanType;
307             } else {
308                 document.getElementById('relatedBeanColumn').style.display = 'none';
309                 document.getElementById('hasRelatedBean').checked = false;
310             }
311
312             if (!SE.addressBook.grid)
313             {
314                 if (hasRelatedBeanId) {
315                         document.getElementById('hasRelatedBean').checked = true;
316                 }
317                 AddressSearchGridInit();
318                         SE.addressBook.relatedBeanId = relatedBeanId;
319             }
320             else
321             {
322                 if (typeof(relatedBeanId) != 'undefined' && relatedBeanId != SE.addressBook.relatedBeanId)
323                 {
324                         SE.addressBook.relatedBeanId = relatedBeanId;
325                         document.getElementById('hasRelatedBean').checked = true;
326                 }
327                 if (document.getElementById('hasRelatedBean').checked == true)
328                 {
329                         SE.addressBook.addressBookDataModel.params['related_bean_id'] = relatedBeanId;
330                         SE.addressBook.addressBookDataModel.params['related_bean_type'] = relatedBeanType;
331                 } else {
332                         SE.addressBook.addressBookDataModel.params['related_bean_id'] = '';
333                         SE.addressBook.addressBookDataModel.params['related_bean_type'] = '';
334                 }
335                 SE.addressBook.addressBookDataModel.params['search_field'] = document.getElementById('input_searchField').value;;
336                         SE.addressBook.addressBookDataModel.params['person'] = document.getElementById('input_searchPerson').value;
337                 SE.addressBook.grid.getDataSource().sendRequest(SUGAR.util.paramsToUrl(SE.addressBook.addressBookDataModel.params),  SE.addressBook.grid.onDataReturnInitializeTable, SE.addressBook.grid);
338             }
339
340             //Remove any lingering rows in the result set table if the module was closed.
341             SE.addressBook.gridResults.deleteRows(0, SUGAR.email2.addressBook.gridResults.getRecordSet().getLength());
342             //Repopulate
343             SE.addressBook.populateResulstTableEmailAddresses();
344
345         this.contactsDialogue.show();
346     },
347     /**
348     *  Clear all email addresses from result table.
349     *
350     */
351     clearAllEmailAddressFieldsFromResultTable: function () {
352         SUGAR.email2.addressBook.gridResults.deleteRows(0, SUGAR.email2.addressBook.gridResults.getRecordSet().getLength());
353         //Unhighlight any rows currently selected if the emails were cleared.
354         SUGAR.email2.addressBook.grid.toggleSelectAll(false);
355         SUGAR.email2.addressBook.grid.reSelectRowsOnRender();
356     },
357     /**
358     *  Take all email address listed in the compose tab To|Cc|Bcc fields and re-populates the
359     *  results table.  This function is called when the address book is displayed.
360     */
361     populateResulstTableEmailAddresses: function () {
362
363         var idx = SE.addressBook.idx;
364         var emailFields = ['to','cc','bcc'];
365
366         for(var k=0;k<emailFields.length;k++)
367         {
368             var elKey = 'address' + emailFields[k].toUpperCase() + idx;
369             var allEmails = document.getElementById(elKey).value;
370             if(allEmails == '')
371                 continue;
372
373             var formatedEmails = SE.composeLayout._getEmailArrayFromString(allEmails);
374
375                 for (var i=0; i<formatedEmails.length; i++)
376                 {
377                     var t_name = formatedEmails[i].name;
378                     var t_emailAddr = formatedEmails[i].email_address;
379                     var displayEmail = t_name + ' <' + t_emailAddr + '>';
380                     if(t_name == '')
381                         t_name = displayEmail = t_emailAddr;
382
383                     var addressType = SE.addressBook.translateAddresType(emailFields[k],true);
384                 SUGAR.email2.addressBook.gridResults.addRow({'type':addressType,'name':t_name,'email_address': t_emailAddr,
385                     'display_email_address': displayEmail,'bean_id': -1,'idx' : SE.addressBook.idx});
386                 }
387         }
388     },
389
390     /**
391     * Checks all entries in the result table against a particular email address, returning true
392     * if the email address is found, false otherwise.
393     */
394     doesEmailAdddressExistInResultTable: function(emailAddress)
395     {
396         if(trim(emailAddress) == '')
397             return false;
398
399         var emailAddressFound = false;
400         var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
401         for (var i=0; i < contacts.length; i++)
402         {
403             var data = SE.addressBook.gridResults.getRecord(contacts[i]).getData();
404             //If we are adding to cc or bcc fields, make them visible.
405             if(data.email_address == emailAddress)
406             {
407                 emailAddressFound = true;
408                 break;
409             }
410         }
411
412         return emailAddressFound;
413     },
414     /**
415     *  Takes all email addresses that the users wishes to add from the address book and populates the To
416     *  fields on the compose tab.
417     */
418     populateEmailAddressFieldsFromResultTable: function()
419     {
420         //Clear the fields first, all email addresses are stored in the address book
421         var idx = SE.addressBook.idx;
422         var emailFields = ['to','cc','bcc'];
423         for(var k=0;k<emailFields.length;k++)
424         {
425             var elKey = 'address' + emailFields[k].toUpperCase() + idx;
426             document.getElementById(elKey).value = "";
427         }
428
429         var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
430         for (var i=0; i < contacts.length; i++)
431         {
432             var data = SE.addressBook.gridResults.getRecord(contacts[i]).getData();
433
434             var addressTypeKey = SE.addressBook.translateAddresType(data.type,false);
435             //If we are adding to cc or bcc fields, make them visible.
436             if(addressTypeKey =='cc' || addressTypeKey =='bcc')
437                 SE.composeLayout.showHiddenAddress(addressTypeKey,data.idx);
438             //Construct the target id
439             var target_id = 'address' + addressTypeKey.toUpperCase() + data.idx
440
441             var target = document.getElementById(target_id);
442             target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, data.display_email_address);
443         }
444
445         //Delete all rows from the result set table
446         SUGAR.email2.addressBook.gridResults.deleteRows(0, SUGAR.email2.addressBook.gridResults.getRecordSet().getLength());
447
448         //Hide the dialogue
449         SE.addressBook.contactsDialogue.hide()
450     },
451     /**
452     *  Insert contacts into the result table.
453     */
454     insertContactToResultTable : function(event,address_type) {
455
456         var contactsDialogue = SE.addressBook.contactsDialogue;
457         var contacts = SE.addressBook.grid.getSelectedRows();
458
459         var rows = SUGAR.email2.addressBook.grid.getRecordSet().getRecords();
460         for (var i = 0; i < rows.length; i++)
461         {
462                         if (typeof(rows[i]) != "undefined" && rows[i].getData().checked )
463                         {
464                             var recId = SE.addressBook.grid.getRecord(rows[i]).getId();
465                 SE.addressBook.insertContactRowToResultTable(recId,address_type);
466                 SUGAR.email2.addressBook.grid.selectRow(rows[i]);
467                 rows[i].setData("selected",true);
468                         }
469         }
470         var checkBoxes = SUGAR.email2.addressBook.grid.get("element").getElementsByTagName('input');
471         for (var i = 0; i < checkBoxes.length; i++) {
472             checkBoxes[i].checked = false;
473         }
474     },
475     /**
476     *
477     */
478     insertContactRowToResultTable : function(rowId, addressType) {
479         var data = SE.addressBook.grid.getRecord(rowId).getData();
480         if(SE.addressBook.doesGridResultsEntryExist(data.email) )
481                 return;
482         var name = data.name.replace(/&#039;/gi,'\'').replace(/&quot;/gi,'"');
483         var ea = name + ' <' + data.email.replace(/&#039;/gi,'\'') + '>';
484         if(addressType == null)
485             addressType = app_strings.LBL_EMAIL_ADDRESS_BOOK_ADD_TO.replace(/:$/,''); //Default to To when using the plus icon.
486         SUGAR.email2.addressBook.gridResults.addRow({'type':addressType,'name':name,'email_address': data.email,'display_email_address': ea,'bean_id': data.bean_id,'idx' : SE.addressBook.idx});
487     },
488     /**
489     * Remove a row from the gridsResult table.
490     */
491     removeRowFromGridResults : function(rowId,emailAddress)
492     {
493         var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
494         for (var i=0; i < contacts.length; i++)
495         {
496             var rec = SE.addressBook.gridResults.getRecord(contacts[i]);
497             var data = rec.getData();
498             if(data.email_address == emailAddress)
499             {
500                 SUGAR.email2.addressBook.gridResults.deleteRow(rec.getId());
501                 break;
502             }
503         }
504
505        SUGAR.email2.addressBook.toggleSearchRowIcon(rowId,true);
506     },
507     /**
508     * Translates between the addressType To|Cc|Bcc labels/keys.
509     */
510     translateAddresType: function(addressType,fromKey)
511     {
512         var displayTo = app_strings.LBL_EMAIL_ADDRESS_BOOK_ADD_TO.replace(/:$/,'');
513         var displayCc = app_strings.LBL_EMAIL_ADDRESS_BOOK_ADD_CC.replace(/:$/,'');
514         var displayBcc = app_strings.LBL_EMAIL_ADDRESS_BOOK_ADD_BCC.replace(/:$/,'');
515         var mappingObject = {};
516
517         if(fromKey)
518             mappingObject = {'to':displayTo, 'cc':displayCc, 'bcc':displayBcc};
519         else
520         {
521             mappingObject[displayTo] = 'to'; //Cant use object literal with variable variable.
522             mappingObject[displayCc] = 'cc';
523             mappingObject[displayBcc] = 'bcc';
524         }
525
526         return typeof(mappingObject[addressType]) != 'undefined' ? mappingObject[addressType] : '';
527
528     },
529     /**
530     *
531     */
532     toggleSearchRowIcon : function(rowId,show)
533     {
534         if(show)
535         {
536             var idToShow = rowId + '_add_img';
537             var idToHide = rowId + '_rm_img';
538         }
539         else
540         {
541             var idToShow = rowId + '_rm_img';
542             var idToHide = rowId + '_add_img';
543         }
544
545
546         Dom.addClass(idToHide, "yui-hidden");
547         Dom.removeClass(idToShow, "yui-hidden");
548     },
549     /**
550     * Determine if an entry has already been added to the grid results table to prevent duplicates.
551     */
552     doesGridResultsEntryExist: function(emailAddrs)
553     {
554
555         var contactExists = false;
556         var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
557         for (var i=0; i < contacts.length; i++)
558         {
559             var data = SE.addressBook.gridResults.getRecord(contacts[i]).getData();
560             if(data.email_address == emailAddrs)
561             {
562                 contactExists = true;
563                 break;
564             }
565         }
566         return contactExists;
567     },
568
569     /**
570      * adds an email address to a string, but first checks if it exists
571      * @param string concat The string we are appending email addresses to
572      * @param string addr Email address to add
573      * @return string
574      */
575     smartAddEmailAddressToComposeField : function(concat, addr) {
576         var re = new RegExp(addr);
577
578         if(!concat.match(re)) {
579             if(concat != "") {
580                 concat += "; " + addr;
581             } else {
582                 concat = addr;
583             }
584         }
585
586         return concat;
587     }
588 };
589 ////    END ADDRESS BOOK
590 ///////////////////////////////////////////////////////////////////////////////
591
592
593
594 ///////////////////////////////////////////////////////////////////////////////
595 ////    AUTOCOMPLETE
596 /**
597  * Auto-complete object
598  */
599 SE.autoComplete = {
600     config : {
601         delimChar : [";", ","],
602         useShadow :    false,
603         useIFrame : false,
604         typeAhead : true,
605         prehighlightClassName : "yui-ac-prehighlight",
606         queryDelay : 0
607     },
608     instances : new Array(),
609
610     /**
611      * Parses an addressBook entry looking for primary address.  If not found, it will return the last found address.
612      * @param object Contact from AddressBook
613      * @return string
614      */
615     getPrimaryAddress : function(contact) {
616         var address = app_strings.LBL_EMAIL_ADDRESS_BOOK_NOT_FOUND;
617
618         for(var eIndex in contact.email) {
619             address = contact.email[eIndex].email_address;
620             if(contact.email[eIndex].primary_address == 1) {
621                 return contact.email[eIndex].email_address;
622             }
623         }
624         return address;
625     },
626
627
628     /**
629      * initializes autocomplete widgets for a given compose view
630      * @param int idx
631      */
632     init : function(idx) {
633         var ds = new YAHOO.widget.DS_JSArray(this.returnDataSource(SE.addressBook._contactCache), {
634             "queryMatchContains" : false,
635             "queryMatchSubset" : true
636         });
637
638         this.instances[idx] = {
639             to : null,
640             cc : null,
641             bcc : null
642         };
643
644
645         // instantiate the autoComplete widgets
646         this.instances[idx]['to'] = new YAHOO.widget.AutoComplete('addressTO'+idx, "addressToAC"+idx, ds, this.config);
647         this.instances[idx]['cc'] = new YAHOO.widget.AutoComplete('addressCC'+idx, "addressCcAC"+idx, ds, this.config);
648         this.instances[idx]['bcc'] = new YAHOO.widget.AutoComplete('addressBCC'+idx, "addressBccAC"+idx, ds, this.config);
649
650         // enable hiding of interfering textareas
651         this.instances[idx]['to'].containerExpandEvent.subscribe(SE.autoComplete.toggleTextareaHide);
652         this.instances[idx]['cc'].containerExpandEvent.subscribe(SE.autoComplete.toggleTextareaHide);
653         this.instances[idx]['bcc'].containerExpandEvent.subscribe(SE.autoComplete.toggleTextareaHide);
654
655         // enable reshowing of hidden textareas
656         this.instances[idx]['to'].containerCollapseEvent.subscribe(SE.autoComplete.toggleTextareaShow);
657         this.instances[idx]['cc'].containerCollapseEvent.subscribe(SE.autoComplete.toggleTextareaShow);
658         this.instances[idx]['bcc'].containerCollapseEvent.subscribe(SE.autoComplete.toggleTextareaShow);
659
660         // enable refreshes of contact lists
661         this.instances[idx]['to'].textboxFocusEvent.subscribe(SE.autoComplete.refreshDataSource);
662         this.instances[idx]['cc'].textboxFocusEvent.subscribe(SE.autoComplete.refreshDataSource);
663         this.instances[idx]['bcc'].textboxFocusEvent.subscribe(SE.autoComplete.refreshDataSource);
664     },
665
666     refreshDataSource : function(sType, aArgs) {
667         var textBoxId = aArgs[0].getInputEl().id; // "addressTo0"
668         var idx;
669         var refresh = SE.autoComplete.returnDataSource(SE.addressBook._contactCache);
670
671         if(textBoxId.indexOf("addressTO") > -1 || textBoxId.indexOf("addressCC") > -1) {
672             idx = textBoxId.substr(9);
673         } else {
674             idx = textBoxId.substr(10);
675         }
676
677         SE.autoComplete.instances[idx]['to'].dataSource.data = refresh;
678         SE.autoComplete.instances[idx]['cc'].dataSource.data = refresh;
679         SE.autoComplete.instances[idx]['bcc'].dataSource.data = refresh;
680     },
681
682     /**
683      * Parses AddressBook entries to return an appropriate DataSource array for YUI.autoComplete
684      */
685     returnDataSource : function(contacts) {
686         var ret = new Array();
687         for(var id in contacts) {
688             if (contacts[id].name) {
689                     var primary = this.getPrimaryAddress(contacts[id]);
690
691                     ret[ret.length] = contacts[id].name.replace(/<[\/]*b>/gi, '') + " <" + primary + ">";
692                     //ret[ret.length] = contacts[id].name + " <" + primary + ">";
693
694                     for(var emailIndex in contacts[id].email) {
695                         ret[ret.length] = contacts[id].email[emailIndex].email_address;
696                     }
697             }
698         }
699
700         return ret;
701     },
702
703     /**
704      * Hides address textareas to prevent autocomplete dropdown from being obscured
705      */
706     toggleTextareaHide : function(sType, aArgs) {
707         var textBoxId = aArgs[0]._oTextbox.id; // "addressTo0"
708         var type = "";
709         var idx = -1;
710
711         if(textBoxId.indexOf("addressTO") > -1) {
712             type = "to";
713         } else if(textBoxId.indexOf("addressCC") > -1) {
714             type = "cc";
715         }
716         idx = textBoxId.substr(9);
717
718         // follow through if not BCC
719         if(type != "") {
720             var cc = document.getElementById("addressCC" + idx);
721             var bcc = document.getElementById("addressBCC" + idx);
722
723             switch(type) {
724                 case "to":
725                     cc.style.visibility = 'hidden';
726                 case "cc":
727                     bcc.style.visibility = 'hidden';
728                 break;
729             }
730         }
731     },
732
733     /**
734      * Redisplays the textareas after an address is committed.
735      */
736     toggleTextareaShow : function(sType, aArgs) {
737         var textBoxId = aArgs[0]._oTextbox.id; // "addressTo0"
738         var type = "";
739         var idx = -1;
740
741         if(textBoxId.indexOf("addressTO") > -1) {
742             type = "to";
743         } else if(textBoxId.indexOf("addressCC") > -1) {
744             type = "cc";
745         }
746         idx = textBoxId.substr(9);
747
748         // follow through if not BCC
749         if(type != "") {
750             document.getElementById("addressCC" + idx).style.visibility = 'visible';
751             document.getElementById("addressBCC" + idx).style.visibility = 'visible';
752         }
753     }
754 };
755
756 ////    END AUTOCOMPLETE
757 ///////////////////////////////////////////////////////////////////////////////
758
759 ///////////////////////////////////////////////////////////////////////////////
760 ////    COMPOSE & SEND
761 /**
762  * expands the options sidebar
763  */
764 SE.composeLayout = {
765     currentInstanceId : 0,
766     ccHidden : true,
767     bccHidden : true,
768     outboundAccountErrors : null,
769     loadedTinyInstances : {}, //Tracks which tinyMCE editors have initalized with html content.
770     subjectMaxlen : 255,
771
772     showAddressDetails : function(e) {
773         var linkElement = document.getElementById("More"+e.id);
774         var spanElement = document.getElementById("Detail"+e.id);
775         var emailAddressList = e.value;
776         if(e.value.length > 96)
777         {
778                 var resultArray = SE.composeLayout._getEmailArrayFromString(emailAddressList);
779             var displayArray = [];
780                 for (var i=0; i<resultArray.length; i++)
781                 {
782                     var t_name = resultArray[i].name;
783                     var t_emailAddr = resultArray[i].email_address;
784                     if(t_name == '')
785                        displayArray.push('<br/>&lt;' + t_emailAddr + '&gt;');
786                     else
787                        displayArray.push(t_name + '<br/>&lt;' + t_emailAddr + '&gt;');
788                 }
789
790             var result = displayArray.join('<br/>');
791                 // Display
792             linkElement.style.display = "inline";
793             linkElement.style.height="10px";
794             linkElement.style.overflow="visible";
795             spanElement.innerHTML = result;
796         }
797         else
798                 linkElement.style.display = "none";
799
800         },
801
802    /**
803     *  Given a string of email address, return an array containing the name portion (if available)
804     *  and email portion.
805     */
806     _getEmailArrayFromString : function (emailAddressList){
807
808         var reg = /@.*?;/g;
809         while ((results = reg.exec(emailAddressList)) != null)
810         {
811             orignial = results[0];
812             parsedResult = results[0].replace(';', ':::::');
813             emailAddressList = emailAddressList.replace (orignial, parsedResult);
814         }
815
816         reg = /@.*?,/g;
817         while ((results = reg.exec(emailAddressList)) != null)
818         {
819             orignial = results[0];
820             parsedResult = results[0].replace(',', ':::::');
821             emailAddressList = emailAddressList.replace (orignial, parsedResult);
822         }
823         //Administrator <johndoe@som.com>  ;1@somwhe.com;2@somwherecomplex.com,3@somwherecomplex.com;4@somwherecomplex.com,5@somwherecomplex.com,
824         var emailArr = emailAddressList.split(":::::");
825         var resultsArray = [];
826         var newArr = [];
827         for (var i=0; i<emailArr.length; i++)
828         {
829             var rposition = emailArr[i].indexOf('<');
830             var lposition = emailArr[i].indexOf('>');
831
832             if(trim(emailArr[i]) != '')
833             {
834                 if(rposition != -1 && lposition != -1)
835                 {
836                     var t_name = emailArr[i].substr(0, rposition-1);
837                     var t_emailAddr = emailArr[i].substr(rposition+1, (lposition-1 - rposition) );
838                     resultsArray.push({'name':t_name, 'email_address': t_emailAddr});
839                 }
840                 else
841                 {
842                     resultsArray.push({'name':'', 'email_address': emailArr[i]});
843                 }
844             }
845         }
846         return resultsArray;
847     },
848     ///////////////////////////////////////////////////////////////////////////
849     ////    COMPOSE FLOW
850     /**
851      * Prepare bucket DIV and yui-ext tab panels
852      */
853     _0_yui : function() {
854         var idx = this.currentInstanceId;
855
856         var composeTab = new YAHOO.SUGAR.ClosableTab({
857                         label: mod_strings.LNK_NEW_SEND_EMAIL,
858                                 scroll : true,
859                                 content : "<div id='htmleditordiv" + idx + "'/>",
860                                 id : "composeTab" + idx,
861                                 closeMsg: app_strings.LBL_EMAIL_CONFIRM_CLOSE,
862                                 active : true
863         }, SE.innerLayout);
864         SE.innerLayout.addTab(composeTab);
865
866         // get template engine with template
867         if (!SE.composeLayout.composeTemplate) {
868                 SE.composeLayout.composeTemplate = new YAHOO.SUGAR.Template(SE.templates['compose']);
869         }
870
871         // create Tab inner layout
872         var composePanel =  this.getComposeLayout();
873         composePanel.getUnitByPosition("right").collapse();
874         composePanel.autoSize();
875
876     },
877         /**
878      * Generate the quick compose layout
879          * @method getQuickComposeLayout
880          * @param {Pannel} parentPanel Parent pannel
881          * @param {Object} o Options
882          * @return {} none
883          **/
884     getQuickComposeLayout : function (parentPanel,o) {
885          var idx = SE.composeLayout.currentInstanceId;
886
887          //Before rendering the parent pannel we need to initalize the grid layout
888          parentPanel.beforeRenderEvent.subscribe(function() {
889
890                 YAHOO.util.Event.onAvailable('htmleditordiv' + idx, function() {
891                         SE.composeLayout._createComposeLayout(idx);
892                         SE.composeLayout[idx].set('height', 350);
893                         SE.composeLayout[idx].render();
894            });
895         });
896
897          //Wait until the Compose Layout has rendered, then add the
898          //options tab and perform the tiny initialization.
899          parentPanel.renderEvent.subscribe(function() {
900
901                 YAHOO.util.Event.onAvailable('htmleditordiv' + idx, function() {
902                 SE.composeLayout._initComposeOptionTabs(idx);
903                 SE.composeLayout[idx].getUnitByPosition("right").collapse();
904                 //Initialize tinyMCE
905             SE.composeLayout._1_tiny(false);
906
907                 //Init templates and address book
908                 SE.composeLayout._2_final();
909
910             SE.composeLayout.quickCreateComposePackage(o);
911
912                 });
913          });
914
915             //Check if we have the div override for the shortcut bar
916         if(typeof o.menu_id != 'undefined') {
917                    parentPanel.render(o.menu_id);
918             } else {
919                    parentPanel.render(document.body);
920             }
921
922         return SE.composeLayout[idx];
923     },
924     /**
925      * Fill in all fields into the quick compose layout.
926          * @method quickCreateComposePackage
927          * @param {Object} o Options
928          * @return {} none
929          **/
930     quickCreateComposePackage: function(o)
931     {
932         //If we have a compose package fill in defaults.
933         if (typeof(o.composePackage) != 'undefined')
934         {
935             composePackage = o.composePackage; //Set the compose data object
936             //Hijack this method called by composePackage as it's not need for quick creates.
937             SE.composeLayout.c0_composeNewEmail = function(){};
938             SE.composeLayout.composePackage(); //Fill in defaults.
939         }
940     },
941     getComposeLayout : function() {
942         var idx = SE.composeLayout.currentInstanceId;
943
944         this._createComposeLayout(idx);
945         SE.composeLayout[idx].render();
946         this._initComposeOptionTabs(idx);
947
948         return SE.composeLayout[idx];
949         },
950
951         /**
952         *       Create the layout manager for the compose window.
953         */
954         _createComposeLayout : function(idx)
955         {
956                 SE.composeLayout[idx] = new YAHOO.widget.Layout('htmleditordiv' + idx, {
957                 parent: SE.complexLayout,
958                 border:true,
959             hideOnLayout: true,
960             height: 400,
961                         units: [{
962                                         position: "center",
963                         animate: false,
964                         scroll: false,
965                         split:true,
966                         body:
967                                 SE.composeLayout.composeTemplate.exec({
968                                 'app_strings':app_strings,
969                                 'mod_strings':mod_strings,
970                                 'linkbeans_options' : linkBeans,
971                                 'idx' : SE.composeLayout.currentInstanceId
972                                 })
973                     },{
974                         position: "right",
975                                     scroll:true,
976                                     collapse: true,
977                                     collapsed: true,
978                                     resize: true,
979                                     border:true,
980                                     animate: false,
981                                     width:'230',
982                                     body: "<div class='composeRightTabs' id='composeRightTabs" + idx + "'/>",
983                                     titlebar: true,
984                                     split: true,
985                                     header: app_strings.LBL_EMAIL_OPTIONS
986                     }]
987                 });
988         },
989
990         /**
991         *  Create compose tab which will populate the 'right' container in the compose window.
992         */
993         _initComposeOptionTabs : function(idx)
994         {
995                 var cTabs = new YAHOO.widget.TabView("composeRightTabs" + idx);
996                 var tab = new YAHOO.widget.Tab({
997                                 label: app_strings.LBL_EMAIL_ATTACHMENT,
998                                 scroll : true,
999                                 content : SUGAR.util.getAndRemove("divAttachments" + idx).innerHTML,
1000                                 id : "divAttachments" + idx,
1001                                 active : true
1002                         });
1003
1004                 tab.layout = SE.composeLayout[idx];
1005
1006            tab.on("activeChange", function(o){
1007                         if (o.newValue) {
1008                                 this.layout.getUnitByPosition("right").set("header", app_strings.LBL_EMAIL_ATTACHMENT);
1009                         }
1010                 });
1011
1012                 cTabs.addTab(tab);
1013
1014                 tab = new YAHOO.widget.Tab({
1015                                 label: app_strings.LBL_EMAIL_OPTIONS,
1016                                 scroll : true,
1017                                 content : SUGAR.util.getAndRemove("divOptions" + idx).innerHTML,
1018                                 id : "divOptions" + idx,
1019                                 active : false
1020                         });
1021
1022                 tab.layout = SE.composeLayout[idx];
1023                 tab.on("activeChange", function(o){
1024                         if (o.newValue) {
1025                                 this.layout.getUnitByPosition("right").set("header", app_strings.LBL_EMAIL_OPTIONS);
1026                         }
1027                 });
1028                 cTabs.addTab(tab);
1029
1030                 SE.composeLayout[idx].autoSize = function() {
1031                         var pEl = this.get("element").parentNode.parentNode.parentNode;
1032                         this.set("height", pEl.clientHeight-30);
1033                         this.render();
1034                 }
1035
1036                 SE.composeLayout[idx].rightTabs = cTabs;
1037     },
1038     isParentTypeValid : function(idx) {
1039                 var parentTypeValue = document.getElementById('data_parent_type' + idx).value;
1040                 var parentNameValue = document.getElementById('data_parent_name' + idx).value;
1041                 if (trim(parentTypeValue) == ""){
1042                         alert(mod_strings.LBL_ERROR_SELECT_MODULE);
1043                         return false;
1044                 } // if
1045                 return true;
1046     },
1047
1048     isParentTypeAndNameValid : function(idx) {
1049                 var parentTypeValue = document.getElementById('data_parent_type' + idx).value;
1050                 var parentNameValue = document.getElementById('data_parent_name' + idx).value;
1051                 var parentIdValue = document.getElementById('data_parent_id' + idx).value;
1052                 if ((trim(parentTypeValue) != "" && trim(parentNameValue) == "") ||
1053                         (trim(parentTypeValue) != "" && trim(parentNameValue) != "" && parentIdValue == "")){
1054                                 alert(mod_strings.LBL_ERROR_SELECT_MODULE_SELECT);
1055                         return false;
1056                 } // if
1057                 return true;
1058     },
1059
1060     callopenpopupForEmail2 : function(idx,options) {
1061
1062         var formName = 'emailCompose' + idx;
1063
1064         if(typeof(options) != 'undefined' && typeof(options.form_name) != 'undefined')
1065             formName = options.form_name;
1066
1067                 var parentTypeValue = document.getElementById('data_parent_type' + idx).value;
1068                 var parentNameValue = document.getElementById('data_parent_name' + idx).value;
1069                 if (!SE.composeLayout.isParentTypeValid(idx)) {
1070                         return;
1071                 } // if
1072                 open_popup(document.getElementById('data_parent_type' + idx).value,600,400,'&tree=ProductsProd',true,false,
1073                 {
1074                         call_back_function:"SE.composeLayout.popupAddEmail",
1075                         form_name:formName,
1076                         field_to_name_array:{
1077                                 id:'data_parent_id' + idx,
1078                                 name:'data_parent_name' + idx,
1079                                 email1:'email1'}
1080                 });
1081         },
1082
1083         popupAddEmail : function(o)
1084         {
1085                 var nameKey = "data_parent_name" + SE.composeLayout.currentInstanceId;
1086                 var data = o.name_to_value_array;
1087                 if (typeof (data[nameKey]) != "undefined" && data[nameKey] != ""
1088                         && typeof (data["email1"]) != "undefined" && data["email1"] != "" && data["email1"] != "undefined")
1089         {
1090                 var target = Dom.get("addressTO" + SE.composeLayout.currentInstanceId);
1091                 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, data[nameKey] + "<" + data.email1 + ">");
1092         }
1093                 set_return(o);
1094         },
1095     /**
1096      * Prepare TinyMCE
1097      */
1098     _1_tiny : function(isReplyForward) {
1099         var idx = SE.composeLayout.currentInstanceId;
1100         var elId = SE.tinyInstances.currentHtmleditor = 'htmleditor' + idx;
1101         SE.tinyInstances[elId] = { };
1102         SE.tinyInstances[elId].ready = false;
1103
1104         if (!SUGAR.util.isTouchScreen()) {
1105             var t = tinyMCE.getInstanceById(elId);
1106         }
1107         if(typeof(t) == 'undefined')  {
1108             if (!SUGAR.util.isTouchScreen()) {
1109                 tinyMCE.execCommand('mceAddControl', false, elId);
1110             }
1111             YAHOO.util.Event.onAvailable(elId + "_parent", function() {
1112                 SE.composeLayout.resizeEditorSetSignature(idx,!isReplyForward);
1113                 }, this);
1114         }
1115     },
1116
1117     resizeEditorSetSignature : function(idx,setSignature)
1118     {
1119         var instance = SE.util.getTiny(SE.tinyInstances.currentHtmleditor);
1120
1121         if(typeof(instance) == 'undefined' || (typeof(SE.composeLayout.loadedTinyInstances[idx]) != 'undefined' && SE.composeLayout.loadedTinyInstances[idx] == false)) {
1122             setTimeout("SE.composeLayout.resizeEditorSetSignature(" + idx + ",'"+setSignature+"');",500);
1123                     return;
1124                 }
1125
1126         SE.composeLayout.resizeEditor(idx);
1127         if(setSignature) {
1128             setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",250);
1129         }
1130
1131     },
1132
1133     resizeEditor : function(idx)
1134     {
1135         var cof = Dom.get('composeOverFrame' + idx);
1136         var head = Dom.get('composeHeaderTable' + idx);
1137         var targetHeight = cof.clientHeight - head.clientHeight;
1138         var instance = SE.util.getTiny('htmleditor' + idx);
1139
1140         try {
1141         var parentEl = Dom.get(instance.editorId + '_parent');
1142         var toolbar = Dom.getElementsByClassName("mceFirst", "tr", parentEl)[0];
1143         var contentEl  = instance.contentAreaContainer;
1144         var iFrame = contentEl.firstChild;
1145         var tinMceToolbarOffset = 18;
1146         iFrame.style.height = (targetHeight - toolbar.offsetHeight - tinMceToolbarOffset)  + "px";
1147
1148         } catch(e) {
1149             setTimeout("SE.composeLayout.resizeEditor("+idx+");",1000);
1150         }
1151     },
1152
1153     /**
1154      * Initializes d&d, auto-complete, email templates
1155      */
1156     _2_final : function() {
1157         var idx = SE.composeLayout.currentInstanceId;
1158
1159         if(this.emailTemplates) {
1160             this.setComposeOptions(idx);
1161         } else {
1162             //populate email template cache
1163             AjaxObject.target = '';
1164             AjaxObject.startRequest(callbackComposeCache, urlStandard + "&emailUIAction=fillComposeCache");
1165         }
1166
1167         // handle drop targets for addressBook
1168        var to =  new YAHOO.util.DDTarget('addressTO' +idx, 'addressBookDD', {notifyDrop:this.handleDrop});
1169        var cc =  new YAHOO.util.DDTarget('addressCC' +idx, 'addressBookDD', {notifyDrop:this.handleDrop});
1170        var bcc = new YAHOO.util.DDTarget('addressBCC'+idx, 'addressBookDD', {notifyDrop:this.handleDrop});
1171        to.notifyDrop = cc.notifyDrop = bcc.notifyDrop = this.handleDrop;
1172
1173         // auto-complete setup
1174         SE.autoComplete.init(idx);
1175
1176         // set focus on to:
1177         document.getElementById("addressTO" + idx).focus();
1178     },
1179
1180         /**
1181      * hide tinyMCE tool bar if send email as plaintext is checked
1182      */
1183     renderTinyMCEToolBar : function (idx, hide) {
1184         if (hide) {
1185                 document.getElementById('htmleditor' + idx + '_toolbar1').style.display = 'none';
1186         } else {
1187                 document.getElementById('htmleditor' + idx + '_toolbar1').style.display = '';
1188         }
1189     },
1190
1191     c1_composeEmail : function(isReplyForward, retry) {
1192         if (!retry) {
1193             this._0_yui();
1194         }
1195         if  (!SUGAR.util.isTouchScreen() && (typeof(tinyMCE) == 'undefined' || typeof(tinyMCE.settings) == 'undefined')){
1196             setTimeout("SE.composeLayout.c1_composeEmail(" + isReplyForward + ", true);", 500);
1197         } else {
1198                 this._1_tiny(isReplyForward);
1199                 this._2_final();
1200
1201                 if(isReplyForward) {
1202                     this.replyForwardEmailStage2();
1203                 }
1204         }
1205     },
1206
1207     /**
1208      * takes draft info and prepopulates
1209      */
1210     c0_composeDraft : function() {
1211         this.getNewInstanceId();
1212         inCompose = true;
1213         document.getElementById('_blank').innerHTML = '';
1214         var idx = SE.composeLayout.currentInstanceId;
1215                 SE.composeLayout.draftObject = new Object();
1216                 SE.composeLayout.draftObject.id = idx;
1217                 SE.composeLayout.draftObject.isDraft = true;
1218         SE.composeLayout.currentInstanceId = idx;
1219         SE.tinyInstances.currentHtmleditor = 'htmleditor' + SE.composeLayout.currentInstanceId;
1220         SE.tinyInstances[SE.tinyInstances.currentHtmleditor] = new Object();
1221         SE.tinyInstances[SE.tinyInstances.currentHtmleditor].ready = false;
1222
1223         SE.composeLayout._0_yui();
1224         SE.composeLayout._1_tiny(true);
1225
1226         // final touches
1227         SE.composeLayout._2_final();
1228
1229         /* Draft-specific final processing. Need a delay to allow Tiny to render before calling setText() */
1230         setTimeout("AjaxObject.handleReplyForwardForDraft(SE.o);", 1000);
1231     },
1232
1233     /**
1234      * Strip & Prep editor hidden fields
1235      */
1236     c0_composeNewEmail : function() {
1237         this.getNewInstanceId();
1238         this.c1_composeEmail(false);
1239     },
1240
1241     /**
1242      * Sends async request to get the compose view.
1243      * Requests come from "reply" or "forwards"
1244      */
1245     c0_replyForwardEmail : function(ieId, uid, mbox, type) {
1246         SE.composeLayout.replyForwardObj = new Object();
1247         SE.composeLayout.replyForwardObj.ieId = ieId;
1248         SE.composeLayout.replyForwardObj.uid = uid;
1249         SE.composeLayout.replyForwardObj.mbox = mbox;
1250         SE.composeLayout.replyForwardObj.type = type;
1251
1252         if(mbox == 'sugar::Emails') {
1253             SE.composeLayout.replyForwardObj.sugarEmail = true;
1254         }
1255
1256         SE.composeLayout.getNewInstanceId();
1257         SE.composeLayout.c1_composeEmail(true);
1258     },
1259     ////    END COMPOSE FLOW
1260     ///////////////////////////////////////////////////////////////////////////
1261
1262     /**
1263      * Called when a contact, email, or mailinglist is dropped
1264      * into one of the compose fields.
1265      */
1266     handleDrop : function (source, event, data, target) {
1267         var nodes;
1268         if (!target) {
1269             target = event.getTarget();
1270             if (data.single) {
1271                 data.nodes = [data.nodes];
1272             }
1273             nodes = data.nodes;
1274         } else {
1275             target = document.getElementById(target);
1276             nodes = data;
1277         }
1278
1279         if (target.id.indexOf('address') > -1) {
1280             // dropped onto email to/cc/bcc field
1281             for(var i in nodes) {
1282                 var node = nodes[i].getData();
1283                 var email = "";
1284                 if (node[1].indexOf('contact') > -1) {
1285                     email = SE.addressBook.getFormattedAddress(node[0]);
1286                 } else if (node[1].indexOf('address-email') > -1){
1287                     email = node[3].replace(/&nbsp;/gi, '');
1288                     email = email.replace('&lt;', '<').replace('&gt;', '>');
1289                     var tr = source.getTrEl(nodes[i]);
1290                     while (tr && !Dom.hasClass(tr, "address-contact")) {
1291                         tr = source.getPreviousTrEl(tr);
1292                     }
1293                     var CID = source.getRecord(tr).getData()[0];
1294                     var o = SE.addressBook._contactCache[CID];
1295                     var name = new String(o.name);
1296                     var finalName = name.replace(/(<([^>]+)>)/ig, "");
1297                     email = finalName + email;
1298                 }
1299                 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, email);
1300             }
1301         }
1302     },
1303
1304
1305     /////////////////////////////////////////////////////////////////////////////
1306     ///    EMAIL TEMPLATE CODE
1307     applyEmailTemplate : function (idx, id) {
1308
1309         //bug #20680
1310         var box_title = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_SHOW_TITLE;
1311                 var box_msg = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_SHOW_MSG;
1312                 var box_none_msg = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_CLEAR_MSG;
1313
1314                 //bug #6224
1315                 var to_addr = document.getElementById('addressTO'+idx);
1316                 if (to_addr.value.search(/[^;,]{6,}[;,][^;,]{6,}/) != -1)
1317                 {
1318                         box_title = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_WARNING_TITLE;
1319                         box_msg = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_MULTIPLE_RECIPIENTS + '<br /><br />' + box_msg;
1320                 }
1321
1322                 // id is selected index of email template drop-down
1323                 if(id == '' || id == "0") {
1324                         YAHOO.SUGAR.MessageBox.show({
1325                    title:box_title,
1326                    msg: box_none_msg,
1327                    type: 'confirm',
1328                    fn: function(btn){
1329                                 if(btn=='no'){return;};
1330                                 SUGAR.email2.composeLayout.processNoneResult(idx, id);},
1331                    modal:true,
1332                    scope:this
1333                });
1334                return;
1335                 }
1336
1337                 YAHOO.SUGAR.MessageBox.show({
1338            title:box_title,
1339            msg: box_msg,
1340            type: 'confirm',
1341            fn: function(btn){
1342                         if(btn=='no'){return;};
1343                         SUGAR.email2.composeLayout.processResult(idx, id);},
1344            modal:true,
1345            scope:this
1346        });
1347     },
1348
1349     processNoneResult : function(idx, id) {
1350         var tiny = SE.util.getTiny('htmleditor' + idx);
1351         var tinyHTML = tiny.getContent();
1352         var openTag = '<div><span><span>';
1353         var htmllow = tinyHTML.toLowerCase();
1354         var start = htmllow.indexOf(openTag);
1355                 if (start > -1) {
1356                 tinyHTML = tinyHTML.substr(start);
1357             tiny.setContent(tinyHTML);
1358                 } else {
1359             tiny.setContent('');
1360                 }
1361         //now that content is set, call method to set signature
1362         setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",500);
1363     },
1364
1365         processResult : function(idx , id){
1366                 var post_data = {"module":"EmailTemplates","record":id};
1367                 var global_rpcClient =  new SugarRPCClient();
1368
1369                 result = global_rpcClient.call_method('retrieve', post_data, true);
1370                 if(!result['record']) return;
1371                 json_objects['email_template_object'] = result['record'];
1372                 this.appendEmailTemplateJSON();
1373
1374         // get attachments if any
1375         AjaxObject.target = '';
1376         AjaxObject.startRequest(callbackLoadAttachments, urlStandard + "&emailUIAction=getTemplateAttachments&parent_id=" + id);
1377     },
1378
1379     appendEmailTemplateJSON : function() {
1380         var idx = SE.composeLayout.currentInstanceId; // post increment
1381
1382         // query based on template, contact_id0,related_to
1383         //jchi 09/10/2008 refix #7743
1384         if(json_objects['email_template_object']['fields']['subject'] != '' )
1385         {
1386             // cn: bug 7743, don't stomp populated Subject Line
1387             document.getElementById('emailSubject' + idx).value = decodeURI(encodeURI(json_objects['email_template_object']['fields']['subject']));
1388         }
1389         var text = '';
1390         if(json_objects['email_template_object']['fields']['text_only'] == 1)
1391         {
1392                 text = "<p>" + decodeURI(encodeURI(json_objects['email_template_object']['fields']['body'])).replace(/<BR>/ig, '</p><p>').replace(/<br>/gi, "</p><p>").replace(/&amp;/gi,'&').replace(/&lt;/gi,'<').replace(/&gt;/gi,'>').replace(/&#039;/gi,'\'').replace(/&quot;/gi,'"') + "</p>";
1393                 document.getElementById('setEditor' + idx).checked = true;
1394                 SUGAR.email2.composeLayout.renderTinyMCEToolBar(idx, 1);
1395         }
1396         else
1397         {
1398                 text = decodeURI(encodeURI(json_objects['email_template_object']['fields']['body_html'])).replace(/<BR>/ig, '\n').replace(/<br>/gi, "\n").replace(/&amp;/gi,'&').replace(/&lt;/gi,'<').replace(/&gt;/gi,'>').replace(/&#039;/gi,'\'').replace(/&quot;/gi,'"');
1399                 document.getElementById('setEditor' + idx).checked = false;
1400                 SUGAR.email2.composeLayout.renderTinyMCEToolBar(idx, 0);
1401         }
1402
1403
1404         var tiny = SE.util.getTiny('htmleditor' + idx);
1405         var tinyHTML = tiny.getContent();
1406         var openTag = '<div><span><span>';
1407         var closeTag = '</span></span></div>';
1408         var htmllow = tinyHTML.toLowerCase();
1409         var start = htmllow.indexOf(openTag);
1410                 if (start > -1) {
1411                 var htmlPart2 = tinyHTML.substr(start);
1412                 tinyHTML = text + htmlPart2;
1413                 tiny.setContent(tinyHTML);
1414                 } else {
1415                 tiny.setContent(text);
1416                 }
1417                 //now that content is set, call method to set signature
1418         setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",500);
1419     },
1420
1421     /**
1422      * Writes out the signature in the email editor
1423      */
1424     setSignature : function(idx)
1425     {
1426         if (!tinyMCE)
1427             return false;
1428         var hide = document.getElementById('setEditor' + idx).checked;
1429         SE.composeLayout.renderTinyMCEToolBar(idx,hide);
1430         //wait for signatures to load before trying to set them
1431         if (!SE.composeLayout.signatures) {
1432             setTimeout("SE.composeLayout.setSignature(" + idx + ");", 1000);
1433                         return;
1434         }
1435
1436         if(idx != null) {
1437             var sel = document.getElementById('signatures' + idx);
1438         } else {
1439             var sel = document.getElementById('signature_id');
1440             idx = SE.tinyInstances.currentHtmleditor;
1441         }
1442
1443         //Ensure that the tinyMCE html has been rendered.
1444         if(typeof(SE.composeLayout.loadedTinyInstances[idx]) != 'undefined' && SE.composeLayout.loadedTinyInstances[idx] == false) {
1445             setTimeout("SE.composeLayout.setSignature(" + idx + ");",1000);
1446                     return;
1447                 }
1448
1449         var signature = '';
1450
1451         try {
1452             signature = sel.options[sel.selectedIndex].value;
1453         } catch(e) {
1454
1455         }
1456
1457         // The tags are used for finding the signature
1458         var openTag = '<div id="signature-begin"><br />';
1459         var closeTag = '<span>&nbsp;</span></div>';
1460
1461         // Get tinyMCE instance
1462         var t = tinyMCE.getInstanceById('htmleditor' + idx);
1463
1464         // IE 6 Hack
1465         if(typeof(t) != 'undefined')
1466         {
1467             t.contentDocument = t.contentWindow.document;
1468             var html = t.getContent();
1469         }
1470         else
1471         {
1472             var html = '';
1473         }
1474
1475         var htmllow = html.toLowerCase();
1476         var start = htmllow.indexOf(openTag);
1477         // Start looking for the closeTag where the start tag was found
1478         var end = htmllow.indexOf(closeTag, start);
1479         if (end >= 0)
1480         {
1481             end += closeTag.length;
1482         }
1483         else
1484         {
1485             end = htmllow.length;
1486         }
1487
1488         // selected "none" - remove signature from email
1489         if (signature == '')
1490         {
1491             if (start > -1)
1492             {
1493                 var htmlPart1 = html.substr(0, start);
1494                 var htmlPart2 = html.substr(end, html.length);
1495
1496                 html = htmlPart1 + htmlPart2;
1497                 t.setContent(html);
1498             }
1499             SE.signatures.lastAttemptedLoad = '';
1500             return false;
1501         }
1502
1503         if (!SE.signatures.lastAttemptedLoad) // lazy load place holder
1504         {
1505             SE.signatures.lastAttemptedLoad = '';
1506         }
1507
1508         SE.signatures.lastAttemptedLoad = signature;
1509
1510         if (typeof(SE.signatures[signature]) == 'undefined')
1511         {
1512             //lazy load
1513             SE.signatures.lastAttemptedLoad = ''; // reset this flag for recursion
1514             SE.signatures.targetInstance = (idx) ? idx : "";
1515             AjaxObject.target = '';
1516             AjaxObject.startRequest(callbackLoadSignature, urlStandard + "&emailUIAction=getSignature&id="+signature);
1517         }
1518         else
1519         {
1520             var newSignature = this.prepareSignature(SE.signatures[signature]);
1521
1522             // clear out old signature
1523             if (SE.signatures.lastAttemptedLoad && start > -1)
1524             {
1525                 var htmlPart1 = html.substr(0, start);
1526                 var htmlPart2 = html.substr(end, html.length);
1527
1528                 html = htmlPart1 + htmlPart2;
1529             }
1530
1531             // pre|append
1532                         start = html.indexOf('<div><hr></div>');
1533             if (SE.userPrefs.signatures.signature_prepend == 'true' && start > -1) // Prepend
1534             {
1535                                 var htmlPart1 = html.substr(0, start);
1536                                 var htmlPart2 = html.substr(start, html.length);
1537                 var newHtml = htmlPart1 + openTag + newSignature + closeTag + htmlPart2;
1538             }
1539             else if (SUGAR.email2.userPrefs.signatures.signature_prepend == 'true') // Prepend
1540             {
1541                 //bug 48285
1542                 var newHtml = html;
1543
1544                 //remove custom spacing
1545                 var spacing = '<span id="spacing"><br /><br /><br /></span>&nbsp;';
1546                 var customSpacingStart = html.indexOf(spacing);
1547
1548                 if (customSpacingStart > -1)
1549                 {
1550                     var part1 = newHtml.substr(0, customSpacingStart);
1551                     var part2 = newHtml.substr(customSpacingStart+spacing.length, newHtml.length);
1552                     newHtml = part1 + part2;
1553                 }
1554
1555                 //append signature
1556                 var bodyStartTag = '<body>';
1557                 var body = newHtml.indexOf(bodyStartTag);
1558
1559                 if (body > -1)
1560                 {
1561                     var part1 = newHtml.substr(0, body+bodyStartTag.length);
1562                     var part2 = newHtml.substr(body+bodyStartTag.length, newHtml.length);
1563                     newHtml = part1 + spacing + openTag + newSignature + closeTag + part2;
1564                 }
1565                 else
1566                 {
1567                     newHtml = openTag + newSignature + closeTag + newHtml;
1568                 }
1569                 //end bug 48285
1570             }
1571             else // Append
1572             {
1573                 // On full compose mail has <body> element empty 
1574                 var bodyStringEmpty = htmllow.indexOf("<body>") > -1 && htmllow.replace(/\s/g, "").match(/<body>.+<\/body>/) == null;
1575                 // On quick compose a new document has html.length == 0
1576                 if (htmllow.length == 0 || bodyStringEmpty)
1577                 {
1578                     // Prepend <br /> to openTag because if it's an empty document
1579                     // or a document with nothing other than whitespace in <body></body>
1580                         // TinyMCE will pick the id of the div containing the signature
1581                     // when adding a new row and duplicate it so this might cause
1582                     // trouble when changing signatures
1583                     openTag = "<br />" + openTag;
1584                 }
1585
1586                 var body = html.indexOf('</body>');
1587                 if (body > -1)
1588                 {
1589                     var part1 = html.substr(0, body);
1590                     var part2 = html.substr(body, html.length);
1591                     var newHtml = part1 + openTag + newSignature + closeTag + part2;
1592                 }
1593                 else
1594                 {
1595                     var newHtml = html + openTag + newSignature + closeTag;
1596                 }
1597             }
1598             t.setContent(newHtml);
1599         }
1600     },
1601
1602     prepareSignature : function(str) {
1603         var signature = new String(str);
1604
1605         signature = signature.replace(/&lt;/gi, '<');
1606         signature = signature.replace(/&gt;/gi, '>');
1607
1608         return signature;
1609     },
1610
1611
1612     showAttachmentPanel : function(idx) {
1613         var east = SE.composeLayout[idx].getUnitByPosition("right");
1614         var tabs = SE.composeLayout[idx].rightTabs;
1615         east.expand();
1616         tabs.set("activeTab", tabs.getTab(0));
1617     },
1618
1619     /**
1620      * expands sidebar and displays options panel
1621      */
1622     showOptionsPanel : function(idx) {
1623         var east = SE.composeLayout[idx].getUnitByPosition("right");
1624         var tabs = SE.composeLayout[idx].rightTabs;
1625         east.expand();
1626         tabs.set("activeTab", tabs.getTab(1));
1627     },
1628
1629     /**
1630      * Selects the Contacts tab
1631      */
1632     showContactsPanel : function() {
1633         SE.complexLayout.regions.west.showPanel("contactsTab");
1634     },
1635
1636     /**
1637      * Generates fields for Select Document
1638      */
1639     addDocumentField : function(idx) {
1640         var basket = document.getElementById('addedDocuments' + idx);
1641         if(basket) {
1642             var index = (basket.childNodes.length / 7) - 1;
1643             if(index < 0)
1644                 index = 0;
1645         } else {
1646             index = 0;
1647         }
1648
1649         var test = document.getElementById('documentId' + idx + index);
1650
1651         while(test != null) {
1652             index++;
1653             test = document.getElementById('documentId' + idx + index);
1654         }
1655
1656         var documentCup = document.createElement("div");
1657         documentCup.id = 'documentCup' + idx + index;
1658         documentCup.innerHTML = "<input type='hidden' name='document" + idx + index + "' id='document" + idx + index + "' />" +
1659                 // document id field
1660                 "<input type='hidden' name='documentId" + idx + index + "' id='documentId" + idx + index + "' />" +
1661                 // document name field
1662                 "<input value='' size='15' disabled='true' type='text' name='documentName" + idx + index + "' id='documentName" + idx + index + "' />" +
1663                 // select button
1664                 "<button class='button firstChild' type='button' name='documentSelect" + idx + index + "' id='documentSelect" + idx + index + "'" +
1665                     "onclick='SE.composeLayout.selectDocument(\"" + index + "\");' value='" + app_strings.LBL_EMAIL_SELECT + "'>" +
1666                 "<img src='index.php?entryPoint=getImage&themeName=" + SUGAR.themes.theme_name + "&imageName=id-ff-select.png' ></button>" +
1667                 // remove button
1668                 "<button class='button lastChild' type='button' name='documentRemove" + idx + index + "' id='documentRemove" + idx + index + "'" +
1669                     "onclick='SE.composeLayout.deleteDocumentField(\"documentCup" + idx + index + "\");' value='" + app_strings.LBL_EMAIL_REMOVE + "'>" +
1670                  "<img src='index.php?entryPoint=getImage&themeName=" + SUGAR.themes.theme_name + "&imageName=id-ff-clear.png' ></button>" +
1671                 "<br/>";
1672
1673         basket.appendChild(documentCup);
1674         //basket.innerHTML += out;
1675         return index;
1676     },
1677
1678     /**
1679      * Makes async call to save a draft of the email
1680      * @param int Instance index
1681      */
1682     saveDraft : function(tinyInstance) {
1683         SE.tinyInstances.currentHtmleditor = 'htmleditor' + tinyInstance;
1684         this.sendEmail(tinyInstance, true);
1685     },
1686
1687     selectDocument : function(target) {
1688         URL="index.php?module=Emails&action=PopupDocuments&to_pdf=true&target=" + target;
1689         windowName = 'selectDocument';
1690         windowFeatures = 'width=800' + ',height=600' + ',resizable=1,scrollbars=1';
1691
1692         win = SUGAR.util.openWindow(URL, windowName, windowFeatures);
1693         if(window.focus) {
1694             // put the focus on the popup if the browser supports the focus() method
1695             win.focus();
1696         }
1697     },
1698
1699     /**
1700      * Modal popup for file attachment dialogue
1701      */
1702     addFileField : function() {
1703         if(!SE.addFileDialog){ // lazy initialize the dialog and only create it once
1704             SE.addFileDialog = new YAHOO.widget.Dialog("addFileDialog", {
1705                 modal:true,
1706                 visible:false,
1707                 fixedcenter:true,
1708                 constraintoviewport: true,
1709                 scroll: true,
1710                 keylisteners : new YAHOO.util.KeyListener(document, { keys:27 }, {
1711                         fn:function(){SE.addFileDialog.hide();}
1712                 })
1713             });
1714             SE.addFileDialog.setHeader(app_strings.LBL_EMAIL_ATTACHMENTS);
1715             SE.addFileDialog.render();
1716            // SE.addFileDialog.addKeyListener(27, , SE.addFileDialog);
1717         }
1718         Dom.removeClass("addFileDialog", "yui-hidden");
1719
1720         SE.addFileDialog.show();
1721     },
1722
1723     /**
1724      * Async upload of file to temp dir
1725      */
1726     uploadAttachment : function() {
1727         if(document.getElementById('email_attachment').value != "") {
1728             var formObject = document.getElementById('uploadAttachment');
1729             YAHOO.util.Connect.setForm(formObject, true, true);
1730             AjaxObject.target = '';
1731             AjaxObject.startRequest(callbackUploadAttachment, null);
1732         } else {
1733             alert(app_strings.LBL_EMAIL_ERROR_NO_FILE);
1734         }
1735     },
1736
1737     /**
1738      * Adds a SugarDocument to an outbound email.  Action occurs in a popup window displaying a ListView from the Documents module
1739      * @param string target in focus compose layout
1740      */
1741     setDocument : function(idx, target, documentId, documentName, docRevId) {
1742         // fields are named/id'd [fieldName][instanceId][index]
1743         var addedDocs = document.getElementById("addedDocuments" + idx);
1744         var docId = document.getElementById('documentId' + idx + target);
1745         var docName = document.getElementById('documentName' + idx + target);
1746         var docRevisionId = document.getElementById('document' + idx + target);
1747         docId.value = documentId;
1748         docName.value = documentName;
1749         docRevisionId.value = docRevId;
1750     },
1751
1752     /**
1753      * Removes the bucket div containing the document input fields
1754      */
1755     deleteDocumentField : function(documentCup) {
1756         var f0 = document.getElementById(documentCup);
1757         f0.parentNode.removeChild(f0);
1758     },
1759
1760     /**
1761      * Removes a Template Attachment field
1762      * @param int
1763      * @param int
1764      */
1765     deleteTemplateAttachmentField : function(idx, index) {
1766         // create not-in-array values for removal filtering
1767         var r = document.getElementById("templateAttachmentsRemove" + idx).value;
1768
1769         if(r != "") {
1770             r += "::";
1771         }
1772
1773         r += document.getElementById('templateAttachmentId' + idx + index).value;
1774         document.getElementById("templateAttachmentsRemove" + idx).value = r;
1775
1776         var target = 'templateAttachmentCup' + idx + index;
1777         d =  document.getElementById(target);
1778         d.parentNode.removeChild(d);
1779     },
1780
1781     /**
1782      * Async removal of uploaded temp file
1783      * @param string index Should be a concatenation of idx and index
1784      * @param string
1785      */
1786     deleteUploadAttachment : function(index, file) {
1787         var d = document.getElementById('email_attachment_bucket' + index);
1788         d.parentNode.removeChild(d);
1789
1790         // make async call to delete cached file
1791         AjaxObject.target = '';
1792         AjaxObject.startRequest('', urlStandard + "&emailUIAction=removeUploadedAttachment&file="+unescape(file));
1793     },
1794
1795     /**
1796      * Attaches files coming from Email Templates
1797      */
1798     addTemplateAttachmentField : function(idx) {
1799         // expose title
1800         document.getElementById('templateAttachmentsTitle' + idx).style.display = 'block';
1801
1802         var basket = document.getElementById('addedTemplateAttachments' + idx);
1803
1804         if(basket) {
1805             var index = basket.childNodes.length;
1806             if(index < 0)
1807                 index = 0;
1808         } else {
1809             index = 0;
1810         }
1811
1812         var out = "<div id='templateAttachmentCup" + idx + index + "'>" +
1813                                 // remove button
1814                                 "<img src='index.php?entryPoint=getImage&themeName=" + SUGAR.themes.theme_name + "&imageName=minus.gif' " +
1815                                         "style='cursor:pointer' align='absmiddle' onclick='SUGAR.email2.composeLayout.deleteTemplateAttachmentField(\"" +
1816                                         idx + "\",\"" + index + "\");'/>" +
1817                                 // file icon
1818                                 "<img src='index.php?entryPoint=getImage&themeName=" + SUGAR.themes.theme_name + "&imageName=attachment.gif' " + "align='absmiddle' />" +
1819                                 // templateAttachment field
1820                                 "<input type='hidden' value='" + "' name='templateAttachment" + idx + index + "' id='templateAttachment" + idx + index + "' />" +
1821                                 // docId field
1822                                 "<input type='hidden' value='" + "' name='templateAttachmentId" + idx + index + "' id='templateAttachmentId" + idx + index + "' />" +
1823                                 // file name
1824                                 "<span id='templateAttachmentName"  + idx + index + "'" + ">&nbsp;</span>" +
1825                                 "<br id='br" + index + "></br>" +
1826                                 "<br id='brdoc" + index + "></br>" +
1827                         "</div>";
1828                 basket.innerHTML = basket.innerHTML + out;
1829
1830         return index;
1831     },
1832
1833     /**
1834      * Sends one email via async call
1835      * @param int idx Editor instance ID
1836      * @param bool isDraft
1837      */
1838     sendEmail : function(idx, isDraft) {
1839
1840         //If the outbound account has an error message associate with it, alert the user and refuse to continue.
1841         var obAccountID = document.getElementById('addressFrom' + idx).value;
1842
1843         if( typeof(SUGAR.email2.composeLayout.outboundAccountErrors[obAccountID]) != 'undefined' )
1844         {
1845             SUGAR.showMessageBox(app_strings.LBL_EMAIL_ERROR_DESC, SUGAR.email2.composeLayout.outboundAccountErrors[obAccountID], 'alert');
1846             return false;
1847         }
1848
1849
1850         var form = document.getElementById('emailCompose' + idx);
1851         var composeOptionsFormName = "composeOptionsForm" + idx;
1852
1853
1854         var t = SE.util.getTiny('htmleditor' + idx);
1855         if (t != null || typeof(t) != "undefined") {
1856             var html = t.getContent();
1857         } else {
1858             var html = "<p>" + document.getElementById('htmleditor' + idx).value + "</p>";
1859         }
1860
1861             var subj = document.getElementById('emailSubject' + idx).value;
1862         var to = trim(document.getElementById('addressTO' + idx).value);
1863         var cc = trim(document.getElementById('addressCC' + idx).value);
1864         var bcc = trim(document.getElementById('addressBCC' + idx).value);
1865         var email_id = document.getElementById('email_id' + idx).value;
1866         var composeType = document.getElementById('composeType').value;
1867         var parent_type = document.getElementById("parent_type").value;
1868         var parent_id = document.getElementById("parent_id").value;
1869
1870         var el_uid = document.getElementById("uid");
1871         var uid = (el_uid == null) ? '' : el_uid.value;
1872
1873         var el_ieId = document.getElementById("ieId");
1874         var ieId = (el_ieId == null) ? '' : el_ieId.value;
1875
1876         var el_mbox = document.getElementById("mbox");
1877         var mbox = (el_mbox == null) ? '' : el_mbox.value;
1878
1879         if (!isValidEmail(to) || !isValidEmail(cc) || !isValidEmail(bcc)) {
1880                         alert(app_strings.LBL_EMAIL_COMPOSE_INVALID_ADDRESS);
1881                 return false;
1882         }
1883
1884         if (!SE.composeLayout.isParentTypeAndNameValid(idx)) {
1885                 return;
1886         } // if
1887                 var parentTypeValue = document.getElementById('data_parent_type' + idx).value;
1888                 var parentIdValue = document.getElementById('data_parent_id' + idx).value;
1889         parent_id = parentIdValue;
1890         parent_type = parentTypeValue;
1891
1892         var in_draft = (document.getElementById('type' + idx).value == 'draft') ? true : false;
1893         // baseline viability check
1894
1895         if(to == "" && cc == '' && bcc == '' && !isDraft) {
1896             alert(app_strings.LBL_EMAIL_COMPOSE_ERR_NO_RECIPIENTS);
1897             return false;
1898         } else if(subj == '' && !isDraft) {
1899             if(!confirm(app_strings.LBL_EMAIL_COMPOSE_NO_SUBJECT)) {
1900                 return false;
1901             } else {
1902                 subj = app_strings.LBL_EMAIL_COMPOSE_NO_SUBJECT_LITERAL;
1903             }
1904         } else if(html == '' && !isDraft) {
1905             if(!confirm(app_strings.LBL_EMAIL_COMPOSE_NO_BODY)) {
1906                 return false;
1907             }
1908         }
1909
1910         SE.util.clearHiddenFieldValues('emailCompose' + idx);
1911                 document.getElementById('data_parent_id' + idx).value = parentIdValue;
1912                 var title = (isDraft) ? app_strings.LBL_EMAIL_SAVE_DRAFT : app_strings.LBL_EMAIL_SENDING_EMAIL;
1913         SUGAR.showMessageBox(title, app_strings.LBL_EMAIL_ONE_MOMENT);
1914         html = html.replace(/&lt;/ig, "sugarLessThan");
1915         html = html.replace(/&gt;/ig, "sugarGreaterThan");
1916
1917         form.sendDescription.value = html;
1918         form.sendSubject.value = subj;
1919         form.sendTo.value = to;
1920         form.sendCc.value = cc;
1921         form.sendBcc.value = bcc;
1922         form.email_id.value = email_id;
1923         form.composeType.value = composeType;
1924         form.composeLayoutId.value = 'composeLayout' + idx;
1925         form.setEditor.value = (document.getElementById('setEditor' + idx).checked == false) ? 1 : 0;
1926         form.saveToSugar.value = 1;
1927         form.fromAccount.value = document.getElementById('addressFrom' + idx).value;
1928         form.parent_type.value = parent_type;
1929         form.parent_id.value = parent_id;
1930         form.uid.value = uid;
1931         form.ieId.value = ieId;
1932         form.mbox.value = mbox;
1933
1934         // email attachments
1935         var addedFiles = document.getElementById('addedFiles' + idx);
1936         if(addedFiles) {
1937             for(i=0; i<addedFiles.childNodes.length; i++) {
1938                 var bucket = addedFiles.childNodes[i];
1939
1940                 for(j=0; j<bucket.childNodes.length; j++) {
1941                     var node = bucket.childNodes[j];
1942                     var nName = new String(node.name);
1943
1944                     if(node.type == 'hidden' && nName.match(/email_attachment/)) {
1945                         if(form.attachments.value != '') {
1946                             form.attachments.value += "::";
1947                         }
1948                         form.attachments.value += unescape(node.value);
1949                     }
1950                 }
1951             }
1952         }
1953
1954         // sugar documents
1955         var addedDocs = document.getElementById('addedDocuments' + idx);
1956         if(addedDocs) {
1957             for(i=0; i<addedDocs.childNodes.length; i++) {
1958                 var cNode = addedDocs.childNodes[i];
1959                 for(j=0; j<cNode.childNodes.length; j++) {
1960                     var node = cNode.childNodes[j];
1961                     var nName = new String(node.name);
1962                     if(node.type == 'hidden' && nName.match(/documentId/)) {
1963                         if(form.documents.value != '') {
1964                             form.documents.value += "::";
1965                         }
1966                         form.documents.value += node.value;
1967                     }
1968                 }
1969             }
1970         }
1971
1972         // template attachments
1973         var addedTemplateAttachments = document.getElementById('addedTemplateAttachments' + idx);
1974         if(addedTemplateAttachments) {
1975             for(i=0; i<addedTemplateAttachments.childNodes.length; i++) {
1976                 var cNode = addedTemplateAttachments.childNodes[i];
1977                 for(j=0; j<cNode.childNodes.length; j++) {
1978                     var node = cNode.childNodes[j];
1979                     var nName = new String(node.name);
1980                     if(node.type == 'hidden' && nName.match(/templateAttachmentId/)) {
1981                         if(form.templateAttachments.value != "") {
1982                             form.templateAttachments.value += "::";
1983                         }
1984                         form.templateAttachments.value += node.value;
1985                     }
1986                 }
1987             }
1988         }
1989
1990         // remove attachments
1991         form.templateAttachmentsRemove.value = document.getElementById("templateAttachmentsRemove" + idx).value;
1992
1993         YAHOO.util.Connect.setForm(form);
1994
1995         AjaxObject.target = 'frameFlex';
1996
1997         // sending a draft email
1998         if(!isDraft && in_draft) {
1999             // remove row
2000             SE.listView.removeRowByUid(email_id);
2001         }
2002
2003         var sendCallback = (isDraft) ? AjaxObject.composeLayout.callback.saveDraft : callbackSendEmail;
2004         var emailUiAction = (isDraft) ? "&emailUIAction=sendEmail&saveDraft=true" : "&emailUIAction=sendEmail";
2005
2006         AjaxObject.startRequest(sendCallback, urlStandard + emailUiAction);
2007     },
2008
2009     /**
2010      * Handles clicking the email address link from a given view
2011      */
2012     composePackage : function() {
2013         if(composePackage != null) {
2014             SE.composeLayout.c0_composeNewEmail();
2015
2016
2017             if(composePackage.to_email_addrs) {
2018                 document.getElementById("addressTO" + SE.composeLayout.currentInstanceId).value = composePackage.to_email_addrs;
2019             } // if
2020
2021             if (composePackage.cc_addrs) {
2022                 document.getElementById("addressCC" + SE.composeLayout.currentInstanceId).value = composePackage.cc_addrs;
2023                 SE.composeLayout.showHiddenAddress('cc', SE.composeLayout.currentInstanceId);
2024             }
2025
2026             if (composePackage.subject != null && composePackage.subject.length > 0) {
2027                 document.getElementById("emailSubject" + SE.composeLayout.currentInstanceId).value = composePackage.subject;
2028             }
2029
2030             //If no parent fields are set in the composePackage, ensure they are cleared.
2031             var parentFields = ['parent_type','parent_name','parent_id'];
2032             for(var i=0;i<parentFields.length;i++)
2033             {
2034                 if ( typeof(composePackage[parentFields[i]]) == 'undefined' )
2035                     composePackage[parentFields[i]] = "";
2036             }
2037
2038             document.getElementById("parent_type").value = composePackage.parent_type;
2039             document.getElementById('data_parent_type' + SE.composeLayout.currentInstanceId).value = composePackage.parent_type;
2040             document.getElementById("parent_id").value = composePackage.parent_id;
2041             document.getElementById('data_parent_id' + SE.composeLayout.currentInstanceId).value = composePackage.parent_id;
2042             document.getElementById('data_parent_name' + SE.composeLayout.currentInstanceId).value = composePackage.parent_name;
2043
2044             if(composePackage.email_id != null && composePackage.email_id.length > 0) {
2045                 document.getElementById("email_id" + SE.composeLayout.currentInstanceId).value = composePackage.email_id;
2046             } // if
2047             if (composePackage.body != null && composePackage.body.length > 0) {
2048                         var tiny = SE.util.getTiny('htmleditor' + SE.composeLayout.currentInstanceId);
2049                         SE.composeLayout.loadedTinyInstances[SE.composeLayout.currentInstanceId] = false;
2050                         setTimeout("SE.composeLayout.setContentOnThisTiny();", 3000);
2051             } // if
2052             if (composePackage.attachments != null) {
2053                                 SE.composeLayout.loadAttachments(composePackage.attachments);
2054             } // if
2055
2056             if (composePackage.fromAccounts != null && composePackage.fromAccounts.status) {
2057                                 var addressFrom = document.getElementById('addressFrom' + SE.composeLayout.currentInstanceId);
2058                         SE.util.emptySelectOptions(addressFrom);
2059                         var fromAccountOpts = composePackage.fromAccounts.data;
2060                         for(i=0; i<fromAccountOpts.length; i++) {
2061                               var key = fromAccountOpts[i].value;
2062                               var display = fromAccountOpts[i].text;
2063                               var opt = new Option(display, key);
2064                               if (fromAccountOpts[i].selected) {
2065                                 opt.selected = true;
2066                               }
2067                               addressFrom.options.add(opt);
2068                         }
2069
2070             } // if
2071         } // if
2072     },
2073
2074     setContentOnThisTiny : function(recursive) {
2075         var tiny = SE.util.getTiny('htmleditor' + SE.composeLayout.currentInstanceId);
2076         var tinyHTML = tiny.getContent();
2077         composePackage.body = decodeURI(encodeURI(composePackage.body));
2078         // cn: bug 14361 - text-only templates don't fill compose screen
2079         if(composePackage.body == '') {
2080             composePackage.body = decodeURI(encodeURI(composePackage.body)).replace(/<BR>/ig, '\n').replace(/<br>/gi, "\n").replace(/&amp;/gi,'&').replace(/&lt;/gi,'<').replace(/&gt;/gi,'>').replace(/&#039;/gi,'\'').replace(/&quot;/gi,'"');
2081         } // if
2082         //Flag determines if we should clear the tiny contents or just append
2083         if (typeof(composePackage.clearBody) != 'undefined' && composePackage.clearBody)
2084         {
2085             SE.composeLayout.tinyHTML = '';
2086         }
2087         else
2088         {
2089
2090             //check to see if tiny is defined, and this is not a recursive call if not, then call self function one more time
2091             if(typeof tiny == 'undefined'  &&  typeof recursive == 'undefined'){
2092                 //call this same function again, this time setting the recursive flag to true
2093                 setTimeout("SE.composeLayout.setContentOnThisTiny(true);", 3000);
2094                 return;
2095             }
2096
2097             //bug 48179
2098             //check tinyHTML for closing tags
2099             var body = tinyHTML.lastIndexOf('</body>');
2100             spacing = '<span id="spacing"><br /><br /><br /></span>&nbsp;';
2101
2102             if (body > -1)
2103             {
2104                 var part1 = tinyHTML.substr(0, body);
2105                 var part2 = tinyHTML.substr(body, tinyHTML.length);
2106                 var newHtml = part1 + spacing + composePackage.body + part2;
2107             }
2108             else
2109             {
2110                 var newHtml = tinyHTML + spacing + composePackage.body;
2111             }
2112             //end bug 48179
2113
2114             SE.composeLayout.tinyHTML = newHtml;
2115         }
2116
2117          tiny.setContent(SE.composeLayout.tinyHTML);
2118          //Indicate that the contents has been loaded successfully.
2119          SE.composeLayout.loadedTinyInstances[SE.composeLayout.currentInstanceId] = true;
2120     },
2121     /**
2122      * Confirms closure of a compose screen if "x" is clicked
2123      */
2124     confirmClose : function(panel) {
2125         if(confirm(app_strings.LBL_EMAIL_CONFIRM_CLOSE)) {
2126             SE.composeLayout.closeCompose(panel.id);
2127             return true;
2128         } else {
2129             return false;
2130         }
2131     },
2132
2133     /**
2134      * forces close of a compose screen
2135      */
2136     forceCloseCompose : function(id) {
2137         SE.composeLayout.closeCompose(id);
2138
2139         // handle flow back to originating view
2140         if(composePackage) {
2141             // check if it's a module we need to return to
2142             if(composePackage.return_module && composePackage.return_action && composePackage.return_id) {
2143                 if(confirm(app_strings.LBL_EMAIL_RETURN_TO_VIEW)) {
2144                     var url = "index.php?module=" + composePackage.return_module + "&action=" + composePackage.return_action + "&record=" + composePackage.return_id;
2145                     window.location = url;
2146                 }
2147             }
2148         }
2149     },
2150
2151     /**
2152      * closes the editor that just sent email
2153      * @param string id ID of composeLayout tab
2154      */
2155     closeCompose : function(id) {
2156         // destroy tinyMCE instance
2157         var idx = id.substr(13, id.length);
2158         var instanceId = "htmleditor" + idx;
2159         tinyMCE.execCommand('mceRemoveControl', false, instanceId);
2160
2161         // nullify DOM and namespace values.
2162         inCompose = false;
2163         SE.composeLayout[idx] = null;
2164         SE.tinyInstances[instanceId] = null;
2165         var tabsArray = SE.innerLayout.get("tabs");
2166         for (i = 0 ; i < tabsArray.length ; i++) {
2167                 if (tabsArray[i].get("id") == ('composeTab' + idx)) {
2168                         tabsArray[i].close();
2169                         break;
2170                 }
2171         }
2172         //SE.innerLayout.getTab(idx).close();
2173     },
2174
2175     /**
2176     *  Enable the quick search for the compose relate field or search tab
2177     */
2178     enableQuickSearchRelate: function(idx,overides){
2179
2180         if(typeof overides != 'undefined')
2181         {
2182             var newModuleID = overides['moduleSelectField']; //data_parent_type_search
2183             var newModule = document.getElementById(newModuleID).value;
2184             var formName = overides['formName'];
2185             var fieldName = overides['fieldName'];
2186             var fieldId = overides['fieldId'];
2187             var fullName = formName + "_" + fieldName;
2188             var postBlurFunction = null;
2189         }
2190         else
2191         {
2192             var newModule = document.getElementById('data_parent_type'+idx).value;
2193             var formName = 'emailCompose'+idx;
2194             var fieldName = 'data_parent_name'+idx;
2195             var fieldId = 'data_parent_id'+idx;
2196             var fullName = formName + "_" + fieldName;
2197             var postBlurFunction = "SE.composeLayout.qsAddAddress";
2198         }
2199
2200         if(typeof sqs_objects == 'undefined')
2201             window['sqs_objects'] = new Array;
2202
2203         window['sqs_objects'][fullName] = {
2204             form:formName,
2205                         method:"query",
2206                         modules:[newModule],
2207                         group:"or",
2208             field_list:["name","id", "email1"],populate_list:[fieldName,fieldId],required_list:[fieldId],
2209             conditions:[{name:"name",op:"like_custom",end:"%",value:""}],
2210                         post_onblur_function: postBlurFunction,
2211             order:"name","limit":"30","no_match_text":"No Match"};
2212
2213
2214         if(typeof QSProcessedFieldsArray != 'undefined')
2215                 QSProcessedFieldsArray[fullName] = false;
2216         if (typeof(QSFieldsArray) != 'undefined' && typeof(QSFieldsArray[fullName]) != 'undefined') {
2217                 QSFieldsArray[fullName].destroy();
2218                 delete QSFieldsArray[fullName];
2219         }
2220         if (Dom.get(fullName + "_results")) {
2221                 Dom.get(fullName + "_results").parentNode.removeChild(Dom.get(fullName + "_results"));
2222         }
2223
2224         enableQS(false);
2225     },
2226
2227         qsAddAddress : function(o) {
2228         if (o.name != "" && o.email1 != "")
2229         {
2230                 var target = Dom.get("addressTO" + SE.composeLayout.currentInstanceId);
2231                 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, o.name + "<" + o.email1 + ">");
2232         }
2233     },
2234     /**
2235      * Returns a new instance ID, 0-index
2236      */
2237     getNewInstanceId : function() {
2238         this.currentInstanceId = this.currentInstanceId + 1;
2239         return this.currentInstanceId;
2240     },
2241
2242     /**
2243      * Takes an array of objects that contain the filename and GUID of a Note (attachment or Sugar Document) and applies the values to the compose screen.  Valid use-cases are applying an EmailTemplate or resuming a Draft Email.
2244      */
2245     loadAttachments : function(result) {
2246         var idx = SE.composeLayout.currentInstanceId;
2247
2248         if(typeof(result) == 'object') {
2249                 //jchi #20680. Clean the former template attachments;
2250                 var basket = document.getElementById('addedTemplateAttachments' + idx);
2251                         if(basket.innerHTML != ''){
2252                                 confirm(mod_strings.LBL_CHECK_ATTACHMENTS, mod_strings.LBL_HAS_ATTACHMENTS, function(btn){
2253                                         if (btn != 'yes'){
2254                                                 basket.innerHTML = '';
2255                                         }
2256                                 });
2257                         }
2258             for(i in result) {
2259                 if(typeof result[i] == 'object') {
2260                     var index = SE.composeLayout.addTemplateAttachmentField(idx);
2261                     var bean = result[i];
2262                     document.getElementById('templateAttachmentId' + idx + index).value = bean['id'];
2263                     document.getElementById('templateAttachmentName' + idx + index).innerHTML += bean['filename'];
2264                 }
2265             }
2266         }
2267     },
2268
2269     /**
2270      * fills drop-down values for email templates and signatures
2271      */
2272     setComposeOptions : function(idx) {
2273         // send from accounts
2274         var addressFrom = document.getElementById('addressFrom' + idx);
2275
2276         if (addressFrom.options.length <= 0) {
2277                 SE.util.emptySelectOptions(addressFrom);
2278                 var fromAccountOpts = SE.composeLayout.fromAccounts;
2279                 for (id = 0 ; id < fromAccountOpts.length ; id++) {
2280                       var key = fromAccountOpts[id].value;
2281                       var display = fromAccountOpts[id].text;
2282                       var is_default = false;
2283                       if(key == SUGAR.default_inbound_accnt_id)
2284                         is_default = true;
2285                       var opt = new Option(display, key);
2286                       addressFrom.options.add(opt);
2287                       addressFrom.options[id].selected = is_default; //Safari bug new Option(x,y,true) does not work.
2288                 }
2289         }
2290
2291         // email templates
2292         var et = document.getElementById('email_template' + idx);
2293         SE.util.emptySelectOptions(et);
2294
2295         for(var key in this.emailTemplates) { // iterate through assoc array
2296             var display = this.emailTemplates[key];
2297             var opt = new Option(display, key);
2298             et.options.add(opt);
2299         }
2300
2301         // signatures
2302         var sigs = document.getElementById('signatures' + idx);
2303         SE.util.emptySelectOptions(sigs);
2304
2305         for(var key in this.signatures) { // iterate through assoc array
2306             var display = this.signatures[key];
2307             var opt = new Option(display, key);
2308
2309             if(key == SE.userPrefs.signatures.signature_default) {
2310                 opt.selected = true;
2311             }
2312
2313             sigs.options.add(opt);
2314         }
2315
2316         // html/plain email?
2317         var htmlEmail = document.getElementById('setEditor' + idx);
2318         if(SE.userPrefs.emailSettings.sendPlainText == 1) {
2319             htmlEmail.checked = true;
2320         } else {
2321                 htmlEmail.checked = false;
2322         }
2323
2324         SE.tinyInstances[SE.tinyInstances.currentHtmleditor].ready = true;
2325     },
2326
2327     /**
2328      * After compose screen is rendered, async call to get email body from Sugar
2329      */
2330     replyForwardEmailStage2 : function() {
2331         SE.util.clearHiddenFieldValues('emailUIForm');
2332         SUGAR.showMessageBox(app_strings.LBL_EMAIL_RETRIEVING_MESSAGE, app_strings.LBL_EMAIL_ONE_MOMENT);
2333
2334         var ieId = SE.composeLayout.replyForwardObj.ieId;
2335         var uid = SE.composeLayout.replyForwardObj.uid;
2336         var mbox = SE.composeLayout.replyForwardObj.mbox;
2337         var type = SE.composeLayout.replyForwardObj.type;
2338         var idx = SE.composeLayout.currentInstanceId;
2339
2340         var sugarEmail = (SE.composeLayout.replyForwardObj.sugarEmail) ? '&sugarEmail=true' : "";
2341
2342         document.getElementById('emailSubject' + idx).value = type;
2343         document.getElementById('emailUIAction').value = 'composeEmail';
2344         document.getElementById('composeType').value = type;
2345         document.getElementById('ieId').value = ieId;
2346         document.getElementById('uid').value = uid;
2347         document.getElementById('mbox').value = mbox;
2348                 document.getElementById('setEditor' + idx).checked = SE.userPrefs.emailSettings.sendPlainText == 1 ? true : false;
2349         var formObject = document.getElementById('emailUIForm');
2350         YAHOO.util.Connect.setForm(formObject);
2351
2352         var sendType = type;
2353         AjaxObject.startRequest(callbackReplyForward, urlStandard + "&composeType=" + type + sugarEmail);
2354     },
2355
2356     /**
2357      * Move email addresses from To field to BCC field
2358      */
2359     moveToBCC : function (addrType,idx) {
2360
2361         var toVal = $.trim($("#addressTO"+idx).val());
2362         var BCCVal =$.trim($("#addressBCC"+idx).val());
2363
2364         if (toVal.length != 0)
2365         {
2366             // get rid of first comma in BCC field and last comma in TO field
2367             // so we don't end up with double commas in BCC field
2368             BCCVal = BCCVal.replace(/^,/, '');
2369             toVal = toVal.replace(/\,$/, '');
2370
2371             $("#addressBCC"+idx).val(toVal +","+BCCVal);
2372             $("#addressTO"+idx).val("");     // empty out the to field
2373         }
2374         // show the BCC field
2375         SE.composeLayout.showHiddenAddress('bcc', SE.composeLayout.currentInstanceId);
2376     },
2377
2378     /**
2379     *  Show the hidden cc or bcc fields
2380     */
2381     showHiddenAddress: function(addrType,idx){
2382
2383         Dom.removeClass(addrType+"_tr"+idx, "yui-hidden");
2384         Dom.addClass(addrType+"_span"+idx, "yui-hidden");
2385                 Dom.addClass("bcc_cc_sep"+idx, "yui-hidden");
2386                 this[addrType+'Hidden'+idx] = false;
2387
2388                 //After bcc or cc is added, move options below last addr field
2389                 Dom.insertAfter("add_addr_options_tr"+idx, 'bcc_tr'+idx);
2390
2391                 //If both cc and bcc hidden, remove the empty row containing text.
2392                 if( ( typeof(this['ccHidden'+idx]) != 'undefined' && typeof(this['bccHidden'+idx]) != 'undefined')
2393                            && ( this['ccHidden'+idx]  == false && this['bccHidden'+idx] == false) )
2394                         Dom.addClass("add_addr_options_tr"+idx, "yui-hidden");
2395
2396                 SE.composeLayout.resizeEditor(idx);
2397     },
2398     /**
2399     *  Hide the cc and bcc fields if they were shown.
2400     */
2401     hideHiddenAddresses: function(idx){
2402
2403         var addrTypes = ['cc','bcc'];
2404         for(var i = 0;i<addrTypes.length;i++)
2405         {
2406             Dom.addClass(addrTypes[i] + "_tr"+idx, "yui-hidden");
2407             Dom.removeClass(addrTypes[i] + "_span"+idx, "yui-hidden");
2408             this[addrTypes[i] + 'Hidden'+idx] = true
2409         }
2410
2411         Dom.removeClass("bcc_cc_sep"+idx, "yui-hidden");
2412         Dom.removeClass("add_addr_options_tr"+idx, "yui-hidden");
2413         Dom.insertBefore("add_addr_options_tr"+idx, 'bcc_tr'+idx);
2414     }
2415 };
2416
2417 ////    END SE.composeLayout
2418 ///////////////////////////////////////////////////////////////////////////////
2419 ///////////////////////////////////////////////////////////////////////////////
2420 ////    SE.util
2421 SE.util = {
2422     /**
2423      * Cleans serialized UID lists of duplicates
2424      * @param string
2425      * @return string
2426      */
2427     cleanUids : function(str) {
2428         var seen = new Object();
2429         var clean = "";
2430         var arr = new String(str).split(",");
2431
2432         for(var i=0; i<arr.length; i++) {
2433             if(seen[arr[i]]) {
2434                 continue;
2435             }
2436
2437             clean += (clean != "") ? "," : "";
2438             clean += arr[i];
2439             seen[arr[i]] = true;
2440         }
2441
2442         return clean;
2443     },
2444
2445     /**
2446      * Clears hidden field values
2447      * @param string id ID of form element to clear
2448      */
2449     clearHiddenFieldValues : function(id) {
2450         var form = document.getElementById(id);
2451
2452         for(i=0; i<form.elements.length; i++) {
2453             if(form.elements[i].type == 'hidden') {
2454                 var e = form.elements[i];
2455                 if(e.name != 'action' && e.name != 'module' && e.name != 'to_pdf') {
2456                     e.value = '';
2457                 }
2458             }
2459         }
2460     },
2461
2462     /**
2463      * Reduces a SELECT drop-down to 0 items to prepare for new ones
2464      */
2465     emptySelectOptions : function(el) {
2466         if(el) {
2467             for(i=el.childNodes.length - 1; i >= 0; i--) {
2468                 if(el.childNodes[i]) {
2469                     el.removeChild(el.childNodes[i]);
2470                 }
2471             }
2472         }
2473     },
2474
2475     /**
2476      * Returns the MBOX path in the manner php_imap expects:
2477      * ie: INBOX.DEBUG.test
2478      * @param string str Current serialized value, Home.personal.test.INBOX.DEBUG.test
2479      */
2480     generateMboxPath : function(str) {
2481         var ex = str.split("::");
2482
2483         /* we have a serialized MBOX path */
2484         if(ex.length > 1) {
2485             var start = false;
2486             var ret = '';
2487             for(var i=0; i<ex.length; i++) {
2488                 if(ex[i] == 'INBOX') {
2489                     start = true;
2490                 }
2491
2492                 if(start == true) {
2493                     if(ret != "") {
2494                         ret += ".";
2495                     }
2496                     ret += ex[i];
2497                 }
2498             }
2499         } else {
2500             /* we have a Sugar folder GUID - do nothing */
2501             return str;
2502         }
2503
2504         return ret;
2505     },
2506
2507     /**
2508      * returns a SUGAR GUID by navigating the DOM tree a few moves backwards
2509      * @param HTMLElement el
2510      * @return string GUID of found element or empty on failure
2511      */
2512     getGuidFromElement : function(el) {
2513         var GUID = '';
2514         var iterations = 4;
2515         var passedEl = el;
2516
2517         // upwards
2518         for(var i=0; i<iterations; i++) {
2519             if(el) {
2520                 if(el.id.match(SE.reGUID)) {
2521                     return el.id;
2522                 } else {
2523                     el = el.parentNode;
2524                 }
2525             }
2526         }
2527
2528         return GUID;
2529     },
2530
2531     /**
2532      * Returns the ID value for the current in-focus, active panel (in the innerLayout, not complexLayout)
2533      * @return string
2534      */
2535     getPanelId : function() {
2536         return SE.innerLayout.get("activeTab").id ? SE.innerLayout.get("activeTab").id : "Preview";
2537     },
2538
2539     /**
2540      * wrapper to handle weirdness with IE
2541      * @param string instanceId
2542      * @return tinyMCE Controller object
2543      */
2544     getTiny : function(instanceId) {
2545         if(instanceId == '') {
2546             return null;
2547         }
2548
2549         var t = tinyMCE.getInstanceById(instanceId);
2550
2551         if(this.isIe()) {
2552             this.sleep(200);
2553             YAHOO.util.Event.onContentReady(instanceId, function(t) { return t; });
2554         }
2555         return t;
2556     },
2557
2558     /**
2559      * Simple check for MSIE browser
2560      * @return bool
2561      */
2562     isIe : function() {
2563         var nav = new String(navigator.appVersion);
2564         if(nav.match(/MSIE/)) {
2565             return true;
2566         }
2567         return false;
2568     },
2569
2570     /**
2571      * Recursively removes an element from the DOM
2572      * @param HTMLElement
2573      */
2574     removeElementRecursive : function(el) {
2575         this.emptySelectOptions(el);
2576     },
2577
2578     /**
2579      * Fakes a sleep
2580      * @param int
2581      */
2582     sleep : function(secs) {
2583         setTimeout("void(0);", secs);
2584     },
2585
2586     /**
2587      * Converts a <select> element to an Ext.form.combobox
2588      */
2589      convertSelect : function(select) {
2590        alert('in convertSelect');
2591        if (typeof(select) == "string") {
2592            select = document.getElementById(select);
2593        }
2594      },
2595
2596      findChildNode : function (parent, property, value) {
2597          for (i in parent.children) {
2598                  var child = parent.children[i];
2599                  if (child.data[property] && child.data[property] == value || child[property] && child[property] == value)
2600                          return child;
2601                  var searchChild = SE.util.findChildNode(child, property, value);
2602                  if (searchChild)
2603                          return searchChild;
2604          }
2605          return false;
2606      },
2607
2608      cascadeNodes : function (parent, fn, scope, args) {
2609          for (i in parent.children) {
2610                  var child = parent.children[i];
2611                  var s = scope ? scope : child;
2612                  var a = args ? args : child;
2613                  fn.call(s, a);
2614                  SE.util.cascadeNodes(child, fn, scope, args);
2615          }
2616      }
2617 };
2618
2619
2620 ////    END UTIL
2621 ///////////////////////////////////////////////////////////////////////////////
2622
2623
2624 })();