4013 lines
141 KiB
JavaScript
4013 lines
141 KiB
JavaScript
var TracWysiwyg = function(textarea, options) {
|
|
var self = this;
|
|
var editorMode = TracWysiwyg.getEditorMode();
|
|
|
|
this.autolink = true;
|
|
this.textarea = textarea;
|
|
this.options = options = options || {};
|
|
var wikitextToolbar = null;
|
|
var textareaResizable = null;
|
|
if (/\btrac-resizable\b/i.test(textarea.className)) {
|
|
var tmp = textarea.parentNode;
|
|
tmp = tmp && tmp.parentNode;
|
|
if (tmp && /\btrac-resizable\b/i.test(tmp.className)) {
|
|
wikitextToolbar = tmp.previousSibling;
|
|
textareaResizable = tmp;
|
|
}
|
|
}
|
|
else {
|
|
wikitextToolbar = textarea.previousSibling;
|
|
}
|
|
if (wikitextToolbar && (wikitextToolbar.nodeType != 1 || wikitextToolbar.className != "wikitoolbar")) {
|
|
wikitextToolbar = null;
|
|
}
|
|
this.textareaResizable = textareaResizable;
|
|
this.wikitextToolbar = wikitextToolbar;
|
|
|
|
this.createEditable(document, textarea, textareaResizable);
|
|
var frame = this.frame;
|
|
var resizable = this.resizable;
|
|
|
|
this.contentWindow = frame.contentWindow;
|
|
this.contentDocument = this.contentWindow.document;
|
|
|
|
this.initializeEditor(this.contentDocument);
|
|
this.wysiwygToolbar = this.createWysiwygToolbar(document);
|
|
this.styleMenu = this.createStyleMenu(document);
|
|
this.decorationMenu = this.createDecorationMenu(document);
|
|
this.tableMenu = this.createTableMenu(document);
|
|
this.menus = [ this.styleMenu, this.decorationMenu, this.tableMenu ];
|
|
this.toolbarButtons = this.setupMenuEvents();
|
|
this.toggleEditorButtons = null;
|
|
this.autolinkButton = null;
|
|
this.savedWysiwygHTML = null;
|
|
|
|
this.setupToggleEditorButtons();
|
|
this.setupSyncTextAreaHeight();
|
|
|
|
var styleStatic = { position: "static", left: "-9999px", top: "-9999px" };
|
|
var styleAbsolute = { position: "absolute", left: "-9999px", top: "-9999px" };
|
|
switch (editorMode) {
|
|
case "textarea":
|
|
TracWysiwyg.setStyle(textareaResizable || textarea, styleStatic);
|
|
if (wikitextToolbar) {
|
|
TracWysiwyg.setStyle(wikitextToolbar, styleStatic);
|
|
}
|
|
TracWysiwyg.setStyle(resizable || frame, { position: "absolute",
|
|
left: "-9999px", top: TracWysiwyg.elementPosition(textareaResizable || textarea).top + "px" });
|
|
TracWysiwyg.setStyle(this.wysiwygToolbar, styleAbsolute);
|
|
TracWysiwyg.setStyle(this.autolinkButton.parentNode, { display: "none" });
|
|
textarea.setAttribute("tabIndex", "");
|
|
frame.setAttribute("tabIndex", "-1");
|
|
break;
|
|
case "wysiwyg":
|
|
TracWysiwyg.setStyle(textareaResizable || textarea, { position: "absolute",
|
|
left: "-9999px", top: TracWysiwyg.elementPosition(textareaResizable || textarea).top + "px" });
|
|
if (wikitextToolbar) {
|
|
TracWysiwyg.setStyle(wikitextToolbar, styleAbsolute);
|
|
}
|
|
TracWysiwyg.setStyle(resizable || frame, styleStatic);
|
|
TracWysiwyg.setStyle(this.wysiwygToolbar, styleStatic);
|
|
TracWysiwyg.setStyle(this.autolinkButton.parentNode, { display: "" });
|
|
textarea.setAttribute("tabIndex", "-1");
|
|
frame.setAttribute("tabIndex", "");
|
|
break;
|
|
}
|
|
|
|
var body = document.body;
|
|
for (var i = 0; i < this.menus.length; i++) {
|
|
body.insertBefore(this.menus[i], body.firstChild);
|
|
}
|
|
var element = wikitextToolbar || textareaResizable || textarea;
|
|
element.parentNode.insertBefore(this.toggleEditorButtons, element);
|
|
element.parentNode.insertBefore(this.wysiwygToolbar, element);
|
|
|
|
function lazySetup() {
|
|
if (self.contentDocument.body) {
|
|
var exception;
|
|
try { self.execCommand("useCSS", false); } catch (e) { }
|
|
try { self.execCommand("styleWithCSS", false); } catch (e) { }
|
|
if (editorMode == "wysiwyg") {
|
|
try { self.loadWysiwygDocument() } catch (e) { exception = e }
|
|
}
|
|
self.setupEditorEvents();
|
|
self.setupFormEvent();
|
|
if (exception) {
|
|
(self.textareaResizable || self.textarea).style.position = "static";
|
|
if (self.wikitextToolbar) {
|
|
self.wikitextToolbar.style.position = "static";
|
|
}
|
|
(self.resizable || self.frame).style.position = self.wysiwygToolbar.style.position = "absolute";
|
|
self.autolinkButton.parentNode.style.display = "none";
|
|
alert("Failed to activate the wysiwyg editor.");
|
|
throw exception;
|
|
}
|
|
}
|
|
else {
|
|
setTimeout(lazySetup, 100);
|
|
}
|
|
}
|
|
lazySetup();
|
|
};
|
|
|
|
TracWysiwyg.prototype.initializeEditor = function(d) {
|
|
var l = window.location;
|
|
var html = [];
|
|
html.push(
|
|
'<!DOCTYPE html PUBLIC',
|
|
' "-//W3C//DTD XHTML 1.0 Transitional//EN"',
|
|
' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n',
|
|
'<html xmlns="http://www.w3.org/1999/xhtml">',
|
|
'<head>',
|
|
'<base href="', l.protocol, '//', l.host, '/" />',
|
|
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />');
|
|
var stylesheets = TracWysiwyg.tracPaths.stylesheets;
|
|
if (!stylesheets) {
|
|
// Work around wysiwyg stops with Agilo
|
|
var base = TracWysiwyg.tracPaths.base.replace(/\/*$/, "/");
|
|
stylesheets = [ base + "chrome/common/css/trac.css", base + "chrome/tracwysiwyg/editor.css" ];
|
|
}
|
|
var length = stylesheets.length;
|
|
for (var i = 0; i < length; i++) {
|
|
html.push('<link rel="stylesheet" href="' + stylesheets[i] + '" type="text/css" />');
|
|
}
|
|
html.push('<title></title>', '</head>', '<body></body>', '</html>');
|
|
|
|
var first = !window.opera && d.addEventListener ? true : false;
|
|
if (first) {
|
|
d.designMode = "On";
|
|
}
|
|
d.open();
|
|
d.write(html.join(""));
|
|
d.close();
|
|
if (!first) {
|
|
d.designMode = "On";
|
|
if (d != this.contentWindow.document) {
|
|
this.contentDocument = this.contentWindow.document;
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.toggleAutolink = function() {
|
|
this.autolink = !this.autolink;
|
|
this.autolinkButton.checked = this.autolink;
|
|
};
|
|
|
|
TracWysiwyg.prototype.listenerToggleAutolink = function(input) {
|
|
var self = this;
|
|
return function(event) {
|
|
self.autolink = input.checked;
|
|
};
|
|
};
|
|
|
|
TracWysiwyg.prototype.listenerToggleEditor = function(type) {
|
|
var self = this;
|
|
|
|
switch (type) {
|
|
case "textarea":
|
|
return function(event) {
|
|
var textarea = self.textareaResizable || self.textarea;
|
|
if (textarea.style.position == "absolute") {
|
|
self.hideAllMenus();
|
|
self.loadTracWikiText();
|
|
textarea.style.position = "static";
|
|
self.textarea.setAttribute("tabIndex", "");
|
|
if (self.wikitextToolbar) {
|
|
self.wikitextToolbar.style.position = "static";
|
|
}
|
|
self.syncTextAreaHeight();
|
|
(self.resizable || self.frame).style.position = self.wysiwygToolbar.style.position = "absolute";
|
|
self.frame.setAttribute("tabIndex", "-1");
|
|
self.autolinkButton.parentNode.style.display = "none";
|
|
TracWysiwyg.setEditorMode(type);
|
|
}
|
|
self.focusTextarea();
|
|
};
|
|
case "wysiwyg":
|
|
return function(event) {
|
|
var frame = self.resizable || self.frame;
|
|
if (frame.style.position == "absolute") {
|
|
try {
|
|
self.loadWysiwygDocument();
|
|
}
|
|
catch (e) {
|
|
TracWysiwyg.stopEvent(event || window.event);
|
|
alert("Failed to activate the wysiwyg editor.");
|
|
throw e;
|
|
}
|
|
(self.textareaResizable || self.textarea).style.position = "absolute";
|
|
self.textarea.setAttribute("tabIndex", "-1");
|
|
if (self.wikitextToolbar) {
|
|
self.wikitextToolbar.style.position = "absolute";
|
|
}
|
|
frame.style.position = self.wysiwygToolbar.style.position = "static";
|
|
self.frame.setAttribute("tabIndex", "");
|
|
self.autolinkButton.parentNode.style.display = "";
|
|
TracWysiwyg.setEditorMode(type);
|
|
}
|
|
self.focusWysiwyg();
|
|
};
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.activeEditor = function() {
|
|
return this.textarea.style.position == "absolute" ? "wysiwyg" : "textarea";
|
|
};
|
|
|
|
TracWysiwyg.prototype.setupFormEvent = function() {
|
|
var self = this;
|
|
|
|
function listener(event) {
|
|
var textarea = self.textareaResizable || self.textarea;
|
|
try {
|
|
if (textarea.style.position == "absolute") {
|
|
var body = self.contentDocument.body;
|
|
if (self.savedWysiwygHTML !== null && body.innerHTML != self.savedWysiwygHTML) {
|
|
self.textarea.value = self.domToWikitext(body, self.options);
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
TracWysiwyg.stopEvent(event || window.event);
|
|
}
|
|
}
|
|
addEvent(this.textarea.form, "submit", listener);
|
|
};
|
|
|
|
TracWysiwyg.prototype.createEditable = function(d, textarea, textareaResizable) {
|
|
var self = this;
|
|
var getStyle = TracWysiwyg.getStyle;
|
|
var dimension = getDimension(textarea);
|
|
if (!dimension.width || !dimension.height) {
|
|
setTimeout(lazy, 100);
|
|
}
|
|
if (!dimension.width) {
|
|
dimension.width = parseInt(getStyle(textarea, "fontSize"), 10) * (textarea.cols || 10) * 0.5;
|
|
}
|
|
if (!dimension.height) {
|
|
dimension.height = parseInt(getStyle(textarea, "lineHeight"), 10) * (textarea.rows || 3);
|
|
}
|
|
var wrapper = d.createElement("div");
|
|
wrapper.innerHTML = '<iframe class="wysiwyg" '
|
|
+ 'src="javascript:\'\'" '
|
|
+ 'width="' + dimension.width + '" height="' + dimension.height + '" '
|
|
+ 'frameborder="0" marginwidth="0" marginheight="0">'
|
|
+ '</iframe>';
|
|
var frame = this.frame = wrapper.firstChild;
|
|
|
|
if (textareaResizable) {
|
|
var offset = null;
|
|
var offsetFrame = null;
|
|
var contentDocument = null;
|
|
var grip = d.createElement("div");
|
|
grip.className = "trac-grip";
|
|
if (/^[0-9]+$/.exec(dimension.width)) {
|
|
grip.style.width = dimension.width + "px";
|
|
}
|
|
addEvent(grip, "mousedown", beginDrag);
|
|
wrapper.appendChild(grip);
|
|
var resizable = d.createElement("div");
|
|
resizable.className = "trac-resizable";
|
|
resizable.appendChild(wrapper);
|
|
grip.style.marginLeft = (frame.offsetLeft - grip.offsetLeft) + 'px';
|
|
grip.style.marginRight = (grip.offsetWidth - frame.offsetWidth) +'px';
|
|
this.resizable = resizable;
|
|
textareaResizable.parentNode.insertBefore(resizable, textareaResizable.nextSibling);
|
|
}
|
|
else {
|
|
textarea.parentNode.insertBefore(frame, textarea.nextSibling);
|
|
}
|
|
|
|
function beginDrag(event) {
|
|
offset = frame.height - event.pageY;
|
|
contentDocument = self.contentDocument;
|
|
frame.blur();
|
|
addEvent(d, "mousemove", dragging);
|
|
addEvent(d, "mouseup", endDrag);
|
|
addEvent(contentDocument, "mousemove", draggingForFrame);
|
|
addEvent(contentDocument, "mouseup", endDrag);
|
|
}
|
|
|
|
var topPageY = 0, framePageY = 0;
|
|
function dragging(event) {
|
|
var height = Math.max(32, offset + event.pageY);
|
|
textarea.style.height = height + "px";
|
|
frame.height = height;
|
|
}
|
|
|
|
function draggingForFrame(event) {
|
|
var height = Math.max(32, event.clientY);
|
|
textarea.style.height = height + "px";
|
|
frame.height = height;
|
|
}
|
|
|
|
function endDrag(event) {
|
|
self.focusWysiwyg();
|
|
TracWysiwyg.removeEvent(d, "mousemove", dragging);
|
|
TracWysiwyg.removeEvent(d, "mouseup", endDrag);
|
|
TracWysiwyg.removeEvent(contentDocument, "mousemove", draggingForFrame);
|
|
TracWysiwyg.removeEvent(contentDocument, "mouseup", endDrag);
|
|
}
|
|
|
|
function getDimension(textarea) {
|
|
var width = textarea.offsetWidth;
|
|
if (width) {
|
|
var parentWidth = textarea.parentNode.offsetWidth
|
|
+ parseInt(getStyle(textarea, 'borderLeftWidth'), 10)
|
|
+ parseInt(getStyle(textarea, 'borderRightWidth'), 10);
|
|
if (width == parentWidth) {
|
|
width = "100%";
|
|
}
|
|
}
|
|
return { width: width, height: textarea.offsetHeight };
|
|
}
|
|
|
|
function lazy() {
|
|
var dimension = getDimension(textarea);
|
|
if (dimension.width && dimension.height) {
|
|
self.frame.width = dimension.width;
|
|
self.frame.height = dimension.height;
|
|
if (textareaResizable) {
|
|
grip.style.width = /^[0-9]+$/.exec(dimension.width) ? dimension.width + "px" : dimension.width;
|
|
}
|
|
return;
|
|
}
|
|
setTimeout(lazy, 100);
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.createWysiwygToolbar = function(d) {
|
|
var html = [
|
|
'<ul>',
|
|
'<li class="wysiwyg-menu-style" title="Style">',
|
|
'<a id="wt-style" href="#">',
|
|
'<span class="wysiwyg-menu-style">Style</span>',
|
|
'<span class="wysiwyg-menu-paragraph">Normal</span>',
|
|
'<span class="wysiwyg-menu-heading1">Header 1</span>',
|
|
'<span class="wysiwyg-menu-heading2">Header 2</span>',
|
|
'<span class="wysiwyg-menu-heading3">Header 3</span>',
|
|
'<span class="wysiwyg-menu-heading4">Header 4</span>',
|
|
'<span class="wysiwyg-menu-heading5">Header 5</span>',
|
|
'<span class="wysiwyg-menu-heading6">Header 6</span>',
|
|
'<span class="wysiwyg-menu-code">Code block</span>',
|
|
'<span class="wysiwyg-menu-quote">Quote</span>',
|
|
'</a></li>',
|
|
'<li title="Bold (Ctrl+B)"><a id="wt-strong" href="#"></a></li>',
|
|
'<li title="Italic (Ctrl+I)"><a id="wt-em" href="#"></a></li>',
|
|
'<li title="Underline (Ctrl+U)"><a id="wt-underline" href="#"></a></li>',
|
|
'<li title="Monospace"><a id="wt-monospace" href="#"></a></li>',
|
|
'<li><a id="wt-decorationmenu" href="#"></a></li>',
|
|
'<li title="Remove format"><a id="wt-remove" href="#"></a></li>',
|
|
'<li title="Link"><a id="wt-link" href="#"></a></li>',
|
|
'<li title="Unlink"><a id="wt-unlink" href="#"></a></li>',
|
|
'<li title="Ordered list"><a id="wt-ol" href="#"></a></li>',
|
|
'<li title="List"><a id="wt-ul" href="#"></a></li>',
|
|
'<li title="Outdent"><a id="wt-outdent" href="#"></a></li>',
|
|
'<li title="Indent"><a id="wt-indent" href="#"></a></li>',
|
|
'<li title="Table"><a id="wt-table" href="#"></a></li>',
|
|
'<li><a id="wt-tablemenu" href="#"></a></li>',
|
|
'<li title="Horizontal rule"><a id="wt-hr" href="#"></a></li>',
|
|
'<li title="Line break (Shift+Enter)"><a id="wt-br" href="#"></a></li>',
|
|
'</ul>' ];
|
|
var div = d.createElement("div");
|
|
div.className = "wysiwyg-toolbar";
|
|
div.innerHTML = html.join("").replace(/ href="#">/g, ' href="#" onmousedown="return false" tabindex="-1">');
|
|
return div;
|
|
};
|
|
|
|
TracWysiwyg.prototype.createStyleMenu = function(d) {
|
|
var html = [
|
|
'<p><a id="wt-paragraph" href="#">Normal</a></p>',
|
|
'<h1><a id="wt-heading1" href="#">Header 1</a></h1>',
|
|
'<h2><a id="wt-heading2" href="#">Header 2</a></h2>',
|
|
'<h3><a id="wt-heading3" href="#">Header 3</a></h3>',
|
|
'<h4><a id="wt-heading4" href="#">Header 4</a></h4>',
|
|
'<h5><a id="wt-heading5" href="#">Header 5</a></h5>',
|
|
'<h6><a id="wt-heading6" href="#">Header 6</a></h6>',
|
|
'<pre class="wiki"><a id="wt-code" href="#">Code block</a></pre>',
|
|
'<blockquote class="citation"><a id="wt-quote" href="#">Quote</a></blockquote>' ];
|
|
var menu = d.createElement("div");
|
|
menu.className = "wysiwyg-menu";
|
|
TracWysiwyg.setStyle(menu, { position: "absolute", left: "-1000px", top: "-1000px", zIndex: 1000 });
|
|
menu.innerHTML = html.join("").replace(/ href="#">/g, ' href="#" onmousedown="return false" tabindex="-1">');
|
|
return menu;
|
|
};
|
|
|
|
TracWysiwyg.prototype.createDecorationMenu = function(d) {
|
|
var html = [
|
|
'<ul class="menu">',
|
|
'<li><a id="wt-strike" href="#">Strike through</a></li>',
|
|
'<li><a id="wt-sup" href="#">Superscript</a></li>',
|
|
'<li><a id="wt-sub" href="#">Subscript</a></li>',
|
|
'</ul>' ];
|
|
var menu = d.createElement("div");
|
|
menu.className = "wysiwyg-menu";
|
|
TracWysiwyg.setStyle(menu, { position: "absolute", left: "-1000px", top: "-1000px", zIndex: 1000 });
|
|
menu.innerHTML = html.join("").replace(/ href="#">/g, ' href="#" onmousedown="return false" tabindex="-1">');
|
|
return menu;
|
|
};
|
|
|
|
TracWysiwyg.prototype.createTableMenu = function(d) {
|
|
var html = [
|
|
'<ul class="menu">',
|
|
'<li><a id="wt-insert-row-before" href="#">Insert row before</a></li>',
|
|
'<li><a id="wt-insert-row-after" href="#">Insert row after</a></li>',
|
|
'<li><a id="wt-insert-col-before" href="#">Insert column before</a></li>',
|
|
'<li><a id="wt-insert-col-after" href="#">Insert column after</a></li>',
|
|
'<li><a id="wt-delete-row" href="#">Delete row</a></li>',
|
|
'<li><a id="wt-delete-col" href="#">Delete column</a></li>',
|
|
'</ul>' ];
|
|
var menu = d.createElement("div");
|
|
menu.className = "wysiwyg-menu";
|
|
TracWysiwyg.setStyle(menu, { position: "absolute", left: "-1000px", top: "-1000px", zIndex: 1000 });
|
|
menu.innerHTML = html.join("").replace(/ href="#">/g, ' href="#" onmousedown="return false" tabindex="-1">');
|
|
return menu;
|
|
};
|
|
|
|
TracWysiwyg.prototype.setupMenuEvents = function() {
|
|
function addToolbarEvent(element, self, args) {
|
|
var method = args.shift();
|
|
addEvent(element, "click", function(event) {
|
|
var w = self.contentWindow;
|
|
TracWysiwyg.stopEvent(event || w.event);
|
|
var keepMenus = false, exception;
|
|
try { keepMenus = method.apply(self, args) } catch (e) { exception = e }
|
|
if (!keepMenus) {
|
|
self.hideAllMenus();
|
|
}
|
|
element.blur();
|
|
w.focus();
|
|
if (exception) {
|
|
throw exception;
|
|
}
|
|
});
|
|
}
|
|
|
|
function argsByType(self, name, element) {
|
|
switch (name) {
|
|
case "style": return [ self.toggleMenu, self.styleMenu, element ];
|
|
case "strong": return [ self.execDecorate, "bold" ];
|
|
case "em": return [ self.execDecorate, "italic" ];
|
|
case "underline": return [ self.execDecorate, "underline" ];
|
|
case "strike": return [ self.execDecorate, "strikethrough" ];
|
|
case "sub": return [ self.execDecorate, "subscript" ];
|
|
case "sup": return [ self.execDecorate, "superscript" ];
|
|
case "monospace": return [ self.execDecorate, "monospace" ];
|
|
case "decorationmenu": return [ self.toggleMenu, self.decorationMenu, element ];
|
|
case "remove": return [ self.execCommand, "removeformat" ];
|
|
case "paragraph": return [ self.formatParagraph ];
|
|
case "heading1": return [ self.formatHeaderBlock, "h1" ];
|
|
case "heading2": return [ self.formatHeaderBlock, "h2" ];
|
|
case "heading3": return [ self.formatHeaderBlock, "h3" ];
|
|
case "heading4": return [ self.formatHeaderBlock, "h4" ];
|
|
case "heading5": return [ self.formatHeaderBlock, "h5" ];
|
|
case "heading6": return [ self.formatHeaderBlock, "h6" ];
|
|
case "link": return [ self.createLink ];
|
|
case "unlink": return [ self.execCommand, "unlink" ];
|
|
case "ol": return [ self.insertOrderedList ];
|
|
case "ul": return [ self.insertUnorderedList ];
|
|
case "outdent": return [ self.outdent ];
|
|
case "indent": return [ self.indent ];
|
|
case "table": return [ self.insertTable ];
|
|
case "tablemenu": return [ self.toggleMenu, self.tableMenu, element ];
|
|
case "insert-row-before": return [ self.insertTableRow, false ];
|
|
case "insert-row-after": return [ self.insertTableRow, true ];
|
|
case "insert-col-before": return [ self.insertTableColumn, false ];
|
|
case "insert-col-after": return [ self.insertTableColumn, true ];
|
|
case "delete-row": return [ self.deleteTableRow ];
|
|
case "delete-col": return [ self.deleteTableColumn ];
|
|
case "code": return [ self.formatCodeBlock ];
|
|
case "quote": return [ self.formatQuoteBlock ];
|
|
case "hr": return [ self.insertHorizontalRule ];
|
|
case "br": return [ self.insertLineBreak ];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function setup(container) {
|
|
var elements = container.getElementsByTagName("a");
|
|
var length = elements.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var element = elements[i];
|
|
var name = element.id.replace(/^wt-/, "");
|
|
var args = argsByType(this, name, element);
|
|
if (args) {
|
|
addToolbarEvent(element, this, args);
|
|
buttons[name] = element;
|
|
}
|
|
}
|
|
}
|
|
|
|
var buttons = {};
|
|
setup.call(this, this.wysiwygToolbar);
|
|
for (var i = 0; i < this.menus.length; i++) {
|
|
setup.call(this, this.menus[i]);
|
|
}
|
|
return buttons;
|
|
};
|
|
|
|
TracWysiwyg.prototype.toggleMenu = function(menu, element) {
|
|
if (parseInt(menu.style.left, 10) < 0) {
|
|
this.hideAllMenus(menu);
|
|
var position = TracWysiwyg.elementPosition(element);
|
|
TracWysiwyg.setStyle(menu, { left: position[0] + "px", top: (position[1] + 18) + "px" });
|
|
}
|
|
else {
|
|
this.hideAllMenus();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TracWysiwyg.prototype.hideAllMenus = function(except) {
|
|
var menus = this.menus;
|
|
var length = menus.length;
|
|
for (var i = 0; i < length; i++) {
|
|
if (menus[i] != except) {
|
|
TracWysiwyg.setStyle(menus[i], { left: "-1000px", top: "-1000px" });
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.execDecorate = function(name) {
|
|
if (this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
var position = this.getSelectionPosition();
|
|
var ancestor = {};
|
|
ancestor.start = getSelfOrAncestor(position.start, /^(?:a|tt)$/);
|
|
ancestor.end = getSelfOrAncestor(position.end, /^(?:a|tt)$/);
|
|
this.expandSelectionToElement(ancestor);
|
|
|
|
if (name != "monospace") {
|
|
this.execCommand(name);
|
|
}
|
|
else {
|
|
this.execDecorateMonospace();
|
|
}
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.execDecorateMonospace = function() {
|
|
var html = this.getSelectionHTML();
|
|
var removePattern = /<tt.*?>|<\/tt>/gi;
|
|
if (/^<tt.*?>/i.test(html) && /<\/tt>$/i.test(html)) {
|
|
html = html.replace(removePattern, "");
|
|
}
|
|
else {
|
|
var id = this.generateDomId();
|
|
html = '<tt id="' + id + '">' + html.replace(removePattern, "") + "</tt>";
|
|
}
|
|
this.insertHTML(html);
|
|
var node = this.contentDocument.getElementById(id);
|
|
if (node) {
|
|
this.selectNode(node);
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.execCommand = function(name, arg) {
|
|
return this.contentDocument.execCommand(name, false, arg);
|
|
};
|
|
|
|
TracWysiwyg.prototype.setupEditorEvents = function() {
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
var self = this;
|
|
var d = this.contentDocument;
|
|
var w = this.contentWindow;
|
|
var ime = false;
|
|
|
|
function listenerKeydown(event) {
|
|
var method = null;
|
|
var args = null;
|
|
event = event || self.contentWindow.event;
|
|
var keyCode = event.keyCode;
|
|
switch (keyCode) {
|
|
case 0x09: // TAB
|
|
var range = self.getSelectionRange();
|
|
var stop = false;
|
|
var element = getSelfOrAncestor(range.startContainer, /^(?:li|pre|table)$/);
|
|
if (element) {
|
|
switch (element.tagName.toLowerCase()) {
|
|
case "li":
|
|
self.execCommand(event.shiftKey ? "outdent" : "indent");
|
|
self.selectionChanged();
|
|
stop = true;
|
|
break;
|
|
case "pre":
|
|
self.insertHTML("\t");
|
|
stop = true;
|
|
break;
|
|
case "table":
|
|
if (getSelfOrAncestor(range.endContainer, "table") == element) {
|
|
self.moveFocusInTable(!event.shiftKey);
|
|
self.selectionChanged();
|
|
stop = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (stop) {
|
|
TracWysiwyg.stopEvent(event);
|
|
}
|
|
return;
|
|
case 0xe5:
|
|
ime = true;
|
|
break;
|
|
}
|
|
switch ((keyCode & 0x00fffff) | (event.ctrlKey ? 0x40000000 : 0)
|
|
| (event.shiftKey ? 0x20000000 : 0) | (event.altKey ? 0x10000000 : 0))
|
|
{
|
|
case 0x40000042: // C-b
|
|
method = self.execDecorate;
|
|
args = [ "bold" ];
|
|
break;
|
|
case 0x40000049: // C-i
|
|
method = self.execDecorate;
|
|
args = [ "italic" ];
|
|
break;
|
|
case 0x4000004c: // C-l
|
|
method = self.toggleAutolink;
|
|
args = [];
|
|
break;
|
|
case 0x40000055: // C-u
|
|
method = self.execDecorate;
|
|
args = [ "underline" ];
|
|
break;
|
|
case 0x40000059: // C-y
|
|
method = self.execCommand;
|
|
args = [ "redo" ];
|
|
break;
|
|
case 0x4000005a: // C-z
|
|
method = self.execCommand;
|
|
args = [ "undo" ];
|
|
break;
|
|
}
|
|
if (method !== null) {
|
|
TracWysiwyg.stopEvent(event);
|
|
method.apply(self, args);
|
|
self.selectionChanged();
|
|
}
|
|
else if (keyCode) {
|
|
var focus = self.getFocusNode();
|
|
if (!getSelfOrAncestor(focus, /^(?:p|li|h[1-6]|t[dh]|d[td]|pre|blockquote)$/)) {
|
|
self.execCommand("formatblock", "<p>");
|
|
}
|
|
}
|
|
}
|
|
addEvent(d, window.opera ? "keypress" : "keydown", listenerKeydown);
|
|
|
|
function listenerKeypress(event) {
|
|
event = event || self.contentWindow.event;
|
|
var modifier = (event.ctrlKey ? 0x40000000 : 0)
|
|
| (event.shiftKey ? 0x20000000 : 0) | (event.altKey ? 0x10000000 : 0);
|
|
switch (event.charCode || event.keyCode) {
|
|
case 0x20: // SPACE
|
|
self.detectTracLink(event);
|
|
return;
|
|
case 0x3e: // ">"
|
|
self.detectTracLink(event);
|
|
return;
|
|
case 0x0d: // ENTER
|
|
self.detectTracLink(event);
|
|
switch (modifier) {
|
|
case 0:
|
|
if (self.insertParagraphOnEnter) {
|
|
self.insertParagraphOnEnter(event);
|
|
}
|
|
break;
|
|
case 0x20000000: // Shift
|
|
if (self.insertLineBreakOnShiftEnter) {
|
|
self.insertLineBreakOnShiftEnter(event);
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
addEvent(d, "keypress", listenerKeypress);
|
|
|
|
function listenerKeyup(event) {
|
|
var keyCode = event.keyCode;
|
|
if (ime) {
|
|
switch (keyCode) {
|
|
case 0x20: // SPACE
|
|
self.detectTracLink(event);
|
|
break;
|
|
}
|
|
ime = false;
|
|
}
|
|
self.selectionChanged();
|
|
}
|
|
addEvent(d, "keyup", listenerKeyup);
|
|
|
|
function listenerMouseup(event) {
|
|
self.selectionChanged();
|
|
}
|
|
addEvent(d, "mouseup", listenerMouseup);
|
|
|
|
function listenerClick(event) {
|
|
self.hideAllMenus();
|
|
self.selectionChanged();
|
|
}
|
|
addEvent(d, "click", listenerClick);
|
|
};
|
|
|
|
TracWysiwyg.prototype.loadWysiwygDocument = function() {
|
|
var d = this.contentDocument;
|
|
var container = d.body;
|
|
var tmp;
|
|
|
|
while (tmp = container.lastChild) {
|
|
container.removeChild(tmp);
|
|
}
|
|
var fragment = this.wikitextToFragment(this.textarea.value, d, this.options);
|
|
container.appendChild(fragment);
|
|
this.savedWysiwygHTML = container.innerHTML;
|
|
};
|
|
|
|
TracWysiwyg.prototype.focusWysiwyg = function() {
|
|
var self = this;
|
|
var w = this.contentWindow;
|
|
function lazy() {
|
|
w.focus();
|
|
try { self.execCommand("useCSS", false); } catch (e) { }
|
|
try { self.execCommand("styleWithCSS", false); } catch (e) { }
|
|
self.selectionChanged();
|
|
}
|
|
setTimeout(lazy, 10);
|
|
};
|
|
|
|
TracWysiwyg.prototype.loadTracWikiText = function() {
|
|
this.textarea.value = this.domToWikitext(this.contentDocument.body, this.options);
|
|
this.savedWysiwygHTML = null;
|
|
};
|
|
|
|
TracWysiwyg.prototype.focusTextarea = function() {
|
|
this.textarea.focus();
|
|
};
|
|
|
|
TracWysiwyg.prototype.setupToggleEditorButtons = function() {
|
|
var div = document.createElement("div");
|
|
var mode = TracWysiwyg.editorMode;
|
|
var html = ''
|
|
+ '<label for="editor-autolink-@" title="Links as you type (Ctrl-L)">'
|
|
+ '<input type="checkbox" id="editor-autolink-@" checked="checked" />'
|
|
+ 'autolink </label>'
|
|
+ '<label for="editor-wysiwyg-@">'
|
|
+ '<input type="radio" name="__EDITOR__@" value="wysiwyg" id="editor-wysiwyg-@" '
|
|
+ (mode == "wysiwyg" ? 'checked="checked"' : '') + ' />'
|
|
+ 'wysiwyg</label> '
|
|
+ '<label for="editor-textarea-@">'
|
|
+ '<input type="radio" name="__EDITOR__@" value="textarea" id="editor-textarea-@" '
|
|
+ (mode == "textarea" ? 'checked="checked"' : '') + ' />'
|
|
+ 'textarea</label> '
|
|
+ ' ';
|
|
div.className = "editor-toggle";
|
|
div.innerHTML = html.replace(/@/g, ++TracWysiwyg.count);
|
|
this.toggleEditorButtons = div;
|
|
|
|
var buttons = div.getElementsByTagName("input");
|
|
for (var i = 0; i < buttons.length; i++) {
|
|
var button = buttons[i];
|
|
switch (button.type) {
|
|
case "checkbox":
|
|
var listener = this.listenerToggleAutolink(button);
|
|
addEvent(button, "click", listener);
|
|
addEvent(button, "keypress", listener);
|
|
this.autolinkButton = button;
|
|
break;
|
|
case "radio":
|
|
addEvent(button, "click", this.listenerToggleEditor(button.value));
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.setupSyncTextAreaHeight = function() {
|
|
var self = this;
|
|
var d = document;
|
|
var timer = null;
|
|
|
|
var editrows = document.getElementById("editrows");
|
|
if (editrows) {
|
|
addEvent(editrows, "change", changeHeight);
|
|
}
|
|
if (this.textareaResizable) {
|
|
addEvent(this.textarea.nextSibling, "mousedown", beginDrag);
|
|
}
|
|
|
|
function changeHeight() {
|
|
if (timer !== null) {
|
|
clearTimeout(timer);
|
|
}
|
|
setTimeout(sync, 10);
|
|
}
|
|
|
|
function beginDrag(event) {
|
|
addEvent(d, "mousemove", changeHeight);
|
|
addEvent(d, "mouseup", endDrag);
|
|
}
|
|
|
|
function endDrag(event) {
|
|
TracWysiwyg.removeEvent(d, "mousemove", changeHeight);
|
|
TracWysiwyg.removeEvent(d, "mouseup", endDrag);
|
|
}
|
|
|
|
function sync() {
|
|
timer = null;
|
|
self.syncTextAreaHeight();
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.syncTextAreaHeight = function() {
|
|
var height = this.textarea.offsetHeight;
|
|
var frame = this.frame;
|
|
if (height > 0 && frame.height != height) {
|
|
frame.height = height;
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.detectTracLink = function(event) {
|
|
if (!this.autolink) {
|
|
return;
|
|
}
|
|
var range = this.getSelectionRange();
|
|
var node = range.startContainer;
|
|
if (!node || !range.collapsed) {
|
|
return;
|
|
}
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
if (getSelfOrAncestor(node, /^(?:a|tt|pre)$/)) {
|
|
return;
|
|
}
|
|
|
|
var offset = range.startOffset;
|
|
if (node.nodeType != 3) {
|
|
node = node.childNodes[offset];
|
|
while (node && node.nodeType != 3) {
|
|
node = node.lastChild;
|
|
}
|
|
if (!node) {
|
|
return;
|
|
}
|
|
offset = node.nodeValue.length;
|
|
}
|
|
else if (offset == 0) {
|
|
node = node.previousSibling;
|
|
if (!node || node.nodeType == 1) {
|
|
return;
|
|
}
|
|
offset = node.nodeValue.length;
|
|
}
|
|
var startContainer = node;
|
|
var endContainer = node;
|
|
var text = [ node.nodeValue.substring(0, offset) ];
|
|
for ( ; ; ) {
|
|
if (/[ \t\r\n\f\v]/.test(text[text.length - 1])) {
|
|
break;
|
|
}
|
|
node = node.previousSibling;
|
|
if (!node || node.nodeType == 1) {
|
|
break;
|
|
}
|
|
text.push(node.nodeValue);
|
|
startContainer = node;
|
|
}
|
|
text.reverse();
|
|
text = text.join("");
|
|
if (!text) {
|
|
return;
|
|
}
|
|
|
|
var pattern = this.wikiDetectTracLinkPattern;
|
|
pattern.lastIndex = /[^ \t\r\n\f\v]*$/.exec(text).index;
|
|
var match, tmp;
|
|
for (tmp = pattern.exec(text); tmp; tmp = pattern.exec(text)) {
|
|
match = tmp;
|
|
}
|
|
if (!match) {
|
|
return;
|
|
}
|
|
|
|
var label = match[0];
|
|
var link = this.normalizeTracLink(label);
|
|
var id = this.generateDomId();
|
|
var anchor = this.createAnchor(link, label, { id: id, "data-tracwysiwyg-autolink": "true" });
|
|
var anonymous = this.contentDocument.createElement("div");
|
|
anonymous.appendChild(anchor);
|
|
var html = anonymous.innerHTML;
|
|
|
|
node = endContainer;
|
|
var startOffset = match.index;
|
|
while (startContainer != node && startOffset >= startContainer.nodeValue.length) {
|
|
startOffset -= startContainer.nodeValue.length;
|
|
startContainer = startContainer.nextSibling;
|
|
}
|
|
var endOffset = startOffset + label.length;
|
|
endContainer = startContainer;
|
|
while (endContainer != node && endOffset >= endContainer.nodeValue.length) {
|
|
endOffset -= endContainer.nodeValue.length;
|
|
endContainer = endContainer.nextSibling;
|
|
}
|
|
this.selectRange(startContainer, startOffset, endContainer, endOffset);
|
|
|
|
offset = text.length - match.index - label.length;
|
|
if (offset == 0) {
|
|
switch (event.keyCode) {
|
|
case 0x20: // SPACE
|
|
this.insertHTML(html + "\u00a0");
|
|
TracWysiwyg.stopEvent(event);
|
|
return;
|
|
case 0x0d: // ENTER
|
|
if (event.shiftKey) {
|
|
if (window.opera || !anonymous.addEventListener) {
|
|
this.insertHTML(html + "<br>");
|
|
if (window.opera) {
|
|
anchor = this.contentDocument.getElementById(id);
|
|
node = anchor.parentNode;
|
|
offset = node.childNodes.length;
|
|
this.selectRange(node, offset, node, offset);
|
|
}
|
|
TracWysiwyg.stopEvent(event);
|
|
return;
|
|
}
|
|
}
|
|
this.insertHTML(html);
|
|
anchor = this.contentDocument.getElementById(id);
|
|
node = event.shiftKey ? anchor.parentNode : anchor;
|
|
offset = node.childNodes.length;
|
|
this.selectRange(node, offset, node, offset);
|
|
return;
|
|
}
|
|
}
|
|
this.insertHTML(html);
|
|
anchor = this.contentDocument.getElementById(id);
|
|
node = anchor.nextSibling;
|
|
if (!node) {
|
|
node = anchor.parentNode;
|
|
offset = node.childNodes.length;
|
|
}
|
|
this.selectRange(node, offset, node, offset);
|
|
};
|
|
|
|
TracWysiwyg.prototype.formatParagraph = function() {
|
|
if (this.selectionContainsTagName("table")) {
|
|
return;
|
|
}
|
|
this.execCommand("formatblock", "<p>");
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.formatHeaderBlock = function(name) {
|
|
if (this.selectionContainsTagName("table")) {
|
|
return;
|
|
}
|
|
this.execCommand("formatblock", "<" + name + ">");
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertOrderedList = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
this.execCommand("insertorderedlist");
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertUnorderedList = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
this.execCommand("insertunorderedlist");
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.outdent = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
this.execCommand("outdent");
|
|
};
|
|
|
|
TracWysiwyg.prototype.indent = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
this.execCommand("indent");
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertTable = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
var id = this.generateDomId();
|
|
this.insertHTML(this.tableHTML(id, 2, 3));
|
|
var element = this.contentDocument.getElementById(id)
|
|
if (element) {
|
|
this.selectNodeContents(element);
|
|
}
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype._tableHTML = function(row, col) {
|
|
var tr = "<tr>" + ((1 << col) - 1).toString(2).replace(/1/g, "<td></td>") + "</tr>";
|
|
var html = [
|
|
'<table class="wiki">', '<tbody>',
|
|
((1 << row) - 1).toString(2).replace(/1/g, tr),
|
|
'</tbody>', '</table>' ];
|
|
return html.join("");
|
|
};
|
|
|
|
TracWysiwyg.prototype._getFocusForTable = function() {
|
|
var hash = { node: null, cell: null, row: null, table: null };
|
|
hash.node = this.getFocusNode();
|
|
hash.cell = hash.node ? TracWysiwyg.getSelfOrAncestor(hash.node, /^t[dh]$/) : null;
|
|
hash.row = hash.cell ? TracWysiwyg.getSelfOrAncestor(hash.cell, "tr") : null;
|
|
hash.table = hash.row ? TracWysiwyg.getSelfOrAncestor(hash.row, "table") : null;
|
|
return hash;
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertTableRow = function(after) {
|
|
var focus = this._getFocusForTable();
|
|
if (focus.table && focus.row) {
|
|
var d = this.contentDocument;
|
|
var cells = focus.row.getElementsByTagName("td");
|
|
var row = focus.table.insertRow(focus.row.rowIndex + (after ? 1 : 0));
|
|
for (var j = 0; j < cells.length; j++) {
|
|
this.insertTableCell(row, 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertTableColumn = function(after) {
|
|
var focus = this._getFocusForTable();
|
|
if (focus.table && focus.cell) {
|
|
var d = this.contentDocument;
|
|
var rows = focus.table.rows;
|
|
var length = rows.length;
|
|
var cellIndex = focus.cell.cellIndex + (after ? 1 : 0);
|
|
for (var i = 0; i < length; i++) {
|
|
var row = rows[i];
|
|
this.insertTableCell(row, Math.min(cellIndex, row.cells.length));
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.deleteTableRow = function() {
|
|
var focus = this._getFocusForTable();
|
|
if (focus.table && focus.row) {
|
|
focus.table.deleteRow(focus.row.rowIndex);
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.deleteTableColumn = function() {
|
|
var focus = this._getFocusForTable();
|
|
if (focus.table && focus.cell) {
|
|
var rows = focus.table.rows;
|
|
var length = rows.length;
|
|
var cellIndex = focus.cell.cellIndex;
|
|
for (var i = 0; i < length; i++) {
|
|
var row = rows[i];
|
|
if (cellIndex < row.cells.length) {
|
|
row.deleteCell(cellIndex);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.moveFocusInTable = function(forward) {
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
var focus = this.getFocusNode();
|
|
var element = getSelfOrAncestor(focus, /^(?:t[dhr]|table)$/);
|
|
var target, table, rows, cells;
|
|
switch (element.tagName.toLowerCase()) {
|
|
case "td": case "th":
|
|
focus = element;
|
|
var row = getSelfOrAncestor(element, "tr");
|
|
cells = row.cells;
|
|
if (forward) {
|
|
if (focus.cellIndex + 1 < cells.length) {
|
|
target = cells[focus.cellIndex + 1];
|
|
}
|
|
else {
|
|
table = getSelfOrAncestor(row, /^(?:tbody|table)$/);
|
|
rows = table.rows;
|
|
target = row.rowIndex + 1 < rows.length ? rows[row.rowIndex + 1].cells[0] : null;
|
|
}
|
|
}
|
|
else {
|
|
if (focus.cellIndex > 0) {
|
|
target = cells[focus.cellIndex - 1];
|
|
}
|
|
else {
|
|
table = getSelfOrAncestor(row, /^(?:tbody|table)$/);
|
|
rows = table.rows;
|
|
if (row.rowIndex > 0) {
|
|
cells = rows[row.rowIndex - 1].cells;
|
|
target = cells[cells.length - 1];
|
|
}
|
|
else {
|
|
target = null;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "tr":
|
|
cells = element.cells;
|
|
target = cells[forward ? 0 : cells.length - 1];
|
|
break;
|
|
case "tbody": case "table":
|
|
rows = element.rows;
|
|
cells = rows[forward ? 0 : rows.length - 1].cells;
|
|
target = cells[forward ? 0 : cells.length - 1];
|
|
break;
|
|
}
|
|
if (target) {
|
|
this.selectNodeContents(target);
|
|
}
|
|
else if (table) {
|
|
table = getSelfOrAncestor(table, "table");
|
|
var parent = table.parentNode;
|
|
var elements = parent.childNodes;
|
|
var length = elements.length;
|
|
for (var offset = 0; offset < length; offset++) {
|
|
if (table == elements[offset]) {
|
|
if (forward) {
|
|
offset++;
|
|
}
|
|
this.selectRange(parent, offset, parent, offset);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.formatCodeBlock = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
var text = this.getSelectionText();
|
|
if (!text) {
|
|
var node = this.getFocusNode();
|
|
while (node.nodeType == 3) {
|
|
node = node.parentNode;
|
|
}
|
|
text = TracWysiwyg.getTextContent(node);
|
|
this.selectNode(node);
|
|
}
|
|
|
|
var fragment = this.getSelectionFragment();
|
|
text = this.domToWikitext(fragment, { formatCodeBlock: true }).replace(/\s+$/, "");
|
|
|
|
var d = this.contentDocument;
|
|
var anonymous = d.createElement("div");
|
|
var pre = d.createElement("pre");
|
|
pre.className = "wiki";
|
|
anonymous.appendChild(pre);
|
|
if (text) {
|
|
pre.appendChild(d.createTextNode(text));
|
|
}
|
|
|
|
this.insertHTML(anonymous.innerHTML);
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.formatQuoteBlock = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
var d = this.contentDocument;
|
|
var anonymous = d.createElement("div");
|
|
|
|
var container = d.createElement("blockquote");
|
|
container.className = "citation";
|
|
var fragment = this.getSelectionFragment();
|
|
var childNodes = fragment.childNodes;
|
|
for (var i = childNodes.length - 1; i >= 0; i--) {
|
|
var child = childNodes[i];
|
|
var text = null;
|
|
switch (child.nodeType) {
|
|
case 1:
|
|
if (child.tagName.toLowerCase() != "blockquote" || child.className != "citation") {
|
|
text = TracWysiwyg.getTextContent(child);
|
|
}
|
|
break;
|
|
case 3:
|
|
text = child.nodeValue;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (text !== null) {
|
|
if (!text) {
|
|
continue;
|
|
}
|
|
var tmp = d.createElement("p");
|
|
tmp.appendChild(d.createTextNode(text));
|
|
child = tmp;
|
|
}
|
|
container.insertBefore(child, container.firstChild);
|
|
}
|
|
if (container.childNodes.length == 0) {
|
|
container.appendChild(d.createElement("p"));
|
|
}
|
|
anonymous.appendChild(container);
|
|
|
|
this.insertHTML(anonymous.innerHTML);
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.insertHorizontalRule = function() {
|
|
if (this.selectionContainsTagName("table") || this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
if (!this.execCommand("inserthorizontalrule")) {
|
|
this.insertHTML("<hr />");
|
|
}
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.createLink = function() {
|
|
if (this.selectionContainsTagName("pre")) {
|
|
return;
|
|
}
|
|
|
|
var focus = this.getFocusNode();
|
|
var anchor = TracWysiwyg.getSelfOrAncestor(focus, "a");
|
|
var expand = anchor || TracWysiwyg.getSelfOrAncestor(focus, "tt");
|
|
var currLink;
|
|
if (anchor) {
|
|
var attrs;
|
|
var autolink = anchor.getAttribute("data-tracwysiwyg-autolink");
|
|
if (autolink === null) {
|
|
attrs = TracWysiwyg.unserializeFromHref(anchor.href);
|
|
autolink = attrs["data-tracwysiwyg-autolink"];
|
|
}
|
|
if (autolink == "true") {
|
|
var pattern = this.wikiDetectTracLinkPattern;
|
|
pattern.lastIndex = 0;
|
|
var label = TracWysiwyg.getTextContent(anchor);
|
|
var match = pattern.exec(label);
|
|
if (match && match.index == 0 && match[0].length == label.length) {
|
|
currLink = this.normalizeTracLink(label);
|
|
}
|
|
}
|
|
if (!currLink) {
|
|
currLink = anchor.getAttribute("data-tracwysiwyg-link") || attrs["data-tracwysiwyg-link"]
|
|
|| anchor.href;
|
|
}
|
|
}
|
|
else {
|
|
currLink = "";
|
|
}
|
|
if (expand) {
|
|
this.selectNodeContents(expand);
|
|
}
|
|
var text = this.getSelectionText() || "";
|
|
var newLink = (prompt(text ? "Enter TracLink:" : "Insert TracLink:", currLink) || "").replace(/^\s+|\s+$/g, "");
|
|
if (newLink && newLink != currLink) {
|
|
text = text || newLink;
|
|
newLink = this.normalizeTracLink(newLink);
|
|
var id = this.generateDomId();
|
|
var d = this.contentDocument;
|
|
var anonymous = d.createElement("div");
|
|
anchor = this.createAnchor(newLink, text, { id: id });
|
|
anonymous.appendChild(anchor);
|
|
this.insertHTML(anonymous.innerHTML);
|
|
anchor = d.getElementById(id);
|
|
if (anchor) {
|
|
this.selectNodeContents(anchor);
|
|
}
|
|
}
|
|
this.selectionChanged();
|
|
};
|
|
|
|
TracWysiwyg.prototype.createAnchor = function(link, label, attrs) {
|
|
var d = this.contentDocument;
|
|
var anchor = d.createElement("a");
|
|
var href = {};
|
|
for (var name in attrs) {
|
|
var value = attrs[name];
|
|
href[name] = value;
|
|
anchor.setAttribute(name, value);
|
|
}
|
|
href["data-tracwysiwyg-link"] = link;
|
|
anchor.href = TracWysiwyg.serializeToHref(href);
|
|
anchor.title = link;
|
|
anchor.setAttribute("data-tracwysiwyg-link", link);
|
|
anchor.setAttribute("onclick", "return false;");
|
|
anchor.appendChild(d.createTextNode(label));
|
|
return anchor;
|
|
};
|
|
TracWysiwyg.prototype.collectChildNodes = function(dest, source) {
|
|
var childNodes = source.childNodes;
|
|
for (var i = childNodes.length - 1; i >= 0; i--) {
|
|
dest.insertBefore(childNodes[i], dest.firstChild);
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.generateDomId = function() {
|
|
var d = this.contentDocument;
|
|
for ( ; ; ) {
|
|
var id = "tmp-" + (new Date().valueOf().toString(36));
|
|
if (!d.getElementById(id)) {
|
|
return id;
|
|
}
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.prototype.selectionChanged = function() {
|
|
var status = {
|
|
strong: false, em: false, underline: false, strike: false, sub: false,
|
|
sup: false, monospace: false, paragraph: false, heading1: false,
|
|
heading2: false, heading3: false, heading4: false, heading5: false,
|
|
heading6: false, link: false, ol: false, ul: false, outdent: false,
|
|
indent: false, table: false, code: false, quote: false, hr: false,
|
|
br: false };
|
|
var tagNameToKey = {
|
|
b: "strong", i: "em", u: "underline", del: "strike", tt: "monospace",
|
|
p: "paragraph", h1: "heading1", h2: "heading2", h3: "heading3",
|
|
h4: "heading4", h5: "heading5", h6: "heading6", a: "link", pre: "code",
|
|
blockquote: "quote" };
|
|
var position = this.getSelectionPosition();
|
|
|
|
var node;
|
|
if (position.start) {
|
|
node = position.start == position.end ? position.start.firstChild : position.start.nextSibling;
|
|
node = node || position.start;
|
|
}
|
|
else {
|
|
node = null;
|
|
}
|
|
while (node) {
|
|
if (node.nodeType == 1) {
|
|
var name = node.tagName.toLowerCase();
|
|
if (name in tagNameToKey) {
|
|
name = tagNameToKey[name];
|
|
}
|
|
status[name] = true;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
|
|
var toolbarButtons = this.toolbarButtons;
|
|
for (var name in status) {
|
|
var button = toolbarButtons[name];
|
|
if (button) {
|
|
var parent = button.parentNode;
|
|
parent.className = (parent.className || "").replace(/ *\bselected\b|$/, status[name] ? " selected" : "");
|
|
}
|
|
}
|
|
|
|
var styles = [ "quote", "paragraph", "code", "heading1",
|
|
"heading2", "heading3", "heading4", "heading5", "heading6" ];
|
|
var styleButton = toolbarButtons["style"];
|
|
var styleButtonClass = "wysiwyg-menu-style";
|
|
for (var i = 0; i < styles.length; i++) {
|
|
var name = styles[i];
|
|
if (status[name]) {
|
|
styleButtonClass = "wysiwyg-menu-" + name;
|
|
break;
|
|
}
|
|
}
|
|
styleButton.parentNode.className = styleButtonClass;
|
|
};
|
|
|
|
(function() {
|
|
var _linkScheme = "[a-zA-Z][a-zA-Z0-9+-.]*";
|
|
// cf. WikiSystem.XML_NAME, http://www.w3.org/TR/REC-xml/#id
|
|
var _xmlName = "[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD](?:[-:_.A-Za-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*[-_A-Za-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD])?"
|
|
var _quotedString = "'[^']+'|" + '"[^"]+"';
|
|
var _changesetId = "(?:\\d+|[a-fA-F\\d]{6,})";
|
|
var _ticketLink = "#\\d+";
|
|
var _reportLink = "\\{\\d+\\}";
|
|
var _changesetPath = "/[^\\]]*";
|
|
var _changesetLinkBracket = "\\[" + _changesetId + "(?:" + _changesetPath + ")?\\]";
|
|
var _changesetLinkRev = "r" + _changesetId + "\\b(?!:" + _changesetId + ")";
|
|
var _logLinkBracket = "\\[" + _changesetId + "[-:]" + _changesetId + "(?:" + _changesetPath + ")?\\]";
|
|
var _logLinkRev = "r" + _changesetId + "[-:]" + _changesetId + "\\b";
|
|
var _tracLink = _linkScheme + ":(?:" + _quotedString
|
|
+ "|[a-zA-Z0-9/?!#@](?:(?:\\|(?=[^| \\t\\r\\f\\v])|[^|<> \\t\\r\\f\\v])*[a-zA-Z0-9/=])?)";
|
|
var _wikiPageName = "[A-Z][a-z]+(?:[A-Z][a-z]*[a-z/])+(?:#[\\w:][-\\w\\d.:]*)?"
|
|
+ "(?=:(?:$|[ \\t\\r\\f\\v])|[^:a-zA-Z]|[ \\t\\r\\f\\v]|$)";
|
|
var wikiInlineRules = [];
|
|
wikiInlineRules.push("!?'''''"); // 1. bolditalic
|
|
wikiInlineRules.push("!?'''"); // 2. bold
|
|
wikiInlineRules.push("!?''"); // 3. italic
|
|
wikiInlineRules.push("!?__"); // 4. underline
|
|
wikiInlineRules.push("!?~~"); // 5. strike
|
|
wikiInlineRules.push("!?,,"); // 6. subscript
|
|
wikiInlineRules.push("!?\\^"); // 7. superscript
|
|
wikiInlineRules.push("!?\\{\\{\\{.*?\\}\\}\\}"); // 8. code block
|
|
wikiInlineRules.push("!?`.*?`"); // 9. inline
|
|
wikiInlineRules.push("[!&]?" + _ticketLink); // 10. ticket
|
|
wikiInlineRules.push("!?" + _reportLink); // 11. report
|
|
// 12. changeset
|
|
wikiInlineRules.push("!?" + _changesetLinkBracket + "|(?:\\b|!)" + _changesetLinkRev);
|
|
// 13. log
|
|
wikiInlineRules.push("!?" + _logLinkBracket + "|(?:\\b|!)" + _logLinkRev);
|
|
wikiInlineRules.push("!?" + _tracLink); // 14. wiki:TracLinks
|
|
wikiInlineRules.push("!?\\[(?:" // 15. [wiki:TracLinks label] or [/relative label]
|
|
+ "[/.#][^ \\t\\r\\f\\v[\\]]*|"
|
|
+ _linkScheme + ":(?:" + _quotedString + "|[^\\] \\t\\r\\f\\v]*)|"
|
|
+ _wikiPageName + "[ \\t\\r\\f\\v]+(?:" + _quotedString + "|[^\\]]+)"
|
|
+ ")(?:[ \\t\\r\\f\\v]+(?:" + _quotedString + "|[^\\]]+))?\\]");
|
|
// 16. [[macro]]
|
|
wikiInlineRules.push("!?\\[\\[(?:[\\w/+-]+\\??|\\?)(?:\\]\\]|\\(.*?\\)\\]\\])");
|
|
// 17. WikiPageName
|
|
wikiInlineRules.push("(?:\\b|!)" + _wikiPageName);
|
|
// 18. ["internal free link"]
|
|
wikiInlineRules.push("!?\\[(?:" + _quotedString + ")\\]");
|
|
// 19. <wiki:Trac bracket links>
|
|
wikiInlineRules.push("!?<@:[^>]+>".replace(/@/g, _linkScheme));
|
|
// 20. [=#anchor label]
|
|
wikiInlineRules.push("!?\\[=#" + _xmlName + "(?:[ \\t\\r\\f\\v]+[^\\]]*)?\\]");
|
|
|
|
var wikiToDomInlineRules = wikiInlineRules.slice(0);
|
|
// 1001. escaping double pipes
|
|
wikiToDomInlineRules.push("!=?(?:\\|\\|)+(?:[ \\t\\r\\f\\v]*$|)");
|
|
|
|
var wikiRules = wikiToDomInlineRules.slice(0);
|
|
wikiRules.push("^(?: *>)+[ \\t\\r\\f\\v]*"); // -1. citation
|
|
// -2. header
|
|
wikiRules.push("^[ \\t\\r\\f\\v]*={1,6}[ \\t\\r\\f\\v]+.*?(?:#" + _xmlName + ")?[ \\t\\r\\f\\v]*$");
|
|
// -3. list
|
|
wikiRules.push("^[ \\t\\r\\f\\v]*(?:[-*]|[0-9]+\\.|[a-zA-Z]\\.|[ivxIVX]{1,5}\\.) ");
|
|
// -4. definition
|
|
wikiRules.push("^[ \\t\\r\\f\\v]+(?:`[^`]*`|\\{\\{\\{.*?\\}\\}\\}|[^`{:]|:[^:])+::(?:[ \\t\\r\\f\\v]+|$)");
|
|
wikiRules.push("^[ \\t\\r\\f\\v]+(?=[^ \\t\\r\\f\\v])"); // -5. leading space
|
|
wikiRules.push("=?(?:\\|\\|)+[ \\t\\r\\f\\v]*\\\\?$"); // -6. closing table row
|
|
wikiRules.push("=?(?:\\|\\|)+=?"); // -7. cell
|
|
|
|
var domToWikiInlineRules = wikiInlineRules.slice(0);
|
|
domToWikiInlineRules.push("!?=?(?:\\|\\|)+=?"); // cell
|
|
|
|
var wikiSyntaxRules = [];
|
|
wikiSyntaxRules.push(_ticketLink);
|
|
wikiSyntaxRules.push(_reportLink);
|
|
wikiSyntaxRules.push(_changesetLinkBracket);
|
|
wikiSyntaxRules.push(_changesetLinkRev);
|
|
wikiSyntaxRules.push(_logLinkBracket);
|
|
wikiSyntaxRules.push(_logLinkRev);
|
|
|
|
var wikiDetectTracLinkRules = [];
|
|
wikiDetectTracLinkRules.push(_ticketLink);
|
|
wikiDetectTracLinkRules.push(_reportLink);
|
|
wikiDetectTracLinkRules.push(_changesetLinkBracket);
|
|
wikiDetectTracLinkRules.push("\\b" + _changesetLinkRev);
|
|
wikiDetectTracLinkRules.push(_logLinkBracket);
|
|
wikiDetectTracLinkRules.push("\\b" + _logLinkRev);
|
|
wikiDetectTracLinkRules.push(_tracLink);
|
|
wikiDetectTracLinkRules.push("\\b" + _wikiPageName);
|
|
|
|
var domToWikiInlinePattern = new RegExp("(?:" + domToWikiInlineRules.join("|") + ")", "g");
|
|
var wikiRulesPattern = new RegExp("(?:(" + wikiRules.join(")|(") + "))", "g");
|
|
var wikiSyntaxPattern = new RegExp("^(?:" + wikiSyntaxRules.join("|") + ")$");
|
|
var wikiSyntaxLogPattern = new RegExp("^[\\[r]" + _changesetId + "[-:]");
|
|
var wikiDetectTracLinkPattern = new RegExp("(?:" + wikiDetectTracLinkRules.join("|") + ")", "g");
|
|
|
|
TracWysiwyg.prototype._linkScheme = _linkScheme;
|
|
TracWysiwyg.prototype._quotedString = _quotedString;
|
|
TracWysiwyg.prototype._changesetId = _changesetId;
|
|
TracWysiwyg.prototype._tracLink = _tracLink;
|
|
TracWysiwyg.prototype._wikiPageName = _wikiPageName;
|
|
TracWysiwyg.prototype.wikiInlineRules = wikiInlineRules;
|
|
TracWysiwyg.prototype.wikiToDomInlineRules = wikiToDomInlineRules;
|
|
TracWysiwyg.prototype.xmlNamePattern = new RegExp("^" + _xmlName + "$");
|
|
TracWysiwyg.prototype.domToWikiInlinePattern = domToWikiInlinePattern;
|
|
TracWysiwyg.prototype.wikiRulesPattern = wikiRulesPattern;
|
|
TracWysiwyg.prototype.wikiSyntaxPattern = wikiSyntaxPattern;
|
|
TracWysiwyg.prototype.wikiSyntaxLogPattern = wikiSyntaxLogPattern;
|
|
TracWysiwyg.prototype.wikiDetectTracLinkPattern = wikiDetectTracLinkPattern;
|
|
})();
|
|
|
|
TracWysiwyg.prototype.normalizeTracLink = function(link) {
|
|
link = this.convertWikiSyntax(link);
|
|
if (/^[\/.#]/.test(link)) {
|
|
link = encode(link);
|
|
}
|
|
if (!/^[\w.+-]+:/.test(link)) {
|
|
link = "wiki:" + link;
|
|
}
|
|
if (/^wiki:[^\"\']/.test(link) && /\s/.test(link)) {
|
|
if (link.indexOf('"') === -1) {
|
|
link = 'wiki:"' + link + '"';
|
|
}
|
|
else if (link.indexOf("'") === -1) {
|
|
link = "wiki:'" + link + "'";
|
|
}
|
|
else {
|
|
link = 'wiki:"' + link.replace(/"/g, "%22") + '"';
|
|
}
|
|
}
|
|
return link;
|
|
};
|
|
|
|
TracWysiwyg.prototype.convertWikiSyntax = function(link) {
|
|
var match = this.wikiSyntaxPattern.exec(link);
|
|
if (match) {
|
|
switch (match[0].charCodeAt(0)) {
|
|
case 0x7b: // "{"
|
|
link = "report:" + link.slice(1, -1);
|
|
break;
|
|
case 0x5b: // "["
|
|
link = (this.wikiSyntaxLogPattern.test(link) ? "log:@" : "changeset:") + link.slice(1, -1);
|
|
break;
|
|
case 0x23: // #
|
|
link = "ticket:" + link.substring(1);
|
|
break;
|
|
case 0x72: // r
|
|
link = (this.wikiSyntaxLogPattern.test(link) ? "log:@" : "changeset:") + link.substring(1);
|
|
break;
|
|
}
|
|
}
|
|
return link;
|
|
};
|
|
|
|
TracWysiwyg.prototype.isInlineNode = function(node) {
|
|
if (node) {
|
|
switch (node.nodeType) {
|
|
case 1:
|
|
return (node.tagName.toLowerCase() in this.wikiInlineTags);
|
|
case 3:
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
(function() {
|
|
var blocks = {
|
|
p: true, blockquote: true, div: true,
|
|
li: true, ul: true, ol: true,
|
|
dl: true, dt: true, dd: true,
|
|
h1: true, h2: true, h3: true, h4: true, h5: true, h6: true,
|
|
table: true, thead: true, tbody: true, tr: true, td: true, th: true };
|
|
|
|
function generator(prop, blocks) {
|
|
return function (node) {
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
for ( ; ; ) {
|
|
if (node[prop]) {
|
|
return false;
|
|
}
|
|
node = node.parentNode;
|
|
if (!node) {
|
|
return true;
|
|
}
|
|
if (node.nodeType == 1 && node.tagName.toLowerCase() in blocks) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
|
|
TracWysiwyg.prototype.isLastChildInBlockNode = generator("nextSibling", blocks);
|
|
TracWysiwyg.prototype.isFirstChildInBlockNode = generator("previousSibling", blocks);
|
|
})();
|
|
|
|
TracWysiwyg.prototype.wikitextToFragment = function(wikitext, contentDocument, options) {
|
|
options = options || {};
|
|
var escapeNewlines = !!options.escapeNewlines;
|
|
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
var _linkScheme = this._linkScheme;
|
|
var _quotedString = this._quotedString;
|
|
var wikiInlineRulesCount = this.wikiInlineRules.length;
|
|
var wikiToDomInlineRulesCount = this.wikiToDomInlineRules.length;
|
|
var wikiRulesPattern = new RegExp(this.wikiRulesPattern.source, "g");
|
|
|
|
var self = this;
|
|
var fragment = contentDocument.createDocumentFragment();
|
|
var holder = fragment;
|
|
var lines = wikitext.split("\n");
|
|
var codeText = null;
|
|
var currentHeader = null;
|
|
var quoteDepth = [];
|
|
var listDepth = [];
|
|
var decorationStatus;
|
|
var decorationStack;
|
|
var inCodeBlock, inParagraph, inDefList, inTable, inTableRow, continueTableRow;
|
|
inCodeBlock = inParagraph = inDefList = inTable = inTableRow = continueTableRow = false;
|
|
|
|
function handleCodeBlock(line) {
|
|
if (/^ *\{\{\{ *$/.test(line)) {
|
|
inCodeBlock++;
|
|
if (inCodeBlock == 1) {
|
|
closeParagraph();
|
|
codeText = [];
|
|
}
|
|
else {
|
|
codeText.push(line);
|
|
}
|
|
}
|
|
else if (/^ *\}\}\} *$/.test(line)) {
|
|
inCodeBlock--;
|
|
if (inCodeBlock == 0) {
|
|
var pre = contentDocument.createElement("pre");
|
|
pre.className = "wiki";
|
|
pre.appendChild(contentDocument.createTextNode(codeText.join(
|
|
pre.addEventListener && !window.opera ? "\n" : "\n\r")));
|
|
holder.appendChild(pre);
|
|
codeText = [];
|
|
}
|
|
else {
|
|
codeText.push(line);
|
|
}
|
|
}
|
|
else {
|
|
codeText.push(line);
|
|
}
|
|
}
|
|
|
|
function handleCitation(value) {
|
|
var quote = /^(?: *>)+/.exec(value)[0];
|
|
var depth = quote.replace(/ +/g, "").length;
|
|
|
|
if (depth > quoteDepth.length) {
|
|
closeToFragment("blockquote");
|
|
while (depth > quoteDepth.length) {
|
|
openQuote((new RegExp("^(?: *>){" + (quoteDepth.length + 1) + "}")).exec(quote)[0].length, true);
|
|
}
|
|
}
|
|
else if (depth == quoteDepth.length) {
|
|
// nothing to do
|
|
}
|
|
else {
|
|
closeParagraph();
|
|
while (depth < quoteDepth.length) {
|
|
closeQuote();
|
|
}
|
|
}
|
|
}
|
|
|
|
function openQuote(length, citation) {
|
|
var target = holder;
|
|
if (target != fragment) {
|
|
target = getSelfOrAncestor(target, "blockquote");
|
|
}
|
|
|
|
var element = contentDocument.createElement("blockquote");
|
|
if (citation) {
|
|
element.className = "citation";
|
|
}
|
|
(target || fragment).appendChild(element);
|
|
holder = element;
|
|
quoteDepth.push(length);
|
|
}
|
|
|
|
function closeQuote() {
|
|
var target = getSelfOrAncestor(holder, "blockquote");
|
|
holder = target.parentNode;
|
|
quoteDepth.pop();
|
|
}
|
|
|
|
function handleHeader(line) {
|
|
var match = /^\s*(=+)[ \t\r\f\v]+.*?(?:#([^ \t\r\f\v]+))?[ \t\r\f\v]*$/.exec(line);
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
|
|
closeToFragment();
|
|
var tag = "h" + match[1].length;
|
|
var element = contentDocument.createElement(tag);
|
|
if (match[2]) {
|
|
element.id = match[2];
|
|
}
|
|
fragment.appendChild(element);
|
|
holder = element;
|
|
return tag;
|
|
}
|
|
|
|
function closeHeader() {
|
|
if (currentHeader) {
|
|
var target = getSelfOrAncestor(holder, currentHeader);
|
|
holder = target.parentNode;
|
|
currentHeader = null;
|
|
}
|
|
}
|
|
|
|
function handleInline(name) {
|
|
if (name == "bolditalic") {
|
|
if (decorationStatus.italic) {
|
|
handleInline("italic");
|
|
handleInline("bold");
|
|
}
|
|
else {
|
|
handleInline("bold");
|
|
handleInline("italic");
|
|
}
|
|
return;
|
|
}
|
|
|
|
var d = contentDocument;
|
|
if (decorationStatus[name]) {
|
|
var tagNames = [];
|
|
for (var index = decorationStack.length - 1; index >= 0; index--) {
|
|
var tagName = holder.tagName;
|
|
holder = holder.parentNode;
|
|
if (decorationStack[index] == name) {
|
|
break;
|
|
}
|
|
tagNames.push(tagName);
|
|
}
|
|
decorationStack.splice(index, 1);
|
|
decorationStatus[name] = false;
|
|
while (tagNames.length > 0) {
|
|
var element = d.createElement(tagNames.pop());
|
|
holder.appendChild(element);
|
|
holder = element;
|
|
}
|
|
return;
|
|
}
|
|
|
|
var tagName;
|
|
switch (name) {
|
|
case "bold": tagName = "b"; break;
|
|
case "italic": tagName = "i"; break;
|
|
case "underline": tagName = "u"; break;
|
|
case "strike": tagName = "del"; break;
|
|
case "subscript": tagName = "sub"; break;
|
|
case "superscript": tagName = "sup"; break;
|
|
}
|
|
|
|
if (holder == fragment) {
|
|
openParagraph();
|
|
}
|
|
element = d.createElement(tagName);
|
|
holder.appendChild(element);
|
|
holder = element;
|
|
decorationStatus[name] = true;
|
|
decorationStack.push(name);
|
|
}
|
|
|
|
function handleInlineCode(value, length) {
|
|
var d = contentDocument;
|
|
var element = d.createElement("tt");
|
|
value = value.slice(length, -length);
|
|
if (value.length > 0) {
|
|
element.appendChild(d.createTextNode(value));
|
|
holder.appendChild(element);
|
|
}
|
|
}
|
|
|
|
function createAnchor(link, label) {
|
|
var anchor = self.createAnchor(link, label);
|
|
holder.appendChild(anchor);
|
|
}
|
|
|
|
function handleTracLinks(value) {
|
|
var match = handleTracLinks.pattern.exec(value);
|
|
if (match) {
|
|
var link = match[1];
|
|
if (!/^(?:[\w.+-]+:|[\/.#].*)/.test(link)) {
|
|
link = "wiki:" + link;
|
|
}
|
|
var text = (match[2] || match[1].replace(/^[\w.+-]+:/, "")).replace(/^(["'])(.*)\1$/g, "$2");
|
|
createAnchor(link, text);
|
|
}
|
|
else {
|
|
holder.appendChild(contentDocument.createTextNode(value));
|
|
}
|
|
}
|
|
handleTracLinks.pattern = new RegExp("\\["
|
|
+ "((?:" + _linkScheme + ":)?(?:" + _quotedString + "|[^\\]\\s]+))"
|
|
+ "(?:\\s+(.*))?\\]");
|
|
|
|
function handleTracWikiLink(value) {
|
|
createAnchor(value, value);
|
|
}
|
|
|
|
function handleBracketLinks(value) {
|
|
var d = contentDocument;
|
|
var link = value.slice(1, -1);
|
|
var anchor = self.createAnchor(link, link);
|
|
var _holder = holder;
|
|
_holder.appendChild(d.createTextNode("<"));
|
|
_holder.appendChild(anchor);
|
|
_holder.appendChild(d.createTextNode(">"));
|
|
}
|
|
|
|
function handleWikiPageName(name, label) {
|
|
createAnchor("wiki:" + name, label || name);
|
|
}
|
|
|
|
function handleTracOtherLinks(value) {
|
|
createAnchor(self.convertWikiSyntax(value), value);
|
|
}
|
|
|
|
function handleTracTicketLink(value) {
|
|
if (!/^&/.test(value)) {
|
|
handleTracOtherLinks(value);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function handleWikiAnchor(text) {
|
|
var match = /^\[=#([^ \t\r\f\v\]]+)(?:[ \t\r\f\v]+([^\]]*))?\]$/.exec(text);
|
|
var d = contentDocument;
|
|
var element = d.createElement("span");
|
|
element.className = "wikianchor";
|
|
element.id = match[1];
|
|
if (match[2]) {
|
|
element.appendChild(self.wikitextToOnelinerFragment(match[2], d, self.options));
|
|
}
|
|
holder.appendChild(element);
|
|
}
|
|
|
|
function handleList(value) {
|
|
var match = /^(\s*)(?:([-*])|((?:([0-9]+)|([a-z])|([A-Z])|[ivxIVX]{1,5})))/.exec(value);
|
|
var tag, className, depth, start;
|
|
if (!match) {
|
|
holder.appendChild(contentDocument.createTextNode(value));
|
|
return;
|
|
}
|
|
|
|
depth = match[1].length;
|
|
if (match[2]) {
|
|
tag = "ul";
|
|
}
|
|
else if (match[3]) {
|
|
tag = "ol";
|
|
switch (match[3]) {
|
|
case "0":
|
|
className = "arabiczero";
|
|
break;
|
|
case "1":
|
|
break;
|
|
case "i":
|
|
className = "lowerroman";
|
|
break;
|
|
case "I":
|
|
className = "upperroman";
|
|
break;
|
|
default:
|
|
if (match[4]) {
|
|
start = parseInt(match[4], 10);
|
|
}
|
|
else if (match[5]) {
|
|
className = "loweralpha";
|
|
}
|
|
else if (match[6]) {
|
|
className = "upperalpha";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
var last = listDepth.length - 1;
|
|
if (depth > (last >= 0 ? listDepth[last] : -1)) {
|
|
closeToFragment("li");
|
|
openList(tag, className, start, depth);
|
|
}
|
|
else {
|
|
var container, list;
|
|
if (listDepth.length > 1 && depth < listDepth[last]) {
|
|
do {
|
|
if (depth >= listDepth[last]) {
|
|
break;
|
|
}
|
|
closeList();
|
|
last = listDepth.length - 1;
|
|
} while (listDepth.length > 1);
|
|
container = holder;
|
|
}
|
|
else {
|
|
list = getSelfOrAncestor(holder, "li");
|
|
self.appendBogusLineBreak(list);
|
|
container = list.parentNode;
|
|
}
|
|
if (tag != container.tagName.toLowerCase()) {
|
|
holder = container.parentNode;
|
|
listDepth.pop();
|
|
openList(tag, className, start, depth);
|
|
}
|
|
else {
|
|
var tmp = contentDocument.createElement("li");
|
|
container.appendChild(tmp);
|
|
holder = tmp;
|
|
listDepth[last] = depth;
|
|
}
|
|
}
|
|
}
|
|
|
|
function openList(tag, className, start, depth) {
|
|
var d = contentDocument;
|
|
var h = holder;
|
|
|
|
var container = d.createElement(tag);
|
|
if (className) {
|
|
container.className = className;
|
|
}
|
|
if (start) {
|
|
container.setAttribute("start", start);
|
|
}
|
|
var list = d.createElement("li");
|
|
container.appendChild(list);
|
|
|
|
var target;
|
|
if (h == fragment) {
|
|
target = fragment;
|
|
}
|
|
else {
|
|
target = getSelfOrAncestor(h, "li");
|
|
target = target ? target.parentNode : h;
|
|
}
|
|
target.appendChild(container);
|
|
holder = list;
|
|
listDepth.push(depth);
|
|
}
|
|
|
|
function closeList() {
|
|
var h = holder;
|
|
var target = getSelfOrAncestor(h, "li");
|
|
if (target) {
|
|
self.appendBogusLineBreak(target);
|
|
holder = target.parentNode.parentNode;
|
|
}
|
|
else {
|
|
holder = h.parentNode;
|
|
}
|
|
listDepth.pop();
|
|
}
|
|
|
|
function handleDefinition(value) {
|
|
var d = contentDocument;
|
|
var h = holder;
|
|
var dl = null;
|
|
if (inDefList) {
|
|
dl = getSelfOrAncestor(h, "dl");
|
|
}
|
|
else {
|
|
closeParagraph();
|
|
dl = d.createElement("dl");
|
|
fragment.appendChild(dl);
|
|
inDefList = true;
|
|
}
|
|
|
|
var match = /^ +(.*?)\s*::/.exec(value);
|
|
var dt = d.createElement("dt");
|
|
var oneliner = self.wikitextToOnelinerFragment(match[1], d, self.options);
|
|
dt.appendChild(oneliner);
|
|
dl.appendChild(dt);
|
|
|
|
var dd = d.createElement("dd");
|
|
dl.appendChild(dd);
|
|
holder = dd;
|
|
}
|
|
|
|
function closeDefList() {
|
|
var element = getSelfOrAncestor(holder, "dl");
|
|
if (element) {
|
|
holder = element.parentNode;
|
|
}
|
|
inDefList = false;
|
|
}
|
|
|
|
function handleIndent(value) {
|
|
var depth = value.length;
|
|
var last = quoteDepth.length - 1;
|
|
|
|
if (depth > (last >= 0 ? quoteDepth[last] : 0)) {
|
|
closeParagraph();
|
|
closeTable();
|
|
openQuote(depth, false);
|
|
}
|
|
else {
|
|
while (quoteDepth.length > 0) {
|
|
if (depth >= quoteDepth[last]) {
|
|
break;
|
|
}
|
|
closeToFragment("blockquote");
|
|
closeQuote();
|
|
last = quoteDepth.length - 1;
|
|
}
|
|
quoteDepth[last] = depth;
|
|
}
|
|
}
|
|
|
|
function openParagraph() {
|
|
if (!inParagraph) {
|
|
var element = contentDocument.createElement("p");
|
|
holder.appendChild(element);
|
|
holder = element;
|
|
inParagraph = true;
|
|
}
|
|
}
|
|
|
|
function closeParagraph() {
|
|
if (inParagraph) {
|
|
var target = holder;
|
|
if (target != fragment) {
|
|
target = getSelfOrAncestor(target, "p");
|
|
self.appendBogusLineBreak(target);
|
|
}
|
|
holder = target.parentNode;
|
|
inParagraph = false;
|
|
}
|
|
}
|
|
|
|
function handleTableCell(action, colspan, header, align) {
|
|
var d = contentDocument;
|
|
var h, table, tbody;
|
|
|
|
if (!inTable) {
|
|
closeToFragment("blockquote");
|
|
h = holder;
|
|
table = d.createElement("table");
|
|
table.className = "wiki";
|
|
tbody = d.createElement("tbody");
|
|
table.appendChild(tbody);
|
|
h.appendChild(table);
|
|
inTable = true;
|
|
inTableRow = false;
|
|
}
|
|
else {
|
|
h = holder;
|
|
tbody = getSelfOrAncestor(h, "tbody");
|
|
}
|
|
|
|
if (inTableRow) {
|
|
var cell = getSelfOrAncestor(h, "td");
|
|
if (cell) {
|
|
self.appendBogusLineBreak(cell);
|
|
}
|
|
}
|
|
|
|
var row;
|
|
switch (action) {
|
|
case 1:
|
|
row = d.createElement("tr");
|
|
tbody.appendChild(row);
|
|
inTableRow = true;
|
|
break;
|
|
case 0:
|
|
row = getSelfOrAncestor(h, "tr");
|
|
break;
|
|
case -1:
|
|
if (inTableRow) {
|
|
var target = getSelfOrAncestor(h, "tr");
|
|
holder = target.parentNode;
|
|
inTableRow = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
var cell = d.createElement(header ? "th" : "td");
|
|
if (colspan > 1) {
|
|
cell.setAttribute("colSpan", colspan);
|
|
}
|
|
switch (align) {
|
|
case -1: align = "left"; break;
|
|
case 0: align = "center"; break;
|
|
case 1: align = "right"; break;
|
|
default: align = null; break;
|
|
}
|
|
if (align !== null) {
|
|
cell.setAttribute("align", align);
|
|
}
|
|
row.appendChild(cell);
|
|
holder = cell;
|
|
decorationStatus = {};
|
|
}
|
|
|
|
function closeTable() {
|
|
if (inTable) {
|
|
var target = getSelfOrAncestor(holder, "table");
|
|
holder = target.parentNode;
|
|
inTable = inTableRow = false;
|
|
}
|
|
}
|
|
|
|
function closeToFragment(stopTag) {
|
|
var element = holder;
|
|
var _fragment = fragment;
|
|
stopTag = stopTag ? stopTag.toLowerCase() : null;
|
|
|
|
while (element != _fragment) {
|
|
var tag = element.tagName.toLowerCase();
|
|
if (tag == stopTag) {
|
|
holder = element;
|
|
return;
|
|
}
|
|
var method;
|
|
switch (tag) {
|
|
case "p":
|
|
method = closeParagraph;
|
|
break;
|
|
case "li": case "ul": case "ol":
|
|
method = closeList;
|
|
break;
|
|
case "dd":
|
|
method = closeDefList;
|
|
break;
|
|
case "blockquote":
|
|
method = closeQuote;
|
|
break;
|
|
case "td": case "tr": case "tbody": case "table":
|
|
method = closeTable;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (method) {
|
|
method();
|
|
element = holder;
|
|
}
|
|
else {
|
|
element = element.parentNode;
|
|
}
|
|
}
|
|
|
|
holder = _fragment;
|
|
}
|
|
|
|
function getMatchNumber(match) {
|
|
var length = match.length;
|
|
for (var i = 1; i < length; i++) {
|
|
if (match[i]) {
|
|
if (i <= wikiInlineRulesCount) {
|
|
return i;
|
|
}
|
|
if (i <= wikiToDomInlineRulesCount) {
|
|
return i - wikiInlineRulesCount + 1000;
|
|
}
|
|
return wikiToDomInlineRulesCount - i;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
for (var indexLines = 0; indexLines < lines.length; indexLines++) {
|
|
var line = lines[indexLines].replace(/\r$/, "");
|
|
if (inCodeBlock || /^ *\{\{\{ *$/.test(line)) {
|
|
handleCodeBlock(line);
|
|
continue;
|
|
}
|
|
if (/^----/.test(line)) {
|
|
closeToFragment();
|
|
fragment.appendChild(contentDocument.createElement("hr"));
|
|
continue;
|
|
}
|
|
if (line.length == 0) {
|
|
closeToFragment();
|
|
continue;
|
|
}
|
|
line = line.replace(/\t/g, " ");
|
|
line = line.replace(/\u00a0/g, " ");
|
|
|
|
wikiRulesPattern.lastIndex = 0;
|
|
var prevIndex = wikiRulesPattern.lastIndex;
|
|
decorationStatus = {};
|
|
decorationStack = [];
|
|
for ( ; ; ) {
|
|
var match = wikiRulesPattern.exec(line);
|
|
var matchNumber = null;
|
|
var text = null;
|
|
if (match) {
|
|
matchNumber = getMatchNumber(match);
|
|
if (prevIndex < match.index) {
|
|
text = line.substring(prevIndex, match.index);
|
|
}
|
|
}
|
|
else {
|
|
text = line.substring(prevIndex);
|
|
}
|
|
|
|
if ((prevIndex == 0 && text || match && match.index == 0 && matchNumber > 0)
|
|
&& (!inParagraph || quoteDepth.length > 0)
|
|
&& (!inDefList || !/^ /.test(line)))
|
|
{
|
|
closeToFragment();
|
|
}
|
|
if (text || match && matchNumber > 0) {
|
|
if (inParagraph && (prevIndex == 0 || quoteDepth.length > 0)) {
|
|
if (escapeNewlines) {
|
|
if (quoteDepth.length == 0) {
|
|
holder.appendChild(contentDocument.createElement("br"));
|
|
}
|
|
}
|
|
else {
|
|
text = text ? (" " + text) : " ";
|
|
}
|
|
}
|
|
if (!inTable && quoteDepth.length > 0 || holder == fragment) {
|
|
if (!inParagraph) {
|
|
openParagraph();
|
|
}
|
|
}
|
|
if (text) {
|
|
holder.appendChild(contentDocument.createTextNode(text));
|
|
}
|
|
}
|
|
if (!match) {
|
|
break;
|
|
}
|
|
prevIndex = wikiRulesPattern.lastIndex;
|
|
var matchText = match[0];
|
|
|
|
if (!/^!/.test(matchText)) { // start '!'
|
|
switch (matchNumber) {
|
|
case 1: // bolditalic
|
|
handleInline("bolditalic");
|
|
continue;
|
|
case 2: // bold
|
|
handleInline("bold");
|
|
continue;
|
|
case 3: // italic
|
|
handleInline("italic");
|
|
continue;
|
|
case 4: // underline
|
|
handleInline("underline");
|
|
continue;
|
|
case 5: // strike
|
|
handleInline("strike");
|
|
continue;
|
|
case 6: // subscript
|
|
handleInline("subscript");
|
|
continue;
|
|
case 7: // superscript
|
|
handleInline("superscript");
|
|
continue;
|
|
case 8: // code block
|
|
handleInlineCode(matchText, 3);
|
|
continue;
|
|
case 9: // inline
|
|
handleInlineCode(matchText, 1);
|
|
continue;
|
|
case 10: // ticket
|
|
if (handleTracTicketLink(matchText)) {
|
|
continue;
|
|
}
|
|
break;
|
|
case 11: // report
|
|
case 12: // changeset
|
|
case 13: // log
|
|
handleTracOtherLinks(matchText);
|
|
continue;
|
|
case 14: // wiki:TracLinks
|
|
handleTracWikiLink(matchText);
|
|
continue;
|
|
case 15: // [wiki:TracLinks label]
|
|
handleTracLinks(matchText);
|
|
continue;
|
|
case 16: // [[macro]]
|
|
break;
|
|
case 17: // WikiPageName
|
|
handleWikiPageName(matchText);
|
|
continue;
|
|
case 18: // ["internal free link"]
|
|
handleWikiPageName(matchText.slice(1, -1), matchText.slice(2, -2));
|
|
continue;
|
|
case 19: // <wiki:Trac bracket links>
|
|
handleBracketLinks(matchText);
|
|
continue;
|
|
case 20: // [=#anchor label]
|
|
handleWikiAnchor(matchText);
|
|
continue;
|
|
case 1001: // escaping double escape
|
|
break;
|
|
case -1: // citation
|
|
if (escapeNewlines && inParagraph) {
|
|
holder.appendChild(contentDocument.createElement("br"));
|
|
}
|
|
handleCitation(matchText);
|
|
if (escapeNewlines) {
|
|
openParagraph();
|
|
}
|
|
continue;
|
|
case -2: // header
|
|
currentHeader = handleHeader(matchText);
|
|
if (currentHeader) {
|
|
line = line.replace(/(?:[ \t\r\f\v]+#[^ \t\r\f\v]+)?[ \t\r\f\v]*$/, "");
|
|
var m = /^\s*(=+)[ \t\r\f\v]+/.exec(line);
|
|
if (line.slice(-m[1].length) == m[1]) {
|
|
line = line.slice(0, -m[1].length).replace(/[ \t\r\f\v]+$/, "");
|
|
}
|
|
wikiRulesPattern.lastIndex = prevIndex = m[0].length;
|
|
continue;
|
|
}
|
|
break;
|
|
case -3: // list
|
|
handleList(matchText)
|
|
continue;
|
|
case -4: // definition
|
|
handleDefinition(matchText);
|
|
continue;
|
|
case -5: // leading space
|
|
if (listDepth.length == 0 && !inDefList) {
|
|
handleIndent(matchText);
|
|
continue;
|
|
}
|
|
if (!this.isInlineNode(holder.lastChild)) {
|
|
continue;
|
|
}
|
|
matchText = matchText.replace(/^\s+/, " ");
|
|
break;
|
|
case -6: // closing table row
|
|
if (inTable) {
|
|
if (matchText.slice(-1) != "\\") {
|
|
handleTableCell(-1);
|
|
}
|
|
else {
|
|
continueTableRow = true;
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
case -7: // cell
|
|
if (quoteDepth.length > 0 && match.index == 0) {
|
|
closeToFragment();
|
|
}
|
|
var align = null;
|
|
for ( ; ; ) { // lookahead next double pipes
|
|
var m = wikiRulesPattern.exec(line);
|
|
switch (m ? getMatchNumber(m) : 0) {
|
|
case 0: case -6: case -7:
|
|
var end = m ? m.index : line.length;
|
|
if (prevIndex < end) {
|
|
var tmp = line.substring(prevIndex, end);
|
|
var m = /^([ \t\r\n\f\v]*)(.*?)([ \t\r\n\f\v]*)$/.exec(tmp);
|
|
if (m) {
|
|
if (m[1].length == tmp.length) {
|
|
align = null;
|
|
}
|
|
else if ((m[1].length == 0) === (m[3].length == 0)) {
|
|
align = m[1].length >= 2 && m[3].length >= 2 ? 0 : null;
|
|
}
|
|
else {
|
|
align = m[1].length == 0 ? -1 : 1;
|
|
}
|
|
tmp = m[2];
|
|
}
|
|
line = line.substring(0, prevIndex) + tmp + line.substring(end);
|
|
}
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
wikiRulesPattern.lastIndex = prevIndex;
|
|
handleTableCell(inTableRow ? 0 : 1,
|
|
matchText.replace(/^=|=$/g, '').length / 2, matchText.slice(-1) == "=", align);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (matchText) {
|
|
if (listDepth.length == 0 && !currentHeader && !inDefList && !inTable) {
|
|
openParagraph();
|
|
}
|
|
var tmp;
|
|
if (matchNumber == 16) {
|
|
tmp = /^!?\[\[br\]\]$/i.test(matchText)
|
|
? (matchText.charCodeAt(0) == 0x21
|
|
? contentDocument.createTextNode(matchText.substring(1))
|
|
: contentDocument.createElement("br"))
|
|
: contentDocument.createTextNode(matchText);
|
|
}
|
|
else {
|
|
tmp = contentDocument.createTextNode(/^!/.test(matchText) ? matchText.substring(1) : matchText);
|
|
}
|
|
holder.appendChild(tmp);
|
|
}
|
|
}
|
|
if (currentHeader) {
|
|
closeHeader();
|
|
}
|
|
if (inTable) {
|
|
if (continueTableRow) {
|
|
continueTableRow = false;
|
|
}
|
|
else {
|
|
handleTableCell(-1);
|
|
}
|
|
}
|
|
}
|
|
closeToFragment();
|
|
|
|
return fragment;
|
|
};
|
|
|
|
TracWysiwyg.prototype.wikitextToOnelinerFragment = function(wikitext, contentDocument, options) {
|
|
var source = this.wikitextToFragment(wikitext, contentDocument, options);
|
|
var fragment = contentDocument.createDocumentFragment();
|
|
this.collectChildNodes(fragment, source.firstChild);
|
|
return fragment;
|
|
};
|
|
|
|
TracWysiwyg.prototype.wikiOpenTokens = {
|
|
"h1": "= ", "h2": "== ", "h3": "=== ", "h4": "==== ", "h5": "===== ", "h6": "====== ",
|
|
"b": "'''", "strong": "'''",
|
|
"i": "''", "em": "''",
|
|
"u": "__",
|
|
"del": "~~", "strike": "~~",
|
|
"sub": ",,",
|
|
"sup": "^",
|
|
"hr": "----\n",
|
|
"dl": true,
|
|
"dt": " ",
|
|
"dd": " ",
|
|
"table": true,
|
|
"tbody": true };
|
|
|
|
TracWysiwyg.prototype.wikiCloseTokens = {
|
|
"#text": true,
|
|
"a": true,
|
|
"tt": true,
|
|
"h1": " =", "h2": " ==", "h3": " ===", "h4": " ====", "h5": " =====", "h6": " ======",
|
|
"b": "'''", "strong": "'''",
|
|
"i": "''", "em": "''",
|
|
"u": "__",
|
|
"del": "~~", "strike": "~~",
|
|
"sub": ",,",
|
|
"sup": "^",
|
|
"br": true,
|
|
"hr": true,
|
|
"dl": "\n",
|
|
"dt": "::",
|
|
"dd": "\n",
|
|
"tbody": true,
|
|
"tr": "||\n",
|
|
"td": true, "th": true };
|
|
|
|
TracWysiwyg.prototype.wikiBlockTags = {
|
|
"h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true,
|
|
"table": true, "dl": true, "hr": true };
|
|
|
|
TracWysiwyg.prototype.wikiInlineTags = {
|
|
"a": true, "tt": true, "b": true, "strong": true, "i": true, "em": true,
|
|
"u": true, "del": true, "strike": true, "sub": true, "sup": true,
|
|
"br": true, "span": true };
|
|
|
|
TracWysiwyg.prototype.domToWikitext = function(root, options) {
|
|
options = options || {};
|
|
var formatCodeBlock = !!options.formatCodeBlock;
|
|
var escapeNewlines = !!options.escapeNewlines;
|
|
|
|
var self = this;
|
|
var getTextContent = TracWysiwyg.getTextContent;
|
|
var getSelfOrAncestor = TracWysiwyg.getSelfOrAncestor;
|
|
var wikiOpenTokens = this.wikiOpenTokens;
|
|
var wikiCloseTokens = this.wikiCloseTokens;
|
|
var wikiInlineTags = this.wikiInlineTags;
|
|
var wikiBlockTags = this.wikiBlockTags;
|
|
var xmlNamePattern = this.xmlNamePattern;
|
|
var domToWikiInlinePattern = this.domToWikiInlinePattern;
|
|
var wikiSyntaxPattern = this.wikiSyntaxPattern;
|
|
var tracLinkPattern = new RegExp("^" + this._tracLink + "$");
|
|
var wikiPageNamePattern = new RegExp("^" + this._wikiPageName + "$");
|
|
var decorationTokenPattern = /^(?:'''|''|__|\^|,,)$/;
|
|
|
|
var texts = [];
|
|
var stack = [];
|
|
var last = root;
|
|
var listDepth = 0;
|
|
var quoteDepth = 0;
|
|
var quoteCitation = false;
|
|
var inCodeBlock = false;
|
|
var skipNode = null;
|
|
var openBracket = false;
|
|
|
|
function escapeText(s) {
|
|
var match = /^!?\[\[(.+)\]\]$/.exec(s);
|
|
if (match) {
|
|
return match[1].toLowerCase() != "br" ? s : "!" + s;
|
|
}
|
|
if (/^&#\d+/.test(s)) {
|
|
return s;
|
|
}
|
|
return "!" + s;
|
|
}
|
|
|
|
function isTailEscape() {
|
|
var t = texts;
|
|
var length = t.length;
|
|
return length > 0 ? /!$/.test(t[length - 1]) : false;
|
|
}
|
|
|
|
function tokenFromSpan(node) {
|
|
if (node.className == "underline") {
|
|
return wikiOpenTokens["u"];
|
|
}
|
|
var style = node.style;
|
|
if (style.fontWeight == "bold") {
|
|
return wikiOpenTokens["b"];
|
|
}
|
|
if (style.fontStyle == "italic") {
|
|
return wikiOpenTokens["i"];
|
|
}
|
|
switch (style.textDecoration) {
|
|
case "underline":
|
|
return wikiOpenTokens["u"];
|
|
case "line-through":
|
|
return wikiOpenTokens["del"];
|
|
}
|
|
switch (style.verticalAlign) {
|
|
case "sub":
|
|
return wikiOpenTokens["sub"];
|
|
case "sup":
|
|
return wikiOpenTokens["sup"];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function nodeDecorations(node) {
|
|
var _wikiOpenTokens = wikiOpenTokens;
|
|
var _decorationTokenPattern = decorationTokenPattern;
|
|
var hash = {};
|
|
|
|
for ( ; ; ) {
|
|
var childNodes = node.childNodes;
|
|
if (!childNodes || childNodes.length != 1) {
|
|
break;
|
|
}
|
|
var child = childNodes[0];
|
|
if (child.nodeType != 1) {
|
|
break;
|
|
}
|
|
var token = _wikiOpenTokens[child.tagName.toLowerCase()];
|
|
if (_decorationTokenPattern.test(token)) {
|
|
hash[token] = true;
|
|
}
|
|
node = child;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
function pushTextWithDecorations(text, node, traclink) {
|
|
var _texts = texts;
|
|
var _decorationTokenPattern = decorationTokenPattern;
|
|
var decorationsHash = nodeDecorations(node);
|
|
var decorations = [];
|
|
var cancelDecorations = [];
|
|
|
|
while (_texts.length > 0) {
|
|
var token = _texts[_texts.length - 1];
|
|
if (_decorationTokenPattern.test(token)) {
|
|
if (decorationsHash[token]) {
|
|
delete decorationsHash[token];
|
|
cancelDecorations.push(_texts.pop());
|
|
continue;
|
|
}
|
|
if ((token == "'''" || token == "''") && _texts.length > 1) {
|
|
var moreToken = _texts[_texts.length - 2];
|
|
if (_decorationTokenPattern.test(moreToken)
|
|
&& token + moreToken == "'''''"
|
|
&& decorationsHash[moreToken])
|
|
{
|
|
delete decorationsHash[moreToken];
|
|
cancelDecorations.push(moreToken);
|
|
_texts[_texts.length - 2] = _texts[_texts.length - 1];
|
|
_texts.pop();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
for (var token in decorationsHash) {
|
|
decorations.push(token);
|
|
}
|
|
decorations.sort();
|
|
|
|
if (decorations.length > 0) {
|
|
_texts.push.apply(_texts, decorations);
|
|
}
|
|
if (traclink) {
|
|
if (_texts.length > 0 && /[\w.+-]$/.test(_texts[_texts.length - 1])) {
|
|
_texts.push(traclink);
|
|
}
|
|
else {
|
|
text = new String(text);
|
|
text["tracwysiwyg-traclink"] = traclink;
|
|
_texts.push(text);
|
|
}
|
|
}
|
|
else {
|
|
_texts.push(text);
|
|
}
|
|
if (decorations.length > 0) {
|
|
decorations.reverse();
|
|
_texts.push.apply(_texts, decorations);
|
|
}
|
|
if (cancelDecorations.length > 0) {
|
|
cancelDecorations.reverse();
|
|
_texts.push.apply(_texts, cancelDecorations);
|
|
}
|
|
}
|
|
|
|
function pushToken(token) {
|
|
var _texts = texts;
|
|
var _decorationTokenPattern = decorationTokenPattern;
|
|
var length = _texts.length;
|
|
if (length == 0 || !_decorationTokenPattern.test(token)) {
|
|
_texts.push(token);
|
|
return;
|
|
}
|
|
var last = _texts[length - 1];
|
|
if (!_decorationTokenPattern.test(last)) {
|
|
_texts.push(token);
|
|
return;
|
|
}
|
|
if (last == token) {
|
|
_texts.pop();
|
|
return;
|
|
}
|
|
if (length < 2 || last + token != "'''''") {
|
|
_texts.push(token);
|
|
return;
|
|
}
|
|
if (_texts[length - 2] == token) {
|
|
_texts[length - 2] = _texts[length - 1];
|
|
_texts.pop();
|
|
}
|
|
else {
|
|
_texts.push(token);
|
|
}
|
|
}
|
|
|
|
function tracLinkText(link, label) {
|
|
if (!/\]/.test(label) && !/^[\"\']/.test(label)) {
|
|
return "[" + link + " " + label + "]";
|
|
}
|
|
if (!/\"/.test(label)) {
|
|
return "[" + link + ' "' + label + '"]';
|
|
}
|
|
if (!/\'/.test(label)) {
|
|
return "[" + link + " '" + label + "']";
|
|
}
|
|
return "[" + link + ' "' + label.replace(/"+/g, "") + '"]';
|
|
}
|
|
|
|
function pushAnchor(node, bracket) {
|
|
var link = node.getAttribute("data-tracwysiwyg-link");
|
|
var autolink = node.getAttribute("data-tracwysiwyg-autolink");
|
|
var attrs;
|
|
if (link === null) {
|
|
attrs = TracWysiwyg.unserializeFromHref(node.href);
|
|
link = attrs["data-tracwysiwyg-link"];
|
|
autolink = attrs["data-tracwysiwyg-autolink"];
|
|
}
|
|
link = (link || node.href).replace(/^\s+|\s+$/g, "");
|
|
var label = getTextContent(node).replace(/^\s+|\s+$/g, "");
|
|
if (!label) {
|
|
return;
|
|
}
|
|
var text = null;
|
|
var traclink = null;
|
|
if (autolink == "true") {
|
|
if (wikiPageNamePattern.test(label)) {
|
|
text = label;
|
|
link = "wiki:" + label;
|
|
traclink = "[wiki:" + label + "]";
|
|
}
|
|
else if (wikiSyntaxPattern.test(label)) {
|
|
text = label;
|
|
link = self.convertWikiSyntax(label);
|
|
}
|
|
else if (tracLinkPattern.test(label)) {
|
|
text = link = label;
|
|
}
|
|
}
|
|
else {
|
|
if (link == label) {
|
|
if (bracket) {
|
|
text = label;
|
|
}
|
|
else if (tracLinkPattern.test(label)) {
|
|
text = label;
|
|
}
|
|
}
|
|
}
|
|
if (!text) {
|
|
var match = /^([\w.+-]+):(@?(.*))$/.exec(link);
|
|
if (match) {
|
|
if (label == match[2]) {
|
|
if (match[1] == "wiki" && wikiPageNamePattern.test(match[2])) {
|
|
text = match[2];
|
|
traclink = "[wiki:" + text + "]";
|
|
}
|
|
else {
|
|
text = "[" + link + "]";
|
|
}
|
|
}
|
|
else {
|
|
var usingLabel = false;
|
|
switch (match[1]) {
|
|
case "changeset":
|
|
usingLabel = label == "[" + match[2] + "]" || /^\d+$/.test(match[2]) && label == "r" + match[2];
|
|
break;
|
|
case "log":
|
|
usingLabel = label == "[" + match[3] + "]" || label == "r" + match[3];
|
|
break;
|
|
case "report":
|
|
usingLabel = label == "{" + match[2] + "}";
|
|
break;
|
|
case "ticket":
|
|
usingLabel = label == "#" + match[2];
|
|
break;
|
|
}
|
|
if (usingLabel) {
|
|
text = label;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (isTailEscape()) {
|
|
texts.push(" ");
|
|
}
|
|
if (text === null) {
|
|
text = tracLinkText(link, label);
|
|
}
|
|
if (!traclink && /^[\w.+-]/.test(text)) {
|
|
traclink = tracLinkText(link, label);
|
|
}
|
|
pushTextWithDecorations(text, node, traclink);
|
|
}
|
|
|
|
function string(source, times) {
|
|
var value = (1 << times) - 1;
|
|
if (value <= 0) {
|
|
return "";
|
|
}
|
|
else {
|
|
return value.toString(2).replace(/1/g, source);
|
|
}
|
|
}
|
|
|
|
function open(name, node) {
|
|
if (skipNode !== null) {
|
|
return;
|
|
}
|
|
var _texts = texts;
|
|
var token = wikiOpenTokens[name];
|
|
if (token !== undefined) {
|
|
if (name in wikiBlockTags && self.isInlineNode(node.previousSibling)) {
|
|
_texts.push("\n");
|
|
}
|
|
if (token !== true) {
|
|
if (name in wikiInlineTags && isTailEscape()) {
|
|
_texts.push(" ");
|
|
}
|
|
pushToken(token);
|
|
}
|
|
openBracket = false;
|
|
}
|
|
else {
|
|
switch (name) {
|
|
case "#text":
|
|
var value = node.nodeValue;
|
|
if (value) {
|
|
if (!inCodeBlock) {
|
|
if (value && !self.isInlineNode(node.previousSibling || node.parentNode)) {
|
|
value = value.replace(/^[ \t\r\n\f\v]+/g, "");
|
|
}
|
|
if (value && !self.isInlineNode(node.nextSibling || node.parentNode)) {
|
|
value = value.replace(/[ \t\r\n\f\v]+$/g, "");
|
|
}
|
|
value = value.replace(/\r?\n/g, " ");
|
|
if (!formatCodeBlock) {
|
|
value = value.replace(domToWikiInlinePattern, escapeText);
|
|
}
|
|
openBracket = /<$/.test(value);
|
|
}
|
|
if (value) {
|
|
var length = _texts.length;
|
|
var prev = length > 0 ? _texts[length - 1] : null;
|
|
if (prev && prev["tracwysiwyg-traclink"] && tracLinkPattern.test(prev + value.substring(0, 1))) {
|
|
_texts[length - 1] = prev["tracwysiwyg-traclink"];
|
|
}
|
|
_texts.push(value);
|
|
}
|
|
}
|
|
break;
|
|
case "p":
|
|
if (quoteDepth > 0) {
|
|
_texts.push(string(quoteCitation ? "> " : " ", quoteDepth));
|
|
}
|
|
else if (!/[^ \t\r\n\f\v]/.test(getTextContent(node))) {
|
|
skipNode = node;
|
|
}
|
|
break;
|
|
case "a":
|
|
skipNode = node;
|
|
var bracket = false;
|
|
if (openBracket) {
|
|
var nextSibling = node.nextSibling;
|
|
bracket = nextSibling && nextSibling.nodeType == 3 && /^>/.test(nextSibling.nodeValue);
|
|
openBracket = false;
|
|
}
|
|
pushAnchor(node, bracket);
|
|
break;
|
|
case "li":
|
|
_texts.push(" " + string(" ", listDepth - 1));
|
|
var container = node.parentNode;
|
|
if ((container.tagName || "").toLowerCase() == "ol") {
|
|
var start = container.getAttribute("start") || "";
|
|
if (start != "1" && /^(?:[0-9]+|[a-zA-Z]|[ivxIVX]{1,5})$/.test(start)) {
|
|
_texts.push(start, ". ");
|
|
}
|
|
else {
|
|
switch (container.className) {
|
|
case "arabiczero": _texts.push("0. "); break;
|
|
case "lowerroman": _texts.push("i. "); break;
|
|
case "upperroman": _texts.push("I. "); break;
|
|
case "loweralpha": _texts.push("a. "); break;
|
|
case "upperalpha": _texts.push("A. "); break;
|
|
default: _texts.push("1. "); break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
_texts.push("* ");
|
|
}
|
|
break;
|
|
case "ul": case "ol":
|
|
if (listDepth == 0) {
|
|
if (self.isInlineNode(node.previousSibling)) {
|
|
_texts.push("\n");
|
|
}
|
|
}
|
|
else if (listDepth > 0) {
|
|
if (node.parentNode.tagName.toLowerCase() == "li") {
|
|
_texts.push("\n");
|
|
}
|
|
}
|
|
listDepth++;
|
|
break;
|
|
case "br":
|
|
if (!self.isBogusLineBreak(node)) {
|
|
var value = null;
|
|
if (inCodeBlock) {
|
|
value = "\n";
|
|
}
|
|
else if (formatCodeBlock) {
|
|
switch (((node.parentNode || {}).tagName || "").toLowerCase()) {
|
|
case "li":
|
|
value = "\n " + string(" ", listDepth);
|
|
break;
|
|
case "p": case "blockquote":
|
|
value = "\n";
|
|
if (quoteDepth > 0) {
|
|
value += string(quoteCitation ? "> " : " ", quoteDepth);
|
|
}
|
|
break;
|
|
case "dd":
|
|
value = "\n ";
|
|
break;
|
|
case "dt":
|
|
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
|
|
value = " ";
|
|
break;
|
|
default:
|
|
value = "\n";
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (escapeNewlines && getSelfOrAncestor(node, /^(?:p|blockquote)$/)) {
|
|
value = quoteDepth > 0
|
|
? "\n" + string(quoteCitation ? "> " : " ", quoteDepth)
|
|
: "\n";
|
|
}
|
|
if (!value) {
|
|
value = "[[BR]]";
|
|
var length = _texts.length;
|
|
if (length > 0) {
|
|
var lastText = _texts[length - 1];
|
|
var tmp = lastText + "[[BR]]";
|
|
var _pattern = domToWikiInlinePattern;
|
|
_pattern.lastIndex = 0;
|
|
var lastMatch, match;
|
|
while (match = _pattern.exec(tmp)) {
|
|
lastMatch = match;
|
|
}
|
|
if (lastMatch && lastMatch.index < lastText.length
|
|
&& lastMatch.index + lastMatch[0].length > lastText.length)
|
|
{
|
|
value = " [[BR]]";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_texts.push(value);
|
|
}
|
|
break;
|
|
case "pre":
|
|
_texts.push(
|
|
/^(?:li|dd)$/i.test(node.parentNode.tagName) || self.isInlineNode(node.previousSibling)
|
|
? "\n{{{\n" : "{{{\n");
|
|
inCodeBlock = true;
|
|
break;
|
|
case "blockquote":
|
|
if (self.isInlineNode(node.previousSibling)) {
|
|
_texts.push("\n");
|
|
}
|
|
quoteDepth++;
|
|
if (quoteDepth == 1) {
|
|
quoteCitation = (node.className == "citation");
|
|
}
|
|
break;
|
|
case "th":
|
|
var header = true;
|
|
case "td":
|
|
skipNode = node;
|
|
var colspan = node.getAttribute("colSpan");
|
|
colspan = colspan ? parseInt(colspan, 10) : 0;
|
|
_texts.push(colspan > 1 ? string("||", colspan) : "||");
|
|
if (header) {
|
|
_texts.push("=");
|
|
}
|
|
var align = node.style.textAlign;
|
|
if (!align) {
|
|
align = (node.getAttribute("align") || "").toLowerCase();
|
|
}
|
|
var text = self.domToWikitext(node, self.options).replace(/ *\n/g, "[[BR]]").replace(/^ +| +$/g, "");
|
|
if (text) {
|
|
switch (align) {
|
|
case "left": _texts.push(text, " "); break;
|
|
case "center": _texts.push(" ", text, " "); break;
|
|
case "right": _texts.push(" ", text); break;
|
|
default: _texts.push(" ", text, " "); break;
|
|
}
|
|
}
|
|
else {
|
|
_texts.push(" ");
|
|
}
|
|
if (header) {
|
|
_texts.push("=");
|
|
}
|
|
break;
|
|
case "tr":
|
|
if (quoteDepth > 0) {
|
|
_texts.push(string(quoteCitation ? ">" : " ", quoteDepth));
|
|
}
|
|
break;
|
|
case "tt":
|
|
skipNode = node;
|
|
var value = getTextContent(node);
|
|
var text;
|
|
if (value) {
|
|
if (isTailEscape()) {
|
|
_texts.push(" ");
|
|
}
|
|
if (!/`/.test(value)) {
|
|
text = "`" + value + "`";
|
|
}
|
|
else if (!/\{\{\{|\}\}\}/.test(value)) {
|
|
text = "{{{" + value + "}}}";
|
|
}
|
|
else {
|
|
text = value.replace(/[^`]+|`+/g, function(m) {
|
|
return m.charCodeAt(0) != 0x60 ? "`" + m + "`" : "{{{" + m + "}}}";
|
|
});
|
|
}
|
|
pushTextWithDecorations(text, node);
|
|
}
|
|
break;
|
|
case "span":
|
|
if (node.className == "wikianchor" && xmlNamePattern.test(node.id || "")) {
|
|
skipNode = node;
|
|
var text = self.domToWikitext(node, self.options).replace(/^ +| +$|\]/g, "");
|
|
_texts.push("[=#", node.id, text ? " " + text + "]" : "]");
|
|
}
|
|
else {
|
|
var token = tokenFromSpan(node);
|
|
if (token !== undefined) {
|
|
if (name in wikiInlineTags && isTailEscape()) {
|
|
_texts.push(" ");
|
|
}
|
|
pushToken(token);
|
|
}
|
|
}
|
|
break;
|
|
case "script":
|
|
case "style":
|
|
skipNode = node;
|
|
break;
|
|
}
|
|
if (name != "#text") {
|
|
openBracket = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
function close(name, node) {
|
|
if (skipNode !== null) {
|
|
if (skipNode == node) {
|
|
skipNode = null;
|
|
}
|
|
return;
|
|
}
|
|
var _texts = texts;
|
|
var token = wikiCloseTokens[name];
|
|
if (token === true) {
|
|
// nothing to do
|
|
}
|
|
else if (token !== undefined) {
|
|
if (name in wikiInlineTags && isTailEscape()) {
|
|
_texts.push(" ");
|
|
}
|
|
pushToken(token);
|
|
}
|
|
else {
|
|
switch (name) {
|
|
case "p":
|
|
_texts.push(quoteDepth == 0 ? "\n\n" : "\n");
|
|
break;
|
|
case "li":
|
|
if (node.getElementsByTagName("li").length == 0) {
|
|
_texts.push("\n");
|
|
}
|
|
break;
|
|
case "ul": case "ol":
|
|
listDepth--;
|
|
if (listDepth == 0) {
|
|
_texts.push("\n");
|
|
}
|
|
break;
|
|
case "pre":
|
|
var text;
|
|
var parentNode = node.parentNode;
|
|
if (parentNode && /^(?:li|dd)$/i.test(parentNode.tagName)) {
|
|
var nextSibling = node.nextSibling;
|
|
if (!nextSibling) {
|
|
text = "\n}}}";
|
|
}
|
|
else if (nextSibling.nodeType != 1) {
|
|
text = "\n}}}\n";
|
|
}
|
|
else if (nextSibling.tagName.toLowerCase() == "pre") {
|
|
text = "\n}}}";
|
|
}
|
|
else {
|
|
text = "\n}}}\n";
|
|
}
|
|
if (text.slice(-1) == "\n") {
|
|
text += listDepth > 0 ? " " + string(" ", listDepth) : " ";
|
|
}
|
|
}
|
|
else {
|
|
text = "\n}}}\n";
|
|
}
|
|
_texts.push(text);
|
|
inCodeBlock = false;
|
|
break;
|
|
case "blockquote":
|
|
quoteDepth--;
|
|
if (quoteDepth == 0) {
|
|
_texts.push("\n");
|
|
}
|
|
break;
|
|
case "span":
|
|
var token = tokenFromSpan(node);
|
|
if (token !== undefined) {
|
|
if (name in wikiInlineTags && isTailEscape()) {
|
|
_texts.push(" ");
|
|
}
|
|
_texts.push(token);
|
|
}
|
|
break;
|
|
case "table":
|
|
if (quoteDepth == 0) {
|
|
_texts.push("\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (/^h[1-6]$/.test(name)) {
|
|
if (xmlNamePattern.test(node.id || "")) {
|
|
_texts.push(" #", node.id);
|
|
}
|
|
_texts.push("\n");
|
|
}
|
|
}
|
|
|
|
function iterator(node) {
|
|
var name = null;
|
|
switch (node && node.nodeType) {
|
|
case 1: // element
|
|
name = node.tagName.toLowerCase();
|
|
break;
|
|
case 3: // text
|
|
name = "#text";
|
|
break;
|
|
}
|
|
|
|
if (node && last == node.parentNode) { // down
|
|
// nothing to do
|
|
}
|
|
else if (node && last == node.previousSibling) { // forward
|
|
close(stack.pop(), last);
|
|
}
|
|
else { // up, forward
|
|
var tmp = last;
|
|
var nodeParent = node ? node.parentNode : root;
|
|
for ( ; ; ) {
|
|
var parent = tmp.parentNode;
|
|
if (parent == node) {
|
|
break;
|
|
}
|
|
close(stack.pop(), tmp);
|
|
if (parent == nodeParent || !parent) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
tmp = parent;
|
|
}
|
|
}
|
|
open(name, node);
|
|
stack.push(name);
|
|
last = node;
|
|
}
|
|
|
|
this.treeWalk(root, iterator);
|
|
return texts.join("").replace(/^(?: *\n)+|(?: *\n)+$/g, "");
|
|
};
|
|
|
|
if (window.getSelection) {
|
|
TracWysiwyg.prototype.appendBogusLineBreak = function(element) {
|
|
var wikiInlineTags = this.wikiInlineTags;
|
|
var last = element.lastChild;
|
|
for ( ; ; ) {
|
|
if (!last) {
|
|
break;
|
|
}
|
|
if (last.nodeType != 1) {
|
|
return;
|
|
}
|
|
var name = last.tagName.toLowerCase();
|
|
if (name == "br") {
|
|
break;
|
|
}
|
|
if (!(name in wikiInlineTags)) {
|
|
return;
|
|
}
|
|
last = last.lastChild || last.previousSibling;
|
|
}
|
|
var br = this.contentDocument.createElement("br");
|
|
element.appendChild(br);
|
|
};
|
|
TracWysiwyg.prototype.isBogusLineBreak = TracWysiwyg.prototype.isLastChildInBlockNode;
|
|
TracWysiwyg.prototype.insertParagraphOnEnter = function(event) {
|
|
var range = this.getSelectionRange();
|
|
var node = range.endContainer;
|
|
var header = null;
|
|
if (node && node.nodeType == 3 && range.endOffset == node.nodeValue.length) {
|
|
var nextSibling = node.nextSibling;
|
|
if (!nextSibling || nextSibling.tagName.toLowerCase() == "br") {
|
|
while (node) {
|
|
if (node.nodeType == 1 && /^h[1-6]$/i.exec(node.tagName)) {
|
|
header = node;
|
|
break;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
if (header) {
|
|
var parent = header.parentNode;
|
|
var childNodes = parent.childNodes;
|
|
var length = childNodes.length;
|
|
for (var offset = 0; offset < length; offset++) {
|
|
if (childNodes[offset] == header) {
|
|
offset++;
|
|
break;
|
|
}
|
|
}
|
|
this.selectRange(parent, offset, parent, offset);
|
|
this.insertHTML('<p><br></p>');
|
|
TracWysiwyg.stopEvent(event);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.tableHTML = function(id, row, col) {
|
|
var html = this._tableHTML(row, col);
|
|
return html.replace(/<td><\/td>/g, '<td><br></td>').replace(/<td>/, '<td id="' + id + '">');
|
|
};
|
|
TracWysiwyg.prototype.insertTableCell = function(row, index) {
|
|
var cell = row.insertCell(index);
|
|
this.appendBogusLineBreak(cell);
|
|
return cell;
|
|
};
|
|
TracWysiwyg.prototype.getFocusNode = function() {
|
|
return this.contentWindow.getSelection().focusNode;
|
|
};
|
|
if (window.opera) {
|
|
TracWysiwyg.prototype.insertLineBreak = function() {
|
|
this.execCommand("inserthtml", "<br>");
|
|
};
|
|
TracWysiwyg.prototype.insertLineBreakOnShiftEnter = null;
|
|
}
|
|
else if (window.getSelection().setBaseAndExtent) { // Safari 2+
|
|
TracWysiwyg.prototype.insertLineBreak = function() {
|
|
this.execCommand("insertlinebreak");
|
|
};
|
|
TracWysiwyg.prototype.insertLineBreakOnShiftEnter = function(event) {
|
|
this.insertLineBreak();
|
|
TracWysiwyg.stopEvent(event);
|
|
};
|
|
}
|
|
else { // Firefox 2+
|
|
TracWysiwyg.prototype.insertLineBreak = function() {
|
|
var d = this.contentDocument;
|
|
var event = d.createEvent("KeyboardEvent");
|
|
event.initKeyEvent("keypress", true, true, null, false, false, true, false, 0x000d, 0);
|
|
d.body.dispatchEvent(event);
|
|
};
|
|
TracWysiwyg.prototype.insertLineBreakOnShiftEnter = null;
|
|
}
|
|
if (window.getSelection().removeAllRanges) {
|
|
TracWysiwyg.prototype.selectNode = function(node) {
|
|
var selection = this.contentWindow.getSelection();
|
|
selection.removeAllRanges();
|
|
var range = this.contentDocument.createRange();
|
|
range.selectNode(node);
|
|
selection.addRange(range);
|
|
};
|
|
TracWysiwyg.prototype.selectNodeContents = function(node) {
|
|
var selection = this.contentWindow.getSelection();
|
|
selection.removeAllRanges();
|
|
var range = this.contentDocument.createRange();
|
|
range.selectNodeContents(node);
|
|
selection.addRange(range);
|
|
};
|
|
TracWysiwyg.prototype.selectRange = function(start, startOffset, end, endOffset) {
|
|
var selection = this.contentWindow.getSelection();
|
|
selection.removeAllRanges();
|
|
var range = this.contentDocument.createRange();
|
|
range.setStart(start, startOffset);
|
|
range.setEnd(end, endOffset);
|
|
selection.addRange(range);
|
|
};
|
|
TracWysiwyg.prototype.getNativeSelectionRange = function() {
|
|
var selection = this.contentWindow.getSelection();
|
|
return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
|
};
|
|
TracWysiwyg.prototype.expandSelectionToElement = function(arg) {
|
|
if (arg.start || arg.end) {
|
|
var selection = this.contentWindow.getSelection();
|
|
var range = this.getNativeSelectionRange() || this.contentDocument.createRange();
|
|
selection.removeAllRanges();
|
|
if (arg.start) {
|
|
range.setStartBefore(arg.start);
|
|
}
|
|
if (arg.end) {
|
|
range.setEndAfter(arg.end);
|
|
}
|
|
selection.addRange(range);
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.insertHTML = function(html) {
|
|
this.execCommand("inserthtml", html);
|
|
};
|
|
}
|
|
else { // Safari 2
|
|
TracWysiwyg.prototype.selectNode = function(node) {
|
|
var selection = this.contentWindow.getSelection();
|
|
var range = this.contentDocument.createRange();
|
|
range.selectNode(node);
|
|
selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
|
|
range.detach();
|
|
};
|
|
TracWysiwyg.prototype.selectNodeContents = function(node) {
|
|
this.selectRange(node, 0, node, node.childNodes.length);
|
|
};
|
|
TracWysiwyg.prototype.selectRange = function(start, startOffset, end, endOffset) {
|
|
var selection = this.contentWindow.getSelection();
|
|
selection.setBaseAndExtent(start, startOffset, end, endOffset);
|
|
};
|
|
TracWysiwyg.prototype.getNativeSelectionRange = function() {
|
|
var selection = this.contentWindow.getSelection();
|
|
if (selection.anchorNode) {
|
|
var range = this.contentDocument.createRange();
|
|
range.setStart(selection.baseNode, selection.baseOffset);
|
|
range.setEnd(selection.extentNode, selection.extentOffset);
|
|
if (range.collapsed && !selection.isCollapsed) {
|
|
range.setStart(selection.extentNode, selection.extentOffset);
|
|
range.setEnd(selection.baseNode, selection.baseOffset);
|
|
}
|
|
return range;
|
|
}
|
|
return null;
|
|
};
|
|
TracWysiwyg.prototype.expandSelectionToElement = function(arg) {
|
|
if (arg.start || arg.end) {
|
|
var selection = this.contentWindow.getSelection();
|
|
var range = this.getNativeSelectionRange();
|
|
if (arg.start) {
|
|
range.setStartBefore(arg.start);
|
|
}
|
|
if (arg.end) {
|
|
range.setEndAfter(arg.end);
|
|
}
|
|
selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
|
|
range.detach();
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.insertHTML = function(html) {
|
|
var range = this.getNativeSelectionRange();
|
|
if (range) {
|
|
var d = this.contentDocument;
|
|
var tmp = d.createRange();
|
|
tmp.setStart(d.body, 0);
|
|
tmp.setEnd(d.body, 0);
|
|
var fragment = tmp.createContextualFragment(html);
|
|
range.deleteContents();
|
|
range.insertNode(fragment);
|
|
range.detach();
|
|
tmp.detach();
|
|
}
|
|
};
|
|
}
|
|
TracWysiwyg.prototype.getSelectionRange = TracWysiwyg.prototype.getNativeSelectionRange;
|
|
TracWysiwyg.prototype.getSelectionText = function() {
|
|
var range = this.getNativeSelectionRange();
|
|
return range ? range.toString() : null;
|
|
};
|
|
TracWysiwyg.prototype.getSelectionHTML = function() {
|
|
var fragment = this.getSelectionFragment();
|
|
var anonymous = this.contentDocument.createElement("div");
|
|
anonymous.appendChild(fragment);
|
|
return anonymous.innerHTML;
|
|
};
|
|
TracWysiwyg.prototype.getSelectionFragment = function() {
|
|
var range = this.getNativeSelectionRange();
|
|
return range ? range.cloneContents() : this.contentDocument.createDocumentFragment();
|
|
};
|
|
TracWysiwyg.prototype.getSelectionPosition = function() {
|
|
var range = this.getNativeSelectionRange();
|
|
var position = { start: null, end: null };
|
|
if (range) {
|
|
position.start = range.startContainer;
|
|
position.end = range.endContainer;
|
|
}
|
|
return position;
|
|
};
|
|
TracWysiwyg.prototype.selectionContainsTagName = function(name) {
|
|
var selection = this.contentWindow.getSelection();
|
|
var range = this.getNativeSelectionRange();
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
var ancestor = range.commonAncestorContainer;
|
|
if (!ancestor) {
|
|
return false;
|
|
}
|
|
if (TracWysiwyg.getSelfOrAncestor(ancestor, name)) {
|
|
return true;
|
|
}
|
|
if (ancestor.nodeType != 1) {
|
|
return false;
|
|
}
|
|
var elements = ancestor.getElementsByTagName(name);
|
|
var length = elements.length;
|
|
for (var i = 0; i < length; i++) {
|
|
if (selection.containsNode(elements[i], true)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
else if (document.selection) {
|
|
TracWysiwyg.prototype.appendBogusLineBreak = function(element) { };
|
|
TracWysiwyg.prototype.isBogusLineBreak = function(node) { return false };
|
|
TracWysiwyg.prototype.insertParagraphOnEnter = null;
|
|
TracWysiwyg.prototype.insertLineBreak = function() {
|
|
this.insertHTML("<br>");
|
|
};
|
|
TracWysiwyg.prototype.insertLineBreakOnShiftEnter = null;
|
|
TracWysiwyg.prototype.tableHTML = function(id, row, col) {
|
|
var html = this._tableHTML(row, col);
|
|
return html.replace(/<td>/, '<td id="' + id + '">');
|
|
};
|
|
TracWysiwyg.prototype.insertTableCell = function(row, index) {
|
|
return row.insertCell(index);
|
|
};
|
|
TracWysiwyg.prototype.getFocusNode = function() {
|
|
this.contentWindow.focus();
|
|
var d = this.contentDocument;
|
|
var range = d.selection.createRange();
|
|
var node = range.item ? range.item(0) : range.parentElement();
|
|
return node.ownerDocument == d ? node : null;
|
|
};
|
|
TracWysiwyg.prototype.selectNode = function(node) {
|
|
var d = this.contentDocument;
|
|
var body = d.body;
|
|
var range;
|
|
d.selection.empty();
|
|
try {
|
|
range = body.createControlRange();
|
|
range.addElement(node);
|
|
}
|
|
catch (e) {
|
|
range = body.createTextRange();
|
|
range.moveToElementText(node);
|
|
}
|
|
range.select();
|
|
};
|
|
TracWysiwyg.prototype.selectNodeContents = function(node) {
|
|
var d = this.contentDocument;
|
|
d.selection.empty();
|
|
var range = d.body.createTextRange();
|
|
range.moveToElementText(node);
|
|
range.select();
|
|
};
|
|
TracWysiwyg.prototype.selectRange = function(start, startOffset, end, endOffset) {
|
|
var d = this.contentDocument;
|
|
var body = d.body;
|
|
d.selection.empty();
|
|
var range = endPoint(start, startOffset);
|
|
if (start != end || startOffset != endOffset) {
|
|
range.setEndPoint("EndToEnd", endPoint(end, endOffset));
|
|
}
|
|
range.select();
|
|
|
|
function endPoint(node, offset) {
|
|
var range;
|
|
if (node.nodeType == 1) {
|
|
var childNodes = node.childNodes;
|
|
if (offset >= childNodes.length) {
|
|
range = body.createTextRange();
|
|
range.moveToElementText(node);
|
|
range.collapse(false);
|
|
return range;
|
|
}
|
|
node = childNodes[offset];
|
|
if (node.nodeType == 1) {
|
|
range = body.createTextRange();
|
|
range.moveToElementText(node);
|
|
range.collapse(true);
|
|
switch (node.tagName.toLowerCase()) {
|
|
case "table":
|
|
range.move("character", -1);
|
|
break;
|
|
}
|
|
return range;
|
|
}
|
|
return endPoint(node, 0);
|
|
}
|
|
if (node.nodeType != 3) {
|
|
throw "selectRange: nodeType != @".replace(/@/, node.nodeType);
|
|
}
|
|
|
|
range = body.createTextRange();
|
|
var element = node.previousSibling;
|
|
while (element) {
|
|
var nodeType = element.nodeType;
|
|
if (nodeType == 1) {
|
|
range.moveToElementText(element);
|
|
range.collapse(false);
|
|
break;
|
|
}
|
|
if (nodeType == 3) {
|
|
offset += element.nodeValue.length;
|
|
}
|
|
element = element.previousSibling;
|
|
}
|
|
if (!element) {
|
|
range.moveToElementText(node.parentNode);
|
|
range.collapse(true);
|
|
}
|
|
if (offset != 0) {
|
|
range.move("character", offset);
|
|
}
|
|
return range;
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.getSelectionRange = function() {
|
|
var body = this.contentDocument.body;
|
|
var pseudo = {};
|
|
var start = this.getNativeSelectionRange();
|
|
if (start.item) {
|
|
var element = start.item(0);
|
|
var parent = element.parentNode;
|
|
var childNodes = parent.childNodes;
|
|
var length = childNodes.length;
|
|
for (var i = 0; i < length; i++) {
|
|
if (childNodes[i] == element) {
|
|
pseudo.startOffset = i;
|
|
pseudo.endOffset = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
pseudo.collapsed = false;
|
|
pseudo.startContainer = pseudo.endContainer = parent;
|
|
return pseudo;
|
|
}
|
|
var end = start.duplicate();
|
|
pseudo.collapsed = start.compareEndPoints("StartToEnd", end) == 0;
|
|
start.collapse(true);
|
|
end.collapse(false);
|
|
|
|
function nextElement(range) {
|
|
var parent = range.parentElement();
|
|
var childNodes = parent.childNodes;
|
|
var length = childNodes.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var node = childNodes[i];
|
|
if (node.nodeType == 1) {
|
|
var tmp = body.createTextRange();
|
|
tmp.moveToElementText(node);
|
|
if (range.compareEndPoints("EndToStart", tmp) <= 0) {
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function nodeOffset(range, parent, element, index, length) {
|
|
var tmp = body.createTextRange();
|
|
tmp.moveToElementText(element || parent);
|
|
tmp.collapse(!!element);
|
|
tmp.move("character", -index);
|
|
if (!element) {
|
|
length++;
|
|
}
|
|
for ( ; length >= 0; length--) {
|
|
if (tmp.compareEndPoints("EndToStart", range) == 0) {
|
|
return length;
|
|
}
|
|
tmp.move("character", -1);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function setContainerOffset(range, containerKey, offsetKey) {
|
|
var parent = range.parentElement();
|
|
var element = nextElement(range);
|
|
var index = 0;
|
|
var node = element ? element.previousSibling : parent.lastChild;
|
|
var offset, length;
|
|
while (node && node.nodeType == 3) {
|
|
length = node.nodeValue.length;
|
|
offset = nodeOffset(range, parent, element, index, length);
|
|
if (offset !== null) {
|
|
pseudo[containerKey] = node;
|
|
pseudo[offsetKey] = offset;
|
|
return;
|
|
}
|
|
index += length;
|
|
node = node.previousSibling;
|
|
}
|
|
var childNodes = parent.childNodes;
|
|
length = childNodes.length;
|
|
if (length > 0) {
|
|
pseudo[containerKey] = parent;
|
|
pseudo[offsetKey] = containerKey == "startContainer" ? 0 : length - 1;
|
|
return;
|
|
}
|
|
element = parent;
|
|
parent = element.parentNode;
|
|
childNodes = parent.childNodes;
|
|
length = childNodes.length;
|
|
for (offset = 0; offset < length; offset++) {
|
|
if (element == childNodes[offset]) {
|
|
pseudo[containerKey] = parent;
|
|
pseudo[offsetKey] = offset;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
setContainerOffset(start, "startContainer", "startOffset");
|
|
setContainerOffset(end, "endContainer", "endOffset");
|
|
return pseudo;
|
|
};
|
|
TracWysiwyg.prototype.getNativeSelectionRange = function() {
|
|
this.contentWindow.focus();
|
|
return this.contentDocument.selection.createRange();
|
|
};
|
|
TracWysiwyg.prototype.getSelectionText = function() {
|
|
var range = this.getNativeSelectionRange();
|
|
if (range) {
|
|
return range.item ? range.item(0).innerText : range.text;
|
|
}
|
|
return null;
|
|
};
|
|
TracWysiwyg.prototype.getSelectionHTML = function() {
|
|
var range = this.getNativeSelectionRange();
|
|
if (range) {
|
|
return range.item ? range.item(0).innerHTML : range.htmlText;
|
|
}
|
|
return null;
|
|
};
|
|
TracWysiwyg.prototype.getSelectionFragment = function() {
|
|
var d = this.contentDocument;
|
|
var fragment = d.createDocumentFragment();
|
|
var anonymous = d.createElement("div");
|
|
anonymous.innerHTML = this.getSelectionHTML();
|
|
this.collectChildNodes(fragment, anonymous);
|
|
return fragment;
|
|
};
|
|
TracWysiwyg.prototype.getSelectionPosition = function() {
|
|
this.contentWindow.focus();
|
|
var d = this.contentDocument;
|
|
var range = d.selection.createRange();
|
|
var startNode = null;
|
|
var endNode = null;
|
|
if (range.item) {
|
|
if (range.item(0).ownerDocument == d) {
|
|
startNode = range.item(0);
|
|
endNode = range.item(range.length - 1);
|
|
}
|
|
}
|
|
else {
|
|
if (range.parentElement().ownerDocument == d) {
|
|
var startRange = range.duplicate();
|
|
startRange.collapse(true);
|
|
startNode = startRange.parentElement();
|
|
var endRange = range.duplicate();
|
|
endRange.collapse(false);
|
|
endNode = endRange.parentElement();
|
|
}
|
|
}
|
|
return { start: startNode, end: endNode };
|
|
};
|
|
TracWysiwyg.prototype.expandSelectionToElement = function(arg) {
|
|
this.contentWindow.focus();
|
|
var d = this.contentDocument;
|
|
var body = d.body;
|
|
var range = d.selection.createRange();
|
|
var tmp;
|
|
if (arg.start) {
|
|
tmp = body.createTextRange();
|
|
tmp.moveToElementText(arg.start);
|
|
range.setEndPoint("StartToStart", tmp);
|
|
}
|
|
if (arg.end) {
|
|
tmp = body.createTextRange();
|
|
tmp.moveToElementText(arg.end);
|
|
range.setEndPoint("EndToEnd", tmp);
|
|
}
|
|
if (tmp) {
|
|
range.select();
|
|
}
|
|
};
|
|
TracWysiwyg.prototype.selectionContainsTagName = function(name) {
|
|
this.contentWindow.focus();
|
|
var d = this.contentDocument;
|
|
var selection = d.selection;
|
|
var range = selection.createRange();
|
|
var parent = range.item ? range.item(0) : range.parentElement();
|
|
if (!parent) {
|
|
return false;
|
|
}
|
|
if (TracWysiwyg.getSelfOrAncestor(parent, name)) {
|
|
return true;
|
|
}
|
|
var elements = parent.getElementsByTagName(name);
|
|
var length = elements.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var testRange = selection.createRange();
|
|
testRange.moveToElementText(elements[i]);
|
|
if (range.compareEndPoints("StartToEnd", testRange) <= 0
|
|
&& range.compareEndPoints("EndToStart", testRange) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
TracWysiwyg.prototype.insertHTML = function(html) {
|
|
this.contentWindow.focus();
|
|
var selection = this.contentDocument.selection;
|
|
var range = selection.createRange();
|
|
range.pasteHTML(html.replace(/\t/g, "	"));
|
|
range.collapse(false);
|
|
range.select();
|
|
range = this.contentDocument.selection.createRange();
|
|
};
|
|
}
|
|
else {
|
|
TracWysiwyg.prototype.appendBogusLineBreak = function(element) { };
|
|
TracWysiwyg.prototype.insertParagraphOnEnter = null;
|
|
TracWysiwyg.prototype.insertLineBreak = function() { };
|
|
TracWysiwyg.prototype.insertTableCell = function(row, index) { return null };
|
|
TracWysiwyg.prototype.getFocusNode = function() { return null };
|
|
TracWysiwyg.prototype.selectNode = function(node) { };
|
|
TracWysiwyg.prototype.selectNodeContents = function(node) { return null };
|
|
TracWysiwyg.prototype.selectRange = function(start, startOffset, end, endOffset) { };
|
|
TracWysiwyg.prototype.getSelectionRange = function() { return null };
|
|
TracWysiwyg.prototype.getNativeSelectionRange = function() { return null };
|
|
TracWysiwyg.prototype.getSelectionText = function() { return null };
|
|
TracWysiwyg.prototype.getSelectionHTML = function() { return null };
|
|
TracWysiwyg.prototype.getSelectionFragment = function() { return null };
|
|
TracWysiwyg.prototype.getSelectionPosition = function() { return null };
|
|
TracWysiwyg.prototype.expandSelectionToElement = function(arg) { };
|
|
TracWysiwyg.prototype.selectionContainsTagName = function(name) { return false };
|
|
TracWysiwyg.prototype.insertHTML = function(html) { };
|
|
}
|
|
|
|
TracWysiwyg.prototype._treeWalkEmulation = function(root, iterator) {
|
|
if (!root.firstChild) {
|
|
iterator(null);
|
|
return;
|
|
}
|
|
var element = root;
|
|
var tmp;
|
|
while (element) {
|
|
if (tmp = element.firstChild) {
|
|
element = tmp;
|
|
}
|
|
else if (tmp = element.nextSibling) {
|
|
element = tmp;
|
|
}
|
|
else {
|
|
for ( ; ; ) {
|
|
element = element.parentNode;
|
|
if (element == root || !element) {
|
|
iterator(null);
|
|
return;
|
|
}
|
|
if (tmp = element.nextSibling) {
|
|
element = tmp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
iterator(element);
|
|
}
|
|
};
|
|
|
|
if (document.createTreeWalker) {
|
|
TracWysiwyg.prototype.treeWalk = function(root, iterator) {
|
|
var walker = root.ownerDocument.createTreeWalker(
|
|
root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, true);
|
|
while (walker.nextNode()) {
|
|
iterator(walker.currentNode);
|
|
}
|
|
iterator(null);
|
|
};
|
|
}
|
|
else {
|
|
TracWysiwyg.prototype.treeWalk = TracWysiwyg.prototype._treeWalkEmulation;
|
|
}
|
|
|
|
TracWysiwyg.instances = [];
|
|
TracWysiwyg.count = 0;
|
|
TracWysiwyg.tracPaths = null;
|
|
|
|
TracWysiwyg.newInstance = function(textarea, options) {
|
|
var instance = new TracWysiwyg(textarea, options);
|
|
TracWysiwyg.instances.push(instance);
|
|
return instance;
|
|
};
|
|
|
|
TracWysiwyg.findInstance = function(textarea) {
|
|
var instances = TracWysiwyg.instances;
|
|
var length = instances.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var instance = instances[i];
|
|
if (instance.textarea == textarea) {
|
|
return instance;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
TracWysiwyg.getTracPaths = function() {
|
|
var stylesheets = [];
|
|
var paths = { stylesheets: stylesheets };
|
|
|
|
var d = document;
|
|
var head = d.getElementsByTagName("head")[0];
|
|
var links = head.getElementsByTagName("link");
|
|
var length = links.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var link = links[i];
|
|
var href = link.getAttribute("href") || "";
|
|
var type = link.getAttribute("type") || "";
|
|
switch ((link.getAttribute("rel") || "").toLowerCase()) {
|
|
case "tracwysiwyg.base":
|
|
paths.base = href;
|
|
break;
|
|
case "tracwysiwyg.stylesheet":
|
|
stylesheets.push(href);
|
|
break;
|
|
}
|
|
}
|
|
if (paths.base && stylesheets.length > 0) {
|
|
return paths;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
TracWysiwyg.getOptions = function() {
|
|
var options = {};
|
|
if (typeof window._tracwysiwyg != "undefined") {
|
|
options = _tracwysiwyg;
|
|
}
|
|
return options;
|
|
};
|
|
|
|
TracWysiwyg.getEditorMode = function() {
|
|
if (TracWysiwyg.editorMode) {
|
|
return TracWysiwyg.editorMode;
|
|
}
|
|
|
|
var mode = null;
|
|
var cookies = (document.cookie || "").split(";");
|
|
var length = cookies.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var match = /^\s*tracwysiwyg=(\S*)/.exec(cookies[i]);
|
|
if (match) {
|
|
switch (match[1]) {
|
|
case "wysiwyg":
|
|
mode = match[1];
|
|
break;
|
|
default: // "textarea"
|
|
mode = null;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
TracWysiwyg.editorMode = mode || "textarea";
|
|
return TracWysiwyg.editorMode;
|
|
};
|
|
|
|
TracWysiwyg.setEditorMode = function(mode) {
|
|
switch (mode) {
|
|
case "wysiwyg":
|
|
break;
|
|
default: // "textarea"
|
|
mode = "textarea";
|
|
break;
|
|
}
|
|
TracWysiwyg.editorMode = mode;
|
|
|
|
var now = new Date();
|
|
if (!/\/$/.test(TracWysiwyg.tracPaths.base)) {
|
|
expires = new Date(now.getTime() - 86400000);
|
|
pieces = [ "tracwysiwyg=",
|
|
"path=" + TracWysiwyg.tracPaths.base + "/",
|
|
"expires=" + expires.toUTCString() ];
|
|
document.cookie = pieces.join("; ");
|
|
}
|
|
var expires = new Date(now.getTime() + 365 * 86400 * 1000);
|
|
var pieces = [ "tracwysiwyg=" + mode,
|
|
"path=" + TracWysiwyg.tracPaths.base,
|
|
"expires=" + expires.toUTCString() ];
|
|
document.cookie = pieces.join("; ");
|
|
};
|
|
|
|
TracWysiwyg.removeEvent = function(element, type, func) {
|
|
jQuery(element).unbind(type, func);
|
|
};
|
|
|
|
TracWysiwyg.stopEvent = function(event) {
|
|
if (event.preventDefault) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
else {
|
|
event.returnValue = false;
|
|
event.cancelBubble = true;
|
|
}
|
|
};
|
|
|
|
TracWysiwyg.setStyle = function(element, object) {
|
|
var style = element.style;
|
|
for (var name in object) {
|
|
style[name] = object[name];
|
|
}
|
|
};
|
|
|
|
if (document.defaultView) {
|
|
TracWysiwyg.getStyle = function(element, name) {
|
|
var value = element.style[name];
|
|
if (!value) {
|
|
var style = element.ownerDocument.defaultView.getComputedStyle(element, null)
|
|
value = style ? style[name] : null;
|
|
}
|
|
return value;
|
|
};
|
|
}
|
|
else {
|
|
TracWysiwyg.getStyle = function(element, name) {
|
|
return element.style[name] || element.currentStyle[name];
|
|
};
|
|
}
|
|
|
|
TracWysiwyg.elementPosition = function(element) {
|
|
function vector(left, top) {
|
|
var value = [ left, top ];
|
|
value.left = left;
|
|
value.top = top;
|
|
return value;
|
|
}
|
|
var position = TracWysiwyg.getStyle(element, "position");
|
|
var left = 0, top = 0;
|
|
for (var node = element; node; node = node.offsetParent) {
|
|
left += node.offsetLeft || 0;
|
|
top += node.offsetTop || 0;
|
|
}
|
|
if (position != "absolute") {
|
|
return vector(left, top);
|
|
}
|
|
var offset = TracWysiwyg.elementPosition(element.offsetParent);
|
|
return vector(left - offset.left, top - offset.top);
|
|
};
|
|
|
|
TracWysiwyg.getSelfOrAncestor = function(element, name) {
|
|
var target = element;
|
|
var d = element.ownerDocument;
|
|
if (name instanceof RegExp) {
|
|
while (target && target != d) {
|
|
switch (target.nodeType) {
|
|
case 1: // element
|
|
if (name.test(target.tagName.toLowerCase())) {
|
|
return target;
|
|
}
|
|
break;
|
|
case 11: // fragment
|
|
return null;
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
}
|
|
else {
|
|
name = name.toLowerCase();
|
|
while (target && target != d) {
|
|
switch (target.nodeType) {
|
|
case 1: // element
|
|
if (target.tagName.toLowerCase() == name) {
|
|
return target;
|
|
}
|
|
break;
|
|
case 11: // fragment
|
|
return null;
|
|
}
|
|
target = target.parentNode;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
TracWysiwyg.serializeToHref = function(attrs) {
|
|
var texts = [];
|
|
for (var name in attrs) {
|
|
if (/^data(?:-|$)/.exec(name)) {
|
|
texts.push(encodeURIComponent(name) + "=" + encodeURIComponent(attrs[name]));
|
|
}
|
|
}
|
|
return "#" + texts.join("&");
|
|
};
|
|
|
|
TracWysiwyg.unserializeFromHref = function(href, name) {
|
|
var attrs = {};
|
|
if (href.indexOf("#") !== -1) {
|
|
var pieces = href.replace(/^[^#]*#/, '').split(/&/g);
|
|
var length = pieces.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var pair = pieces[i].split(/=/g, 2);
|
|
attrs[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
|
|
}
|
|
}
|
|
return name ? attrs[name] : attrs;
|
|
};
|
|
|
|
TracWysiwyg.getTextContent = (function() {
|
|
var anonymous = document.createElement("div");
|
|
if (typeof anonymous.textContent != "undefined") {
|
|
return function(element) { return element.textContent };
|
|
}
|
|
else if (typeof anonymous.innerText != "undefined") {
|
|
return function(element) { return element.innerText };
|
|
}
|
|
else {
|
|
return function(element) { return null };
|
|
}
|
|
})();
|
|
|
|
TracWysiwyg.initialize = function() {
|
|
if ("replace".replace(/[a-e]/g, function(m) { return "*" }) != "r*pl***") {
|
|
return;
|
|
}
|
|
if (typeof document.designMode == "undefined") {
|
|
return;
|
|
}
|
|
TracWysiwyg.tracPaths = TracWysiwyg.getTracPaths();
|
|
if (!TracWysiwyg.tracPaths) {
|
|
return;
|
|
}
|
|
var options = TracWysiwyg.getOptions();
|
|
var textareas = document.getElementsByTagName("textarea");
|
|
for (var i = 0; i < textareas.length; i++) {
|
|
var textarea = textareas[i];
|
|
if (/\bwikitext\b/.test(textarea.className || "")) {
|
|
TracWysiwyg.newInstance(textarea, options);
|
|
}
|
|
}
|
|
};
|