/* ***** BEGIN LICENSE BLOCK ***** * This file is part of DotClear. * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All * rights reserved. * * DotClear is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DotClear is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DotClear; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ***** END LICENSE BLOCK ***** */ jsToolBar.prototype.can_wwg = (document.designMode != undefined); jsToolBar.prototype.iframe = null; jsToolBar.prototype.iwin = null; jsToolBar.prototype.ibody = null; jsToolBar.prototype.iframe_css = null; /* Editor methods -------------------------------------------------------- */ jsToolBar.prototype.drawToolBar = jsToolBar.prototype.draw; jsToolBar.prototype.draw = function(mode) { mode = mode || 'xhtml'; if (this.can_wwg) { this.mode = 'wysiwyg'; this.drawToolBar('wysiwyg'); this.initWindow(); } else { this.drawToolBar(mode); } }; jsToolBar.prototype.switchMode = function(mode) { mode = mode || 'xhtml'; if (mode == 'xhtml') { this.draw(mode); } else { if (this.wwg_mode) { this.syncContents('iframe'); } this.removeEditor(); this.textarea.style.display = ''; this.drawToolBar(mode); } }; jsToolBar.prototype.syncContents = function(from) { from = from || 'textarea'; var This = this; if (from == 'textarea') { initContent(); } else { this.validBlockquote(); var html = this.applyHtmlFilters(this.ibody.innerHTML); if (html == '
') { html = '

'; } this.textarea.value = html; } function initContent() { if (!This.iframe.contentWindow.document || !This.iframe.contentWindow.document.body) { setTimeout(initContent, 1); return; } This.ibody = This.iframe.contentWindow.document.body; if (This.textarea.value != '' && This.textarea.value != '

') { This.ibody.innerHTML = This.applyWysiwygFilters(This.textarea.value); if (This.ibody.createTextRange) { //cursor at the begin for IE var IErange = This.ibody.createTextRange(); IErange.execCommand("SelectAll"); IErange.collapse(); IErange.select(); } } else if (window.navigator.product != undefined && window.navigator.product == 'Gecko') { This.ibody.innerHTML = '


'; } else { var idoc = This.iwin.document; var para = idoc.createElement('p'); para.appendChild(idoc.createTextNode('')); while (idoc.body.hasChildNodes()) { idoc.body.removeChild(idoc.body.lastChild); } idoc.body.appendChild(para); } } }; jsToolBar.prototype.htmlFilters = { tagsoup: function(str){ return this.tagsoup2xhtml(str); } }; jsToolBar.prototype.applyHtmlFilters = function(str) { for (var fn in this.htmlFilters) { str = this.htmlFilters[fn].call(this, str); } return str; }; jsToolBar.prototype.wysiwygFilters = {}; jsToolBar.prototype.applyWysiwygFilters = function( str) { for (var fn in this.wysiwygFilters) { str = this.wysiwygFilters[fn].call(this, str); } return str; }; jsToolBar.prototype.switchEdit = function() { if (this.wwg_mode) { this.textarea.style.display = ''; this.iframe.style.display = 'none'; this.syncContents('iframe'); this.drawToolBar('xhtml'); this.wwg_mode = false; this.focusEditor(); } else { this.iframe.style.display = ''; this.textarea.style.display = 'none'; this.syncContents('textarea'); this.drawToolBar('wysiwyg'); this.wwg_mode = true; this.focusEditor(); } this.setSwitcher(); }; /** Creates iframe for editor, inits a blank document */ jsToolBar.prototype.initWindow = function() { var This = this; this.iframe = document.createElement('iframe'); this.textarea.parentNode.insertBefore(this.iframe,this.textarea.nextSibling); this.switcher = document.createElement('ul'); this.switcher.className = 'jstSwitcher'; this.editor.appendChild(this.switcher); this.iframe.height = this.textarea.offsetHeight + 0; this.iframe.width = this.textarea.offsetWidth + 0; if (this.textarea.tabIndex != undefined) { this.iframe.tabIndex = this.textarea.tabIndex; } function initIframe() { var doc = This.iframe.contentWindow.document; if (!doc) { setTimeout(initIframe,1); return false; } doc.open(); var html = '\n'+ '\n'+ '\n'+ (This.base_url != '' ? '' : '')+ '\n'+ '\n'+ '\n'+ ''; doc.write(html); doc.close(); if (document.all) { // for IE doc.designMode = 'on'; // warning : doc is now inaccessible for IE6 sp1 } This.iwin = This.iframe.contentWindow; This.syncContents('textarea'); if (This.wwg_mode == undefined) { This.wwg_mode = true; } if (This.wwg_mode) { This.textarea.style.display = 'none'; } else { This.iframe.style.display = 'none'; } // update textarea on submit if (This.textarea.form) { chainHandler(This.textarea.form,'onsubmit', function() { if (This.wwg_mode) { This.syncContents('iframe'); } }); } for (var evt in This.iwinEvents) { var event = This.iwinEvents[evt]; This.addIwinEvent(This.iframe.contentWindow.document, event.type, event.fn, This); } This.setSwitcher(); setTimeout(function(){This.focusEditor();},1); return true; } initIframe(); }; jsToolBar.prototype.addIwinEvent = function(target, type, fn, scope) { var myFn = function(e){fn.call(scope, e)}; addEvent(target, type, myFn, true); // fix memory leak addEvent(scope.iwin, 'unload', function(){ removeEvent(target, type, myFn, true); }, true); }; jsToolBar.prototype.iwinEvents = { block1: { type: 'mouseup', fn: function(){ this.adjustBlockLevelCombo() } }, block2: { type: 'keyup', fn: function(){ this.adjustBlockLevelCombo() } } }; /** Insert a mode switcher after editor area */ jsToolBar.prototype.switcher_visual_title = 'visual'; jsToolBar.prototype.switcher_source_title = 'source'; jsToolBar.prototype.setSwitcher = function() { while (this.switcher.hasChildNodes()) { this.switcher.removeChild(this.switcher.firstChild); } var This = this; function setLink(title,link) { var li = document.createElement('li'); if (link) { var a = document.createElement('a'); a.href = '#'; a.editor = This; a.onclick = function() { this.editor.switchEdit(); return false; }; a.appendChild(document.createTextNode(title)); } else { li.className = 'jstSwitcherCurrent'; a = document.createTextNode(title); } li.appendChild(a); This.switcher.appendChild(li); } setLink(this.switcher_visual_title,!this.wwg_mode); setLink(this.switcher_source_title,this.wwg_mode); }; /** Removes editor area and mode switcher */ jsToolBar.prototype.removeEditor = function() { if (this.iframe != null) { this.iframe.parentNode.removeChild(this.iframe); this.iframe = null; } if (this.switcher != undefined && this.switcher.parentNode != undefined) { this.switcher.parentNode.removeChild(this.switcher); } }; /** Focus on the editor area */ jsToolBar.prototype.focusEditor = function() { if (this.wwg_mode) { try { this.iwin.document.designMode = 'on'; } catch (e) {}; // Firefox needs this var This = this; setTimeout(function() {This.iframe.contentWindow.focus()},1); } else { this.textarea.focus(); } }; /** Resizer */ jsToolBar.prototype.resizeSetStartH = function() { if (this.wwg_mode && this.iframe != undefined) { this.dragStartH = this.iframe.offsetHeight; return; } this.dragStartH = this.textarea.offsetHeight + 0; }; jsToolBar.prototype.resizeDragMove = function(event) { var new_height = (this.dragStartH+event.clientY-this.dragStartY)+'px'; if (this.iframe != undefined) { this.iframe.style.height = new_height; } this.textarea.style.height = new_height; }; /* Editing methods -------------------------------------------------------- */ /** Replaces current selection by given node */ jsToolBar.prototype.insertNode = function(node) { var range; if (this.iwin.getSelection) { // Gecko var sel = this.iwin.getSelection(); range = sel.getRangeAt(0); // deselect all ranges sel.removeAllRanges(); // empty range range.deleteContents(); // Insert node range.insertNode(node); range.selectNodeContents(node); range.setEndAfter(node); if (range.endContainer.childNodes.length > range.endOffset && range.endContainer.nodeType != Node.TEXT_NODE) { range.setEnd(range.endContainer.childNodes[range.endOffset], 0); } else { range.setEnd(range.endContainer.childNodes[0]); } sel.addRange(range); sel.collapseToEnd(); } else { // IE // lambda element var p = this.iwin.document.createElement('div'); p.appendChild(node); range = this.iwin.document.selection.createRange(); range.execCommand('delete'); // insert innerHTML from element range.pasteHTML(p.innerHTML); range.collapse(false); range.select(); } this.iwin.focus(); }; /** Returns a document fragment with selected nodes */ jsToolBar.prototype.getSelectedNode = function() { if (this.iwin.getSelection) { // Gecko var sel = this.iwin.getSelection(); var range = sel.getRangeAt(0); var content = range.cloneContents(); } else { // IE var sel = this.iwin.document.selection; var d = this.iwin.document.createElement('div'); d.innerHTML = sel.createRange().htmlText; var content = this.iwin.document.createDocumentFragment(); for (var i=0; i < d.childNodes.length; i++) { content.appendChild(d.childNodes[i].cloneNode(true)); } } return content; }; /** Returns string representation for selected node */ jsToolBar.prototype.getSelectedText = function() { if (this.iwin.getSelection) { // Gecko return this.iwin.getSelection().toString(); } else { // IE var range = this.iwin.document.selection.createRange(); return range.text; } }; jsToolBar.prototype.replaceNodeByContent = function(node) { var content = this.iwin.document.createDocumentFragment(); for (var i=0; i < node.childNodes.length; i++) { content.appendChild(node.childNodes[i].cloneNode(true)); } node.parentNode.replaceChild(content, node); }; jsToolBar.prototype.getBlockLevel = function() { var blockElts = ['p','h1','h2','h3','h4','h5','h6']; var range, commonAncestorContainer; if (this.iwin.getSelection) { //gecko var selection = this.iwin.getSelection(); range = selection.getRangeAt(0); commonAncestorContainer = range.commonAncestorContainer; while (commonAncestorContainer.nodeType != 1) { commonAncestorContainer = commonAncestorContainer.parentNode; } } else { //ie range = this.iwin.document.selection.createRange(); commonAncestorContainer = range.parentElement(); } var ancestorTagName = commonAncestorContainer.tagName.toLowerCase(); while (arrayIndexOf(blockElts, ancestorTagName)==-1 && ancestorTagName!='body') { commonAncestorContainer = commonAncestorContainer.parentNode; ancestorTagName = commonAncestorContainer.tagName.toLowerCase(); } if (ancestorTagName == 'body') return null; else return commonAncestorContainer; }; jsToolBar.prototype.adjustBlockLevelCombo = function() { var blockLevel = this.getBlockLevel(); if (blockLevel !== null) this.toolNodes.blocks.value = blockLevel.tagName.toLowerCase(); else { if (this.mode == 'wysiwyg') this.toolNodes.blocks.value = 'none'; if (this.mode == 'xhtml') this.toolNodes.blocks.value = 'nonebis'; } }; /** HTML code cleanup -------------------------------------------------------- */ jsToolBar.prototype.simpleCleanRegex = new Array( /* Remove every tags we don't need */ [//gim,''], [/[\w\W]*?<\/style>/gim, ''], [/<\/?font[\w\W]*?>/gim, ''], /* Replacements */ [/<(\/?)(B|b|STRONG)([\s>\/])/g, "<$1strong$3"], [/<(\/?)(I|i|EM)([\s>\/])/g, "<$1em$3"], [/]*?[^\/])>/gi, ""], [/]*?[^\/])>/gi, ""], [/]*?[^\/])>/gi, ""], [/]*?[^\/])>/gi, ""], [/]*?[^\/])>/gi, ""], [/
]*?[^\/])>/gi, "
"], [/
]*?[^\/])>/gi, "
"], [/<(\/?)U([\s>\/])/gi, "<$1ins$2"], [/<(\/?)STRIKE([\s>\/])/gi, "<$1del$2"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$1"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/span>/gm, "$2"], [/([\w\W]*?)<\/strong>/gm, "$1"], [/<([a-z]+) style="font-weight: normal;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="font-weight: bold;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="font-style: italic;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="text-decoration: underline;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="text-decoration: line-through;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="text-decoration: underline line-through;">([\w\W]*?)<\/\1>/gm, "<$1>$2"], [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: bold; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: italic; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: bold; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: italic; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline; ?){3}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: line-through; ?){3}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline line-through; ?){3}">([\w\W]*?)<\/\1>/gm, "<$1>$3"], [/

(.*)(\n)+<\/blockquote><\/p>/i,"
$1
\n"], /* mise en forme identique contigue */ [/<\/(strong|em|ins|del|q|code)>(\s*?)<\1>/gim, "$2"], [/<(br|BR)>/g, "
"], [/<(hr|HR)>/g, "
"], /* opera est trop strict ;)) */ [/([^\s])\/>/g, "$1 />"], /* br intempestifs de fin de block */ [/
\s*<\/(h1|h2|h3|h4|h5|h6|ul|ol|li|p|blockquote|div)/gi, "([^\n\u000B\r\f])/gi, "\n$2"], [/
/g, "
"] ); /** Cleanup HTML code */ jsToolBar.prototype.tagsoup2xhtml = function(html) { for (var reg in this.simpleCleanRegex) { html = html.replace(this.simpleCleanRegex[reg][0], this.simpleCleanRegex[reg][1]); } /* tags vides */ /* note : on tente de ne pas tenir compte des commentaires html, ceux-ci permettent entre autre d'inserer des commentaires conditionnels pour ie */ while ( /(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/.test(html) ) { html = html.replace(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/g, ""); } /* tous les tags en minuscule */ html = html.replace(/<(\/?)([A-Z0-9]+)/g, function(match0, match1, match2) { return "<" + match1 + match2.toLowerCase(); }); /* IE laisse souvent des attributs sans guillemets */ var myRegexp = /<[^>]+((\s+\w+\s*=\s*)([^"'][\w~@+$,%\/:.#?=&;!*()-]*))[^>]*?>/; while ( myRegexp.test(html)) { html = html.replace( myRegexp, function (str, val1, val2, val3){ var tamponRegex = new RegExp(regexpEscape(val1)); return str.replace(tamponRegex, val2+'"'+val3+'"'); } ) } /* les navigateurs rajoutent une unite aux longueurs css nulles */ /* note: a ameliorer ! */ while ( /(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/.test(html)) { html = html.replace(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/gi, "$1"+"0$4"); } /* correction des fins de lignes : le textarea edite contient des \n * le wysiwyg des \r\n , et le textarea mis a jour SANS etre affiche des \r\n ! */ html = html.replace(/\r\n/g,"\n"); /* Trim */ html = html.replace(/^\s+/gm,''); html = html.replace(/\s+$/gm,''); return html; }; jsToolBar.prototype.validBlockquote = function() { var blockElts = ['address','blockquote','dl','div','fieldset','form','h1', 'h2','h3','h4','h5','h6','hr','ol','p','pre','table','ul']; var BQs = this.iwin.document.getElementsByTagName('blockquote'); var bqChilds; for (var bq = 0; bq < BQs.length; bq++) { bqChilds = BQs[bq].childNodes; var frag = this.iwin.document.createDocumentFragment(); for (var i = (bqChilds.length-1); i >= 0; i--) { if (bqChilds[i].nodeType == 1 && // Node.ELEMENT_NODE arrayIndexOf(blockElts, bqChilds[i].tagName.toLowerCase()) >= 0) { if (frag.childNodes.length > 0) { var p = this.iwin.document.createElement('p'); p.appendChild(frag); BQs[bq].replaceChild(p, bqChilds[i+1]); frag = this.iwin.document.createDocumentFragment(); } } else { if (frag.childNodes.length > 0) BQs[bq].removeChild(bqChilds[i+1]); frag.insertBefore(bqChilds[i].cloneNode(true), frag.firstChild); } } if (frag.childNodes.length > 0) { var p = this.iwin.document.createElement('p'); p.appendChild(frag); BQs[bq].replaceChild(p, bqChilds[0]); } } }; /* Removing text formating */ jsToolBar.prototype.removeFormatRegexp = new Array( [/(<[a-z][^>]*)margin\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)margin-bottom\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)margin-left\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)margin-right\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)margin-top\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)padding\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)padding-bottom\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)padding-left\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)padding-right\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)padding-top\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font-family\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font-size\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font-style\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font-variant\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)font-weight\s*:[^;]*;/mg, "$1"], [/(<[a-z][^>]*)color\s*:[^;]*;/mg, "$1"] ); jsToolBar.prototype.removeTextFormating = function(html) { for (var reg in this.removeFormatRegexp) { html = html.replace(this.removeFormatRegexp[reg][0], this.removeFormatRegexp[reg][1]); } html = this.tagsoup2xhtml(html); html = html.replace(/style="\s*?"/mgi,''); return html; }; /** Toolbar elements -------------------------------------------------------- */ jsToolBar.prototype.elements.blocks.wysiwyg = { list: ['none','p','h1','h2','h3','h4','h5','h6'], fn: function(opt) { if (opt=='none') { var blockLevel = this.getBlockLevel(); if (blockLevel !== null) { this.replaceNodeByContent(blockLevel); } this.iwin.focus(); } else { try { this.iwin.document.execCommand('formatblock',false,'<'+opt+'>'); } catch(e){}; this.iwin.focus(); } } }; jsToolBar.prototype.elements.strong.fn.wysiwyg = function() { this.iwin.document.execCommand('bold', false, null); this.iwin.focus(); }; jsToolBar.prototype.elements.em.fn.wysiwyg = function() { this.iwin.document.execCommand('italic', false, null); this.iwin.focus(); }; jsToolBar.prototype.elements.ins.fn.wysiwyg = function() { this.iwin.document.execCommand('underline', false, null); this.iwin.focus(); }; jsToolBar.prototype.elements.del.fn.wysiwyg = function() { this.iwin.document.execCommand('strikethrough', false, null); this.iwin.focus(); }; jsToolBar.prototype.elements.quote.fn.wysiwyg = function() { var n = this.getSelectedNode(); var q = this.iwin.document.createElement('q'); q.appendChild(n); this.insertNode(q); }; jsToolBar.prototype.elements.code.fn.wysiwyg = function() { var n = this.getSelectedNode(); var code = this.iwin.document.createElement('code'); code.appendChild(n); this.insertNode(code); }; jsToolBar.prototype.elements.br.fn.wysiwyg = function() { var n = this.iwin.document.createElement('br'); this.insertNode(n); }; jsToolBar.prototype.elements.blockquote.fn.wysiwyg = function() { var n = this.getSelectedNode(); var q = this.iwin.document.createElement('blockquote'); q.appendChild(n); this.insertNode(q); }; jsToolBar.prototype.elements.pre.fn.wysiwyg = function() { this.iwin.document.execCommand('formatblock',false,'
');
	this.iwin.focus();
};

