Skip to content

Instantly share code, notes, and snippets.

@jankuca
Created January 17, 2011 14:05
Show Gist options
  • Save jankuca/782871 to your computer and use it in GitHub Desktop.
Save jankuca/782871 to your computer and use it in GitHub Desktop.
Modified WysiHat v0.2.1; not forked the original repository because rake is required
/* WysiHat - WYSIWYG JavaScript framework, version 0.2.1
* (c) 2008-2010 Joshua Peek
*
* WysiHat is freely distributable under the terms of an MIT-style license.
*--------------------------------------------------------------------------*/
(function (window) {
var WysiHat = {};
WysiHat.Editor = {
attach: function (textarea, options, block) {
options = $H(options);
textarea = $(textarea);
//textarea.hide();
var model = options.get('model') || WysiHat.iFrame;
var initializer = block;
return model.create(textarea, function (editArea) {
//var document = editArea.getDocument();
var window = editArea.getWindow();
editArea.load();
Event.observe(window, 'focus', function (event) { editArea.focus(); });
Event.observe(window, 'blur', function (event) { editArea.blur(); });
editArea._observeEvents();
//if (Prototype.Browser.Gecko) {
// editArea.execCommand('undo', false, null);
//}
if (initializer) {
initializer(editArea);
}
editArea.focus();
});
},
detach: function (editor) {
editor.writeAttribute('contenteditable', 'false')._stopObservingEvents();
},
include: function (module) {
this.includedModules = this.includedModules || $A([]);
this.includedModules.push(module);
},
extend: function (object) {
var modules = this.includedModules || $A([]);
modules.each(function (module) {
Object.extend(object, module);
});
}
};
WysiHat.Commands = (function () {
function boldSelection() {
this.execCommand('bold', false, null);
}
function boldSelected() {
return this.queryCommandState('bold');
}
function underlineSelection() {
this.execCommand('underline', false, null);
}
function underlineSelected() {
return this.queryCommandState('underline');
}
function italicSelection() {
this.execCommand('italic', false, null);
}
function italicSelected() {
return this.queryCommandState('italic');
}
function strikethroughSelection() {
this.execCommand('strikethrough', false, null);
}
function blockquoteSelection() {
this.execCommand('blockquote', false, null);
}
function fontSelection(font) {
this.execCommand('fontname', false, font);
}
function fontSizeSelection(fontSize) {
this.execCommand('fontsize', false, fontSize);
}
function colorSelection(color) {
this.execCommand('forecolor', false, color);
}
function backgroundColorSelection(color) {
if (Prototype.Browser.Gecko) {
this.execCommand('hilitecolor', false, color);
} else {
this.execCommand('backcolor', false, color);
}
}
function alignSelection(alignment) {
this.execCommand('justify' + alignment);
}
function alignSelected() {
var node = this.selection.getNode();
return Element.getStyle(node, 'textAlign');
}
function linkSelection(url) {
this.execCommand('createLink', false, url);
}
function unlinkSelection() {
var node = this.selection.getNode();
if (this.linkSelected()) {
this.selection.selectNode(node);
}
this.execCommand('unlink', false, null);
}
function linkSelected() {
var node = this.selection.getNode();
return node ? node.tagName.toUpperCase() === 'A' : false;
}
function formatblockSelection(element) {
// var node = this.selection.getNode();
//node.insert({ after: new Element(element).update(node.innerText || node.textContent) });
//if (node !== remove();
document.execCommand('formatblock', false, element);
}
function insertOrderedList() {
this.execCommand('insertorderedlist', false, null);
}
function insertUnorderedList() {
this.execCommand('insertunorderedlist', false, null);
}
function insertImage(url) {
this.execCommand('insertImage', false, url);
}
function insertHTML(html) {
if (Prototype.Browser.IE) {
var range = this._selection.getRange();
range.pasteHTML(html);
range.collapse(false);
range.select();
} else {
this.execCommand('insertHTML', false, html);
}
}
function execCommand(command, ui, value) {
var document = this.getDocument();
if (Prototype.Browser.IE) {
this.selection.restore();
}
var handler = this.commands.get(command);
if (handler) {
handler.bind(this)(value);
} else {
document.execCommand(command, ui, value);
}
}
function queryCommandState(state) {
var document = this.getDocument();
var handler = this.queryCommands.get(state);
if (handler) {
return handler.bind(this)();
} else {
return document.queryCommandState(state);
}
}
function getSelectedStyles() {
var styles = $H({});
var editor = this;
editor.styleSelectors.each(function (style) {
var node = editor.selection.getNode();
styles.set(style.first(), Element.getStyle(node, style.last()));
});
return styles;
}
function isInTag(tag_name) {
tag_name = tag_name.toLowerCase();
var node = this.selection.getNode();
var root = this;
while (node && node.tagName && node !== root) {
if (node.tagName.toLowerCase() === tag_name) {
return true;
}
node = node.parentNode;
}
return false;
}
function removeFormat() {
var node = this.selection.getNode();
node.innerHTML = node.innerText;
if (node.tagName.toLowerCase() === 'span') {
var parent = node.parentNode;
parent.innerHTML = node.innerText;
node = parent;
}
var i, style = node.style;
for (i in style) {
if (style.hasOwnProperty(i)) {
style[i] = '';
}
}
}
return {
boldSelection: boldSelection,
boldSelected: boldSelected,
underlineSelection: underlineSelection,
underlineSelected: underlineSelected,
italicSelection: italicSelection,
italicSelected: italicSelected,
strikethroughSelection: strikethroughSelection,
blockquoteSelection: blockquoteSelection,
fontSelection: fontSelection,
fontSizeSelection: fontSizeSelection,
colorSelection: colorSelection,
backgroundColorSelection: backgroundColorSelection,
alignSelection: alignSelection,
alignSelected: alignSelected,
linkSelection: linkSelection,
unlinkSelection: unlinkSelection,
linkSelected: linkSelected,
formatblockSelection: formatblockSelection,
insertOrderedList: insertOrderedList,
insertUnorderedList: insertUnorderedList,
insertImage: insertImage,
insertHTML: insertHTML,
execCommand: execCommand,
queryCommandState: queryCommandState,
getSelectedStyles: getSelectedStyles,
isInTag: isInTag,
removeFormat: removeFormat,
commands: $H({}),
queryCommands: $H({
link: linkSelected
}),
styleSelectors: $H({
fontname: 'fontFamily',
fontsize: 'fontSize',
forecolor: 'color',
hilitecolor: 'backgroundColor',
backcolor: 'backgroundColor'
})
};
}());
WysiHat.Editor.include(WysiHat.Commands);
WysiHat.Events = (function () {
var eventsToFoward = [
'click',
'dblclick',
'mousedown',
'mouseup',
'mouseover',
'mousemove',
'mouseout',
'keypress',
'keydown',
'keyup'
];
function forwardEvents(document, editor) {
if (!editor) {
return;
}
eventsToFoward.each(function (event) {
Event.observe(document, event, function (e) {
editor.fire('wysihat:' + event);
});
});
}
function observeKeyDownEvents(window, document, editor) {
if (!editor) {
return;
}
Event.observe(document, 'keydown', function (event) {
switch (event.keyCode) {
case 9: // TAB
event.preventDefault();
editor.execCommand(
!event.shiftKey ? 'indent' : 'outdent'
);
return false;
}
});
}
function observePasteEvent(window, document, editor) {
if (!editor) {
return;
}
Event.observe(document, 'keydown', function (event) {
if (event.keyCode === 86) {
editor.fire("wysihat:paste");
}
});
Event.observe(window, 'paste', function (event) {
editor.fire("wysihat:paste");
});
}
function stopObservingPasteEvent(window, document) {
Event.stopObserving(window, 'paste');
}
function observeFocus(window, editor) {
if (!editor) {
return;
}
Event.observe(window, 'focus', function (event) {
editor.fire("wysihat:focus");
});
Event.observe(window, 'blur', function (event) {
editor.fire("wysihat:blur");
});
Event.observe(editor, 'focus', function (event) {
editor.fire("wysihat:focus");
});
Event.observe(editor, 'blur', function (event) {
editor.fire("wysihat:blur");
});
}
function stopObservingFocus(window) {
Event.stopObserving(window, 'focus');
Event.stopObserving(window, 'blur');
}
function observeSelections(document, editor) {
if (!editor) {
return;
}
Event.observe(document, 'mouseup', function (event) {
var range = editor.selection.getRange();
if (range && !range.collapsed) {
editor.fire("wysihat:select");
}
});
}
function observeChanges(document, editor) {
if (!editor) {
return;
}
var previousContents = editor.rawContent();
Event.observe(document, 'keyup', function (event) {
var contents = editor.rawContent();
if (previousContents !== contents) {
editor.fire("wysihat:change");
previousContents = contents;
}
});
}
function observeCursorMovements(document, editor) {
if (!editor) {
return;
}
var previousRange = editor.selection.getRange();
var handler = function (event) {
var range = editor.selection.getRange();
if (previousRange !== range) {
editor.fire("wysihat:cursormove");
previousRange = range;
}
};
Event.observe(document, 'keyup', handler);
Event.observe(document, 'mouseup', handler);
}
function observeEvents() {
if (this._observers_setup) {
return;
}
var document = this.getDocument();
var window = this.getWindow();
forwardEvents(document, this);
observeKeyDownEvents(window, document, this);
observePasteEvent(window, document, this);
observeFocus(window, this);
observeSelections(document, this);
observeChanges(document, this);
observeCursorMovements(document, this);
this._observers_setup = true;
}
function stopObservingEvents() {
if (!this._observers_setup) {
return;
}
var document = this.getDocument();
var window = this.getWindow();
eventsToFoward.each(function (event) {
Event.stopObserving(document, event);
});
stopObservingPasteEvent(window, document);
stopObservingFocus(window);
this._observers_setup = false;
}
return {
_observeEvents: observeEvents,
_stopObservingEvents: stopObservingEvents
};
}());
WysiHat.Editor.include(WysiHat.Events);
WysiHat.Persistence = (function () {
function outputFilter(text) {
return text.formatHTMLOutput();
}
function inputFilter(text) {
return text.formatHTMLInput();
}
function content() {
return this.outputFilter(this.rawContent());
}
function setContent(text) {
this.setRawContent(text);
}
function save() {
this.textarea.value = this.content();
}
function load() {
this.setContent(this.textarea.innerHTML);
}
function reload() {
this.selection.setBookmark();
this.save();
this.load();
this.selection.moveToBookmark();
}
return {
outputFilter: outputFilter,
inputFilter: inputFilter,
content: content,
setContent: setContent,
save: save,
load: load,
reload: reload
};
}());
WysiHat.Editor.include(WysiHat.Persistence);
WysiHat.Window = (function () {
function getDocument() {
return this.contentDocument || this.contentWindow.document;
}
function getWindow() {
if (this.contentDocument && this.contentDocument.defaultView) {
return this.contentDocument.defaultView;
} else if (this.contentWindow.document) {
return this.contentWindow;
} else {
return null;
}
}
function focus() {
this.getWindow().focus();
if (this.hasFocus) {
return;
}
this.hasFocus = true;
}
function blur() {
this.hasFocus = false;
}
return {
getDocument: getDocument,
getWindow: getWindow,
focus: focus,
blur: blur
};
}());
WysiHat.Editor.include(WysiHat.Window);
WysiHat.iFrame = {
create: function (textarea, callback) {
var editArea = new Element('iframe', { 'id': textarea.id + '_editor', 'class': 'editor' });
Object.extend(editArea, WysiHat.iFrame.Methods);
WysiHat.Editor.extend(editArea);
editArea.attach(textarea, callback);
textarea.insert({before: editArea});
return editArea;
}
};
WysiHat.iFrame.Methods = {
attach: function (element, callback) {
this.textarea = element;
this.observe('load', function () {
var document;
try {
document = this.getDocument();
} catch (e) { return; } // No iframe, just stop
this.selection = new WysiHat.Selection(this);
if (this.ready && document.designMode === 'on') {
return;
}
this.setStyle({});
document.designMode = 'on';
callback(this);
this.ready = true;
this.fire('wysihat:ready');
});
},
unattach: function () {
this.remove();
},
whenReady: function (callback) {
if (this.ready) {
callback(this);
} else {
var editor = this;
editor.observe('wysihat:ready', function () { callback(editor); });
}
return this;
},
setStyle: function (styles) {
var document = this.getDocument();
var element = this;
if (!this.ready) {
return setTimeout(function () { element.setStyle(styles); }, 1);
}
var style;
if (Prototype.Browser.IE) {
style = document.createStyleSheet();
style.addRule("body", "border: 0");
style.addRule("p", "margin: 0");
$H(styles).each(function (pair) {
var value = pair.first().underscore().dasherize() + ": " + pair.last();
style.addRule("body", value);
});
} else if (Prototype.Browser.Opera) {
style = Element('style').update("p { margin: 0; }");
var head = document.getElementsByTagName('head')[0];
head.appendChild(style);
} else {
Element.setStyle(document.body, styles);
}
return this;
},
/**
* WysiHat.iFrame.Methods#getStyle(style) -> string
* - style specificication (i.e. backgroundColor)
*
* Returns the style from the element based on the given style
*/
getStyle: function (style) {
var document = this.getDocument();
return Element.getStyle(document.body, style);
},
rawContent: function () {
var document = this.getDocument();
if (document.body) {
return document.body.innerHTML;
} else {
return "";
}
},
setRawContent: function (text) {
var document = this.getDocument();
if (document.body) {
document.body.innerHTML = text;
}
}
};
WysiHat.Editable = {
create: function (textarea, callback) {
/*var editArea = new Element('div', {
'id': textarea.id + '_editor',
'class': 'editor',
'contenteditable': 'true'
});*/
var editArea = textarea;
//editArea.innerHTML = editArea.innerHTML.replace(/(\n|\r)+/,'');
editArea.writeAttribute('contenteditable', 'true');
editArea.writeAttribute('spellcheck', 'false');
editArea.textarea = textarea;
WysiHat.Editor.extend(editArea);
Object.extend(editArea, WysiHat.Editable.Methods);
editArea.selection = new WysiHat.Selection(editArea);
/*if (Prototype.Browser.WebKit) {
editArea.innerHTML = editArea.innerHTML.replace(/<div><br(\s\/)?><\/div><div><br(\s\/)?><\/div>/g,'<div><br /></div>');
}*/
//editArea.innerHTML = editArea.innerHTML.replace(/<div><br(\s\/)?><\/div>/g,'');
callback(editArea);
//textarea.insert({before: editArea});
return editArea;
}
};
WysiHat.Editable.Methods = {
getDocument: function () {
return document;
},
getWindow: function () {
return window;
},
rawContent: function () {
return this.innerHTML;
},
setRawContent: function (text) {
this.innerHTML = text;
}
};
Object.extend(String.prototype, (function () {
function formatHTMLOutput() {
var text = String(this);
text = text.tidyXHTML();
// if (Prototype.Browser.WebKit) {
// text = text.replace(/(<div(\sstyle="[^"]*")?>)+/g, "<p>");
// text = text.replace(/(<\/div>)+/g, "</p>");
//
// text = text.replace(/<p>\s*<\/p>/g, "");
//
// text = text.replace(/<br \/>(\n)*/g, "<br />");
// } else if (Prototype.Browser.Gecko) {
// text = text.replace(/<p>/g, "");
// text = text.replace(/<\/p>(\n)?/g, "\n");
//
// text = text.replace(/<br \/>(\n)*/g, "\n");
// } else if (Prototype.Browser.IE || Prototype.Browser.Opera) {
// text = text.replace(/<p>(&nbsp;|&#160;|\s)<\/p>/g, "<p></p>");
//
// text = text.replace(/<br \/>/g, "");
//
// text = text.replace(/<p>/g, '');
//
// text = text.replace(/&nbsp;/g, '');
//
// text = text.replace(/<\/p>(\n)?/g, "\n");
//
// text = text.gsub(/^<p>/, '');
// text = text.gsub(/<\/p>$/, '');
// }
//
// text = text.gsub(/<b>/, "<strong>");
// text = text.gsub(/<\/b>/, "</strong>");
//
// text = text.gsub(/<i>/, "<em>");
// text = text.gsub(/<\/i>/, "</em>");
//
// text = text.replace(/\n\n+/g, "</p>\n\n<p>");
//
// text = text.gsub(/(([^\n])(\n))(?=([^\n]))/, "#{2}\n");
//text = '<p>' + text + '</p>';
//text = text.replace(/<p>\s*/g, "<p>");
//text = text.replace(/\s*<\/p>/g, "</p>");
//text = text.replace(/\n+/,'');
var element = Element("body");
element.innerHTML = text;
if (Prototype.Browser.WebKit || Prototype.Browser.Gecko) {
var replaced;
var fn = function (span) {
if (span.hasClassName('Apple-style-span')) {
span.removeClassName('Apple-style-span');
if (span.className === '') {
span.removeAttribute('class');
}
replaced = true;
} else if (span.getStyle('fontWeight') === 'bold') {
span.setStyle({fontWeight: ''});
if (span.style.length === 0) {
span.removeAttribute('style');
}
span.update('<strong>' + span.innerHTML + '</strong>');
replaced = true;
} else if (span.getStyle('fontStyle') === 'italic') {
span.setStyle({fontStyle: ''});
if (span.style.length === 0) {
span.removeAttribute('style');
}
span.update('<em>' + span.innerHTML + '</em>');
replaced = true;
} else if (span.getStyle('textDecoration') === 'underline') {
span.setStyle({textDecoration: ''});
if (span.style.length === 0) {
span.removeAttribute('style');
}
span.update('<u>' + span.innerHTML + '</u>');
replaced = true;
} else if (span.attributes.length === 0) {
span.replace(span.innerHTML);
replaced = true;
}
};
do {
replaced = false;
element.select('span').each(fn);
} while (replaced);
}
var acceptableBlankTags = $A(['BR', 'IMG', 'DIV']);
var i;
for (i = 0; i < element.descendants().length; i++) {
var node = element.descendants()[i];
if (node.innerHTML.blank() && !acceptableBlankTags.include(node.nodeName) && node.id !== 'bookmark') {
node.remove();
}
}
text = element.innerHTML;
text = text.tidyXHTML();
// text = text.replace(/<br \/>/g, '');
// text = text.replace(/<\/p>\n<p>/g, "</p>\n\n<p>");
//
// text = text.replace(/<p>\s*<\/p>/g, "");
//
// text = text.replace(/\s*$/g, "");
return text;
}
function formatHTMLInput() {
var text = String(this);
var element = Element("body");
element.innerHTML = text;
if (Prototype.Browser.Gecko || Prototype.Browser.WebKit) {
element.select('strong').each(function (element) {
element.replace('<span style="font-weight: bold;">' + element.innerHTML + '</span>');
});
element.select('em').each(function (element) {
element.replace('<span style="font-style: italic;">' + element.innerHTML + '</span>');
});
element.select('u').each(function (element) {
element.replace('<span style="text-decoration: underline;">' + element.innerHTML + '</span>');
});
}
if (Prototype.Browser.WebKit) {
element.select('span').each(function (span) {
if (span.getStyle('fontWeight') === 'bold') {
span.addClassName('Apple-style-span');
}
if (span.getStyle('fontStyle') === 'italic') {
span.addClassName('Apple-style-span');
}
if (span.getStyle('textDecoration') === 'underline') {
span.addClassName('Apple-style-span');
}
});
}
text = element.innerHTML;
text = text.tidyXHTML();
text = text.replace(/<\/p>(\n)*<p>/g, "\n\n");
text = text.replace(/(\n)?<br( \/)?>(\n)?/g, "\n");
text = text.replace(/^<p>/g, '');
text = text.replace(/<\/p>$/g, '');
if (Prototype.Browser.Gecko) {
text = text.replace(/\n/g, "<br>");
text = text + '<br>';
} else if (Prototype.Browser.WebKit) {
text = text.replace(/\n/g, "</div><div>");
text = '<div>' + text + '</div>';
text = text.replace(/<div><\/div>/g, "<div><br></div>");
} else if (Prototype.Browser.IE || Prototype.Browser.Opera) {
text = text.replace(/\n/g, "</p>\n<p>");
text = '<p>' + text + '</p>';
text = text.replace(/<p><\/p>/g, "<p>&nbsp;</p>");
text = text.replace(/(<p>&nbsp;<\/p>)+$/g, "");
}
return text;
}
function tidyXHTML() {
var text = String(this);
text = text.gsub(/\r\n?/, "\n");
text = text.gsub(/<([A-Z]+)([^>]*)>/, function (match) {
return '<' + match[1].toLowerCase() + match[2] + '>';
});
text = text.gsub(/<\/([A-Z]+)>/, function (match) {
return '</' + match[1].toLowerCase() + '>';
});
text = text.replace(/<br>/g, "<br />");
return text;
}
return {
formatHTMLOutput: formatHTMLOutput,
formatHTMLInput: formatHTMLInput,
tidyXHTML: tidyXHTML
};
}()));
Object.extend(String.prototype, {
sanitize: function (options) {
return Element("div").update(this).sanitize(options).innerHTML.tidyXHTML();
}
});
Element.addMethods({
sanitize: function (element, options) {
element = $(element);
options = $H(options);
var allowed_tags = $A(options.get('tags') || []);
var allowed_attributes = $A(options.get('attributes') || []);
var sanitized = Element(element.nodeName);
$A(element.childNodes).each(function (child) {
if (child.nodeType === 1) {
var children = $(child).sanitize(options).childNodes;
if (allowed_tags.include(child.nodeName.toLowerCase())) {
var new_child = Element(child.nodeName);
allowed_attributes.each(function (attribute) {
var value = child.readAttribute(attribute);
if (value) {
new_child.writeAttribute(attribute, value);
}
});
sanitized.appendChild(new_child);
$A(children).each(function (grandchild) { new_child.appendChild(grandchild); });
} else {
$A(children).each(function (grandchild) { sanitized.appendChild(grandchild); });
}
} else if (child.nodeType === 3) {
sanitized.appendChild(child);
}
});
return sanitized;
}
});
if (typeof Range === 'undefined') {
Range = function (ownerDocument) {
this.ownerDocument = ownerDocument;
this.startContainer = this.ownerDocument.documentElement;
this.startOffset = 0;
this.endContainer = this.ownerDocument.documentElement;
this.endOffset = 0;
this.collapsed = true;
this.commonAncestorContainer = this._commonAncestorContainer(this.startContainer, this.endContainer);
this.detached = false;
this.START_TO_START = 0;
this.START_TO_END = 1;
this.END_TO_END = 2;
this.END_TO_START = 3;
};
Range.CLONE_CONTENTS = 0;
Range.DELETE_CONTENTS = 1;
Range.EXTRACT_CONTENTS = 2;
if (!document.createRange) {
document.createRange = function () {
return new Range(this);
};
}
Object.extend(Range.prototype, (function () {
// defined later
var _compareBoundaryPoints,
_commonAncestorContainer,
_isCollapsed,
_nodeIndex,
_offsetInCharacters,
_processContents;
function cloneContents() {
return _processContents(this, Range.CLONE_CONTENTS);
}
function cloneRange() {
try {
var clone = new Range(this.ownerDocument);
clone.startContainer = this.startContainer;
clone.startOffset = this.startOffset;
clone.endContainer = this.endContainer;
clone.endOffset = this.endOffset;
clone.collapsed = this.collapsed;
clone.commonAncestorContainer = this.commonAncestorContainer;
clone.detached = this.detached;
return clone;
} catch (e) {
return null;
}
}
function collapse(toStart) {
if (toStart) {
this.endContainer = this.startContainer;
this.endOffset = this.startOffset;
this.collapsed = true;
} else {
this.startContainer = this.endContainer;
this.startOffset = this.endOffset;
this.collapsed = true;
}
}
function compareBoundaryPoints(compareHow, sourceRange) {
try {
var cmnSelf, cmnSource, rootSelf, rootSource;
cmnSelf = this.commonAncestorContainer;
cmnSource = sourceRange.commonAncestorContainer;
rootSelf = cmnSelf;
while (rootSelf.parentNode) {
rootSelf = rootSelf.parentNode;
}
rootSource = cmnSource;
while (rootSource.parentNode) {
rootSource = rootSource.parentNode;
}
switch (compareHow) {
case this.START_TO_START:
return _compareBoundaryPoints(this, this.startContainer, this.startOffset, sourceRange.startContainer, sourceRange.startOffset);
case this.START_TO_END:
return _compareBoundaryPoints(this, this.startContainer, this.startOffset, sourceRange.endContainer, sourceRange.endOffset);
case this.END_TO_END:
return _compareBoundaryPoints(this, this.endContainer, this.endOffset, sourceRange.endContainer, sourceRange.endOffset);
case this.END_TO_START:
return _compareBoundaryPoints(this, this.endContainer, this.endOffset, sourceRange.startContainer, sourceRange.startOffset);
}
} catch (e) {}
return null;
}
function deleteContents() {
try {
_processContents(this, Range.DELETE_CONTENTS);
} catch (e) {}
}
function detach() {
this.detached = true;
}
function extractContents() {
try {
return _processContents(this, Range.EXTRACT_CONTENTS);
} catch (e) {
return null;
}
}
function insertNode(newNode) {
try {
var newText, offset;
switch (this.startContainer.nodeType) {
case Node.CDATA_SECTION_NODE:
case Node.TEXT_NODE:
newText = this.startContainer.splitText(this.startOffset);
this.startContainer.parentNode.insertBefore(newNode, newText);
break;
default:
if (this.startContainer.childNodes.length === 0) {
offset = null;
} else {
offset = this.startContainer.childNodes(this.startOffset);
}
this.startContainer.insertBefore(newNode, offset);
}
} catch (e) {}
}
function selectNode(refNode) {
this.setStartBefore(refNode);
this.setEndAfter(refNode);
}
function selectNodeContents(refNode) {
this.setStart(refNode, 0);
this.setEnd(refNode, refNode.childNodes.length);
}
function setStart(refNode, offset) {
try {
var endRootContainer, startRootContainer;
this.startContainer = refNode;
this.startOffset = offset;
endRootContainer = this.endContainer;
while (endRootContainer.parentNode) {
endRootContainer = endRootContainer.parentNode;
}
startRootContainer = this.startContainer;
while (startRootContainer.parentNode) {
startRootContainer = startRootContainer.parentNode;
}
if (startRootContainer !== endRootContainer) {
this.collapse(true);
} else {
if (_compareBoundaryPoints(this, this.startContainer, this.startOffset, this.endContainer, this.endOffset) > 0) {
this.collapse(true);
}
}
this.collapsed = _isCollapsed(this);
this.commonAncestorContainer = _commonAncestorContainer(this.startContainer, this.endContainer);
} catch (e) {}
}
function setStartAfter(refNode) {
this.setStart(refNode.parentNode, _nodeIndex(refNode) + 1);
}
function setStartBefore(refNode) {
this.setStart(refNode.parentNode, _nodeIndex(refNode));
}
function setEnd(refNode, offset) {
try {
var endRootContainer, startRootContainer;
this.endContainer = refNode;
this.endOffset = offset;
endRootContainer = this.endContainer;
while (endRootContainer.parentNode) {
endRootContainer = endRootContainer.parentNode;
}
startRootContainer = this.startContainer;
while (startRootContainer.parentNode) {
startRootContainer = startRootContainer.parentNode;
}
if (startRootContainer !== endRootContainer) {
this.collapse(false);
} else {
if (_compareBoundaryPoints(this, this.startContainer, this.startOffset, this.endContainer, this.endOffset) > 0) {
this.collapse(false);
}
}
this.collapsed = _isCollapsed(this);
this.commonAncestorContainer = _commonAncestorContainer(this.startContainer, this.endContainer);
} catch (e) {}
}
function setEndAfter(refNode) {
this.setEnd(refNode.parentNode, _nodeIndex(refNode) + 1);
}
function setEndBefore(refNode) {
this.setEnd(refNode.parentNode, _nodeIndex(refNode));
}
function surroundContents(newParent) {
try {
var fragment;
while (newParent.firstChild) {
newParent.removeChild(newParent.firstChild);
}
fragment = this.extractContents();
this.insertNode(newParent);
newParent.appendChild(fragment);
this.selectNode(newParent);
} catch (e) {}
}
_compareBoundaryPoints = function (range, containerA, offsetA, containerB, offsetB) {
var c, offsetC, n, cmnRoot, childA, childB;
if (containerA === containerB) {
if (offsetA === offsetB) {
return 0; // equal
} else if (offsetA < offsetB) {
return -1; // before
} else {
return 1; // after
}
}
c = containerB;
while (c && c.parentNode !== containerA) {
c = c.parentNode;
}
if (c) {
offsetC = 0;
n = containerA.firstChild;
while (n !== c && offsetC < offsetA) {
offsetC++;
n = n.nextSibling;
}
if (offsetA <= offsetC) {
return -1; // before
} else {
return 1; // after
}
}
c = containerA;
while (c && c.parentNode !== containerB) {
c = c.parentNode;
}
if (c) {
offsetC = 0;
n = containerB.firstChild;
while (n !== c && offsetC < offsetB) {
offsetC++;
n = n.nextSibling;
}
if (offsetC < offsetB) {
return -1; // before
} else {
return 1; // after
}
}
cmnRoot = range._commonAncestorContainer(containerA, containerB);
childA = containerA;
while (childA && childA.parentNode !== cmnRoot) {
childA = childA.parentNode;
}
if (!childA) {
childA = cmnRoot;
}
childB = containerB;
while (childB && childB.parentNode !== cmnRoot) {
childB = childB.parentNode;
}
if (!childB) {
childB = cmnRoot;
}
if (childA === childB) {
return 0; // equal
}
n = cmnRoot.firstChild;
while (n) {
if (n === childA) {
return -1; // before
}
if (n === childB) {
return 1; // after
}
n = n.nextSibling;
}
return null;
};
_commonAncestorContainer = function (containerA, containerB) {
var parentStart = containerA, parentEnd;
while (parentStart) {
parentEnd = containerB;
while (parentEnd && parentStart !== parentEnd) {
parentEnd = parentEnd.parentNode;
}
if (parentStart === parentEnd) {
break;
}
parentStart = parentStart.parentNode;
}
if (!parentStart && containerA.ownerDocument) {
return containerA.ownerDocument.documentElement;
}
return parentStart;
};
_isCollapsed = function (range) {
return (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
};
_offsetInCharacters = function (node) {
switch (node.nodeType) {
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.ELEMENT_NODE:
case Node.PROCESSING_INSTRUCTION_NODE:
return true;
default:
return false;
}
};
_processContents = function (range, action) {
try {
var cmnRoot, partialStart = null, partialEnd = null, fragment, n, c, i;
var leftContents, leftParent, leftContentsParent;
var rightContents, rightParent, rightContentsParent;
var next, prev;
var processStart, processEnd;
if (range.collapsed) {
return null;
}
cmnRoot = range.commonAncestorContainer;
if (range.startContainer !== cmnRoot) {
partialStart = range.startContainer;
while (partialStart.parentNode !== cmnRoot) {
partialStart = partialStart.parentNode;
}
}
if (range.endContainer !== cmnRoot) {
partialEnd = range.endContainer;
while (partialEnd.parentNode !== cmnRoot) {
partialEnd = partialEnd.parentNode;
}
}
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
fragment = range.ownerDocument.createDocumentFragment();
}
if (range.startContainer === range.endContainer) {
switch (range.startContainer.nodeType) {
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.TEXT_NODE:
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
c = range.startContainer.cloneNode();
c.deleteData(range.endOffset, range.startContainer.data.length - range.endOffset);
c.deleteData(0, range.startOffset);
fragment.appendChild(c);
}
if (action === Range.EXTRACT_CONTENTS || action === Range.DELETE_CONTENTS) {
range.startContainer.deleteData(range.startOffset, range.endOffset - range.startOffset);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE:
break;
default:
n = range.startContainer.firstChild;
for (i = 0; i < range.startOffset; i++) {
n = n.nextSibling;
}
while (n && i < range.endOffset) {
next = n.nextSibling;
if (action === Range.EXTRACT_CONTENTS) {
fragment.appendChild(n);
} else if (action === Range.CLONE_CONTENTS) {
fragment.appendChild(n.cloneNode());
} else {
range.startContainer.removeChild(n);
}
n = next;
i++;
}
}
range.collapse(true);
return fragment;
}
if (range.startContainer !== cmnRoot) {
switch (range.startContainer.nodeType) {
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.TEXT_NODE:
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
c = range.startContainer.cloneNode(true);
c.deleteData(0, range.startOffset);
leftContents = c;
}
if (action === Range.EXTRACT_CONTENTS || action === Range.DELETE_CONTENTS) {
range.startContainer.deleteData(range.startOffset, range.startContainer.data.length - range.startOffset);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE:
break;
default:
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
leftContents = range.startContainer.cloneNode(false);
}
n = range.startContainer.firstChild;
for (i = 0; i < range.startOffset; i++) {
n = n.nextSibling;
}
while (n && i < range.endOffset) {
next = n.nextSibling;
if (action === Range.EXTRACT_CONTENTS) {
fragment.appendChild(n);
} else if (action === Range.CLONE_CONTENTS) {
fragment.appendChild(n.cloneNode());
} else {
range.startContainer.removeChild(n);
}
n = next;
i++;
}
}
leftParent = range.startContainer.parentNode;
n = range.startContainer.nextSibling;
for (; leftParent !== cmnRoot; leftParent = leftParent.parentNode) {
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
leftContentsParent = leftParent.cloneNode(false);
leftContentsParent.appendChild(leftContents);
leftContents = leftContentsParent;
}
for (; n; n = next) {
next = n.nextSibling;
if (action === Range.EXTRACT_CONTENTS) {
leftContents.appendChild(n);
} else if (action === Range.CLONE_CONTENTS) {
leftContents.appendChild(n.cloneNode(true));
} else {
leftParent.removeChild(n);
}
}
n = leftParent.nextSibling;
}
}
if (range.endContainer !== cmnRoot) {
switch (range.endContainer.nodeType) {
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.TEXT_NODE:
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
c = range.endContainer.cloneNode(true);
c.deleteData(range.endOffset, range.endContainer.data.length - range.endOffset);
rightContents = c;
}
if (action === Range.EXTRACT_CONTENTS || action === Range.DELETE_CONTENTS) {
range.endContainer.deleteData(0, range.endOffset);
}
break;
case Node.PROCESSING_INSTRUCTION_NODE:
break;
default:
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
rightContents = range.endContainer.cloneNode(false);
}
n = range.endContainer.firstChild;
if (n && range.endOffset) {
for (i = 0; i + 1 < range.endOffset; i++) {
next = n.nextSibling;
if (!next) {
break;
}
n = next;
}
for (; n; n = prev) {
prev = n.previousSibling;
if (action === Range.EXTRACT_CONTENTS) {
rightContents.insertBefore(n, rightContents.firstChild);
} else if (action === Range.CLONE_CONTENTS) {
rightContents.insertBefore(n.cloneNode(true), rightContents.firstChild);
} else {
range.endContainer.removeChild(n);
}
}
}
}
rightParent = range.endContainer.parentNode;
n = range.endContainer.previousSibling;
for (; rightParent !== cmnRoot; rightParent = rightParent.parentNode) {
if (action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) {
rightContentsParent = rightContents.cloneNode(false);
rightContentsParent.appendChild(rightContents);
rightContents = rightContentsParent;
}
for (; n; n = prev) {
prev = n.previousSibling;
if (action === Range.EXTRACT_CONTENTS) {
rightContents.insertBefore(n, rightContents.firstChild);
} else if (action === Range.CLONE_CONTENTS) {
rightContents.appendChild(n.cloneNode(true), rightContents.firstChild);
} else {
rightParent.removeChild(n);
}
}
n = rightParent.previousSibling;
}
}
if (range.startContainer === cmnRoot) {
processStart = range.startContainer.firstChild;
for (i = 0; i < range.startOffset; i++) {
processStart = processStart.nextSibling;
}
} else {
processStart = range.startContainer;
while (processStart.parentNode !== cmnRoot) {
processStart = processStart.parentNode;
}
processStart = processStart.nextSibling;
}
if (range.endContainer === cmnRoot) {
processEnd = range.endContainer.firstChild;
for (i = 0; i < range.endOffset; i++) {
processEnd = processEnd.nextSibling;
}
} else {
processEnd = range.endContainer;
while (processEnd.parentNode !== cmnRoot) {
processEnd = processEnd.parentNode;
}
}
if ((action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) && leftContents) {
fragment.appendChild(leftContents);
}
if (processStart) {
for (n = processStart; n && n !== processEnd; n = next) {
next = n.nextSibling;
if (action === Range.EXTRACT_CONTENTS) {
fragment.appendChild(n);
} else if (action === Range.CLONE_CONTENTS) {
fragment.appendChild(n.cloneNode(true));
} else {
cmnRoot.removeChild(n);
}
}
}
if ((action === Range.EXTRACT_CONTENTS || action === Range.CLONE_CONTENTS) && rightContents) {
fragment.appendChild(rightContents);
}
if (action === Range.EXTRACT_CONTENTS || action === Range.DELETE_CONTENTS) {
if (!partialStart && !partialEnd) {
range.collapse(true);
} else if (partialStart) {
range.startContainer = partialStart.parentNode;
range.endContainer = partialStart.parentNode;
range.startOffset = range.endOffset = range._nodeIndex(partialStart) + 1;
} else if (partialEnd) {
range.startContainer = partialEnd.parentNode;
range.endContainer = partialEnd.parentNode;
range.startOffset = range.endOffset = range._nodeIndex(partialEnd);
}
}
return fragment;
} catch (e) {
return null;
}
};
_nodeIndex = function (refNode) {
var nodeIndex = 0;
while (refNode.previousSibling) {
nodeIndex++;
refNode = refNode.previousSibling;
}
return nodeIndex;
};
return {
setStart: setStart,
setEnd: setEnd,
setStartBefore: setStartBefore,
setStartAfter: setStartAfter,
setEndBefore: setEndBefore,
setEndAfter: setEndAfter,
collapse: collapse,
selectNode: selectNode,
selectNodeContents: selectNodeContents,
compareBoundaryPoints: compareBoundaryPoints,
deleteContents: deleteContents,
extractContents: extractContents,
cloneContents: cloneContents,
insertNode: insertNode,
surroundContents: surroundContents,
cloneRange: cloneRange,
toString: toString,
detach: detach,
_commonAncestorContainer: _commonAncestorContainer
};
}()));
}
if (!window.getSelection) {
window.getSelection = function () {
return Selection.getInstance();
};
var SelectionImpl = function () {
this.anchorNode = null;
this.anchorOffset = 0;
this.focusNode = null;
this.focusOffset = 0;
this.isCollapsed = true;
this.rangeCount = 0;
this.ranges = [];
};
Object.extend(SelectionImpl.prototype, (function () {
var getNextTextNode, getPreviousTextNode;
function addRange(r) {
return true;
}
function collapse() {
return true;
}
function collapseToStart() {
return true;
}
function collapseToEnd() {
return true;
}
function getRangeAt() {
return true;
}
function removeAllRanges() {
this.anchorNode = null;
this.anchorOffset = 0;
this.focusNode = null;
this.focusOffset = 0;
this.isCollapsed = true;
this.rangeCount = 0;
this.ranges = [];
}
function _addRange(r) {
var start, startOffset;
if (r.startContainer.nodeType !== Node.TEXT_NODE) {
start = this._getRightStart(r.startContainer);
startOffset = 0;
} else {
start = r.startContainer;
startOffset = r.startOffset;
}
var end, endOffset;
if (r.endContainer.nodeType !== Node.TEXT_NODE) {
end = this._getRightEnd(r.endContainer);
endOffset = end.data.length;
} else {
end = r.endContainer;
endOffset = r.endOffset;
}
var rStart = this._selectStart(start, startOffset);
var rEnd = this._selectEnd(end, endOffset);
rStart.setEndPoint('EndToStart', rEnd);
rStart.select();
document.selection._selectedRange = r;
}
function _getRightStart(start, offset) {
if (start.nodeType !== Node.TEXT_NODE) {
if (start.nodeType === Node.ELEMENT_NODE) {
start = start.childNodes(offset);
}
return getNextTextNode(start);
} else {
return null;
}
}
function _getRightEnd(end, offset) {
if (end.nodeType !== Node.TEXT_NODE) {
if (end.nodeType === Node.ELEMENT_NODE) {
end = end.childNodes(offset);
}
return getPreviousTextNode(end);
} else {
return null;
}
}
function _selectStart(start, offset) {
var r = document.body.createTextRange();
if (start.nodeType === Node.TEXT_NODE) {
var moveCharacters = offset, node = start;
var moveToNode = null, collapse = true;
while (node.previousSibling) {
switch (node.previousSibling.nodeType) {
case Node.ELEMENT_NODE:
moveToNode = node.previousSibling;
collapse = false;
break;
case Node.TEXT_NODE:
moveCharacters += node.previousSibling.data.length;
break;
}
if (moveToNode !== null) {
break;
}
node = node.previousSibling;
}
if (moveToNode === null) {
moveToNode = start.parentNode;
}
r.moveToElementText(moveToNode);
r.collapse(collapse);
r.move('Character', moveCharacters);
return r;
} else {
return null;
}
}
function _selectEnd(end, offset) {
var r = document.body.createTextRange(), node = end;
if (end.nodeType === 3) {
var moveCharacters = end.data.length - offset;
var moveToNode = null, collapse = false;
while (node.nextSibling) {
switch (node.nextSibling.nodeType) {
case Node.ELEMENT_NODE:
moveToNode = node.nextSibling;
collapse = true;
break;
case Node.TEXT_NODE:
moveCharacters += node.nextSibling.data.length;
break;
}
if (moveToNode !== null) {
break;
}
node = node.nextSibling;
}
if (moveToNode === null) {
moveToNode = end.parentNode;
collapse = false;
}
switch (moveToNode.nodeName.toLowerCase()) {
case 'p':
case 'div':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
moveCharacters++;
break;
}
r.moveToElementText(moveToNode);
r.collapse(collapse);
r.move('Character', -moveCharacters);
return r;
}
return null;
}
getPreviousTextNode = function (node) {
var stack = [];
var current = null;
while (node) {
stack = [];
current = node;
while (current) {
while (current) {
if (current.nodeType === 3 && current.data.replace(/^\s+|\s+$/, '').length) {
return current;
}
if (current.previousSibling) {
stack.push(current.previousSibling);
}
current = current.lastChild;
}
current = stack.pop();
}
node = node.previousSibling;
}
return null;
};
getNextTextNode = function (node) {
var stack = [];
var current = null;
while (node) {
stack = [];
current = node;
while (current) {
while (current) {
if (current.nodeType === 3 && current.data.replace(/^\s+|\s+$/, '').length) {
return current;
}
if (current.nextSibling) {
stack.push(current.nextSibling);
}
current = current.firstChild;
}
current = stack.pop();
}
node = node.nextSibling;
}
return null;
};
return {
addRange: addRange,
collapse: collapse,
collapseToEnd: collapseToEnd,
collapseToStart: collapseToStart,
getRangeAt: getRangeAt,
removeAllRanges: removeAllRanges,
_addRange: _addRange,
_getRightStart: _getRightStart,
_getRightEnd: _getRightEnd,
_selectStart: _selectStart,
_selectEnd: _selectEnd
};
}()));
window.Selection = function () {
var instance = null;
this.getInstance = function () {
if (instance === null) {
return (instance = new SelectionImpl());
} else {
return instance;
}
};
};
}
Object.extend(Range.prototype, (function () {
function getNode() {
var node = this.commonAncestorContainer;
if (this.startContainer === this.endContainer) {
if (this.startOffset - this.endOffset < 2) {
node = this.startContainer.childNodes[this.startOffset];
}
}
while (node.nodeType === Node.TEXT_NODE) {
node = node.parentNode;
}
return node;
}
return {
getNode: getNode
};
}()));
WysiHat.Selection = Class.create((function () {
var compareRanges, createRangeFromElement, restoreRange, saveRange;
function initialize(editor) {
this.window = editor.getWindow();
this.document = editor.getDocument();
if (Prototype.Browser.IE) {
editor.observe('wysihat:cursormove', saveRange.bind(this));
editor.observe('wysihat:focus', restoreRange);
}
}
function getSelection() {
return this.window.getSelection ? this.window.getSelection() : this.document.selection;
}
function getRange() {
var range = null, selection = this.getSelection();
try {
if (selection.getRangeAt) {
range = selection.getRangeAt(0);
} else {
range = selection.createRange();
}
} catch (e) { return null; }
if (Prototype.Browser.WebKit) {
range.setStart(selection.baseNode, selection.baseOffset);
range.setEnd(selection.extentNode, selection.extentOffset);
}
return range;
}
function selectNode(node) {
var selection = this.getSelection();
var range;
if (Prototype.Browser.IE) {
range = createRangeFromElement(this.document, node);
range.select();
} else if (Prototype.Browser.WebKit) {
selection.setBaseAndExtent(node, 0, node, node.innerText.length);
} else if (Prototype.Browser.Opera) {
range = this.document.createRange();
range.selectNode(node);
selection.removeAllRanges();
selection.addRange(range);
} else {
range = createRangeFromElement(this.document, node);
selection.removeAllRanges();
selection.addRange(range);
}
}
function getNode() {
var nodes = null, candidates = [], children, el;
var range = this.getRange();
if (!range) {
return null;
}
var parent;
if (range.parentElement) {
parent = range.parentElement();
} else {
parent = range.commonAncestorContainer;
}
if (parent) {
while (parent.nodeType !== 1) {
parent = parent.parentNode;
}
if (parent.nodeName.toLowerCase() !== "body") {
el = parent;
do {
el = el.parentNode;
candidates[candidates.length] = el;
} while (el.nodeName.toLowerCase() !== "body");
}
children = parent.all || parent.getElementsByTagName("*");
var j;
for (j = 0; j < children.length; j++) {
candidates[candidates.length] = children[j];
}
nodes = [parent];
var ii, r2;
for (ii = 0; ii < candidates.length; ii++) {
r2 = createRangeFromElement(this.document, candidates[ii]);
if (r2 && compareRanges(range, r2)) {
nodes[nodes.length] = candidates[ii];
}
}
}
return nodes.first();
}
createRangeFromElement = function (document, node) {
var range;
if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(node);
} else if (document.createRange) {
range = document.createRange();
range.selectNodeContents(node);
}
return range;
};
compareRanges = function (r1, r2) {
if (r1.compareEndPoints) {
return !(
r2.compareEndPoints('StartToStart', r1) === 1 &&
r2.compareEndPoints('EndToEnd', r1) === 1 &&
r2.compareEndPoints('StartToEnd', r1) === 1 &&
r2.compareEndPoints('EndToStart', r1) === 1
||
r2.compareEndPoints('StartToStart', r1) === -1 &&
r2.compareEndPoints('EndToEnd', r1) === -1 &&
r2.compareEndPoints('StartToEnd', r1) === -1 &&
r2.compareEndPoints('EndToStart', r1) === -1
);
} else if (r1.compareBoundaryPoints) {
return !(
r2.compareBoundaryPoints(0, r1) === 1 &&
r2.compareBoundaryPoints(2, r1) === 1 &&
r2.compareBoundaryPoints(1, r1) === 1 &&
r2.compareBoundaryPoints(3, r1) === 1
||
r2.compareBoundaryPoints(0, r1) === -1 &&
r2.compareBoundaryPoints(2, r1) === -1 &&
r2.compareBoundaryPoints(1, r1) === -1 &&
r2.compareBoundaryPoints(3, r1) === -1
);
}
return null;
};
function setBookmark() {
var bookmark = this.document.getElementById('bookmark');
if (bookmark) {
bookmark.parentNode.removeChild(bookmark);
}
bookmark = this.document.createElement('span');
bookmark.id = 'bookmark';
bookmark.innerHTML = '&nbsp;';
var range;
if (Prototype.Browser.IE) {
range = this.document.selection.createRange();
var parent = this.document.createElement('div');
parent.appendChild(bookmark);
range.collapse();
range.pasteHTML(parent.innerHTML);
} else {
range = this.getRange();
range.insertNode(bookmark);
}
}
function moveToBookmark() {
var bookmark = this.document.getElementById('bookmark');
if (!bookmark) {
return;
}
var range;
if (Prototype.Browser.IE) {
range = this.getRange();
range.moveToElementText(bookmark);
range.collapse();
range.select();
} else if (Prototype.Browser.WebKit) {
var selection = this.getSelection();
selection.setBaseAndExtent(bookmark, 0, bookmark, 0);
} else {
range = this.getRange();
range.setStartBefore(bookmark);
}
bookmark.parentNode.removeChild(bookmark);
}
var savedRange = null;
saveRange = function () {
savedRange = this.getRange();
};
restoreRange = function () {
if (savedRange) {
savedRange.select();
}
};
return {
initialize: initialize,
getSelection: getSelection,
getRange: getRange,
getNode: getNode,
selectNode: selectNode,
setBookmark: setBookmark,
moveToBookmark: moveToBookmark,
restore: restoreRange
};
}()));
WysiHat.Toolbar = Class.create((function () {
function initialize(editor) {
this.editor = editor;
this.element = this.createToolbarElement();
}
function createToolbarElement() {
var toolbar = new Element('div', { 'class': 'editor_toolbar' });
if (this.editor) {
this.editor.insert({before: toolbar});
}
return toolbar;
}
function addButtonSet(set) {
var toolbar = this;
$A(set).each(function (button) {
toolbar.addButton(button);
});
}
function addGroup() {
var el = new Element('div', { 'class': 'group' });
this.element.insert(el);
this.active_group = el;
this.active_row = null;
el.addDropdown = function () {
var dropdown = new Element('div', { 'class': 'dropdown' })
.setStyle({ 'display': 'none' });
el.insert(dropdown);
el.active_dropdown = dropdown;
var toggle = new Element('a', {
'class': 'toggle-main',
'href': 'javascript:;'
});
toggle.setStyle({ 'display': 'inline' });
toggle.observe('click', function () {
dropdown.setStyle({ 'display': (dropdown.getStyle('display') === 'none' ? 'block' : 'none') });
});
el.insert(toggle);
};
return el;
}
function addRow() {
var el = new Element('div');
this.active_group.insert(el);
this.active_row = el;
return el;
}
function addControl(options) {
options = $H(options);
if (!options.get('name')) {
options.set('name', options.get('label').toLowerCase());
}
var name = options.get('name');
if (!this.active_group) {
this.addGroup();
}
if (!options.get('main') && !this.active_row) {
this.addRow();
}
var container = options.get('main')
? (this.active_group.active_dropdown ? this.active_group.active_dropdown : this.active_group)
: this.active_row;
var el;
if (options.get('element')) {
el = options.get('element');
} else {
el = options.get('main') ? this.createMainButtonElement(options) : this.createButtonElement(options);
}
container.insert(el);
var handler;
if (options.get('handler') !== false) {
handler = this.buttonHandler(name, options);
this.observeButtonClick(el, handler);
}
handler = this.buttonStateHandler(name, options);
this.observeStateChanges(el, name, handler);
return {
name: name,
handler: handler,
toolbar: this,
element: el
};
}
function createMainButtonElement(options) {
var el = new Element('a', {
'class': 'button-main',
'href': 'javascript:;'
});
el.update('<span>' + options.get('label') + '</span>');
el.addClassName(options.get('name'));
return el;
}
function createButtonElement(options) {
var el = new Element('a', {
'class': 'button',
'href': 'javascript:;',
'title': options.get('label')
});
el.addClassName(options.get('name'));
return el;
}
function buttonHandler(name, options) {
if (options.handler) {
return options.handler;
} else if (options.get('handler')) {
return options.get('handler');
} else {
return function (editor) {
if (!editor) {
return;
}
editor.execCommand(name);
};
}
}
function observeButtonClick(element, handler) {
var toolbar = this;
element.observe('click', function (event) {
if (toolbar.editor) {
handler(toolbar.editor);
toolbar.editor.fire("wysihat:change");
toolbar.editor.fire("wysihat:cursormove");
}
Event.stop(event);
});
}
function buttonStateHandler(name, options) {
if (options.query) {
return options.query;
} else if (options.get('query')) {
return options.get('query');
} else {
return function (editor) {
if (!editor) {
return;
}
return editor.queryCommandState(name);
};
}
}
function observeStateChanges(element, name, handler) {
if (!this.editor) {
return;
}
var toolbar = this;
var previousState = false;
var fx = function (event) {
var state = handler(toolbar.editor);
if (state !== previousState) {
previousState = state;
toolbar.updateButtonState(element, name, state);
}
};
toolbar.editor.observe("wysihat:cursormove", fx);
toolbar.editor.observe("keyup", fx);
}
function updateButtonState(element, name, state) {
if (state) {
element.addClassName('selected');
} else {
element.removeClassName('selected');
}
}
return {
initialize: initialize,
createToolbarElement: createToolbarElement,
addButtonSet: addButtonSet,
addGroup: addGroup,
addRow: addRow,
addControl: addControl,
createButtonElement: createButtonElement,
createMainButtonElement: createMainButtonElement,
buttonHandler: buttonHandler,
observeButtonClick: observeButtonClick,
buttonStateHandler: buttonStateHandler,
observeStateChanges: observeStateChanges,
updateButtonState: updateButtonState
};
}()));
WysiHat.Toolbar.ButtonSets = {};
WysiHat.Toolbar.ButtonSets.Basic = $A([
{ label: "Bold" },
{ label: "Underline" },
{ label: "Italic" }
]);
window.WysiHat = WysiHat;
}(window));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment