1 /*********************************************************************************
2 * SugarCRM Community Edition is a customer relationship management program developed by
3 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
38 Event = YAHOO.util.Event,
39 Connect = YAHOO.util.Connect,
43 ///////////////////////////////////////////////////////////////////////////////
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
56 itemSpacing : 'white-space:nowrap; padding:2px;',
62 * YUI bug fix 2527707. Causes nested datatable's in <tables> to cause 404 errors whens earching.
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);
70 // Validate TD element
71 var elCell = column.nodeName.toLowerCase() != "th" ? this.getTdEl(column) : false;
73 oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
75 // Validate TH element
77 elCell = this.getThEl(column);
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];
94 cancelEdit : function() {
95 if(this.editContactDialog)
96 this.editContactDialog.hide();
97 if(this.editMailingListDialog)
98 this.editMailingListDialog.hide();
105 var t = document.getElementById('contactsFilter');
111 * handle context-menu Compose-to call
112 * @param string type 'contacts' or 'lists'
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();
122 var idx = activePanel.substring(10);
125 // determine if we have a selection to work with
126 if(type == 'contacts') {
127 var ids = SE.contactView.getSelectedRows();
129 rows[i] = SE.contactView.getRecord(ids[i]);
131 removeHiddenNodes(rows, SE.contactView);
135 if(rows.length > 0) {
136 SE.composeLayout.handleDrop(
137 (type == 'contacts') ? SE.contactView : SE.emailListsView,
138 null, rows, 'addressTO' + idx );
140 alert(app_strings.LBL_EMAIL_MENU_MAKE_SELECTION);
144 editContact : function() {
145 SE.contextMenus.contactsContextMenu.hide();
146 var element = SE.contactView.getSelectedNodes()[0];
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);
157 * Filters contact entries based on user input
159 filter : function(inputEl) {
160 var ret = new Object();
161 var re = new RegExp(inputEl.value, "gi");
163 for(var i in this._contactCache) {
164 if(this._contactCache[i].name.match(re)) {
165 ret[i] = this._contactCache[i];
169 this.buildContactList(ret);
172 fullForm : function(id, module) {
173 document.location = "index.php?return_module=Emails&return_action=index&module=" + module + "&action=EditView&record=" + id;
177 * returns a formatted email address from the addressBook cache
179 getFormattedAddress : function(id) {
180 var o = this._contactCache[id];
181 var primaryEmail = '';
183 for(var i=0; i<o.email.length; i++) {
184 var currentEmail = o.email[i].email_address;
186 if(o.email[i].primary_address == 1) {
187 primaryEmail = o.email[i].email_address;
191 var finalEmail = (primaryEmail == "") ? currentEmail : primaryEmail;
192 var name = new String(o.name);
193 var finalName = name.replace(/(<([^>]+)>)/ig, "").replace(/'/gi,'\'');
194 var ret = finalName + " <" + finalEmail.replace(/'/gi,'\'') + ">";
200 * Sets up async call to query for matching contacts, users, etc.
202 searchContacts : function() {
203 var fn = document.getElementById('input_searchField').value;
204 var pe = document.getElementById('input_searchPerson').value;
206 var rb = document.getElementById('hasRelatedBean').checked;
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;
214 this.addressBookDataModel.params['related_bean_id'] = '';
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);
225 * Clear Search Crieteria For Addressbook
227 clearAddressBookSearch : function() {
228 document.getElementById('input_searchField').value = "";
229 document.getElementById('input_searchPerson').selectedIndex = 0;
233 * Opens modal select window to add contacts to addressbook
235 selectContactsDialogue : function(destId) {
236 if(!this.contactsDialogue) {
237 var dlg = this.contactsDialogue = new YAHOO.widget.Dialog("contactsDialogue", {
241 constraintoviewport: true,
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} ]
246 dlg.setHeader(app_strings.LBL_EMAIL_ADDRESS_BOOK_SELECT_TITLE);
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";
258 dlg.beforeRenderEvent.subscribe(function() {
259 var dd = new YAHOO.util.DDProxy(dlg.element);
260 dd.setHandleElId(dlg.header);
261 dd.on('endDragEvent', function() {
267 var tp = new YAHOO.widget.TabView("contactsSearchTabs");
269 var tabContent = SUGAR.util.getAndRemove("searchForm");
270 tp.addTab(new YAHOO.widget.Tab({
271 label: app_strings.LBL_EMAIL_ADDRESS_BOOK_TITLE,
273 content : tabContent.innerHTML,
274 id : "addressSearchTab",
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();
286 this.contactsDialogue.render();
289 //Quick Compose does not have an innerLayout component and will always be referenced with ix 0.
290 if (typeof(SUGAR.email2.innerLayout) == 'undefined')
294 var activePanel = SUGAR.email2.innerLayout.get("activeTab").get("id");
295 var idx = activePanel.substring(10);
297 SE.addressBook.idx = idx;
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;
308 document.getElementById('relatedBeanColumn').style.display = 'none';
309 document.getElementById('hasRelatedBean').checked = false;
312 if (!SE.addressBook.grid)
314 if (hasRelatedBeanId) {
315 document.getElementById('hasRelatedBean').checked = true;
317 AddressSearchGridInit();
318 SE.addressBook.relatedBeanId = relatedBeanId;
322 if (typeof(relatedBeanId) != 'undefined' && relatedBeanId != SE.addressBook.relatedBeanId)
324 SE.addressBook.relatedBeanId = relatedBeanId;
325 document.getElementById('hasRelatedBean').checked = true;
327 if (document.getElementById('hasRelatedBean').checked == true)
329 SE.addressBook.addressBookDataModel.params['related_bean_id'] = relatedBeanId;
330 SE.addressBook.addressBookDataModel.params['related_bean_type'] = relatedBeanType;
332 SE.addressBook.addressBookDataModel.params['related_bean_id'] = '';
333 SE.addressBook.addressBookDataModel.params['related_bean_type'] = '';
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);
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());
343 SE.addressBook.populateResulstTableEmailAddresses();
345 this.contactsDialogue.show();
348 * Clear all email addresses from result table.
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();
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.
361 populateResulstTableEmailAddresses: function () {
363 var idx = SE.addressBook.idx;
364 var emailFields = ['to','cc','bcc'];
366 for(var k=0;k<emailFields.length;k++)
368 var elKey = 'address' + emailFields[k].toUpperCase() + idx;
369 var allEmails = document.getElementById(elKey).value;
373 var formatedEmails = SE.composeLayout._getEmailArrayFromString(allEmails);
375 for (var i=0; i<formatedEmails.length; i++)
377 var t_name = formatedEmails[i].name;
378 var t_emailAddr = formatedEmails[i].email_address;
379 var displayEmail = t_name + ' <' + t_emailAddr + '>';
381 t_name = displayEmail = t_emailAddr;
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});
391 * Checks all entries in the result table against a particular email address, returning true
392 * if the email address is found, false otherwise.
394 doesEmailAdddressExistInResultTable: function(emailAddress)
396 if(trim(emailAddress) == '')
399 var emailAddressFound = false;
400 var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
401 for (var i=0; i < contacts.length; i++)
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)
407 emailAddressFound = true;
412 return emailAddressFound;
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.
418 populateEmailAddressFieldsFromResultTable: function()
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++)
425 var elKey = 'address' + emailFields[k].toUpperCase() + idx;
426 document.getElementById(elKey).value = "";
429 var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
430 for (var i=0; i < contacts.length; i++)
432 var data = SE.addressBook.gridResults.getRecord(contacts[i]).getData();
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
441 var target = document.getElementById(target_id);
442 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, data.display_email_address);
445 //Delete all rows from the result set table
446 SUGAR.email2.addressBook.gridResults.deleteRows(0, SUGAR.email2.addressBook.gridResults.getRecordSet().getLength());
449 SE.addressBook.contactsDialogue.hide()
452 * Insert contacts into the result table.
454 insertContactToResultTable : function(event,address_type) {
456 var contactsDialogue = SE.addressBook.contactsDialogue;
457 var contacts = SE.addressBook.grid.getSelectedRows();
459 var rows = SUGAR.email2.addressBook.grid.getRecordSet().getRecords();
460 for (var i = 0; i < rows.length; i++)
462 if (typeof(rows[i]) != "undefined" && rows[i].getData().checked )
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);
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;
478 insertContactRowToResultTable : function(rowId, addressType) {
479 var data = SE.addressBook.grid.getRecord(rowId).getData();
480 if(SE.addressBook.doesGridResultsEntryExist(data.email) )
482 var name = data.name.replace(/'/gi,'\'').replace(/"/gi,'"');
483 var ea = name + ' <' + data.email.replace(/'/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});
489 * Remove a row from the gridsResult table.
491 removeRowFromGridResults : function(rowId,emailAddress)
493 var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
494 for (var i=0; i < contacts.length; i++)
496 var rec = SE.addressBook.gridResults.getRecord(contacts[i]);
497 var data = rec.getData();
498 if(data.email_address == emailAddress)
500 SUGAR.email2.addressBook.gridResults.deleteRow(rec.getId());
505 SUGAR.email2.addressBook.toggleSearchRowIcon(rowId,true);
508 * Translates between the addressType To|Cc|Bcc labels/keys.
510 translateAddresType: function(addressType,fromKey)
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 = {};
518 mappingObject = {'to':displayTo, 'cc':displayCc, 'bcc':displayBcc};
521 mappingObject[displayTo] = 'to'; //Cant use object literal with variable variable.
522 mappingObject[displayCc] = 'cc';
523 mappingObject[displayBcc] = 'bcc';
526 return typeof(mappingObject[addressType]) != 'undefined' ? mappingObject[addressType] : '';
532 toggleSearchRowIcon : function(rowId,show)
536 var idToShow = rowId + '_add_img';
537 var idToHide = rowId + '_rm_img';
541 var idToShow = rowId + '_rm_img';
542 var idToHide = rowId + '_add_img';
546 Dom.addClass(idToHide, "yui-hidden");
547 Dom.removeClass(idToShow, "yui-hidden");
550 * Determine if an entry has already been added to the grid results table to prevent duplicates.
552 doesGridResultsEntryExist: function(emailAddrs)
555 var contactExists = false;
556 var contacts = SE.addressBook.gridResults.getRecordSet().getRecords();
557 for (var i=0; i < contacts.length; i++)
559 var data = SE.addressBook.gridResults.getRecord(contacts[i]).getData();
560 if(data.email_address == emailAddrs)
562 contactExists = true;
566 return contactExists;
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
575 smartAddEmailAddressToComposeField : function(concat, addr) {
576 var re = new RegExp(addr);
578 if(!concat.match(re)) {
580 concat += "; " + addr;
589 //// END ADDRESS BOOK
590 ///////////////////////////////////////////////////////////////////////////////
594 ///////////////////////////////////////////////////////////////////////////////
597 * Auto-complete object
601 delimChar : [";", ","],
605 prehighlightClassName : "yui-ac-prehighlight",
608 instances : new Array(),
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
615 getPrimaryAddress : function(contact) {
616 var address = app_strings.LBL_EMAIL_ADDRESS_BOOK_NOT_FOUND;
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;
629 * initializes autocomplete widgets for a given compose view
632 init : function(idx) {
633 var ds = new YAHOO.widget.DS_JSArray(this.returnDataSource(SE.addressBook._contactCache), {
634 "queryMatchContains" : false,
635 "queryMatchSubset" : true
638 this.instances[idx] = {
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);
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);
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);
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);
666 refreshDataSource : function(sType, aArgs) {
667 var textBoxId = aArgs[0].getInputEl().id; // "addressTo0"
669 var refresh = SE.autoComplete.returnDataSource(SE.addressBook._contactCache);
671 if(textBoxId.indexOf("addressTO") > -1 || textBoxId.indexOf("addressCC") > -1) {
672 idx = textBoxId.substr(9);
674 idx = textBoxId.substr(10);
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;
683 * Parses AddressBook entries to return an appropriate DataSource array for YUI.autoComplete
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]);
691 ret[ret.length] = contacts[id].name.replace(/<[\/]*b>/gi, '') + " <" + primary + ">";
692 //ret[ret.length] = contacts[id].name + " <" + primary + ">";
694 for(var emailIndex in contacts[id].email) {
695 ret[ret.length] = contacts[id].email[emailIndex].email_address;
704 * Hides address textareas to prevent autocomplete dropdown from being obscured
706 toggleTextareaHide : function(sType, aArgs) {
707 var textBoxId = aArgs[0]._oTextbox.id; // "addressTo0"
711 if(textBoxId.indexOf("addressTO") > -1) {
713 } else if(textBoxId.indexOf("addressCC") > -1) {
716 idx = textBoxId.substr(9);
718 // follow through if not BCC
720 var cc = document.getElementById("addressCC" + idx);
721 var bcc = document.getElementById("addressBCC" + idx);
725 cc.style.visibility = 'hidden';
727 bcc.style.visibility = 'hidden';
734 * Redisplays the textareas after an address is committed.
736 toggleTextareaShow : function(sType, aArgs) {
737 var textBoxId = aArgs[0]._oTextbox.id; // "addressTo0"
741 if(textBoxId.indexOf("addressTO") > -1) {
743 } else if(textBoxId.indexOf("addressCC") > -1) {
746 idx = textBoxId.substr(9);
748 // follow through if not BCC
750 document.getElementById("addressCC" + idx).style.visibility = 'visible';
751 document.getElementById("addressBCC" + idx).style.visibility = 'visible';
756 //// END AUTOCOMPLETE
757 ///////////////////////////////////////////////////////////////////////////////
759 ///////////////////////////////////////////////////////////////////////////////
762 * expands the options sidebar
765 currentInstanceId : 0,
768 outboundAccountErrors : null,
769 loadedTinyInstances : {}, //Tracks which tinyMCE editors have initalized with html content.
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)
778 var resultArray = SE.composeLayout._getEmailArrayFromString(emailAddressList);
779 var displayArray = [];
780 for (var i=0; i<resultArray.length; i++)
782 var t_name = resultArray[i].name;
783 var t_emailAddr = resultArray[i].email_address;
785 displayArray.push('<br/><' + t_emailAddr + '>');
787 displayArray.push(t_name + '<br/><' + t_emailAddr + '>');
790 var result = displayArray.join('<br/>');
792 linkElement.style.display = "inline";
793 linkElement.style.height="10px";
794 linkElement.style.overflow="visible";
795 spanElement.innerHTML = result;
798 linkElement.style.display = "none";
803 * Given a string of email address, return an array containing the name portion (if available)
806 _getEmailArrayFromString : function (emailAddressList){
809 while ((results = reg.exec(emailAddressList)) != null)
811 orignial = results[0];
812 parsedResult = results[0].replace(';', ':::::');
813 emailAddressList = emailAddressList.replace (orignial, parsedResult);
817 while ((results = reg.exec(emailAddressList)) != null)
819 orignial = results[0];
820 parsedResult = results[0].replace(',', ':::::');
821 emailAddressList = emailAddressList.replace (orignial, parsedResult);
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 = [];
827 for (var i=0; i<emailArr.length; i++)
829 var rposition = emailArr[i].indexOf('<');
830 var lposition = emailArr[i].indexOf('>');
832 if(trim(emailArr[i]) != '')
834 if(rposition != -1 && lposition != -1)
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});
842 resultsArray.push({'name':'', 'email_address': emailArr[i]});
848 ///////////////////////////////////////////////////////////////////////////
851 * Prepare bucket DIV and yui-ext tab panels
853 _0_yui : function() {
854 var idx = this.currentInstanceId;
856 var composeTab = new YAHOO.SUGAR.ClosableTab({
857 label: mod_strings.LNK_NEW_SEND_EMAIL,
859 content : "<div id='htmleditordiv" + idx + "'/>",
860 id : "composeTab" + idx,
861 closeMsg: app_strings.LBL_EMAIL_CONFIRM_CLOSE,
864 SE.innerLayout.addTab(composeTab);
866 // get template engine with template
867 if (!SE.composeLayout.composeTemplate) {
868 SE.composeLayout.composeTemplate = new YAHOO.SUGAR.Template(SE.templates['compose']);
871 // create Tab inner layout
872 var composePanel = this.getComposeLayout();
873 composePanel.getUnitByPosition("right").collapse();
874 composePanel.autoSize();
878 * Generate the quick compose layout
879 * @method getQuickComposeLayout
880 * @param {Pannel} parentPanel Parent pannel
881 * @param {Object} o Options
884 getQuickComposeLayout : function (parentPanel,o) {
885 var idx = SE.composeLayout.currentInstanceId;
887 //Before rendering the parent pannel we need to initalize the grid layout
888 parentPanel.beforeRenderEvent.subscribe(function() {
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();
897 //Wait until the Compose Layout has rendered, then add the
898 //options tab and perform the tiny initialization.
899 parentPanel.renderEvent.subscribe(function() {
901 YAHOO.util.Event.onAvailable('htmleditordiv' + idx, function() {
902 SE.composeLayout._initComposeOptionTabs(idx);
903 SE.composeLayout[idx].getUnitByPosition("right").collapse();
905 SE.composeLayout._1_tiny(false);
907 //Init templates and address book
908 SE.composeLayout._2_final();
910 SE.composeLayout.quickCreateComposePackage(o);
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);
919 parentPanel.render(document.body);
922 return SE.composeLayout[idx];
925 * Fill in all fields into the quick compose layout.
926 * @method quickCreateComposePackage
927 * @param {Object} o Options
930 quickCreateComposePackage: function(o)
932 //If we have a compose package fill in defaults.
933 if (typeof(o.composePackage) != 'undefined')
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.
941 getComposeLayout : function() {
942 var idx = SE.composeLayout.currentInstanceId;
944 this._createComposeLayout(idx);
945 SE.composeLayout[idx].render();
946 this._initComposeOptionTabs(idx);
948 return SE.composeLayout[idx];
952 * Create the layout manager for the compose window.
954 _createComposeLayout : function(idx)
956 SE.composeLayout[idx] = new YAHOO.widget.Layout('htmleditordiv' + idx, {
957 parent: SE.complexLayout,
967 SE.composeLayout.composeTemplate.exec({
968 'app_strings':app_strings,
969 'mod_strings':mod_strings,
970 'linkbeans_options' : linkBeans,
971 'idx' : SE.composeLayout.currentInstanceId
982 body: "<div class='composeRightTabs' id='composeRightTabs" + idx + "'/>",
985 header: app_strings.LBL_EMAIL_OPTIONS
991 * Create compose tab which will populate the 'right' container in the compose window.
993 _initComposeOptionTabs : function(idx)
995 var cTabs = new YAHOO.widget.TabView("composeRightTabs" + idx);
996 var tab = new YAHOO.widget.Tab({
997 label: app_strings.LBL_EMAIL_ATTACHMENT,
999 content : SUGAR.util.getAndRemove("divAttachments" + idx).innerHTML,
1000 id : "divAttachments" + idx,
1004 tab.layout = SE.composeLayout[idx];
1006 tab.on("activeChange", function(o){
1008 this.layout.getUnitByPosition("right").set("header", app_strings.LBL_EMAIL_ATTACHMENT);
1014 tab = new YAHOO.widget.Tab({
1015 label: app_strings.LBL_EMAIL_OPTIONS,
1017 content : SUGAR.util.getAndRemove("divOptions" + idx).innerHTML,
1018 id : "divOptions" + idx,
1022 tab.layout = SE.composeLayout[idx];
1023 tab.on("activeChange", function(o){
1025 this.layout.getUnitByPosition("right").set("header", app_strings.LBL_EMAIL_OPTIONS);
1030 SE.composeLayout[idx].autoSize = function() {
1031 var pEl = this.get("element").parentNode.parentNode.parentNode;
1032 this.set("height", pEl.clientHeight-30);
1036 SE.composeLayout[idx].rightTabs = cTabs;
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);
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);
1060 callopenpopupForEmail2 : function(idx,options) {
1062 var formName = 'emailCompose' + idx;
1064 if(typeof(options) != 'undefined' && typeof(options.form_name) != 'undefined')
1065 formName = options.form_name;
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)) {
1072 open_popup(document.getElementById('data_parent_type' + idx).value,600,400,'&tree=ProductsProd',true,false,
1074 call_back_function:"SE.composeLayout.popupAddEmail",
1076 field_to_name_array:{
1077 id:'data_parent_id' + idx,
1078 name:'data_parent_name' + idx,
1083 popupAddEmail : function(o)
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")
1090 var target = Dom.get("addressTO" + SE.composeLayout.currentInstanceId);
1091 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, data[nameKey] + "<" + data.email1 + ">");
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;
1104 if (!SUGAR.util.isTouchScreen()) {
1105 var t = tinyMCE.getInstanceById(elId);
1107 if(typeof(t) == 'undefined') {
1108 if (!SUGAR.util.isTouchScreen()) {
1109 tinyMCE.execCommand('mceAddControl', false, elId);
1111 YAHOO.util.Event.onAvailable(elId + "_parent", function() {
1112 SE.composeLayout.resizeEditorSetSignature(idx,!isReplyForward);
1117 resizeEditorSetSignature : function(idx,setSignature)
1119 var instance = SE.util.getTiny(SE.tinyInstances.currentHtmleditor);
1121 if(typeof(instance) == 'undefined' || (typeof(SE.composeLayout.loadedTinyInstances[idx]) != 'undefined' && SE.composeLayout.loadedTinyInstances[idx] == false)) {
1122 setTimeout("SE.composeLayout.resizeEditorSetSignature(" + idx + ",'"+setSignature+"');",500);
1126 SE.composeLayout.resizeEditor(idx);
1128 setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",250);
1133 resizeEditor : function(idx)
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);
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";
1149 setTimeout("SE.composeLayout.resizeEditor("+idx+");",1000);
1154 * Initializes d&d, auto-complete, email templates
1156 _2_final : function() {
1157 var idx = SE.composeLayout.currentInstanceId;
1159 if(this.emailTemplates) {
1160 this.setComposeOptions(idx);
1162 //populate email template cache
1163 AjaxObject.target = '';
1164 AjaxObject.startRequest(callbackComposeCache, urlStandard + "&emailUIAction=fillComposeCache");
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;
1173 // auto-complete setup
1174 SE.autoComplete.init(idx);
1177 document.getElementById("addressTO" + idx).focus();
1181 * hide tinyMCE tool bar if send email as plaintext is checked
1183 renderTinyMCEToolBar : function (idx, hide) {
1185 document.getElementById('htmleditor' + idx + '_toolbar1').style.display = 'none';
1187 document.getElementById('htmleditor' + idx + '_toolbar1').style.display = '';
1191 c1_composeEmail : function(isReplyForward, retry) {
1195 if (!SUGAR.util.isTouchScreen() && (typeof(tinyMCE) == 'undefined' || typeof(tinyMCE.settings) == 'undefined')){
1196 setTimeout("SE.composeLayout.c1_composeEmail(" + isReplyForward + ", true);", 500);
1198 this._1_tiny(isReplyForward);
1201 if(isReplyForward) {
1202 this.replyForwardEmailStage2();
1208 * takes draft info and prepopulates
1210 c0_composeDraft : function() {
1211 this.getNewInstanceId();
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;
1223 SE.composeLayout._0_yui();
1224 SE.composeLayout._1_tiny(true);
1227 SE.composeLayout._2_final();
1229 /* Draft-specific final processing. Need a delay to allow Tiny to render before calling setText() */
1230 setTimeout("AjaxObject.handleReplyForwardForDraft(SE.o);", 1000);
1234 * Strip & Prep editor hidden fields
1236 c0_composeNewEmail : function() {
1237 this.getNewInstanceId();
1238 this.c1_composeEmail(false);
1242 * Sends async request to get the compose view.
1243 * Requests come from "reply" or "forwards"
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;
1252 if(mbox == 'sugar::Emails') {
1253 SE.composeLayout.replyForwardObj.sugarEmail = true;
1256 SE.composeLayout.getNewInstanceId();
1257 SE.composeLayout.c1_composeEmail(true);
1259 //// END COMPOSE FLOW
1260 ///////////////////////////////////////////////////////////////////////////
1263 * Called when a contact, email, or mailinglist is dropped
1264 * into one of the compose fields.
1266 handleDrop : function (source, event, data, target) {
1269 target = event.getTarget();
1271 data.nodes = [data.nodes];
1275 target = document.getElementById(target);
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();
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(/ /gi, '');
1288 email = email.replace('<', '<').replace('>', '>');
1289 var tr = source.getTrEl(nodes[i]);
1290 while (tr && !Dom.hasClass(tr, "address-contact")) {
1291 tr = source.getPreviousTrEl(tr);
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;
1299 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, email);
1305 /////////////////////////////////////////////////////////////////////////////
1306 /// EMAIL TEMPLATE CODE
1307 applyEmailTemplate : function (idx, id) {
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;
1315 var to_addr = document.getElementById('addressTO'+idx);
1316 if (to_addr.value.search(/[^;,]{6,}[;,][^;,]{6,}/) != -1)
1318 box_title = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_WARNING_TITLE;
1319 box_msg = mod_strings.LBL_EMAILTEMPLATE_MESSAGE_MULTIPLE_RECIPIENTS + '<br /><br />' + box_msg;
1322 // id is selected index of email template drop-down
1323 if(id == '' || id == "0") {
1324 YAHOO.SUGAR.MessageBox.show({
1329 if(btn=='no'){return;};
1330 SUGAR.email2.composeLayout.processNoneResult(idx, id);},
1337 YAHOO.SUGAR.MessageBox.show({
1342 if(btn=='no'){return;};
1343 SUGAR.email2.composeLayout.processResult(idx, id);},
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);
1356 tinyHTML = tinyHTML.substr(start);
1357 tiny.setContent(tinyHTML);
1359 tiny.setContent('');
1361 //now that content is set, call method to set signature
1362 setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",500);
1365 processResult : function(idx , id){
1366 var post_data = {"module":"EmailTemplates","record":id};
1367 var global_rpcClient = new SugarRPCClient();
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();
1374 // get attachments if any
1375 AjaxObject.target = '';
1376 AjaxObject.startRequest(callbackLoadAttachments, urlStandard + "&emailUIAction=getTemplateAttachments&parent_id=" + id);
1379 appendEmailTemplateJSON : function() {
1380 var idx = SE.composeLayout.currentInstanceId; // post increment
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'] != '' )
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']));
1390 if(json_objects['email_template_object']['fields']['text_only'] == 1)
1392 text = "<p>" + decodeURI(encodeURI(json_objects['email_template_object']['fields']['body'])).replace(/<BR>/ig, '</p><p>').replace(/<br>/gi, "</p><p>").replace(/&/gi,'&').replace(/</gi,'<').replace(/>/gi,'>').replace(/'/gi,'\'').replace(/"/gi,'"') + "</p>";
1393 document.getElementById('setEditor' + idx).checked = true;
1394 SUGAR.email2.composeLayout.renderTinyMCEToolBar(idx, 1);
1398 text = decodeURI(encodeURI(json_objects['email_template_object']['fields']['body_html'])).replace(/<BR>/ig, '\n').replace(/<br>/gi, "\n").replace(/&/gi,'&').replace(/</gi,'<').replace(/>/gi,'>').replace(/'/gi,'\'').replace(/"/gi,'"');
1399 document.getElementById('setEditor' + idx).checked = false;
1400 SUGAR.email2.composeLayout.renderTinyMCEToolBar(idx, 0);
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);
1411 var htmlPart2 = tinyHTML.substr(start);
1412 tinyHTML = text + htmlPart2;
1413 tiny.setContent(tinyHTML);
1415 tiny.setContent(text);
1417 //now that content is set, call method to set signature
1418 setTimeout("SUGAR.email2.composeLayout.setSignature("+idx+");",500);
1422 * Writes out the signature in the email editor
1424 setSignature : function(idx)
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);
1437 var sel = document.getElementById('signatures' + idx);
1439 var sel = document.getElementById('signature_id');
1440 idx = SE.tinyInstances.currentHtmleditor;
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);
1452 signature = sel.options[sel.selectedIndex].value;
1457 // The tags are used for finding the signature
1458 var openTag = '<div id="signature-begin"><br />';
1459 var closeTag = '<span> </span></div>';
1461 // Get tinyMCE instance
1462 var t = tinyMCE.getInstanceById('htmleditor' + idx);
1465 if(typeof(t) != 'undefined')
1467 t.contentDocument = t.contentWindow.document;
1468 var html = t.getContent();
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);
1481 end += closeTag.length;
1485 end = htmllow.length;
1488 // selected "none" - remove signature from email
1489 if (signature == '')
1493 var htmlPart1 = html.substr(0, start);
1494 var htmlPart2 = html.substr(end, html.length);
1496 html = htmlPart1 + htmlPart2;
1499 SE.signatures.lastAttemptedLoad = '';
1503 if (!SE.signatures.lastAttemptedLoad) // lazy load place holder
1505 SE.signatures.lastAttemptedLoad = '';
1508 SE.signatures.lastAttemptedLoad = signature;
1510 if (typeof(SE.signatures[signature]) == 'undefined')
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);
1520 var newSignature = this.prepareSignature(SE.signatures[signature]);
1522 // clear out old signature
1523 if (SE.signatures.lastAttemptedLoad && start > -1)
1525 var htmlPart1 = html.substr(0, start);
1526 var htmlPart2 = html.substr(end, html.length);
1528 html = htmlPart1 + htmlPart2;
1532 start = html.indexOf('<div><hr></div>');
1533 if (SE.userPrefs.signatures.signature_prepend == 'true' && start > -1) // Prepend
1535 var htmlPart1 = html.substr(0, start);
1536 var htmlPart2 = html.substr(start, html.length);
1537 var newHtml = htmlPart1 + openTag + newSignature + closeTag + htmlPart2;
1539 else if (SUGAR.email2.userPrefs.signatures.signature_prepend == 'true') // Prepend
1544 //remove custom spacing
1545 var spacing = '<span id="spacing"><br /><br /><br /></span> ';
1546 var customSpacingStart = html.indexOf(spacing);
1548 if (customSpacingStart > -1)
1550 var part1 = newHtml.substr(0, customSpacingStart);
1551 var part2 = newHtml.substr(customSpacingStart+spacing.length, newHtml.length);
1552 newHtml = part1 + part2;
1556 var bodyStartTag = '<body>';
1557 var body = newHtml.indexOf(bodyStartTag);
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;
1567 newHtml = openTag + newSignature + closeTag + newHtml;
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)
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;
1586 var body = html.indexOf('</body>');
1589 var part1 = html.substr(0, body);
1590 var part2 = html.substr(body, html.length);
1591 var newHtml = part1 + openTag + newSignature + closeTag + part2;
1595 var newHtml = html + openTag + newSignature + closeTag;
1598 t.setContent(newHtml);
1602 prepareSignature : function(str) {
1603 var signature = new String(str);
1605 signature = signature.replace(/</gi, '<');
1606 signature = signature.replace(/>/gi, '>');
1612 showAttachmentPanel : function(idx) {
1613 var east = SE.composeLayout[idx].getUnitByPosition("right");
1614 var tabs = SE.composeLayout[idx].rightTabs;
1616 tabs.set("activeTab", tabs.getTab(0));
1620 * expands sidebar and displays options panel
1622 showOptionsPanel : function(idx) {
1623 var east = SE.composeLayout[idx].getUnitByPosition("right");
1624 var tabs = SE.composeLayout[idx].rightTabs;
1626 tabs.set("activeTab", tabs.getTab(1));
1630 * Selects the Contacts tab
1632 showContactsPanel : function() {
1633 SE.complexLayout.regions.west.showPanel("contactsTab");
1637 * Generates fields for Select Document
1639 addDocumentField : function(idx) {
1640 var basket = document.getElementById('addedDocuments' + idx);
1642 var index = (basket.childNodes.length / 7) - 1;
1649 var test = document.getElementById('documentId' + idx + index);
1651 while(test != null) {
1653 test = document.getElementById('documentId' + idx + index);
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 + "' />" +
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>" +
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>" +
1673 basket.appendChild(documentCup);
1674 //basket.innerHTML += out;
1679 * Makes async call to save a draft of the email
1680 * @param int Instance index
1682 saveDraft : function(tinyInstance) {
1683 SE.tinyInstances.currentHtmleditor = 'htmleditor' + tinyInstance;
1684 this.sendEmail(tinyInstance, true);
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';
1692 win = SUGAR.util.openWindow(URL, windowName, windowFeatures);
1694 // put the focus on the popup if the browser supports the focus() method
1700 * Modal popup for file attachment dialogue
1702 addFileField : function() {
1703 if(!SE.addFileDialog){ // lazy initialize the dialog and only create it once
1704 SE.addFileDialog = new YAHOO.widget.Dialog("addFileDialog", {
1708 constraintoviewport: true,
1710 keylisteners : new YAHOO.util.KeyListener(document, { keys:27 }, {
1711 fn:function(){SE.addFileDialog.hide();}
1714 SE.addFileDialog.setHeader(app_strings.LBL_EMAIL_ATTACHMENTS);
1715 SE.addFileDialog.render();
1716 // SE.addFileDialog.addKeyListener(27, , SE.addFileDialog);
1718 Dom.removeClass("addFileDialog", "yui-hidden");
1720 SE.addFileDialog.show();
1724 * Async upload of file to temp dir
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);
1733 alert(app_strings.LBL_EMAIL_ERROR_NO_FILE);
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
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;
1753 * Removes the bucket div containing the document input fields
1755 deleteDocumentField : function(documentCup) {
1756 var f0 = document.getElementById(documentCup);
1757 f0.parentNode.removeChild(f0);
1761 * Removes a Template Attachment field
1765 deleteTemplateAttachmentField : function(idx, index) {
1766 // create not-in-array values for removal filtering
1767 var r = document.getElementById("templateAttachmentsRemove" + idx).value;
1773 r += document.getElementById('templateAttachmentId' + idx + index).value;
1774 document.getElementById("templateAttachmentsRemove" + idx).value = r;
1776 var target = 'templateAttachmentCup' + idx + index;
1777 d = document.getElementById(target);
1778 d.parentNode.removeChild(d);
1782 * Async removal of uploaded temp file
1783 * @param string index Should be a concatenation of idx and index
1786 deleteUploadAttachment : function(index, file) {
1787 var d = document.getElementById('email_attachment_bucket' + index);
1788 d.parentNode.removeChild(d);
1790 // make async call to delete cached file
1791 AjaxObject.target = '';
1792 AjaxObject.startRequest('', urlStandard + "&emailUIAction=removeUploadedAttachment&file="+unescape(file));
1796 * Attaches files coming from Email Templates
1798 addTemplateAttachmentField : function(idx) {
1800 document.getElementById('templateAttachmentsTitle' + idx).style.display = 'block';
1802 var basket = document.getElementById('addedTemplateAttachments' + idx);
1805 var index = basket.childNodes.length;
1812 var out = "<div id='templateAttachmentCup" + idx + index + "'>" +
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 + "\");'/>" +
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 + "' />" +
1822 "<input type='hidden' value='" + "' name='templateAttachmentId" + idx + index + "' id='templateAttachmentId" + idx + index + "' />" +
1824 "<span id='templateAttachmentName" + idx + index + "'" + "> </span>" +
1825 "<br id='br" + index + "></br>" +
1826 "<br id='brdoc" + index + "></br>" +
1828 basket.innerHTML = basket.innerHTML + out;
1834 * Sends one email via async call
1835 * @param int idx Editor instance ID
1836 * @param bool isDraft
1838 sendEmail : function(idx, isDraft) {
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;
1843 if( typeof(SUGAR.email2.composeLayout.outboundAccountErrors[obAccountID]) != 'undefined' )
1845 SUGAR.showMessageBox(app_strings.LBL_EMAIL_ERROR_DESC, SUGAR.email2.composeLayout.outboundAccountErrors[obAccountID], 'alert');
1850 var form = document.getElementById('emailCompose' + idx);
1851 var composeOptionsFormName = "composeOptionsForm" + idx;
1854 var t = SE.util.getTiny('htmleditor' + idx);
1855 if (t != null || typeof(t) != "undefined") {
1856 var html = t.getContent();
1858 var html = "<p>" + document.getElementById('htmleditor' + idx).value + "</p>";
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;
1870 var el_uid = document.getElementById("uid");
1871 var uid = (el_uid == null) ? '' : el_uid.value;
1873 var el_ieId = document.getElementById("ieId");
1874 var ieId = (el_ieId == null) ? '' : el_ieId.value;
1876 var el_mbox = document.getElementById("mbox");
1877 var mbox = (el_mbox == null) ? '' : el_mbox.value;
1879 if (!isValidEmail(to) || !isValidEmail(cc) || !isValidEmail(bcc)) {
1880 alert(app_strings.LBL_EMAIL_COMPOSE_INVALID_ADDRESS);
1884 if (!SE.composeLayout.isParentTypeAndNameValid(idx)) {
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;
1892 var in_draft = (document.getElementById('type' + idx).value == 'draft') ? true : false;
1893 // baseline viability check
1895 if(to == "" && cc == '' && bcc == '' && !isDraft) {
1896 alert(app_strings.LBL_EMAIL_COMPOSE_ERR_NO_RECIPIENTS);
1898 } else if(subj == '' && !isDraft) {
1899 if(!confirm(app_strings.LBL_EMAIL_COMPOSE_NO_SUBJECT)) {
1902 subj = app_strings.LBL_EMAIL_COMPOSE_NO_SUBJECT_LITERAL;
1904 } else if(html == '' && !isDraft) {
1905 if(!confirm(app_strings.LBL_EMAIL_COMPOSE_NO_BODY)) {
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(/</ig, "sugarLessThan");
1915 html = html.replace(/>/ig, "sugarGreaterThan");
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;
1934 // email attachments
1935 var addedFiles = document.getElementById('addedFiles' + idx);
1937 for(i=0; i<addedFiles.childNodes.length; i++) {
1938 var bucket = addedFiles.childNodes[i];
1940 for(j=0; j<bucket.childNodes.length; j++) {
1941 var node = bucket.childNodes[j];
1942 var nName = new String(node.name);
1944 if(node.type == 'hidden' && nName.match(/email_attachment/)) {
1945 if(form.attachments.value != '') {
1946 form.attachments.value += "::";
1948 form.attachments.value += unescape(node.value);
1955 var addedDocs = document.getElementById('addedDocuments' + idx);
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 += "::";
1966 form.documents.value += node.value;
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 += "::";
1984 form.templateAttachments.value += node.value;
1990 // remove attachments
1991 form.templateAttachmentsRemove.value = document.getElementById("templateAttachmentsRemove" + idx).value;
1993 YAHOO.util.Connect.setForm(form);
1995 AjaxObject.target = 'frameFlex';
1997 // sending a draft email
1998 if(!isDraft && in_draft) {
2000 SE.listView.removeRowByUid(email_id);
2003 var sendCallback = (isDraft) ? AjaxObject.composeLayout.callback.saveDraft : callbackSendEmail;
2004 var emailUiAction = (isDraft) ? "&emailUIAction=sendEmail&saveDraft=true" : "&emailUIAction=sendEmail";
2006 AjaxObject.startRequest(sendCallback, urlStandard + emailUiAction);
2010 * Handles clicking the email address link from a given view
2012 composePackage : function() {
2013 if(composePackage != null) {
2014 SE.composeLayout.c0_composeNewEmail();
2017 if(composePackage.to_email_addrs) {
2018 document.getElementById("addressTO" + SE.composeLayout.currentInstanceId).value = composePackage.to_email_addrs;
2021 if (composePackage.cc_addrs) {
2022 document.getElementById("addressCC" + SE.composeLayout.currentInstanceId).value = composePackage.cc_addrs;
2023 SE.composeLayout.showHiddenAddress('cc', SE.composeLayout.currentInstanceId);
2026 if (composePackage.subject != null && composePackage.subject.length > 0) {
2027 document.getElementById("emailSubject" + SE.composeLayout.currentInstanceId).value = composePackage.subject;
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++)
2034 if ( typeof(composePackage[parentFields[i]]) == 'undefined' )
2035 composePackage[parentFields[i]] = "";
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;
2044 if(composePackage.email_id != null && composePackage.email_id.length > 0) {
2045 document.getElementById("email_id" + SE.composeLayout.currentInstanceId).value = composePackage.email_id;
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);
2052 if (composePackage.attachments != null) {
2053 SE.composeLayout.loadAttachments(composePackage.attachments);
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;
2067 addressFrom.options.add(opt);
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(/&/gi,'&').replace(/</gi,'<').replace(/>/gi,'>').replace(/'/gi,'\'').replace(/"/gi,'"');
2082 //Flag determines if we should clear the tiny contents or just append
2083 if (typeof(composePackage.clearBody) != 'undefined' && composePackage.clearBody)
2085 SE.composeLayout.tinyHTML = '';
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);
2098 //check tinyHTML for closing tags
2099 var body = tinyHTML.lastIndexOf('</body>');
2100 spacing = '<span id="spacing"><br /><br /><br /></span> ';
2104 var part1 = tinyHTML.substr(0, body);
2105 var part2 = tinyHTML.substr(body, tinyHTML.length);
2106 var newHtml = part1 + spacing + composePackage.body + part2;
2110 var newHtml = tinyHTML + spacing + composePackage.body;
2114 SE.composeLayout.tinyHTML = newHtml;
2117 tiny.setContent(SE.composeLayout.tinyHTML);
2118 //Indicate that the contents has been loaded successfully.
2119 SE.composeLayout.loadedTinyInstances[SE.composeLayout.currentInstanceId] = true;
2122 * Confirms closure of a compose screen if "x" is clicked
2124 confirmClose : function(panel) {
2125 if(confirm(app_strings.LBL_EMAIL_CONFIRM_CLOSE)) {
2126 SE.composeLayout.closeCompose(panel.id);
2134 * forces close of a compose screen
2136 forceCloseCompose : function(id) {
2137 SE.composeLayout.closeCompose(id);
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;
2152 * closes the editor that just sent email
2153 * @param string id ID of composeLayout tab
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);
2161 // nullify DOM and namespace values.
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();
2172 //SE.innerLayout.getTab(idx).close();
2176 * Enable the quick search for the compose relate field or search tab
2178 enableQuickSearchRelate: function(idx,overides){
2180 if(typeof overides != 'undefined')
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;
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";
2200 if(typeof sqs_objects == 'undefined')
2201 window['sqs_objects'] = new Array;
2203 window['sqs_objects'][fullName] = {
2206 modules:[newModule],
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"};
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];
2220 if (Dom.get(fullName + "_results")) {
2221 Dom.get(fullName + "_results").parentNode.removeChild(Dom.get(fullName + "_results"));
2227 qsAddAddress : function(o) {
2228 if (o.name != "" && o.email1 != "")
2230 var target = Dom.get("addressTO" + SE.composeLayout.currentInstanceId);
2231 target.value = SE.addressBook.smartAddEmailAddressToComposeField(target.value, o.name + "<" + o.email1 + ">");
2235 * Returns a new instance ID, 0-index
2237 getNewInstanceId : function() {
2238 this.currentInstanceId = this.currentInstanceId + 1;
2239 return this.currentInstanceId;
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.
2245 loadAttachments : function(result) {
2246 var idx = SE.composeLayout.currentInstanceId;
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){
2254 basket.innerHTML = '';
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'];
2270 * fills drop-down values for email templates and signatures
2272 setComposeOptions : function(idx) {
2273 // send from accounts
2274 var addressFrom = document.getElementById('addressFrom' + idx);
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)
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.
2292 var et = document.getElementById('email_template' + idx);
2293 SE.util.emptySelectOptions(et);
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);
2302 var sigs = document.getElementById('signatures' + idx);
2303 SE.util.emptySelectOptions(sigs);
2305 for(var key in this.signatures) { // iterate through assoc array
2306 var display = this.signatures[key];
2307 var opt = new Option(display, key);
2309 if(key == SE.userPrefs.signatures.signature_default) {
2310 opt.selected = true;
2313 sigs.options.add(opt);
2316 // html/plain email?
2317 var htmlEmail = document.getElementById('setEditor' + idx);
2318 if(SE.userPrefs.emailSettings.sendPlainText == 1) {
2319 htmlEmail.checked = true;
2321 htmlEmail.checked = false;
2324 SE.tinyInstances[SE.tinyInstances.currentHtmleditor].ready = true;
2328 * After compose screen is rendered, async call to get email body from Sugar
2330 replyForwardEmailStage2 : function() {
2331 SE.util.clearHiddenFieldValues('emailUIForm');
2332 SUGAR.showMessageBox(app_strings.LBL_EMAIL_RETRIEVING_MESSAGE, app_strings.LBL_EMAIL_ONE_MOMENT);
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;
2340 var sugarEmail = (SE.composeLayout.replyForwardObj.sugarEmail) ? '&sugarEmail=true' : "";
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);
2352 var sendType = type;
2353 AjaxObject.startRequest(callbackReplyForward, urlStandard + "&composeType=" + type + sugarEmail);
2357 * Show the hidden cc or bcc fields
2359 showHiddenAddress: function(addrType,idx){
2361 Dom.removeClass(addrType+"_tr"+idx, "yui-hidden");
2362 Dom.addClass(addrType+"_span"+idx, "yui-hidden");
2363 Dom.addClass("bcc_cc_sep"+idx, "yui-hidden");
2364 this[addrType+'Hidden'+idx] = false;
2366 //After bcc or cc is added, move options below last addr field
2367 Dom.insertAfter("add_addr_options_tr"+idx, 'bcc_tr'+idx);
2369 //If both cc and bcc hidden, remove the empty row containing text.
2370 if( ( typeof(this['ccHidden'+idx]) != 'undefined' && typeof(this['bccHidden'+idx]) != 'undefined')
2371 && ( this['ccHidden'+idx] == false && this['bccHidden'+idx] == false) )
2372 Dom.addClass("add_addr_options_tr"+idx, "yui-hidden");
2374 SE.composeLayout.resizeEditor(idx);
2377 * Hide the cc and bcc fields if they were shown.
2379 hideHiddenAddresses: function(idx){
2381 var addrTypes = ['cc','bcc'];
2382 for(var i = 0;i<addrTypes.length;i++)
2384 Dom.addClass(addrTypes[i] + "_tr"+idx, "yui-hidden");
2385 Dom.removeClass(addrTypes[i] + "_span"+idx, "yui-hidden");
2386 this[addrTypes[i] + 'Hidden'+idx] = true
2389 Dom.removeClass("bcc_cc_sep"+idx, "yui-hidden");
2390 Dom.removeClass("add_addr_options_tr"+idx, "yui-hidden");
2391 Dom.insertBefore("add_addr_options_tr"+idx, 'bcc_tr'+idx);
2395 //// END SE.composeLayout
2396 ///////////////////////////////////////////////////////////////////////////////
2397 ///////////////////////////////////////////////////////////////////////////////
2401 * Cleans serialized UID lists of duplicates
2405 cleanUids : function(str) {
2406 var seen = new Object();
2408 var arr = new String(str).split(",");
2410 for(var i=0; i<arr.length; i++) {
2415 clean += (clean != "") ? "," : "";
2417 seen[arr[i]] = true;
2424 * Clears hidden field values
2425 * @param string id ID of form element to clear
2427 clearHiddenFieldValues : function(id) {
2428 var form = document.getElementById(id);
2430 for(i=0; i<form.elements.length; i++) {
2431 if(form.elements[i].type == 'hidden') {
2432 var e = form.elements[i];
2433 if(e.name != 'action' && e.name != 'module' && e.name != 'to_pdf') {
2441 * Reduces a SELECT drop-down to 0 items to prepare for new ones
2443 emptySelectOptions : function(el) {
2445 for(i=el.childNodes.length - 1; i >= 0; i--) {
2446 if(el.childNodes[i]) {
2447 el.removeChild(el.childNodes[i]);
2454 * Returns the MBOX path in the manner php_imap expects:
2455 * ie: INBOX.DEBUG.test
2456 * @param string str Current serialized value, Home.personal.test.INBOX.DEBUG.test
2458 generateMboxPath : function(str) {
2459 var ex = str.split("::");
2461 /* we have a serialized MBOX path */
2465 for(var i=0; i<ex.length; i++) {
2466 if(ex[i] == 'INBOX') {
2478 /* we have a Sugar folder GUID - do nothing */
2486 * returns a SUGAR GUID by navigating the DOM tree a few moves backwards
2487 * @param HTMLElement el
2488 * @return string GUID of found element or empty on failure
2490 getGuidFromElement : function(el) {
2496 for(var i=0; i<iterations; i++) {
2498 if(el.id.match(SE.reGUID)) {
2510 * Returns the ID value for the current in-focus, active panel (in the innerLayout, not complexLayout)
2513 getPanelId : function() {
2514 return SE.innerLayout.get("activeTab").id ? SE.innerLayout.get("activeTab").id : "Preview";
2518 * wrapper to handle weirdness with IE
2519 * @param string instanceId
2520 * @return tinyMCE Controller object
2522 getTiny : function(instanceId) {
2523 if(instanceId == '') {
2527 var t = tinyMCE.getInstanceById(instanceId);
2531 YAHOO.util.Event.onContentReady(instanceId, function(t) { return t; });
2537 * Simple check for MSIE browser
2541 var nav = new String(navigator.appVersion);
2542 if(nav.match(/MSIE/)) {
2549 * Recursively removes an element from the DOM
2550 * @param HTMLElement
2552 removeElementRecursive : function(el) {
2553 this.emptySelectOptions(el);
2560 sleep : function(secs) {
2561 setTimeout("void(0);", secs);
2565 * Converts a <select> element to an Ext.form.combobox
2567 convertSelect : function(select) {
2568 alert('in convertSelect');
2569 if (typeof(select) == "string") {
2570 select = document.getElementById(select);
2574 findChildNode : function (parent, property, value) {
2575 for (i in parent.children) {
2576 var child = parent.children[i];
2577 if (child.data[property] && child.data[property] == value || child[property] && child[property] == value)
2579 var searchChild = SE.util.findChildNode(child, property, value);
2586 cascadeNodes : function (parent, fn, scope, args) {
2587 for (i in parent.children) {
2588 var child = parent.children[i];
2589 var s = scope ? scope : child;
2590 var a = args ? args : child;
2592 SE.util.cascadeNodes(child, fn, scope, args);
2599 ///////////////////////////////////////////////////////////////////////////////