jsToolBar.prototype.elements.ul.fn.wysiwyg = function() {
	this.iwin.document.execCommand('insertunorderedlist',false,null);
	this.iwin.focus();
};

jsToolBar.prototype.elements.ol.fn.wysiwyg = function() {
	this.iwin.document.execCommand('insertorderedlist',false,null);
	this.iwin.focus();
};

jsToolBar.prototype.elements.link.fn.wysiwyg = function() {
	var href, hreflang;
	var range, commonAncestorContainer;
	if (this.iwin.getSelection) { //gecko
		var selection = this.iwin.getSelection();
		range = selection.getRangeAt(0);
		commonAncestorContainer = range.commonAncestorContainer;
		while (commonAncestorContainer.nodeType != 1) {
			commonAncestorContainer = commonAncestorContainer.parentNode;
		}
	} else { //ie
		range = this.iwin.document.selection.createRange();
		commonAncestorContainer = range.parentElement();
	}
	
	var ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
	while (ancestorTagName!='a' && ancestorTagName!='body') {
		commonAncestorContainer = commonAncestorContainer.parentNode;
		ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
	}
	
	// Update or remove link?
	if (ancestorTagName == 'a') {
		href = commonAncestorContainer.href || '';
		hreflang = commonAncestorContainer.hreflang || '';
	}
	
	href = window.prompt(this.elements.link.href_prompt,href);
	
	// Remove link
	if (ancestorTagName == 'a' && href=='') {
		this.replaceNodeByContent(commonAncestorContainer);
	}
	if (!href) return; // user cancel
	
	hreflang = window.prompt(this.elements.link.hreflang_prompt, hreflang);
	
	// Update link
	if (ancestorTagName == 'a' && href) {
		commonAncestorContainer.setAttribute('href', href);
		if (hreflang) {
			commonAncestorContainer.setAttribute('hreflang', hreflang);
		} else {
			commonAncestorContainer.removeAttribute('hreflang');
		}
		return;
	}
	
	// Create link
	var n = this.getSelectedNode();
	var a = this.iwin.document.createElement('a');
	a.href = href;
	if (hreflang) a.setAttribute('hreflang',hreflang);
	a.appendChild(n);
	this.insertNode(a);
};



