/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Stefan Preuss <stefan.preuss@open-xchange.com>
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * 
 */

var selAttachment, priority, senderAddress, signature, tmp_attachmentDialog, tmp_attachmentInfostoreDialog, defaultSignature = -1;
var action = "new", myListAddressBook = null, distruGrid = null, msgref = null, autosave = null, undoActive = false;
var internalCache, tmp_attachmentDialog, defaultSignature = -1, activeMailAccount;
var refToMain = corewindow;
var configContainsKey = corewindow.configContainsKey;
var configGetKey = corewindow.configGetKey;
var configSetKey = corewindow.configSetKey;
var options = configGetKey("gui.mail.newmail_options") || { "from": true, "cc": true, "bcc": false, "options": false };
var editorEnabled = (!configGetKey("gui.mail.formatmessage").match(/text\/plain/i));
// not a typo! mail.inlineattachments refers to "Allow html formatted E-Mails?"
var allowHTML = configGetKey("mail.inlineattachments");
// get font settings
var default_font_tynmce = configGetKey("gui.mail.defaul_mail_font");
var default_font_size_tynmce = configGetKey("gui.mail.defaul_mail_font_size");
// required for joining initializing processes
var cb_initMessageContent = null;
var cb_initAddressBook = null;
// passed via window
var aInfoMailAttachmentFile, aInfoMailAttachmentLink;
var multiForward;
var triggerReady = jQuery.noop;