// Remove format and Toggle
jsToolBar.prototype.elements.removeFormat = {
	type: 'button',
	title: 'Remove text formating',
	fn: {}
};
jsToolBar.prototype.elements.removeFormat.disabled = !jsToolBar.prototype.can_wwg;
jsToolBar.prototype.elements.removeFormat.fn.xhtml = function() {
	var html = this.textarea.value;
	html = this.removeTextFormating(html);
	this.textarea.value = html;
};
jsToolBar.prototype.elements.removeFormat.fn.wysiwyg = function() {
	var html = this.iwin.document.body.innerHTML;
	html = this.removeTextFormating(html);
	this.iwin.document.body.innerHTML = html;
};
/** Utilities
-------------------------------------------------------- */
function arrayIndexOf(aArray, aValue){
	if (typeof Array.indexOf == 'function') {
		return aArray.indexOf(aValue);
	} else {
		var index = -1;
		var l = aArray.length;
		for (var i = 0; i < l ; i++) {
			if (aArray[i] === aValue) {
				index = i;
				break;
			}
		}
		return index;
	}
};
function addEvent(obj, evType, fn, useCapture) {
	if (obj.addEventListener){
		obj.addEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.attachEvent){
		var r = obj.attachEvent("on"+evType, fn);
		return r;
	} else {
		return false;
	}
};
function removeEvent(obj, evType, fn, useCapture) {
	if (obj.removeEventListener){
		obj.removeEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.detachEvent){
		var r = obj.detachEvent("on"+evType, fn);
		return r;
	} else {
		return false;
  }
};
function regexpEscape(s) {
	return s.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1")
};