//not valid for all circumstances but sufficient here
function isSafari() { return navigator.userAgent.indexOf("AppleWebKit") >= 0; }
function isFirefox() { return navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') === -1; }

/**
 * callback when tinyMCE get initialized
 */
function cb_initTiny(inst) {	
    /*
     * setting tabindex on the editor iframe content
     */
    if ($ALL(tinyMCE.activeEditor.id+"_ifr")) {
        $ALL(tinyMCE.activeEditor.id+"_ifr").setAttribute("tabIndex", "5");
    }
    /*
     * hide the editor toolbar if the editor has been disabled (e.g. plain/text messages)
     */
	if (!editorEnabled) {    		
		var tdContent = $ALL("editor_content_tbl").getElementsByTagName("td");
		tdContent[0].style.display="none";
		tdContent[1].className="tiny_mceFirst";
	}
}
	
	
/*
 * initialize tinyMCE editor 
 */
tinyMCE.init({
    // global settings
	mode: "textareas",		
	theme: "advanced",
	editor_selector: "mceEditorOX",
	language: (corewindow && configGetKey("language") ? configGetKey("language").substring(0,2) : "en"),		
	plugins: "inlinepopups,table,safari,paste" + (configGetKey("modules.mail.spellcheck") == "true" ? ",spellchecker" : ""),
	content_css: getURL() + "newmail.css",
	
	theme_advanced_buttons1: (editorEnabled ? "newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontselect,fontsizeselect,|,forecolor,backcolor" : "") ,
	theme_advanced_buttons2: (editorEnabled ? "cut,copy,paste,pastetext,pasteword,|,bullist,numlist,|,outdent,indent,|,undo,redo,|,image,link,unlink,|,hr,|,charmap,|,blockquote" + (configGetKey("modules.mail.spellcheck") == "true" ? ",|,spellchecker" : "") : "") ,
    theme_advanced_buttons3: ((editorEnabled && configGetKey("gui.mail.editor_featureset") == 2) ? "tablecontrols,|,sub,sup,|,print|,ltr,rtl" : ""),
	theme_advanced_toolbar_location: "top",
	theme_advanced_toolbar_align: "left",
	theme_advanced_resize_horizontal: false,
	
	extended_valid_elements:
	    "a[name|href|target|title|onclick]," +
	    "img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name]," +
	    "hr[class|width|size|noshade]," +
	    "span[class|align|style]," +
	    "blockquote[type|style],style[type]",
	
	gecko_spellcheck: true,
	button_tile_map: true,
	entity_encoding: "numeric", // lately changed from "raw" to "numeric"
	relative_urls: false,
	convert_urls: false,
	
 	// Needed for 3.x
	forced_root_block: "",
	// depends on whether or not the editor is enabled
	force_br_newlines: false,
	// turned off to prevent duplicate newlines
	force_p_newlines: false,
	// align browser behavior
	remove_linebreaks: true,
	inline_styles: true,
	
	init_instance_callback : "cb_initTiny",
	onBeforeRenderUI: "cb_initTiny",
	file_browser_callback : "cb_mailFileBrowser",
	
	convert_fonts_to_spans : true,
	font_size_style_values : "8pt,10pt,12pt,14pt,18pt,24pt,36pt",
	
    setup: function(ed) {
		// key: enter
		ed.onKeyDown.add(function(ed, e) {
			// pressed enter?
			if ((e.keyCode || e.which) == 13) {
				try {
					// shift key?
					if (e.shiftKey) {
						// add new line
						addNewline(ed);
						tinymce.dom.Event.cancel(e);
					} else {
						// split content
						splitContent(ed, e);
					}
				}
				catch(e) { alert("Ooops! This should not happen. " + e.message); }
			}
		});
	}
});

//-----------------------------------------------------------------------------
// modal dialog
function setModalBusy(state){
    if (state == true){
        ox.api.setModal(true);
        jQuery("#modal-dialog-decorator").addClass("busy");
    }else{
        ox.api.setModal(false);
        jQuery("#modal-dialog-decorator").removeClass("busy");
    }
}

// this one takes care of splitting the preset content
function splitContent(ed, e) {
	// get current range
	var range = ed.selection.getRng();
	// in preset content?
	if(insidePresetContent(range)) {
		// W3C or IE?
		if (range.startContainer) {
			// strategy #1 (W3C compliant)
			splitContent_W3C(ed);
		} else {
			// strategy #2 (IE-specific / IE7 & IE8)
			splitContent_IE(ed);
		}
		// activate "undo" once
		if (!undoActive) {
	    	tinyMCE.activeEditor.execCommand("mceAddUndoLevel");
	    	undoActive = !undoActive;
	    }
		tinymce.dom.Event.cancel(e);
	}
}

function insidePresetContent(range) {
	// get ancestor/parent container
	var container = typeof(range.commonAncestorContainer) == "undefined" ? 
			range.parentElement() : range.commonAncestorContainer;
	// climb up until reaching the body node
	// nodeName is always given in upper case letters
	// check for "container" as well (bug #15435; Safari loses cursor after paste)
	while (container && container.nodeName != "BODY") {
		// found preset content?
		if (container.id == "mcePresetContent") return true;
		// climb up
		container = container.parentNode;
	}
	// never been inside preset content
	return false;
}

//this only works for IE
function splitContent_IE(ed) {
	// get current range
	var range = ed.selection.getRng();
	// get body
	var body = ed.getBody();
	// get two text ranges
    var before = body.createTextRange();
    var after = body.createTextRange();
    // initialize first range & get its content
    before.setEndPoint("EndToStart", range);
    before = before.htmlText;
    // initialize second range & get its content
    after.setEndPoint("StartToEnd", range);
    // BR fix (remove unwanted newline)
    // leading white space in regexp is necessary (don't ask)
    after = after.htmlText.replace(/^(\s*<[^>]+>\s*)<BR\s*\/?>(.*)$/im, '$1$2');
    // create an unique mark
    var mark = "#cursor~mark^";
    // check uniqueness
    while (before.indexOf(mark) >= 0 || after.indexOf(mark) >= 0) {
    	// add random characters until its unique
    	mark += String.fromCharCode(64 + Math.random() * 63);
    }
    // replace editor content
    var markHTML = '<p style="margin: 0px; ' + 
        (editorEnabled ? getStyleTextOfDefaultFont() : '') + 
        '">' + mark + '</p>';
    body.innerHTML = before + markHTML + after;
    // select mark
    range.findText(mark);
    range.select();
    // delete mark (this way!)
    range.pasteHTML("");
    range.collapse(true);
}

// works for FF and Safari
function splitContent_W3C(ed) {
	// get current range
	var range = ed.selection.getRng();
	// range collapsed?
	if (!range.collapsed) {
		// delete selected content now
        ed.execCommand("Delete", false, null);
        // reselect new range
        range = ed.selection.getRng();
    }
	// do magic
	var container = range.commonAncestorContainer;
	var lastBR = null;
    while (container && container.nodeName != "BODY") {
    	// set range to end of container
        range.setEndAfter(container);
        // get parent node
        var p = container.parentNode;
        // add range content before next sibling (or at the end of the parent node)
        var contents = range.extractContents();
        // BR fix (remove unwanted newline)
        var node = contents.firstChild;
        outerloop: while (node) {
        	if (node.hasChildNodes()) {
        		// skip text nodes
        		for(i = 0; i < node.childNodes.length; i++) {
        			if (node.childNodes[i].nodeType == 1) {
        				// follow this node
        				node = node.childNodes[i];
        				continue outerloop;
        			} else if ( node.childNodes[i].nodeType == 3) {
        				// remove zero width space (good for safari)
        				node.childNodes[i].nodeValue = node.childNodes[i].nodeValue.replace('\u200B', '');
        			}
        		}
        	} else if (node.nodeName == "BR") {
        		// remember node
        	    lastBR = node;
        	}
        	break;
        }
        // now insert contents
        if (getTextNodeLength(contents) > 0) {
            // insert this content only if it includes something visible
            // Actually this allows to split a quote after the very last character
            // without getting empty gray blocks below the split
            p.insertBefore(contents, container.nextSibling);
        }
        // climb up
        container = p;
    }
    // last BR?
    if (lastBR) {
        try {
            lastBR.parentNode.removeChild(lastBR);
        } catch(e) { }
    }
	// create new elements
    var dummySpan = ed.getDoc().createElement("span");
    dummySpan.innerHTML = "&nbsp;";
	var para = ed.getDoc().createElement("p");
	para.style.margin = "0px";
	// apply default font settings on new node
	if (editorEnabled) {
	    applyDefaultFontOnNode(para);
	}
	// and both elements to editor
	para.appendChild(dummySpan);
    range.insertNode(para);
    // select the span
    ed.selection.select(dummySpan);
    // and delete it
    ed.execCommand("Delete", false, null);
}

function getTextNodeLength(node) {
    var l = 0;
    for (var i = 0; i < node.childNodes.length; i++) {
        var child = node.childNodes[i];
        // text node?
        if (child.nodeType == 3) {
            // get length after removing white space
            l += child.nodeValue.replace(/\s/g, '').length;
        } else if (child.nodeName && child.nodeName.search(/^(br|img|hr|input)$/i) != -1) {
            // empty tags
            l += 1;
        } else {
            // recurse
            l += getTextNodeLength(child);
        }
    }
    return l;
}

function addNewline(ed) {
	// get current range
	var range = ed.selection.getRng();
	// is IE?
	if (range.pasteHTML) {
		// add BR via pasteHTML
		range.pasteHTML("<br/>&#8203;");
	} else {
		// range collapsed? (= something to delete before)
		if (!range.collapsed) {
			// delete selected content now
	        ed.execCommand("Delete", false, null);
	        // reselect new range
	        range = ed.selection.getRng();
	    }
		// add empty text node (helps setting the cursor properly)
		// \u200B = &#8203; = zero with space
		var textNode = ed.getDoc().createTextNode('\u200B');
		range.insertNode(textNode);
		// add BR as new DOM node
		var br = ed.getDoc().createElement("br");
		range.insertNode(br);
		// put the cursor at the right position (select & delete)
		ed.selection.select(textNode);
		// delete empty text node
        ed.execCommand("Delete", false, null);
	}
}

// -----------------------------------------------------------------------------

var ums = false;
function setBeforeUnload(on) {
    ums = on;
}

window.onbeforeunload = function() {
    if (ums && !isIE()) {
       return _("Do you really want to discard your changes and close the window?");
    }
};

register("Loaded", newMailOnload);
    
register("OX_Cancel_Object", function() {
	function cbyes() {
		oMailFunctionsNew.cleanUp();
		window.onbeforeunload=null;
		window.onunload = null;
		ox.api.window.core.triggerEvent("OX_Window_Close",{handle: url.guid});
		ox.api.window.close(url.guid);
	}
	if (ums) {
		newConfirm(_("Close Window"),
			_("Do you really want to discard your changes and close the window?"),
			AlertPopup.YESNO,null,null,cbyes,null,null,null,function() {});	
	} else {
		cbyes();
	}
});

/**
 * handles resizing of the tinyMCE editor and will be called
 * each time the resized event got triggered
 */
function cb_resize_tinyMCE() {
	/*
	 * getting the iframe element of the editor
	 */
	var editor = $ALL(tinyMCE.activeEditor.id+"_ifr");
	if (!editor) {
		return; // ups, no editor!?
	}
	
	/*
	 * calculates the plain editor height without toolbar
	 * and setting the new height (if it really changed)
	 */
	var editorHeight = $('mailsplitbottom').clientHeight - $ALL("editor_content_tbl").getElementsByTagName("td")[0].offsetHeight;	
	if (editorHeight > 0 && editorHeight != editor.offsetHeight) {
		editor.style.height = editorHeight-1 + "px";
	}
	
    /*
     * works also but ineffective
    tinyMCE.settings.height = $('mailsplitbottom').clientHeight + "px";
    tinyMCE.execCommand('mceRemoveControl', true, 'editor_content');
    tinyMCE.execCommand('mceAddControl', true, 'editor_content');
    */
}

/** 
 * will be executed if the body onload event gots triggered
 */
function newMailWindowOnload() {
    
    // get passed data
    var wd = ox.api.window.getData(url.guid);
    aInfoMailAttachmentFile = wd.aInfoMailAttachmentFile;
    aInfoMailAttachmentLink = wd.aInfoMailAttachmentLink;
    multiForward = wd.multiForward;

	activemodule='mail_new';
	initContents('mail_new');
	initAll();	
	
	window.setTimeout(function() {
		var themeStyle = corewindow.oxThemeStyle || { name: "Default", path: "default" };
	    removeCSSFilesForNewWindow(themeStyle.path);
		loadCSSFileForNewWindow("themes/"+themeStyle.path+"/css/popup.css");
	}, 0);
}

/**
 * handles the initialization of all necessary events, parameter and load
 * contents for the new mail window 
 */
function newMailOnload() {
	
	loadingComplete();
	
	// extend url object
	url = jQuery.extend((url || {}), ox.api.window.getData(url.guid));
	
	// map url given addresses to valid objects
	var tAddr = [ "to", "cc", "bcc" ];
	for (var i=0; i < tAddr.length; i++) {
		if (jQuery.isArray(url[tAddr[i]]) == false) {
			url[tAddr[i]] = [[ null, url[tAddr[i]]]];
		}
	}
	
	// prepare url object and take care
	// everything set correctly
	if (!url.attachments) {
		url.attachments = [{}];
	}
	// wrap any mailtext parameter into valid object
	if (url.mailtext) {
		var attach = url.attachments[0];
		attach.content_type = "text/plain";
		attach.content = url.mailtext;
		url.action = "new";
	}
	
	triggerEvent("OX_Switch_View", "mail_new");	
	
	internalCache = corewindow.internalCache;
	action = url.action;
	
	if (corewindow && corewindow.mail_accounts) {
		var acc =  "default0/INBOX";
		if (url.account) {
			acc = url.account;
		} else {
			if (url.folder && url.folder.length != 0) {
				acc = url.folder;
			} else if (url.id && url.id.length != 0) {
				acc = url.id;
			}
			corewindow.getMailAccountIdByFolder(acc)
		}
		activeMailAccount = corewindow.mail_accounts.data.get(acc);
	}
		
	/*
	 * attach autocomplete to recipient dialogs if enabled in the user options
	 */
	if (configGetKey("gui.mail.autocomplete")) {
		new AutoComplete($('to'), $('AutoCompleteContent'), 25, configGetKey("minimumSearchCharacters"));	
		new AutoComplete($('cc'), $('AutoCompleteContent'), 25, configGetKey("minimumSearchCharacters"));	
		new AutoComplete($('bcc'), $('AutoCompleteContent'), 25, configGetKey("minimumSearchCharacters"));
	}
	
	// enable/disable the header fields
	for (var i in options) {
	   triggerEvent("OX_New_Mail_Show_Additional_Header", i, options[i]);
	}	

	/*
	 * register to resizing event for setting the new height of the tinyMCE editor
	 * when the user resizes the window 
	 */
	resizeEvents.register("Resized", cb_resize_tinyMCE);
	
	// join handlers
	var joint = new Join(finalizeGUI);
	// initialize header (sync)
	initMessageHeader();
	// initialize header (async)
	cb_initMessageContent = joint.add(emptyFunction);
	// initialize address book (async)
	cb_initAddressBook = joint.add(emptyFunction);
	// go!
	initMessageContent(action, url.noimg == "true");
    setTimeout("initAddressBook()", 100);
		
	// disable attachments first
	triggerEvent("SubSelected", []);
	
	// init cb for attachment dialog
	tmp_attachmentDialog = new attachmentDialog(0, cb_attachmentDialog);
	tmp_attachmentInfostoreDialog = new infostoreDialog(0, cb_attachmentDialog);
	addDOMEvent($('addAddressTo'), "mousedown", function(){seachEmailAddress('to')});
	addDOMEvent($('addAddressCc'), "mousedown", function(){seachEmailAddress('cc')});
	addDOMEvent($('addAddressBcc'), "mousedown", function(){seachEmailAddress('bcc')});
	
	/*
	 * if enabled in the user options initialize auto save and register timer which
	 * saves the message each n minutes to the drafts folder 
	 */
	if (!isNaN(configGetKey("gui.mail.auto_save_drafts")) && configGetKey("gui.mail.auto_save_drafts") != 0) {
		window.setInterval("oMailFunctionsNew.autosaveMessage()", ( configGetKey("gui.mail.auto_save_drafts") * 60000 ));	
	}
	
	
	// adding new menu 
	ox.gui.initToolBarNewWindowMail();
}

function finalizeGUI() {
	
    // set focus
	setInitialFocus();
	
	// disable loading screen
    $("loading_data_bg").style.display = "none";

    // bind close button to "Window Close" events for intercom
    window.onunload = function() {
        ox.api.window.core.triggerEvent("OX_Window_Close", {
            handle: url.guid
        });
    };
    
    triggerReady = function () {

        // THIS MUST be called before Window READY !!!
        triggerEvent("OX_menu_Change_Height");
        
        var intercomWindowData = {
            handle: url.guid,
            type: "email",
            id: url.id ? url.id : null,
            folder_id: url.folder ? url.folder : null,
            title: window.document.title
        };
        
        // triggerEvent "Window Ready" for intercom with special intercom Object and whole window object
        ox.api.window.core.triggerEvent("OX_Window_Ready", intercomWindowData, window);
    };
    
    triggerReady();
}

function initAddressBook() {
	// initialized?
	if (!myListAddressBook) {
		var myFields = new Array("to", "cc", "bcc");
		// initialize
		myListAddressBook = new SearchListAddressBook(
				null, "addressbookNewEmail", "newmailSearchResult", 
				null, "newmail_add_list", "newmail_search", myFields
				);
	    myListAddressBook.setAllFolders(
	    		$("newmail-search-all-folders"), $("but_folder")
	    		);
	}
	// build div
	myListAddressBook.buildFloatingDiv('none');
	// join
	cb_initAddressBook();
}

registerView("mail_new", null, 
			function() {
				// set focus
				setInitialFocus();
			}, null,
			function() {
				unregister("cb_mailComboChanged", cb_mailComboChanged);
			},
			function() {
				// set focus
				setInitialFocus();
			}
		);

/**
 * The attachment dialog callback. We take the given objects
 * and convert them into unique attachment objects.
 * @param {attachments} the attachment object
 */
function cb_attachmentDialog(attachments) {
	// nothing to do
	if (!attachments && !attachments.length) return;
	
	// fetch objects and add to global attach obj
	for (var i=0; i<attachments.length; i++) {
		if (!oMailFunctionsNew.attachmentExists(attachments[i].id)) {
			oMailFunctionsNew.addAttachment(
				{id: attachments[i].id, filename: attachments[i].filename, size: null, 
				content_type: attachments[i].content_type || null, 
				msgref: attachments[i].msgref || null, selected: false, 
				infostore: attachments[i].infostore || false});
		}
	}
}

/*
 * The attachment remove callback
 */
register("OX_Object_Attachment_Delete", function () {
    // loop over selection
    var i = 0, list = oMailFunctionsNew.attachObj, removed = false;
    while (i < list.length) {
		if (list[i].selected) {
			oMailFunctionsNew.removeAttachment(list[i].id);
			removed = true;
		} else {
		    i++;
		}
	}
	if (removed) {
	    triggerEvent("SubSelected", []);
	}
});

// -----------------------------------------------------------------------------

register("setSignature", cb_setSignatureText);
function cb_setSignatureText(obj, autoScroll) {

	// adjust parameter
    var autoScroll = typeof autoScroll == "undefined" ? true : autoScroll;
    
	// shortcut to active editor / body
	var ae = tinyMCE.activeEditor;
	var docBody = ae.getBody();
	var presetContent = ae.dom.select("p.mcePresetContent")[0];
	
	// always remove both placeholders now
	// first, remove preceding BR of top signature
	var newlines = ae.dom.select(".mceNewline");
	for (i = 0; i < newlines.length; i++) {
		if (isNewline(newlines[i])) {
			ae.dom.remove(newlines[i]);
		}
	}
	
	// now remove placeholders
	ae.dom.remove('mceSignatureTop');
	ae.dom.remove('mceSignatureBottom');
	
	// position == "none"? (obj is null if no signature is selected)
	if (!obj) { return; }
	
	// prepare signature
	// replace newlines in signature
	var signature_text = replaceLeadingSpaces(obj.signature_text);
	signature_text = signature_text.replace(/\n/g, "<br/>");
	
	// position?
	var tmpPos = (action == "new" || typeof action == "undefined") ? "below" : obj.position;
	// always "below" for new mails (see bug #15111), for example, when sending an infostore link
	switch(tmpPos) {
	
		case "above":
			// add preceding BR
			addBeforeOrBottom(ae, createNewLine(ae));
			// add new DOM node
			var newElement = ae.dom.create('p', {
				'id': 'mceSignatureTop',
				// "style" is important if "allow html" is on in order to 
				// preserve the font settings. "white-space: nowrap" is required 
				// for Ascii-art signatures
				'style':
					'font-family: monospace; white-space: nowrap;' +
					(editorEnabled ? ' margin: 5px 0px 5px 0px;' : ''),
				'class': editorEnabled ? 'mceHTML' : 'mceText'
				}, '');
			addBeforeOrBottom(ae, newElement);
			// create snippet
			var signature_snippet = "" + signature_text + "<br/>";
			// add via DOM (via id - not DOM node - would not work as expected)
			try {
			    ae.dom.setHTML("mceSignatureTop", signature_snippet);
			} catch (e) {
			    // IE throws "type conflict" in rare cases
			}
			// scroll into view
			if (autoScroll) {
			    newElement.scrollIntoView(false);
			    window.scrollTo(0,0);
			}
			break;

		case "below":
			// no break
		default:
			// add preceding BR
			if (editorEnabled) docBody.appendChild(createNewLine(ae));
			// add second preceding BR?
			if (presetContent && !allowHTML) {
				docBody.appendChild(createNewLine(ae));
			}
			// add new DOM node
			var newElement = ae.dom.create('p', {
				'id': 'mceSignatureBottom',
				// see above (top signature)
				'style':
					'font-family: monospace; white-space: nowrap;' +
					(editorEnabled ? ' margin: 5px 0px 5px 0px;' : ''),
				'class': editorEnabled ? 'mceHTML' : 'mceText'
				}, '');
			docBody.appendChild(newElement);
			// create snippet
			var signature_snippet = signature_text + "";
			// add via DOM (via id - not DOM node - would not work as expected)
			try {
			    ae.dom.setHTML("mceSignatureBottom", signature_snippet);
			} catch (e) {
			    // IE throws "type conflict" in rare cases
			}
			// scroll into view
			if (autoScroll) {
			    newElement.scrollIntoView(false);
			    window.scrollTo(0,0);
			}
			break;
	}
	
	// finally - is there a newline before preset content?
	var mceNewlinePreserve = ae.dom.select("p.mceNewlinePreserve")[0];
	var mcePresetContent = ae.dom.select("#mcePresetContent")[0];
	// no?
	if (mcePresetContent && !isNewline(mceNewlinePreserve)) {
		// add newline
		if(!allowHTML) {
			addBeforeOrBottom(ae, createNewLine(ae, "mceNewlinePreserve"));
		}
	}
}

// newlines might be overwritten with user content. Therefore, simply 
// relying on class names or ids might lead to unwanted results
function isNewline(node) {
	if (!node) {
		return false;
	} else {
		if (node.innerHTML == "&nbsp;") {
			return true;
		} else if (node.innerHTML.length > 1) {
			return false;
		} else {
			return true;
		}
	}
}

function createNewLine(activeEditor, className) {
	// default class?
	if (!className) className = "mceNewline";
	// create and return node
	return activeEditor.dom.create('p', {
		'class': className,
		'style': 'margin: 0px;'
		}, '&#8203;');
}

// workarond for safari in order to avoid 
// its NOT_FOUND_ERR (DOM Exception 8)
function addBeforeOrBottom(ae, newNode) {

	// get document
	var docBody = ae.getBody();
	var node = null;
	
	// get node of preceding BR
	node = ae.dom.select("p.mceNewlinePreserve")[0];
	if (!node) {
		// get node of preset content
		node = ae.dom.select("#mcePresetContent")[0];
	}
	if (!node) {
		// add at bottom
		docBody.appendChild(newNode);
	} else {
		// add node
		node.parentNode.insertBefore(newNode, node);
	}
}

function replaceEditorContent(newText) {
    // shortcurt
    var ae = tinyMCE.activeEditor;
	// replace editor content (this way does not overwrite the clipboard)
	var contentBody = ae.dom.select("body");
	// has body element?
	if (contentBody.length > 0) {
		// process HTML first, then replace content
	    ae.dom.setHTML(contentBody[0], ae.dom.processHTML(newText));
		// now cleanup (especially removes font tags)
	    try {
	        // this fixes copy&paste of content that would confuse TinyMCE,
	        // e.g., mixtures of FONT and SPAN tags
	        if (!isSafari()) {
	            // the following command crashes Safari but fortunately Safari
	            // does not need the cleanup in order to fix the aforementioned
	            // copy&paste issue
	            ae.execCommand('mceCleanup');
	        }
	    } catch(e) { alert(e); }
	}
}

// -----------------------------------------------------------------------------

function initMessageHeader() {	
    var aliases = {}, aliases2 = {};
	var defaultName = corewindow.getPersonalFullName();

	// adding e-mail addresses from external accounts to sender addresses list
	if (   configGetKey("modules.mailaccount.module")
	    && corewindow.mail_accounts && corewindow.mail_accounts.ids.length)
	{
        corewindow.mail_accounts.newIterate(corewindow.mail_accounts.ids,
            emptyFunction,
            function(i, data) {
                if (i) {
                    var pn = data.personal ? "\"" + data.personal + "\" " : "";
                    aliases2[pn + "<" + data.primary_address + ">"] =
                        data.primary_address + " (" + data.name + ")";
                } else if (data.personal) {
                    defaultName = data.personal;
                }
            });
	}

    // adding regular aliases from the gui config
    var defaultName = defaultName ? "\"" + defaultName + "\" " : "";
    if (configContainsKey("mail.addresses")) {
        var addrs = configGetKey("mail.addresses");
        for (var i=0; i < addrs.length; i++) {
            aliases[defaultName + "<" + addrs[i] + ">"] = addrs[i];
        }
    }
    
    // put external addresses after regular aliases
    for (var i in aliases2) aliases[i] = aliases2[i];

    var scrollBar = aliases.length > 3 ? 4 : null;
	senderAddress = new ComboBox3(window, "mail.fromCombo", "25em", 0, true, "cb_mailComboChanged", scrollBar);	
	for (var i in aliases) {
		senderAddress.addElement(noI18n(aliases[i]), i);
	}
	senderAddress.getDomNode();
	
	// set the default selected address
	var primaryAddress = defaultName + "<" + configGetKey("mail.sendaddress") + ">";
	if (activeMailAccount && activeMailAccount["primary_address"] && 
			(activeMailAccount["id"] && activeMailAccount["id"] > 0)) {
	    defaultName = activeMailAccount.personal ? "\"" + activeMailAccount.personal + "\" " : "";
		primaryAddress = defaultName + "<" + activeMailAccount.primary_address + ">";
	}
	senderAddress.setKey(primaryAddress);
	
	priority = new ComboBox3(window, "mail.priorityCombo", "13em", 1, true, "cb_mailComboChanged", 3);
    priority.addElement(_("Low"), "4");
    priority.addElement(_("Normal"), "3");
    priority.addElement(_("High"), "2");
	priority.getDomNode();
	register("cb_mailComboChanged", cb_mailComboChanged);
		
    // get number of signatures (+1 = "None")
    var signature_count = configGetKey("gui.mail.signatures").length + 1;
    // add combobox (height depends on number of signatures (max 4 rows)
	signature = new ComboBox3(window, "mail.signatureCombo", "15em", 0, true, "setSignature", Math.min(signature_count, 5));
	// fill combobox
	if (configContainsKey("gui.mail.signatures")) {
        signature.addElement(_("None"), "");
		for (var a=0; a < configGetKey("gui.mail.signatures").length; a++) {
			var signatures = configGetKey("gui.mail.signatures")[a];
            var sig_name = signatures.signature_name.length == 0 ?
                _("Unnamed signature") :
                noI18n(signatures.signature_name);
            signature.addElement(sig_name, signatures);
			if (configGetKey("gui.mail.signatures")[a].signature_default && action != "new") {
				defaultSignature = a;
				signature.selected = a+1;				
			}
		}
	} else {
		signature.addElement("No signature","");
	}
	signature.getDomNode();

	if(!configGetKey("gui.mail.signatures") || ((configGetKey("gui.mail.signatures") && configGetKey("gui.mail.signatures").length == 0))) {
		setTimeout(function() { signature.disable(); }, 0);
	}
	
	if (configGetKey("mail.vcard")) document.getElementsByName("mail.attachVCard")[0].checked=true;	
}

function cb_mailComboChanged() {	
	setBeforeUnload(true);
}

function initMessageContent(m_action, noimg) {
	var addParam = noimg ? "" : "&view=html";
	if (m_action && m_action == "draft") {
		m_action = "get";
		addParam += "&edit=1";
	}
	
	// attached objects?
	var wd = ox.api.window.getData(url.guid);
	if (wd.attachedObjects !== undefined) {
	    // loop objects
	    for (var i = 0; i < wd.attachedObjects.length; i++) {
	        var obj = wd.attachedObjects[i];
	        // attach:
	        oMailFunctionsNew.addObject(obj);
	    }
	}

	// headers
	if (wd.headers) {
	    for (var i in wd.headers) oMailFunctionsNew.headers[i] = wd.headers[i];
    }
	
	if (m_action && m_action != "" && m_action != "new") {
        var sObj = [{ "folder": url.folder, "id": url.id }];
        // multi forward?
        if (jQuery.isArray(multiForward) && multiForward.length) {
        	sObj = multiForward;
        }
		(new JSON()).put(AjaxRoot + "/mail?action=" + m_action + "&session=" + 
			corewindow.session + addParam, sObj, null, 
				function(cb) {
					if (cb && cb.data) {
						if (action != "draft") {
							delete(cb.data.disp_notification_to);
						}
                        // workaround for intercom requirement to set recipient for forwarded messages
                        if (action === "forward") {
                            cb.data.to = url.to ? url.to : [];
                        }
						setMailFields(cb.data);
					}
					// callback
					cb_initMessageContent();
				},
				function(cb) {
                    // mail no longer exists so show alert and then close window
					if (cb && cb.code == "MSG-0032") {
						newAlert(_("Error"), formatError(cb), 
								function() { 
									oMailFunctionsNew.cleanUp();
									corewindow.triggerEvent("OX_Mail_Delete");
									window.onbeforeunload=null;
									window.onunload = null;
									ox.api.window.core.triggerEvent("OX_Window_Close",{handle: url.guid});
									ox.api.window.close(url.guid);
								}
						);
						finalizeGUI();
						return true;
					} else {
					    finalizeGUI();
						return false;
					}
					
				});
		
	} else {
		setMailFields(url);
		
		if (typeof aInfoMailAttachmentFile != "undefined") {
			setMessageText(aInfoMailAttachmentFile);
		} else if (typeof aInfoMailAttachmentLink != "undefined") {
			setMessageText(aInfoMailAttachmentLink);
		}
		
		// callback
		cb_initMessageContent();
	}
}

function setMailFields(mailobject) {
    setRecipients(mailobject.to, "to");
	setRecipients(mailobject.cc, "cc");
	setRecipients(mailobject.bcc, "bcc");
	setSubject(mailobject.subject);
	msgref = mailobject.msgref;
	if (mailobject.nested_msgs && mailobject.nested_msgs.length > 0) {
		for (var i = 0; i < mailobject.nested_msgs.length; i++) {
			var nobj = mailobject.nested_msgs[i];
			var textAttachment = nobj.subject || _("attachment");
			oMailFunctionsNew.addAttachment(
				{ id: nobj.id || "message/rfc822", 
				filename: textAttachment, size: null, 
				content_type: "message/rfc822", 
				msgref: nobj.msgref || null, selected: false,
				disk: nobj.disk || null});
		}
	}
	setMessageText(mailobject.attachments);	
	priority.setKey(mailobject.priority || 1);

    if (mailobject.disp_notification_to && $("acknowledge")) {
        $("acknowledge").checked = true;
    }	
}

function setRecipients(to, field) {
	if (to && to.length && $(field)) {
		$(field).value = corewindow.getAdressString(to, false);
		if (field != "to" && trimStr($(field).value).length != 0) {
		   // don't use trigger event because we won't store the settings! 
		   oMailFunctionsNew.showAdditionalHeaderField(field, true);		  
		}		
	}
}

function setSubject(subject) {
	if (subject) {
	    // set text fields
	    $("subject").value = subject;
	    // set document title as well
	    document.title = _("E-Mail") + ": " + subject;
	}
}

function setMessageText(attachments) {
	
	var mda = $("newMailAttachmentDivExist"), cnn = "";
	for (idfield in attachments) {
		var conArr = attachments[idfield];
		var ct = conArr["content_type"] ? conArr["content_type"].toLowerCase() : "application/unknown";
		var con = conArr["content"];		
		if (ct.search(/text\//) != -1 && con && con != undefined) {
			if (ct.search(/text\/plain/) != -1) {
				cnn += replaceLeadingSpaces(con);
			} else { 
				cnn += con;
			}
		} else {
		 	if(conArr["filename"] && !oMailFunctionsNew.attachmentExists(conArr.id)) {
				oMailFunctionsNew.addAttachment(
					{id: conArr.id || null, filename: conArr.filename || null, 
					size: conArr.size || null, content_type: conArr ? conArr.content_type : null, 
					disp: conArr.disp || null, content: conArr.content || null, 
					disk: conArr.disk || null, selected: false,
					infostore: conArr.infostore || null});
			}
		}
	}
	
	// shortcut to active editor
	var ae = tinyMCE.activeEditor;
	
	// extract body content (always)
	cnn = extractBodyContent(cnn);
	
	// keep as is? if action is empty we assume it's a new message
	// required for API calls
	if ((action == "new" || action == "draft") && cnn.search(/id="mceDummy"/)) {
	    
	    // replace editor content (as is / draft)
        replaceEditorContent(cnn);
	    
	} else {

	    // strip reserved attributes (id/class)
	    cnn = stripReservedAttributes(cnn);

	    // trim last blockquote (BR &#160;)
	    cnn = trimLastBlockquote(cnn);
	
    	// draft or reply?
    	var draft_content = action == "draft" ? "<p>" + cnn + "</p>" : "";
    	var reply_content = action != "draft" ? cnn : "";
    	
    	// clear editor now, set content, preserve/overwrite style
    	// prevents messing up the editor on page reload (e.g., by pressing F5)

    	var monofix = editorEnabled ? 
    					(allowHTML ?
    					// margins & font settings must be defined via inline styles (not CSS) 
    					// in order to survive the server-side mail processing
    					" class='mceHTML' style='margin: 5px 0px 5px 0px;'" : 
    					" class='mceHTML' style='margin: 5px 0px 5px 0px; font-family: monospace;'"
    					) :
    					" class='mceHTML' style='font-family: monospace;'";
    					
    	// add newline before preset content 
    	// go this complicated way to correctly take account of "allowHTML"
    	var newline = !editorEnabled || allowHTML ? "" : '<p class="mceNewlinePreserve">&nbsp;</p>';
    	
    	// default font style
    	var defaultFontStyle = editorEnabled ? getStyleTextOfDefaultFont() : '';
    	
    	// dummy tail (to set the default font at the bottom)
    	var mceDummyTail = editorEnabled ? '<p id="mceDummyTail" style="margin: 0px; ' +
    		defaultFontStyle + '">&#8203;</p>' : '';
    	
    	// build preset content (or draft content)
    	var presetContent = reply_content.length > 0 ?
    				newline + 
    				'<div id="mcePresetContent"' + monofix + '>' +
    				// remove very first BR (not all)
    				reply_content.replace(/^<br\s*\/>/, '') + "</div>" + 
    				mceDummyTail
    			:
    				draft_content;
    				
    	// top dummy node (Firefox needs the span to place the cursor correctly)
    	var mceDummy = '<p id="mceDummy" style="margin: 0px; ' + 
    			defaultFontStyle + '">&#8203;<span></span></p>';
    	
    	// replace editor content
    	replaceEditorContent(mceDummy + presetContent);
	
		// add default signature
    	var signature = configGetKey("gui.mail.signatures")[defaultSignature];
    	if (signature) {
    		cb_setSignatureText(signature, false);
    	} else {
    		// add newline once - prevents firefox cursor bux
    		addBeforeOrBottom(ae, createNewLine(ae, "mceNewlinePreserve"));
    	}
	}
	
	// reselect all content and reset cursor position to top
	ae.selection.select(ae.dom.select("body")[0]);
	ae.selection.collapse(true);

	// show controls?
	if (editorEnabled) {
		try {
			// add controls
			tinyMCE.execCommand('mceAddControl', true, 'editor_content');
		} catch(e) { }
	}
}

function setInitialFocus() {
	// safari needs help to correctly set the focus on the editor 
	// when pressing tab to jump from "subject" to "body"
	var setting_focus = false;
	if (isSafari()) {
		addDOMEvent(tinyMCE.activeEditor.getWin(), "focus", function() {
			// (explicit) set focus on the editor
			if (!setting_focus) {
				setting_focus = true;
				tinyMCE.execCommand('mceFocus', true, 'editor_content');
				setting_focus = false;
			}
		});
	}
	// firefox also needs help with tabbing (in another way)
	if(isFirefox()) {
		addDOMEvent($('subject'), "keypress", function(e) {
			if (e.keyCode == 9 && !e.shiftKey) {
				// set focus on the editor
				firefox_setInitialFocus();
				// cancel native event
				e.preventDefault();
			}
		});
	}
	
	// who needs the focus?
	if (url["action"] != "reply" && url["action"] != "replyall") {
		// set focus on "To" field
		window.focus();
		$("to").focus(); // ok for FF and IE
		$("to").select(); // safari needs this
	} else {
		if (isFirefox()) {
			// firefox needs an extra invitation to set the focus
			// on the dummy node (instead of the body element)
			// in order to apply the font settings correctly
			firefox_setInitialFocus();
		} else {
			// set focus on the editor
			tinyMCE.execCommand('mceFocus', true, 'editor_content');
		}
	}
}

function firefox_setInitialFocus() {
	var ae = tinyMCE.activeEditor;
	//ae.getWin().focus(); // Commented for bug fix #1536 (large blinking cursor in FF; cp. bug fix #14483)
	if (ae.getWin().getSelection) {
		var sel = ae.getWin().getSelection();
		// 	select dummy node
		var dummy = ae.dom.select('#mceDummy span')[0];
		if (!dummy) {
			// select very first element
			dummy = ae.dom.select('body > *')[0];
			// added for drafts which might no longer contain #mceDummy
			// otherwise a large blinking cursor appears and stays visible until first keystroke
		}
		if (dummy) {
			var range = sel.getRangeAt(0);
			// set range & collapse
			range.selectNode(dummy);
			range.collapse(true);
			// now the cursor position is correct
		}
	}
	// switch window focus back and forth (bug fix #14483)
	window.focus();
	ae.getWin().focus();
	// now the cursor appearance is fine
}

function setDefaultFont() {
	// best way is setting defaults directly
	// (instead of mceCommand - safari does not like that way)
	// 1. get dummy node
	var dummyNode = tinyMCE.activeEditor.dom.select("p#mceDummy")[0];
	applyDefaultFontOnNode(dummyNode);
	var dummyTail = tinyMCE.activeEditor.dom.select("p#mceDummyTail")[0];
	applyDefaultFontOnNode(dummyTail);
}

function applyDefaultFontOnNode(node) {
	if (node) {
		// set font family
		if (default_font_tynmce && default_font_tynmce != 'default') {
			node.style.fontFamily = default_font_tynmce;
		}
		// convert index to PT and set font size
		if (default_font_size_tynmce && default_font_size_tynmce != 'default') {
			var PT = [8,10,12,14,18,24,36];
			node.style.fontSize = PT[default_font_size_tynmce-1] + "pt";
		}
	}
}

// needed for breaking quotes
function getStyleTextOfDefaultFont() {
	var styleText = "";
	// set font family
	if (default_font_tynmce && default_font_tynmce != 'default') {
		styleText += "font-family: " + default_font_tynmce + "; "
	}
	// convert index to PT and set font size
	if (default_font_size_tynmce && default_font_size_tynmce != 'default') {
		var PT = [8,10,12,14,18,24,36];
		styleText += "font-size: " + PT[default_font_size_tynmce-1] + "pt; ";
	}
	return styleText;
}

function extractBodyContent(str) {
    // returns the body's inner HTML
    var start = str.toLowerCase().indexOf("<body");
    var end = str.toLowerCase().lastIndexOf("</body");
    return (start != -1 && end != -1) ? str.substring(str.indexOf(">", start)+1, end) : str;
}

function stripReservedAttributes(str) {
	// removes reserved ids and class names from the given string	
	return str.replace(
			/\s*(id|class)\=\"mce(Signature\w+|PresetContent|Text|HTML|Dummy\w*|Newline\w*)\"/ig, ''
			);
}

function trimLastBlockquote(str) {
	return str.replace(/<\/blockquote>\s*(<br\/?>\s*&#160;)[\s\S]*$/i, '');
}


function MailFunctions() {
	this.oo = new Object();
	this.headers = new Object();
	this.ooA = new Object();
	this.attachContainer = new Array();
	this.attachObj = new Array();
    this.savePending = false;
}

MailFunctions.prototype = {

	sendMessage : function(param) {
		var Self = this;	
        if (this.savePending) return;
		if (!param) { 
            Self.getMailValues();
        }
		function cb_ok() {
			Self.savePending = true;
	        if (action == "forward")
                Self.oo["sendtype"] = 2;
	        else if (action == "reply" || action == "replyall")
	            Self.oo["sendtype"] = 1;
	        Self.sAction  = AjaxRoot + "/mail?action=new&session=" + corewindow.session;
	        var sTargetParam = 'mailSendCB';
	        if(!isEmpty(Self.headers))
	        	Self.oo.headers = Self.headers;
	        setModalBusy(true);
	        tmp_attachmentDialog.do_post(Self.oo,callback_new,Self.sAction,sTargetParam,setModalBusy);
		}
        if (trimStr(Self.oo["subject"]).length == 0) {
	          newInput(_("Send"),_("You did not enter a subject. Would you like to enter a subject now?"), 
	                   null, "", AlertPopup.OKCANCEL, function() {
                   	   	   $("subject").value = $("create_window_text_field").value;
                           Self.oo["subject"] = $("create_window_text_field").value;
                           cb_ok();
	                   }, null, null, null, "input", null);
        } else {
        	cb_ok();
        }
	},
	
	saveMessageAsDraft : function() {
		this.getMailValues();
		this.oo["flags"] = 4;
		this.sendMessage("fromDraft");
	},
	
	autosaveMessage: function() {		
		if (!ums) return; // nothing changed, so no need to save anything
		this.getMailValues();
		delete(this.oo["attachments"]);
		delete(this.oo["infostore_ids"]);
		if (autosave) {
			this.oo["msgref"] = autosave;
		} else {
			delete(this.oo["msgref"]);
		}
		this.oo["flags"] = 4;
		// refill message text
		this.oo["attachments"] = [{
		    content_type: configGetKey("gui.mail.formatmessage"),
            content: tinyMCE.activeEditor.getContent()
            }];
		// send to server
		new JSON().put(AjaxRoot + "/mail?action=autosave&session=" + corewindow.session, this.oo, null, 
		function(reply) {
			if (!reply.error && reply.data) {
                corewindow.updateDraftsFolderCache(autosave, reply.data);
                autosave = reply.data;
			}
		});
	},
	
	cleanUp: function() {						
		// delete autosave message if necessary
		if (autosave) {
			corewindow.deleteAutosaveMessage(
			     configGetKey("mail.folder.drafts"), 
			     String(autosave.match(/[0-9]+$/g)), true);
		}
	},
	
	getEditorContent: function() {
		
	    /*
	     * normalize editor (permanent changes)
	     */
	    
	    // get ref to jQuery
	    var $ = jQuery;
	    
	    // get document
	    var d = $("#editor_content_ifr")[0].contentDocument;

	    // remove empty SPANs
	    $("span:empty", d).remove();

	    // find paragraphs with just one BR inside / replace with NBSP
	    $($.grep(
	        $("p > br, p > span > br", d), function (node) {
	            return $(node).parent().children().length === 1;
	        })
	    ).replaceWith("&#160;");
	    
	    // remove empty SPANs (again)
        $("span:empty", d).remove();
	    
		/*
		 * clean up editor's content
		 * must be done with a plain copy of the editor's content
		 * since auto-save comes around here (so do not work directly on the DOM!)
		 */ 
		var editorContent = tinyMCE.activeEditor.getContent();
		// skip if in "save as draft" mode
		if (action != "draft") {
            // 1. remove special char (needed for safari)
            editorContent = editorContent.replace(/&#8203;/g, '');
            // 2. remove empty dummy tail
            var occurences = editorContent.match(/<p id="mceDummyTail"[^>]*>/ig);
            // check if this only occurs once (otherwise the user has edited the bottom of the mail)
            if (occurences && occurences.length == 1) {
            	// remove this one
            	editorContent = editorContent.replace(/<p id="mceDummyTail"[^>]*>(\s|&#160;)*<\/p>/ig, '');
            }
		    // 3. remove internal ids/classes
    		editorContent = stripReservedAttributes(editorContent);
    		// 5. convert P and DIV to SPAN (in plain text mode)
    		if (!editorEnabled) {
    			editorContent = editorContent.replace(/<p[^>]*>|<div[^>]*>/ig, '<span>');
    			editorContent = editorContent.replace(/<\/\s*p>|<\/\s*div>/ig, '</span><br/>');
    		}
		}
		return editorContent;
	},
	
	getMailValues : function() {		
		this.clearObjectContent();		
		this.oo["from"] = senderAddress.getKey();
		this.oo["to"] = $("to").value;
		this.oo["cc"] = $("cc").value;
		this.oo["bcc"] = $("bcc").value;
		this.oo["subject"] = $("subject").value;
		this.oo["priority"] = priority.getKey();
		if (msgref != null){
			this.oo["msgref"] = msgref;
		}
		
		if (document.getElementsByName("acknowledge")[0].checked)
			this.oo["disp_notification_to"] = document.getElementsByName("acknowledge")[0].checked;
		 		
		if(document.getElementsByName("mail.attachVCard")[0].checked)
			this.oo["vcard"] = 1;
	
		// get content type
		this.ooA["content_type"] = action == "draft" && allowHTML ? "text/html" : configGetKey("gui.mail.formatmessage");
		// get content
		this.ooA["content"] = this.getEditorContent();
			
		// build the final attachment object
		var tmp_attachObj = new Array(), infoAttaches = new Array();
		tmp_attachObj.push(this.ooA); // always message text first
		for (var i=0; i<this.attachObj.length; i++) {
		    var attachObj = this.attachObj[i];
		    if (attachObj.infostore) {
		        infoAttaches.push(attachObj.id);
            } else if (attachObj.type == "file" && attachObj.content_type != null) {
				tmp_attachObj.push(attachObj);
			}
		}
		this.oo["attachments"] = tmp_attachObj;
		
		// attach infostore references
		if (infoAttaches.length != 0) {
		    this.oo["infostore_ids"] = infoAttaches;
		}
		
		// build the final data source object
		var tmp_attachObj = new Array();
        for (var i=0; i<this.attachObj.length; i++) {
            var attachObj = this.attachObj[i];
            if (attachObj.type == "datasource") {
                tmp_attachObj.push(attachObj.data);
            }
        }
        this.oo["datasources"] = tmp_attachObj;
	},
	
	clearObjectContent: function() {
		this.oo = {};
		this.ooA = {};
	},
	
	addAttachment: function(attachObj) {
		if (!attachObj || !attachObj.id) return;
	    // set type
		attachObj.type = "file";
		// add
		this.attachObj.push(attachObj);
		
		this.drawAttachmentPanel();
	},
	
	getObjectID: function(obj) {
	    // create id
	    var keys = [];
	    for (var key in obj.compositeID) { keys.push(key); };
	    keys.sort();
	    var values = [];
	    for (var i in keys) { values.push(obj.compositeID[keys[i]]); };
	    return values.join(".");
	},
	
	addObject: function(obj) {
	    if (!obj || !obj.compositeID) return;
	    // get id
	    obj.id = this.getObjectID(obj);
	    // set type
	    obj.type = "datasource";
	    obj.selected = false;
	    // add
	    this.attachObj.push(obj);
	    this.drawAttachmentPanel();
	},
	
	getAttachment: function(id) {
		for (var i=0; i<this.attachObj.length; i++) {
			if (this.attachObj[i].id == id)
				return this.attachObj[i];
		}
		return null;
	},
	
	removeAttachment: function(id) {
		for (var i=0; i<this.attachObj.length; i++) {
			if (this.attachObj[i].id == id) {
				this.attachObj.splice(i,1);
				tmp_attachmentDialog.removeFiles([id]);
			}
		}
		
		this.drawAttachmentPanel();
	},
	
	attachmentExists: function(id) {
		return (this.getAttachment(id) != null ? true: false);
	},
	
	drawAttachmentPanel: function() {
		var mda = $("newMailAttachmentDiv");
		removeChildNodes(mda);
		for (var i=0; i<this.attachObj.length; i++) {
		    var attach = this.attachObj[i], label = "", imgSrc = "";
		    // get label of attachment
		    switch (attach.type) {
		        case "file": 
		            label = attach.filename;
		            imgSrc = getFullImgSrc("img/mail/" + (this.attachObj[i].content_type && this.attachObj[i].content_type.match(/^message\/rfc822/gi) ? "unread.gif" : "attachment.gif"));
		            break;
		        case "datasource":
		            switch (attach.objectType) {
		                case "vCard":
		                    label = attach.name + " (vCard)";
		                    imgSrc = getFullImgSrc("img/infostore/mimetypes/vcard.gif");
		                    break;
                        case "default":
                            label = attach.name;
                            imgSrc = getFullImgSrc("/img/mail/attachment.gif");
                            break;
		            }
		            break;
		    }
			var oDOMDiv = newnode("div", 0, { id: this.attachObj[i].id, className: "mailDetailAttachmentsCont" + (this.attachObj[i].selected ? "Sel background-color-PMG-selection-elements" : "") },
				[ newnode("img",0,
					{ src: imgSrc, align: "absmiddle", padding: "3px"}),
				  	newnode("span",0,{className: "_nothing"},
				  		[ document.createTextNode(this.attachObj[i].size ?
                            //#. Displayed label for attachments
                            //#. %1$s is the file name.
                            //#. %2$s is the file size.
                            //#, c-format
				  		    format(_(" %1$s (%2$s); "), label,
				  		           bytesToString(this.attachObj[i].size)) :
                            //#. Displayed label for attachments without file size
				  		    //#. %1$s is the file name.
				  		    //#, c-format
                            format(_(" %1$s; "), label))]
					  	)
					]
				);	
			
			addDOMEvent(oDOMDiv, "mousedown", (function(id, oMailFunctionsNew) {
				return function(e) {
					cancelDefault(e);	
					oMailFunctionsNew.selectAttachment(id);
				};
			})(this.attachObj[i].id, oMailFunctionsNew));
						
			mda.appendChild(oDOMDiv);
		}
		
		var os = "0em";
		if (this.attachObj.length > 0)	
			os = "3.2em";
		setTimeout(function() { resizeSplit(mda.id, os) },0);
	},
	
	/*
	 * @private
	 * 
	 */
	selectAttachment: function(id) {
		for (var i=0; i<this.attachObj.length; i++) {
			this.attachObj[i].selected=false;
			if (this.attachObj[i].id == id) {
				this.attachObj[i].selected=true;
				triggerEvent("SubSelected", [this.attachObj[i]]);
			}
			document.getElementById(this.attachObj[i].id).className=
				"mailDetailAttachmentsCont" + (this.attachObj[i].selected ? "Sel background-color-PMG-selection-elements" : "");
		}		
	},
	
	showAdditionalHeaderField: function(field, selected) {
		if (!$ALL(field + "_row") || $ALL(field + "_row").style.display == (selected ? "" : "none")) {
				return;
		}
	   $ALL(field + "_row").style.display = selected ? "" : "none";
	   //$ALL("menu_new_mail_view_" + field + "field_checkbox").checked = selected;
	   this.drawAttachmentPanel();
	}
}

var oMailFunctionsNew = new MailFunctions();

function callback_new(cb) {
    oMailFunctionsNew.savePending = false;
	if (cb) {
		if (cb.data && !cb.error) {
			switch(action) {
				case "reply":
				case "replyall":
				    corewindow.updateMailFlagsInternal(url["folder"], url["id"], { flag:1, bool:true });
				    break;
				case "forward":
				    corewindow.updateMailFlagsInternal(url["folder"], url["id"], { flag:256, bool:true });
				    break;
				case "draft":
				    corewindow.updateDraftsFolderCache(url["id"], cb.data);
				    break;				
			}
			oMailFunctionsNew.cleanUp();
			window.onbeforeunload=null;
			window.onunload = null;
			window.setTimeout(
				function() {
				    ox.api.window.core.triggerEvent("OX_Window_Close",{handle: url.guid});
				    ox.api.window.close(url.guid);
				}, 250);
		} else {
			newServerError(cb);
			setModalBusy();
		}
	} else {
		triggerEvent("OX_New_Error", 4, _("The server didn't respond!"));
	}
}

register("OX_SAVE_OBJECT", function() {
			oMailFunctionsNew.sendMessage()
		});

register("OX_Save_Template_Object", function() {
			action = "draft";
			oMailFunctionsNew.saveMessageAsDraft()
		});

register("OX_New_Mail_Show_Additional_Header", function(field, selected) {   
   options[field] = selected;
   configSetKey("gui.mail.newmail_options", options);
	oMailFunctionsNew.showAdditionalHeaderField(field, selected);
});	

var ums = false;
function setBeforeUnload(on) {
    ums = on;
}

/* search EmailAddresses */
function seachEmailAddress(recipientField) {
	centerPopupWindow($('addressbookNewEmail'));
	$('addressbookNewEmail').style.display = 'block';
	addOnClose($("addressbookNewEmail"),closeAddressbookNewEmail);
	myListAddressBook.resetView();
	myListAddressBook.recipientType = recipientField;
	addressbookFolderPath();
	myListAddressBook.getResult();
}

function newmailContact() {		
	var searchString = $('newmail_search').value;
	myListAddressBook.searchstring = searchString;
	myListAddressBook.method = "mail";
				
	myListAddressBook.getResult();
	distruGrid.events.register("Activated", function(ids) {		
		});
	
	distruGrid.events.register("Selected", function(ids) {
			myListAddressBook.numberOfSelection = ids;	
		});		
}

function closeAddressbookNewEmail() {
	var _ftj = myListAddressBook.tmpIdsForSave && 
		myListAddressBook.tmpIdsForSave.length != 0 ? "subject" : myListAddressBook.recipientType;
	setTimeout(function() {
			if(document.getElementById(_ftj) && document.getElementById(_ftj).focus()) 
				document.getElementById(_ftj).focus(); 
		},0);
}

function toogleAdditionalMailHeader(cStatus) {
	var newStatus = cStatus || !$('menu_new_mail_view_optionsfield_checkbox').checked;	
	$('menu_new_mail_view_optionsfield_checkbox').checked = newStatus; 
	triggerEvent('OX_New_Mail_Show_Additional_Header', 'options', newStatus);
}

function addnewmailToList(field) {
	if(myListAddressBook)
		myListAddressBook.addToList('bySelection', field);	
}

function addAllnewmailToList() {
	if (myListAddressBook)
		myListAddressBook.addToList('all');
}

function replaceLeadingSpaces(text) {
	return text.replace(/(^|<br\s*\/?>)([ \t]+)|([ \t]{2,}|\t)/g,
		function(_, prefix, m1, m2) {
			var match = (m1 || m2).replace(/\t/g, "        "); // TODO: real tabs
			var retval = new Array((match.length + 3) >> 1);
			retval[0] = prefix || "";
			var max = match.length >> 1;
			for (var i = 1; i <= max; i++) retval[i] = "&nbsp; ";
			if (match.length & 1) retval[retval.length - 1] = "&nbsp;";
			return retval.join("");
		});
}

function getURL() {
	var URL = window.location.pathname;
   URL = URL.substring(0, URL.lastIndexOf("/")+1);
   return URL;
}

function isIE() {
	var test = navigator.appVersion.match(/MSIE (\d+\.\d+)/, '');
    return (test != null && Number(test[1]) >= 5.5);
}

/**
 * File browser for adding images as Inline Content. 
 * Will be added to the Image selection dialog.
 * @param field_name
 * @param url 
 * @param type
 * @param win
 */
function cb_mailFileBrowser(field_name, url, type, win) {
    if (!win || !win.document) {
        return false;
    }

    var URL = getURL();    

    var doc = win.document;
    if (doc.getElementById("oxmainBG")) {
        doc.getElementById("oxmainBG").parentNode.removeChild(doc.getElementById("oxmainBG"));
    }
    if (doc.getElementById("oxmain")) {
        doc.getElementById("oxmain").parentNode.removeChild(doc.getElementById("oxmain"));
    }

    // build div for file browser
    var nd1 = newnode("div", { zIndex: 2, width: "100%", height: "100%", top: 0, left: 0, position: "fixed", background: "#FFFFFF none repeat scroll 0 0", opacity: 0.6 }, { id: "oxmainBG" }, null, doc);

    var pURL = AjaxRoot + "/file?action=new&module=mail&type=image&session=" + corewindow.session;
    var nd2 = newnode("div", { position: "absolute", zIndex: 3, top: 0, left: 0, width: "100%", height: "100%" },{ id: "oxmain"}, null, doc);
    var nd_form;
    if (isIE()) {
        nd_form = doc.createElement('<form action="' + pURL + '" method="POST" enctype="multipart/form-data" target="hiddenNMFrame" name="mailUploadImage" id="mailUploadImage">');
    } else {
    	nd_form = newnode("form", null, { enctype: "multipart/form-data", method: "post", action: pURL, acceptCharset: "UTF-8", target: "hiddenNMFrame", id: "mailUploadImage", name: "mailUploadImage" }, null, doc);
    }
   
    var nd3 = newnode("div", { width: "90%", position: "absolute", zIndex: "99", top: "85px", left: "12px", padding: "5px", textAlign: "right", border: "1px solid gray", backgroundColor: "white", id: "oxmainContent" }, null, null, doc);  

    nd3.appendChild( newnode("div", { width: "90%", textAlign: "left" }, null, [ doc.createTextNode(_("Select a File:")) ], doc ));
    nd3.appendChild( newnode("input", { border: "solid 1px gray", width: "100%" }, { type: "file", size: "30", name: "file", id: "file" }, null, doc) ); 
   
    var nd4 = newnode("input", { marginTop: "5px" }, { type: "button", name: _("OK"), value: _("OK") }, null, doc);
    addDOMEvent(nd4, "click", function() { win.sendOXFile() }, win);
    nd3.appendChild(nd4);
    
    var nd5 = newnode("input", { margin: "5px 0px 0px 5px" }, { type: "button", name: _("Cancel"), value: _("Cancel") }, null, doc);
    addDOMEvent(nd5, "click", function() { win.disableOXBG() }, win );
    nd3.appendChild(nd5);
    
    nd_form.appendChild(nd3);
    nd2.appendChild(nd_form);   
    doc.body.appendChild(nd1);
    doc.body.appendChild(nd2);   

    doc.body.appendChild( newnode("script", null, { language: "javascript", type: "text/javascript", src: URL + "mailFileBrowser.js" }, null, doc ) );
    doc.body.appendChild( newnode("iframe", { display: "none" }, { scrolling: "no", frameBorder: "0", src: URL + "newInfoItemHidden.html", name: "hiddenNMFrame", id: "hiddenNMFrame"}, null, doc) );

    /* *** Ugly workaround for IE Only *** */
    if (win.frames["hiddenNMFrame"].name != "hiddenNMFrame") { 
        win.frames["hiddenNMFrame"].name = "hiddenNMFrame";
    }

    var ielem = win.document.getElementById("insert");
    if (ielem) {
        ielem.onmousedown = function() {    if (win.oxFile) addImg2Poll(win.oxFile); }
    }
   
    return false;
}

function addImg2Poll(id) {
	var timeout = configGetKey("maxUploadIdleTimeout") ? configGetKey("maxUploadIdleTimeout") : 200000;
	timeout = Math.round(timeout * .9);
	window.setInterval(function() {
		(new JSON()).get(AjaxRoot + "/file?action=keepalive&session=" + corewindow.session + "&id=" + id,
			null, function(cb) { }, function(cb) { return true });
	}, timeout);
}