Skip to content

Instantly share code, notes, and snippets.

@pezholio
Created April 30, 2012 10:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pezholio/2556956 to your computer and use it in GitHub Desktop.
Save pezholio/2556956 to your computer and use it in GitHub Desktop.
jq-wysihat fixes for IE

jq-wysihat is a great tool for wysiwyg if you want to sidestep ugly wysiwyg editors and roll your own. Unfortunately in my experience it wasn't working in IE.

After some fiddling, I managed to get it to work by removing the IE range stuff and including selection.js instead. It now works like a charm!

/* WysiHat - WYSIWYG JavaScript framework, version 0.2.1
* (c) 2008-2010 Joshua Peek
* JQ-WysiHat - jQuery port of WysiHat to run on jQuery
* (c) 2010 Scott Williams
*
* WysiHat is freely distributable under the terms of an MIT-style license.
*--------------------------------------------------------------------------*/
(function ($, window, undefined)
{
var WysiHat = {};
WysiHat.Editor = {
attach: function($textarea) {
var $editArea;
var id = $textarea.attr('id') + '_editor';
if ($editArea == $('#' + id)) { return $editArea; }
$editArea = $('<div id="' + id + '" class="editor" contentEditable="true"></div>');
$editArea.html(WysiHat.Formatting.getBrowserMarkupFrom($textarea.val()));
$.extend($editArea, WysiHat.Commands);
$textarea.before($editArea);
$textarea.hide();
return $editArea;
}
};
WysiHat.BrowserFeatures = (function() {
function createTmpIframe(callback) {
var frame, frameDocument;
frame = $('<iframe></iframe>');
frame.css({
position: 'absolute',
left: '-1000px'
});
frame.onFrameLoaded(function() {
if (typeof frame.contentDocument !== 'undefined') {
frameDocument = frame.contentDocument;
} else if (typeof frame.contentWindow !== 'undefined' && typeof frame.contentWindow.document !== 'undefined') {
frameDocument = frame.contentWindow.document;
}
frameDocument.designMode = 'on';
callback(frameDocument);
frame.remove();
});
$(document.body).insert(frame);
}
var features = {};
function detectParagraphType(document) {
document.body.innerHTML = '';
document.execCommand('insertparagraph', false, null);
var tagName;
element = document.body.childNodes[0];
if (element && element.tagName)
tagName = element.tagName.toLowerCase();
if (tagName == 'div')
features.paragraphType = "div";
else if (document.body.innerHTML == "<p><br></p>")
features.paragraphType = "br";
else
features.paragraphType = "p";
}
function detectIndentType(document) {
document.body.innerHTML = 'tab';
document.execCommand('indent', false, null);
var tagName;
element = document.body.childNodes[0];
if (element && element.tagName)
tagName = element.tagName.toLowerCase();
features.indentInsertsBlockquote = (tagName == 'blockquote');
}
features.run = function run() {
if (features.finished) return;
createTmpIframe(function(document) {
detectParagraphType(document);
detectIndentType(document);
features.finished = true;
});
}
return features;
})();
if (typeof Selection == 'undefined') {
var Selection = {}
}
$.extend(Selection.prototype, (function() {
function getNode() {
if (this.rangeCount > 0)
return this.getRangeAt(0).getNode();
else
return null;
}
function selectNode(element) {
var range = document.createRange();
range.selectNode(element[0]);
this.removeAllRanges();
this.addRange(range);
}
return {
getNode: getNode,
selectNode: selectNode
}
})());
$(document).ready(function() {
function fieldChangeHandler(event, element) {
var $element = $(element);
element = $element.get(0);
var value;
if ($element.attr('contentEditable') === 'true') {
value = $element.html();
}
value = $element.val();
if (value && element.previousValue != value) {
$element.trigger("field:change");
element.previousValue = value;
}
}
$('input,textarea,*[contenteditable=""],*[contenteditable=true]').keyup(fieldChangeHandler);
});
WysiHat.Commands = (function(window) {
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 indentSelection() {
if ($.browser.mozilla) {
var selection, range, node, blockquote;
selection = getSelection();
range = selection.getRangeAt(0);
node = selection.getNode();
if (range.collapsed) {
range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
}
blockquote = $('<blockquote></blockquote>');
range = selection.getRangeAt(0);
range.surroundContents(blockquote);
} else {
this.execCommand('indent', false, null);
}
}
function outdentSelection() {
this.execCommand('outdent', false, null);
}
function toggleIndentation() {
if (this.indentSelected()) {
this.outdentSelection();
} else {
this.indentSelection();
}
}
function indentSelected() {
var node = getSelection().getNode();
return node.is("blockquote, blockquote *");
}
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($.browser.mozilla) {
this.execCommand('hilitecolor', false, color);
} else {
this.execCommand('backcolor', false, color);
}
}
function alignSelection(alignment) {
this.execCommand('justify' + alignment);
}
function alignSelected() {
var node = getSelection().getNode();
return $(node).css('textAlign');
}
function linkSelection(url) {
this.execCommand('createLink', false, url);
}
function unlinkSelection() {
var node = getSelection().getNode();
if (this.linkSelected())
getSelection().selectNode(node);
this.execCommand('unlink', false, null);
}
function linkSelected() {
var node = getSelection().getNode();
return node ? node.get(0).tagName.toUpperCase() == 'A' : false;
}
function formatblockSelection(element){
this.execCommand('formatblock', false, element);
}
function toggleOrderedList() {
var selection, node;
selection = getSelection();
node = selection.getNode();
if (this.orderedListSelected() && !node.is("ol li:last-child, ol li:last-child *")) {
selection.selectNode(node.parent("ol"));
} else if (this.unorderedListSelected()) {
selection.selectNode(node.parent("ul"));
}
this.execCommand('insertorderedlist', false, null);
}
function insertOrderedList() {
this.toggleOrderedList();
}
function orderedListSelected() {
var element = getSelection().getNode();
if (element) return element.is('*[contenteditable=""] ol, *[contenteditable=true] ol, *[contenteditable=""] ol *, *[contenteditable=true] ol *');
return false;
}
function toggleUnorderedList() {
var selection, node;
selection = getSelection();
node = selection.getNode();
if (this.unorderedListSelected() && !node.is("ul li:last-child, ul li:last-child *")) {
selection.selectNode(node.parent("ul"));
} else if (this.orderedListSelected()) {
selection.selectNode(node.parent("ol"));
}
this.execCommand('insertunorderedlist', false, null);
}
function insertUnorderedList() {
this.toggleUnorderedList();
}
function unorderedListSelected() {
var element = getSelection().getNode();
if (element) return element.is('*[contenteditable=""] ul, *[contenteditable=true] ul, *[contenteditable=""] ul *, *[contenteditable=true] ul *');
return false;
}
function insertImage(url) {
this.execCommand('insertImage', false, url);
}
function insertHTML(html) {
if ($.browser.msie) {
var range = window.document.selection.createRange();
range.pasteHTML(html);
range.collapse(false);
range.select();
} else {
this.execCommand('insertHTML', false, html);
}
}
function execCommand(command, ui, value) {
var handler = this.commands[command];
if (handler) {
handler.bind(this)(value);
} else {
try {
window.document.execCommand(command, ui, value);
} catch(e) { return null; }
}
$(document.activeElement).trigger("field:change");
}
function queryCommandState(state) {
var handler = this.queryCommands[state];
if (handler) {
return handler();
} else {
try {
return window.document.queryCommandState(state);
} catch(e) { return null; }
}
}
function getSelectedStyles() {
var styles = {};
var editor = this;
editor.styleSelectors.each(function(style){
var node = editor.selection.getNode();
styles[style.first()] = $(node).css(style.last());
});
return styles;
}
return {
boldSelection: boldSelection,
boldSelected: boldSelected,
underlineSelection: underlineSelection,
underlineSelected: underlineSelected,
italicSelection: italicSelection,
italicSelected: italicSelected,
strikethroughSelection: strikethroughSelection,
indentSelection: indentSelection,
outdentSelection: outdentSelection,
toggleIndentation: toggleIndentation,
indentSelected: indentSelected,
fontSelection: fontSelection,
fontSizeSelection: fontSizeSelection,
colorSelection: colorSelection,
backgroundColorSelection: backgroundColorSelection,
alignSelection: alignSelection,
alignSelected: alignSelected,
linkSelection: linkSelection,
unlinkSelection: unlinkSelection,
linkSelected: linkSelected,
formatblockSelection: formatblockSelection,
toggleOrderedList: toggleOrderedList,
insertOrderedList: insertOrderedList,
orderedListSelected: orderedListSelected,
toggleUnorderedList: toggleUnorderedList,
insertUnorderedList: insertUnorderedList,
unorderedListSelected: unorderedListSelected,
insertImage: insertImage,
insertHTML: insertHTML,
execCommand: execCommand,
queryCommandState: queryCommandState,
getSelectedStyles: getSelectedStyles,
commands: {},
queryCommands: {
link: linkSelected,
orderedlist: orderedListSelected,
unorderedlist: unorderedListSelected
},
styleSelectors: {
fontname: 'fontFamily',
fontsize: 'fontSize',
forecolor: 'color',
hilitecolor: 'backgroundColor',
backcolor: 'backgroundColor'
}
};
})(window);
if ($.browser.msie) {
$.extend(Selection.prototype, (function() {
function setBookmark() {
var bookmark = $('#bookmark');
if (bookmark) bookmark.remove();
bookmark = $('<span id="bookmark">&nbsp;</span>');
var parent = $('<div></div>').html(bookmark);
var range = this._document.selection.createRange();
range.collapse();
range.pasteHTML(parent.html());
}
function moveToBookmark() {
var bookmark = $('#bookmark');
if (!bookmark) return;
var range = this._document.selection.createRange();
range.moveToElementText(bookmark);
range.collapse();
range.select();
bookmark.remove();
}
return {
setBookmark: setBookmark,
moveToBookmark: moveToBookmark
}
})());
} else {
$.extend(Selection.prototype, (function() {
function setBookmark() {
var bookmark = $('#bookmark');
if (bookmark) bookmark.remove();
bookmark = $('<span id="bookmark">&nbsp;</span>');
this.getRangeAt(0).insertNode(bookmark);
}
function moveToBookmark() {
var bookmark = $('#bookmark');
if (!bookmark) return;
var range = document.createRange();
range.setStartBefore(bookmark);
this.removeAllRanges();
this.addRange(range);
bookmark.remove();
}
return {
setBookmark: setBookmark,
moveToBookmark: moveToBookmark
}
})());
}
(function() {
function cloneWithAllowedAttributes(element, allowedAttributes) {
var length = allowedAttributes.length, i;
var result = $('<' + element.tagName.toLowerCase() + '></' + element.tagName.toLowerCase() + '>')
element = $(element);
for (i = 0; i < allowedAttributes.length; i++) {
attribute = allowedAttributes[i];
if (element.attr(attribute)) {
result.attr(attribute, element.attr(attribute));
}
}
return result;
}
function withEachChildNodeOf(element, callback) {
var nodes = $(element).children;
var length = nodes.length, i;
for (i = 0; i < length; i++) callback(nodes[i]);
}
function sanitizeNode(node, tagsToRemove, tagsToAllow, tagsToSkip) {
var parentNode = node.parentNode;
switch (node.nodeType) {
case Node.ELEMENT_NODE:
var tagName = node.tagName.toLowerCase();
if (tagsToSkip) {
var newNode = node.cloneNode(false);
withEachChildNodeOf(node, function(childNode) {
newNode.appendChild(childNode);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
parentNode.insertBefore(newNode, node);
} else if (tagName in tagsToAllow) {
var newNode = cloneWithAllowedAttributes(node, tagsToAllow[tagName]);
withEachChildNodeOf(node, function(childNode) {
newNode.appendChild(childNode);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
parentNode.insertBefore(newNode, node);
} else if (!(tagName in tagsToRemove)) {
withEachChildNodeOf(node, function(childNode) {
parentNode.insertBefore(childNode, node);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
}
case Node.COMMENT_NODE:
parentNode.removeChild(node);
}
}
$.fn.sanitizeContents = function(options) {
var element = $(this);
var tagsToRemove = {};
$.each((options.remove || "").split(","), function(tagName) {
tagsToRemove[$.trim(tagName)] = true;
});
var tagsToAllow = {};
$.each((options.allow || "").split(","), function(selector) {
var parts = $.trim(selector).split(/[\[\]]/);
var tagName = parts[0];
var allowedAttributes = $.grep(parts.slice(1), function(n, i) {
return /./.test(n);
});
tagsToAllow[tagName] = allowedAttributes;
});
var tagsToSkip = options.skip;
withEachChildNodeOf(element, function(childNode) {
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
return element;
}
})();
(function() {
function onReadyStateComplete(document, callback) {
function checkReadyState() {
if (document.readyState === 'complete') {
$(document).unbind('readystatechange', checkReadyState);
callback();
return true;
} else {
return false;
}
}
$(document).bind('readystatechange', checkReadyState);
checkReadyState();
}
function observeFrameContentLoaded(element) {
element = $(element);
var bare = element.get(0);
var loaded, contentLoadedHandler;
loaded = false;
function fireFrameLoaded() {
if (loaded) { return };
loaded = true;
if (contentLoadedHandler) { contentLoadedHandler.stop(); }
element.trigger('frame:loaded');
}
if (window.addEventListener) {
contentLoadedHandler = $(document).bind("DOMFrameContentLoaded", function(event) {
if (element == $(this))
fireFrameLoaded();
});
}
element.load(function() {
var frameDocument;
if (typeof element.contentDocument !== 'undefined') {
frameDocument = element.contentDocument;
} else if (typeof element.contentWindow !== 'undefined' && typeof element.contentWindow.document !== 'undefined') {
frameDocument = element.contentWindow.document;
}
onReadyStateComplete(frameDocument, fireFrameLoaded);
});
return element;
}
function onFrameLoaded($element, callback) {
$element.bind('frame:loaded', callback);
$element.observeFrameContentLoaded();
}
$.fn.observeFrameContentLoaded = observeFrameContentLoaded;
$.fn.onFrameLoaded = onFrameLoaded;
})();
$(document).ready(function() {
var $doc = $(document);
if ('selection' in document && 'onselectionchange' in document) {
var selectionChangeHandler = function() {
var range = document.selection.createRange();
var element = range.parentElement();
$(element).trigger("selection:change");
}
$doc.bind("selectionchange", selectionChangeHandler);
} else {
var previousRange;
var selectionChangeHandler = function() {
var element = document.activeElement;
var elementTagName = element.tagName.toLowerCase();
if (elementTagName == "textarea" || elementTagName == "input") {
previousRange = null;
$(element).trigger("selection:change");
} else {
var selection = getSelection();
if (selection.rangeCount < 1) { return };
var range = selection.getRangeAt(0);
if (range && range.equalRange(previousRange)) {
return;
}
previousRange = range;
element = range.commonAncestorContainer;
while (element.nodeType == Node.TEXT_NODE)
element = element.parentNode;
$(element).trigger("selection:change");
}
};
$doc.mouseup(selectionChangeHandler);
$doc.keyup(selectionChangeHandler);
}
});
WysiHat.Formatting = (function() {
var ACCUMULATING_LINE = {};
var EXPECTING_LIST_ITEM = {};
var ACCUMULATING_LIST_ITEM = {};
return {
getBrowserMarkupFrom: function(applicationMarkup) {
var container = $("<div>" + applicationMarkup + "</div>");
function spanify(element, style) {
$(element).replaceWith(
'<span style="' + style +
'" class="Apple-style-span">' +
element.innerHTML + '</span>'
);
}
function convertStrongsToSpans() {
container.find("strong").each(function(element) {
spanify(element, "font-weight: bold");
});
}
function convertEmsToSpans() {
container.find("em").each(function(element) {
spanify(element, "font-style: italic");
});
}
function convertDivsToParagraphs() {
container.find("div").each(function(element) {
element.replaceWith("<p>" + element.innerHTML + "</p>");
});
}
if ($.browser.webkit || $.browser.mozilla) {
convertStrongsToSpans();
convertEmsToSpans();
} else if ($.browser.msie || $.browser.opera) {
convertDivsToParagraphs();
}
return container.innerHTML;
},
getApplicationMarkupFrom: function(element) {
var mode = ACCUMULATING_LINE, result, container, line, lineContainer, previousAccumulation;
function walk(nodes) {
var length = nodes.length, node, tagName, i;
for (i = 0; i < length; i++) {
node = nodes[i];
if (node.nodeType == Node.ELEMENT_NODE) {
tagName = node.tagName.toLowerCase();
open(tagName, node);
walk(node.childNodes);
close(tagName);
} else if (node.nodeType == Node.TEXT_NODE) {
read(node.nodeValue);
}
}
}
function open(tagName, node) {
if (mode == ACCUMULATING_LINE) {
if (isBlockElement(tagName)) {
if (isEmptyParagraph(node)) {
accumulate($("<br />"));
}
flush();
if (isListElement(tagName)) {
container = insertList(tagName);
mode = EXPECTING_LIST_ITEM;
}
} else if (isLineBreak(tagName)) {
if (isLineBreak(getPreviouslyAccumulatedTagName())) {
previousAccumulation.parentNode.removeChild(previousAccumulation);
flush();
}
accumulate(node.cloneNode(false));
if (!previousAccumulation.previousNode) flush();
} else {
accumulateInlineElement(tagName, node);
}
} else if (mode == EXPECTING_LIST_ITEM) {
if (isListItemElement(tagName)) {
mode = ACCUMULATING_LIST_ITEM;
}
} else if (mode == ACCUMULATING_LIST_ITEM) {
if (isLineBreak(tagName)) {
accumulate(node.cloneNode(false));
} else if (!isBlockElement(tagName)) {
accumulateInlineElement(tagName, node);
}
}
}
function close(tagName) {
if (mode == ACCUMULATING_LINE) {
if (isLineElement(tagName)) {
flush();
}
if (line != lineContainer) {
lineContainer = lineContainer.parentNode;
}
} else if (mode == EXPECTING_LIST_ITEM) {
if (isListElement(tagName)) {
container = result;
mode = ACCUMULATING_LINE;
}
} else if (mode == ACCUMULATING_LIST_ITEM) {
if (isListItemElement(tagName)) {
flush();
mode = EXPECTING_LIST_ITEM;
}
if (line != lineContainer) {
lineContainer = lineContainer.parentNode;
}
}
}
function isBlockElement(tagName) {
return isLineElement(tagName) || isListElement(tagName);
}
function isLineElement(tagName) {
return tagName == "p" || tagName == "div";
}
function isListElement(tagName) {
return tagName == "ol" || tagName == "ul";
}
function isListItemElement(tagName) {
return tagName == "li";
}
function isLineBreak(tagName) {
return tagName == "br";
}
function isEmptyParagraph(node) {
return node.tagName.toLowerCase() == "p" && node.childNodes.length == 0;
}
function read(value) {
accumulate(document.createTextNode(value));
}
function accumulateInlineElement(tagName, node) {
var element = node.cloneNode(false);
if (tagName == "span") {
if ($(node).getStyle("fontWeight") == "bold") {
element = $("<strong></strong>");
} else if ($(node).getStyle("fontStyle") == "italic") {
element = $("<em></em>");
}
}
accumulate(element);
lineContainer = element;
}
function accumulate(node) {
if (mode != EXPECTING_LIST_ITEM) {
if (!line) line = lineContainer = createLine();
previousAccumulation = node;
lineContainer.appendChild(node);
}
}
function getPreviouslyAccumulatedTagName() {
if (previousAccumulation && previousAccumulation.nodeType == Node.ELEMENT_NODE) {
return previousAccumulation.tagName.toLowerCase();
}
}
function flush() {
if (line && line.childNodes.length) {
container.appendChild(line);
line = lineContainer = null;
}
}
function createLine() {
if (mode == ACCUMULATING_LINE) {
return $("<div></div>");
} else if (mode == ACCUMULATING_LIST_ITEM) {
return $("<li></li>");
}
}
function insertList(tagName) {
var list = new Element(tagName);
result.appendChild(list);
return list;
}
result = container = $("<div></div>");
walk(element.childNodes);
flush();
return result.innerHTML;
}
};
})();
WysiHat.Toolbar = function() {
var editor;
var element;
function initialize(ed) {
editor = ed;
element = createToolbarElement();
}
function createToolbarElement() {
var toolbar = $('<div class="editor_toolbar"></div>');
editor.before(toolbar);
return toolbar;
}
function addButtonSet(options) {
$(options.buttons).each(function(index, button){
addButton(button);
});
}
function addDropdown(options, handler) {
if (!options['name']) {
options['name'] = options['label'].toLowerCase();
}
var name = options['name'];
var select = createDropdownElement(element, options);
var handler = buttonHandler(name, options);
observeDropdownChange(select, handler);
}
function addButton(options, handler) {
if (!options['name']) {
options['name'] = options['label'].toLowerCase();
}
var name = options['name'];
var button = createButtonElement(element, options);
var handler = buttonHandler(name, options);
observeButtonClick(button, handler);
var handler = buttonStateHandler(name, options);
observeStateChanges(button, name, handler);
}
function createButtonElement(toolbar, options) {
var button = $('<a class="" href="#"><span>' + options['label'] + '</span></a>');
button.addClass("button");
button.addClass(options['name']);
button.addClass(options['cssClass']);
toolbar.append(button);
return button;
}
function createDropdownElement(toolbar, options) {
var optionTemplate = '<option value="KEY">VALUE</option>',
selectTemplate = '<select>OPTIONS</select>';
builder = '';
for (var i = 0; i < options.options.length; i++) {
var o = options.options[i];
builder += optionTemplate.replace('KEY', o.val).replace('VALUE', o.label);
};
var select = $('<select>' + builder + '</select>');
select.addClass(options['cssClass']);
toolbar.append(select);
return select;
}
function buttonHandler(name, options) {
if (options.handler)
return options.handler;
else if (options['handler'])
return options['handler'];
else
return function(editor) { editor.execCommand(name); };
}
function observeButtonClick(element, handler) {
$(element).click(function() {
handler(editor);
$(document.activeElement).trigger("selection:change");
return false;
});
}
function observeDropdownChange(element, handler) {
$(element).change(function() {
var selectedValue = $(this).val();
handler(editor, selectedValue);
$(document.activeElement).trigger("selection:change");
});
}
function buttonStateHandler(name, options) {
if (options.query)
return options.query;
else if (options['query'])
return options['query'];
else
return function(editor) { return editor.queryCommandState(name); };
}
function observeStateChanges(element, name, handler) {
var previousState;
editor.bind("selection:change", function() {
var state = handler(editor);
if (state != previousState) {
previousState = state;
updateButtonState(element, name, state);
}
});
}
function updateButtonState(elem, name, state) {
if (state)
$(elem).addClass('selected');
else
$(elem).removeClass('selected');
}
return {
initialize: initialize,
createToolbarElement: createToolbarElement,
addButtonSet: addButtonSet,
addButton: addButton,
addDropdown: addDropdown,
createButtonElement: createButtonElement,
buttonHandler: buttonHandler,
observeButtonClick: observeButtonClick,
buttonStateHandler: buttonStateHandler,
observeStateChanges: observeStateChanges,
updateButtonState: updateButtonState
};
};
WysiHat.Toolbar.ButtonSets = {};
WysiHat.Toolbar.ButtonSets.Basic = [
{ label: "Bold" },
{ label: "Underline" },
{ label: "Italic" }
];
WysiHat.Toolbar.ButtonSets.Standard = [
{ label: "Bold", cssClass: 'toolbar_button' },
{ label: "Italic", cssClass: 'toolbar_button' },
{ label: "Strikethrough", cssClass: 'toolbar_button' },
{ label: "Bullets", cssClass: 'toolbar_button', handler: function(editor) { return editor.toggleUnorderedList(); } }
];
$.fn.wysihat = function(options) {
options = $.extend({
buttons: WysiHat.Toolbar.ButtonSets.Standard
}, options);
return this.each(function() {
var editor = WysiHat.Editor.attach($(this));
var toolbar = new WysiHat.Toolbar(editor);
toolbar.initialize(editor);
toolbar.addButtonSet(options);
});
};
window.WysiHat = WysiHat;
}(jQuery, this));
/* WysiHat - WYSIWYG JavaScript framework, version 0.2.1
* (c) 2008-2010 Joshua Peek
* JQ-WysiHat - jQuery port of WysiHat to run on jQuery
* (c) 2010 Scott Williams
*
* WysiHat is freely distributable under the terms of an MIT-style license.
*--------------------------------------------------------------------------*/
(function ($, window, undefined)
{
var WysiHat = {};
WysiHat.Editor = {
attach: function($textarea) {
var $editArea;
var id = $textarea.attr('id') + '_editor';
if ($editArea == $('#' + id)) { return $editArea; }
$editArea = $('<div id="' + id + '" class="editor" contentEditable="true"></div>');
$editArea.html(WysiHat.Formatting.getBrowserMarkupFrom($textarea.val()));
$.extend($editArea, WysiHat.Commands);
$textarea.before($editArea);
$textarea.hide();
return $editArea;
}
};
WysiHat.BrowserFeatures = (function() {
function createTmpIframe(callback) {
var frame, frameDocument;
frame = $('<iframe></iframe>');
frame.css({
position: 'absolute',
left: '-1000px'
});
frame.onFrameLoaded(function() {
if (typeof frame.contentDocument !== 'undefined') {
frameDocument = frame.contentDocument;
} else if (typeof frame.contentWindow !== 'undefined' && typeof frame.contentWindow.document !== 'undefined') {
frameDocument = frame.contentWindow.document;
}
frameDocument.designMode = 'on';
callback(frameDocument);
frame.remove();
});
$(document.body).insert(frame);
}
var features = {};
function detectParagraphType(document) {
document.body.innerHTML = '';
document.execCommand('insertparagraph', false, null);
var tagName;
element = document.body.childNodes[0];
if (element && element.tagName)
tagName = element.tagName.toLowerCase();
if (tagName == 'div')
features.paragraphType = "div";
else if (document.body.innerHTML == "<p><br></p>")
features.paragraphType = "br";
else
features.paragraphType = "p";
}
function detectIndentType(document) {
document.body.innerHTML = 'tab';
document.execCommand('indent', false, null);
var tagName;
element = document.body.childNodes[0];
if (element && element.tagName)
tagName = element.tagName.toLowerCase();
features.indentInsertsBlockquote = (tagName == 'blockquote');
}
features.run = function run() {
if (features.finished) return;
createTmpIframe(function(document) {
detectParagraphType(document);
detectIndentType(document);
features.finished = true;
});
}
return features;
})();
if (typeof Selection == 'undefined') {
var Selection = {}
}
$.extend(Selection.prototype, (function() {
function getNode() {
if (this.rangeCount > 0)
return this.getRangeAt(0).getNode();
else
return null;
}
function selectNode(element) {
var range = document.createRange();
range.selectNode(element[0]);
this.removeAllRanges();
this.addRange(range);
}
return {
getNode: getNode,
selectNode: selectNode
}
})());
$(document).ready(function() {
function fieldChangeHandler(event, element) {
var $element = $(element);
element = $element.get(0);
var value;
if ($element.attr('contentEditable') === 'true') {
value = $element.html();
}
value = $element.val();
if (value && element.previousValue != value) {
$element.trigger("field:change");
element.previousValue = value;
}
}
$('input,textarea,*[contenteditable=""],*[contenteditable=true]').keyup(fieldChangeHandler);
});
WysiHat.Commands = (function(window) {
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 indentSelection() {
if ($.browser.mozilla) {
var selection, range, node, blockquote;
selection = getSelection();
range = selection.getRangeAt(0);
node = selection.getNode();
if (range.collapsed) {
range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
}
blockquote = $('<blockquote></blockquote>');
range = selection.getRangeAt(0);
range.surroundContents(blockquote);
} else {
this.execCommand('indent', false, null);
}
}
function outdentSelection() {
this.execCommand('outdent', false, null);
}
function toggleIndentation() {
if (this.indentSelected()) {
this.outdentSelection();
} else {
this.indentSelection();
}
}
function indentSelected() {
var node = getSelection().getNode();
return node.is("blockquote, blockquote *");
}
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($.browser.mozilla) {
this.execCommand('hilitecolor', false, color);
} else {
this.execCommand('backcolor', false, color);
}
}
function alignSelection(alignment) {
this.execCommand('justify' + alignment);
}
function alignSelected() {
var node = getSelection().getNode();
return $(node).css('textAlign');
}
function linkSelection(url) {
this.execCommand('createLink', false, url);
}
function unlinkSelection() {
var node = getSelection().getNode();
if (this.linkSelected())
getSelection().selectNode(node);
this.execCommand('unlink', false, null);
}
function linkSelected() {
var node = getSelection().getNode();
return node ? node.get(0).tagName.toUpperCase() == 'A' : false;
}
function formatblockSelection(element){
this.execCommand('formatblock', false, element);
}
function toggleOrderedList() {
var selection, node;
selection = getSelection();
node = selection.getNode();
if (this.orderedListSelected() && !node.is("ol li:last-child, ol li:last-child *")) {
selection.selectNode(node.parent("ol"));
} else if (this.unorderedListSelected()) {
selection.selectNode(node.parent("ul"));
}
this.execCommand('insertorderedlist', false, null);
}
function insertOrderedList() {
this.toggleOrderedList();
}
function orderedListSelected() {
var element = getSelection().getNode();
if (element) return element.is('*[contenteditable=""] ol, *[contenteditable=true] ol, *[contenteditable=""] ol *, *[contenteditable=true] ol *');
return false;
}
function toggleUnorderedList() {
var selection, node;
selection = getSelection();
node = selection.getNode();
if (this.unorderedListSelected() && !node.is("ul li:last-child, ul li:last-child *")) {
selection.selectNode(node.parent("ul"));
} else if (this.orderedListSelected()) {
selection.selectNode(node.parent("ol"));
}
this.execCommand('insertunorderedlist', false, null);
}
function insertUnorderedList() {
this.toggleUnorderedList();
}
function unorderedListSelected() {
var element = getSelection().getNode();
if (element) return element.is('*[contenteditable=""] ul, *[contenteditable=true] ul, *[contenteditable=""] ul *, *[contenteditable=true] ul *');
return false;
}
function insertImage(url) {
this.execCommand('insertImage', false, url);
}
function insertHTML(html) {
if ($.browser.msie) {
var range = window.document.selection.createRange();
range.pasteHTML(html);
range.collapse(false);
range.select();
} else {
this.execCommand('insertHTML', false, html);
}
}
function execCommand(command, ui, value) {
var handler = this.commands[command];
if (handler) {
handler.bind(this)(value);
} else {
try {
window.document.execCommand(command, ui, value);
} catch(e) { return null; }
}
$(document.activeElement).trigger("field:change");
}
function queryCommandState(state) {
var handler = this.queryCommands[state];
if (handler) {
return handler();
} else {
try {
return window.document.queryCommandState(state);
} catch(e) { return null; }
}
}
function getSelectedStyles() {
var styles = {};
var editor = this;
editor.styleSelectors.each(function(style){
var node = editor.selection.getNode();
styles[style.first()] = $(node).css(style.last());
});
return styles;
}
return {
boldSelection: boldSelection,
boldSelected: boldSelected,
underlineSelection: underlineSelection,
underlineSelected: underlineSelected,
italicSelection: italicSelection,
italicSelected: italicSelected,
strikethroughSelection: strikethroughSelection,
indentSelection: indentSelection,
outdentSelection: outdentSelection,
toggleIndentation: toggleIndentation,
indentSelected: indentSelected,
fontSelection: fontSelection,
fontSizeSelection: fontSizeSelection,
colorSelection: colorSelection,
backgroundColorSelection: backgroundColorSelection,
alignSelection: alignSelection,
alignSelected: alignSelected,
linkSelection: linkSelection,
unlinkSelection: unlinkSelection,
linkSelected: linkSelected,
formatblockSelection: formatblockSelection,
toggleOrderedList: toggleOrderedList,
insertOrderedList: insertOrderedList,
orderedListSelected: orderedListSelected,
toggleUnorderedList: toggleUnorderedList,
insertUnorderedList: insertUnorderedList,
unorderedListSelected: unorderedListSelected,
insertImage: insertImage,
insertHTML: insertHTML,
execCommand: execCommand,
queryCommandState: queryCommandState,
getSelectedStyles: getSelectedStyles,
commands: {},
queryCommands: {
link: linkSelected,
orderedlist: orderedListSelected,
unorderedlist: unorderedListSelected
},
styleSelectors: {
fontname: 'fontFamily',
fontsize: 'fontSize',
forecolor: 'color',
hilitecolor: 'backgroundColor',
backcolor: 'backgroundColor'
}
};
})(window);
if ($.browser.msie) {
$.extend(Selection.prototype, (function() {
function setBookmark() {
var bookmark = $('#bookmark');
if (bookmark) bookmark.remove();
bookmark = $('<span id="bookmark">&nbsp;</span>');
var parent = $('<div></div>').html(bookmark);
var range = this._document.selection.createRange();
range.collapse();
range.pasteHTML(parent.html());
}
function moveToBookmark() {
var bookmark = $('#bookmark');
if (!bookmark) return;
var range = this._document.selection.createRange();
range.moveToElementText(bookmark);
range.collapse();
range.select();
bookmark.remove();
}
return {
setBookmark: setBookmark,
moveToBookmark: moveToBookmark
}
})());
} else {
$.extend(Selection.prototype, (function() {
function setBookmark() {
var bookmark = $('#bookmark');
if (bookmark) bookmark.remove();
bookmark = $('<span id="bookmark">&nbsp;</span>');
this.getRangeAt(0).insertNode(bookmark);
}
function moveToBookmark() {
var bookmark = $('#bookmark');
if (!bookmark) return;
var range = document.createRange();
range.setStartBefore(bookmark);
this.removeAllRanges();
this.addRange(range);
bookmark.remove();
}
return {
setBookmark: setBookmark,
moveToBookmark: moveToBookmark
}
})());
}
(function() {
function cloneWithAllowedAttributes(element, allowedAttributes) {
var length = allowedAttributes.length, i;
var result = $('<' + element.tagName.toLowerCase() + '></' + element.tagName.toLowerCase() + '>')
element = $(element);
for (i = 0; i < allowedAttributes.length; i++) {
attribute = allowedAttributes[i];
if (element.attr(attribute)) {
result.attr(attribute, element.attr(attribute));
}
}
return result;
}
function withEachChildNodeOf(element, callback) {
var nodes = $(element).children;
var length = nodes.length, i;
for (i = 0; i < length; i++) callback(nodes[i]);
}
function sanitizeNode(node, tagsToRemove, tagsToAllow, tagsToSkip) {
var parentNode = node.parentNode;
switch (node.nodeType) {
case Node.ELEMENT_NODE:
var tagName = node.tagName.toLowerCase();
if (tagsToSkip) {
var newNode = node.cloneNode(false);
withEachChildNodeOf(node, function(childNode) {
newNode.appendChild(childNode);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
parentNode.insertBefore(newNode, node);
} else if (tagName in tagsToAllow) {
var newNode = cloneWithAllowedAttributes(node, tagsToAllow[tagName]);
withEachChildNodeOf(node, function(childNode) {
newNode.appendChild(childNode);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
parentNode.insertBefore(newNode, node);
} else if (!(tagName in tagsToRemove)) {
withEachChildNodeOf(node, function(childNode) {
parentNode.insertBefore(childNode, node);
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
}
case Node.COMMENT_NODE:
parentNode.removeChild(node);
}
}
$.fn.sanitizeContents = function(options) {
var element = $(this);
var tagsToRemove = {};
$.each((options.remove || "").split(","), function(tagName) {
tagsToRemove[$.trim(tagName)] = true;
});
var tagsToAllow = {};
$.each((options.allow || "").split(","), function(selector) {
var parts = $.trim(selector).split(/[\[\]]/);
var tagName = parts[0];
var allowedAttributes = $.grep(parts.slice(1), function(n, i) {
return /./.test(n);
});
tagsToAllow[tagName] = allowedAttributes;
});
var tagsToSkip = options.skip;
withEachChildNodeOf(element, function(childNode) {
sanitizeNode(childNode, tagsToRemove, tagsToAllow, tagsToSkip);
});
return element;
}
})();
(function() {
function onReadyStateComplete(document, callback) {
function checkReadyState() {
if (document.readyState === 'complete') {
$(document).unbind('readystatechange', checkReadyState);
callback();
return true;
} else {
return false;
}
}
$(document).bind('readystatechange', checkReadyState);
checkReadyState();
}
function observeFrameContentLoaded(element) {
element = $(element);
var bare = element.get(0);
var loaded, contentLoadedHandler;
loaded = false;
function fireFrameLoaded() {
if (loaded) { return };
loaded = true;
if (contentLoadedHandler) { contentLoadedHandler.stop(); }
element.trigger('frame:loaded');
}
if (window.addEventListener) {
contentLoadedHandler = $(document).bind("DOMFrameContentLoaded", function(event) {
if (element == $(this))
fireFrameLoaded();
});
}
element.load(function() {
var frameDocument;
if (typeof element.contentDocument !== 'undefined') {
frameDocument = element.contentDocument;
} else if (typeof element.contentWindow !== 'undefined' && typeof element.contentWindow.document !== 'undefined') {
frameDocument = element.contentWindow.document;
}
onReadyStateComplete(frameDocument, fireFrameLoaded);
});
return element;
}
function onFrameLoaded($element, callback) {
$element.bind('frame:loaded', callback);
$element.observeFrameContentLoaded();
}
$.fn.observeFrameContentLoaded = observeFrameContentLoaded;
$.fn.onFrameLoaded = onFrameLoaded;
})();
$(document).ready(function() {
var $doc = $(document);
if ('selection' in document && 'onselectionchange' in document) {
var selectionChangeHandler = function() {
var range = document.selection.createRange();
var element = range.parentElement();
$(element).trigger("selection:change");
}
$doc.bind("selectionchange", selectionChangeHandler);
} else {
var previousRange;
var selectionChangeHandler = function() {
var element = document.activeElement;
var elementTagName = element.tagName.toLowerCase();
if (elementTagName == "textarea" || elementTagName == "input") {
previousRange = null;
$(element).trigger("selection:change");
} else {
var selection = getSelection();
if (selection.rangeCount < 1) { return };
var range = selection.getRangeAt(0);
if (range && range.equalRange(previousRange)) {
return;
}
previousRange = range;
element = range.commonAncestorContainer;
while (element.nodeType == Node.TEXT_NODE)
element = element.parentNode;
$(element).trigger("selection:change");
}
};
$doc.mouseup(selectionChangeHandler);
$doc.keyup(selectionChangeHandler);
}
});
WysiHat.Formatting = (function() {
var ACCUMULATING_LINE = {};
var EXPECTING_LIST_ITEM = {};
var ACCUMULATING_LIST_ITEM = {};
return {
getBrowserMarkupFrom: function(applicationMarkup) {
var container = $("<div>" + applicationMarkup + "</div>");
function spanify(element, style) {
$(element).replaceWith(
'<span style="' + style +
'" class="Apple-style-span">' +
element.innerHTML + '</span>'
);
}
function convertStrongsToSpans() {
container.find("strong").each(function(element) {
spanify(element, "font-weight: bold");
});
}
function convertEmsToSpans() {
container.find("em").each(function(element) {
spanify(element, "font-style: italic");
});
}
function convertDivsToParagraphs() {
container.find("div").each(function(element) {
element.replaceWith("<p>" + element.innerHTML + "</p>");
});
}
if ($.browser.webkit || $.browser.mozilla) {
convertStrongsToSpans();
convertEmsToSpans();
} else if ($.browser.msie || $.browser.opera) {
convertDivsToParagraphs();
}
return container.innerHTML;
},
getApplicationMarkupFrom: function(element) {
var mode = ACCUMULATING_LINE, result, container, line, lineContainer, previousAccumulation;
function walk(nodes) {
var length = nodes.length, node, tagName, i;
for (i = 0; i < length; i++) {
node = nodes[i];
if (node.nodeType == Node.ELEMENT_NODE) {
tagName = node.tagName.toLowerCase();
open(tagName, node);
walk(node.childNodes);
close(tagName);
} else if (node.nodeType == Node.TEXT_NODE) {
read(node.nodeValue);
}
}
}
function open(tagName, node) {
if (mode == ACCUMULATING_LINE) {
if (isBlockElement(tagName)) {
if (isEmptyParagraph(node)) {
accumulate($("<br />"));
}
flush();
if (isListElement(tagName)) {
container = insertList(tagName);
mode = EXPECTING_LIST_ITEM;
}
} else if (isLineBreak(tagName)) {
if (isLineBreak(getPreviouslyAccumulatedTagName())) {
previousAccumulation.parentNode.removeChild(previousAccumulation);
flush();
}
accumulate(node.cloneNode(false));
if (!previousAccumulation.previousNode) flush();
} else {
accumulateInlineElement(tagName, node);
}
} else if (mode == EXPECTING_LIST_ITEM) {
if (isListItemElement(tagName)) {
mode = ACCUMULATING_LIST_ITEM;
}
} else if (mode == ACCUMULATING_LIST_ITEM) {
if (isLineBreak(tagName)) {
accumulate(node.cloneNode(false));
} else if (!isBlockElement(tagName)) {
accumulateInlineElement(tagName, node);
}
}
}
function close(tagName) {
if (mode == ACCUMULATING_LINE) {
if (isLineElement(tagName)) {
flush();
}
if (line != lineContainer) {
lineContainer = lineContainer.parentNode;
}
} else if (mode == EXPECTING_LIST_ITEM) {
if (isListElement(tagName)) {
container = result;
mode = ACCUMULATING_LINE;
}
} else if (mode == ACCUMULATING_LIST_ITEM) {
if (isListItemElement(tagName)) {
flush();
mode = EXPECTING_LIST_ITEM;
}
if (line != lineContainer) {
lineContainer = lineContainer.parentNode;
}
}
}
function isBlockElement(tagName) {
return isLineElement(tagName) || isListElement(tagName);
}
function isLineElement(tagName) {
return tagName == "p" || tagName == "div";
}
function isListElement(tagName) {
return tagName == "ol" || tagName == "ul";
}
function isListItemElement(tagName) {
return tagName == "li";
}
function isLineBreak(tagName) {
return tagName == "br";
}
function isEmptyParagraph(node) {
return node.tagName.toLowerCase() == "p" && node.childNodes.length == 0;
}
function read(value) {
accumulate(document.createTextNode(value));
}
function accumulateInlineElement(tagName, node) {
var element = node.cloneNode(false);
if (tagName == "span") {
if ($(node).getStyle("fontWeight") == "bold") {
element = $("<strong></strong>");
} else if ($(node).getStyle("fontStyle") == "italic") {
element = $("<em></em>");
}
}
accumulate(element);
lineContainer = element;
}
function accumulate(node) {
if (mode != EXPECTING_LIST_ITEM) {
if (!line) line = lineContainer = createLine();
previousAccumulation = node;
lineContainer.appendChild(node);
}
}
function getPreviouslyAccumulatedTagName() {
if (previousAccumulation && previousAccumulation.nodeType == Node.ELEMENT_NODE) {
return previousAccumulation.tagName.toLowerCase();
}
}
function flush() {
if (line && line.childNodes.length) {
container.appendChild(line);
line = lineContainer = null;
}
}
function createLine() {
if (mode == ACCUMULATING_LINE) {
return $("<div></div>");
} else if (mode == ACCUMULATING_LIST_ITEM) {
return $("<li></li>");
}
}
function insertList(tagName) {
var list = new Element(tagName);
result.appendChild(list);
return list;
}
result = container = $("<div></div>");
walk(element.childNodes);
flush();
return result.innerHTML;
}
};
})();
WysiHat.Toolbar = function() {
var editor;
var element;
function initialize(ed) {
editor = ed;
element = createToolbarElement();
}
function createToolbarElement() {
var toolbar = $('<div class="editor_toolbar"></div>');
editor.before(toolbar);
return toolbar;
}
function addButtonSet(options) {
$(options.buttons).each(function(index, button){
addButton(button);
});
}
function addDropdown(options, handler) {
if (!options['name']) {
options['name'] = options['label'].toLowerCase();
}
var name = options['name'];
var select = createDropdownElement(element, options);
var handler = buttonHandler(name, options);
observeDropdownChange(select, handler);
}
function addButton(options, handler) {
if (!options['name']) {
options['name'] = options['label'].toLowerCase();
}
var name = options['name'];
var button = createButtonElement(element, options);
var handler = buttonHandler(name, options);
observeButtonClick(button, handler);
var handler = buttonStateHandler(name, options);
observeStateChanges(button, name, handler);
}
function createButtonElement(toolbar, options) {
var button = $('<a class="" href="#"><span>' + options['label'] + '</span></a>');
button.addClass("button");
button.addClass(options['name']);
button.addClass(options['cssClass']);
toolbar.append(button);
return button;
}
function createDropdownElement(toolbar, options) {
var optionTemplate = '<option value="KEY">VALUE</option>',
selectTemplate = '<select>OPTIONS</select>';
builder = '';
for (var i = 0; i < options.options.length; i++) {
var o = options.options[i];
builder += optionTemplate.replace('KEY', o.val).replace('VALUE', o.label);
};
var select = $('<select>' + builder + '</select>');
select.addClass(options['cssClass']);
toolbar.append(select);
return select;
}
function buttonHandler(name, options) {
if (options.handler)
return options.handler;
else if (options['handler'])
return options['handler'];
else
return function(editor) { editor.execCommand(name); };
}
function observeButtonClick(element, handler) {
$(element).click(function() {
handler(editor);
$(document.activeElement).trigger("selection:change");
return false;
});
}
function observeDropdownChange(element, handler) {
$(element).change(function() {
var selectedValue = $(this).val();
handler(editor, selectedValue);
$(document.activeElement).trigger("selection:change");
});
}
function buttonStateHandler(name, options) {
if (options.query)
return options.query;
else if (options['query'])
return options['query'];
else
return function(editor) { return editor.queryCommandState(name); };
}
function observeStateChanges(element, name, handler) {
var previousState;
editor.bind("selection:change", function() {
var state = handler(editor);
if (state != previousState) {
previousState = state;
updateButtonState(element, name, state);
}
});
}
function updateButtonState(elem, name, state) {
if (state)
$(elem).addClass('selected');
else
$(elem).removeClass('selected');
}
return {
initialize: initialize,
createToolbarElement: createToolbarElement,
addButtonSet: addButtonSet,
addButton: addButton,
addDropdown: addDropdown,
createButtonElement: createButtonElement,
buttonHandler: buttonHandler,
observeButtonClick: observeButtonClick,
buttonStateHandler: buttonStateHandler,
observeStateChanges: observeStateChanges,
updateButtonState: updateButtonState
};
};
WysiHat.Toolbar.ButtonSets = {};
WysiHat.Toolbar.ButtonSets.Basic = [
{ label: "Bold" },
{ label: "Underline" },
{ label: "Italic" }
];
WysiHat.Toolbar.ButtonSets.Standard = [
{ label: "Bold", cssClass: 'toolbar_button' },
{ label: "Italic", cssClass: 'toolbar_button' },
{ label: "Strikethrough", cssClass: 'toolbar_button' },
{ label: "Bullets", cssClass: 'toolbar_button', handler: function(editor) { return editor.toggleUnorderedList(); } }
];
$.fn.wysihat = function(options) {
options = $.extend({
buttons: WysiHat.Toolbar.ButtonSets.Standard
}, options);
return this.each(function() {
var editor = WysiHat.Editor.attach($(this));
var toolbar = new WysiHat.Toolbar(editor);
toolbar.initialize(editor);
toolbar.addButtonSet(options);
});
};
window.WysiHat = WysiHat;
}(jQuery, this));
/*
selection.js Copyright (c) 2011-12 Tim Cameron Ryan. MIT licensed.
*/
(function(){var f={isPreceding:function(a,b){return b.compareDocumentPosition(a)&2},contains:function(a,b){return null!=a.compareDocumentPosition?a.compareDocumentPosition(b)&16:a.contains(b)},isCursorPreceding:function(a,b,d,c){return a===d?b<=c:f.isText(a)&&f.isText(d)?f.isPreceding(a,d):f.isText(a)&&!f.isText(d)?!f.isCursorPreceding(d,c,a,b):!f.contains(a,d)?f.isPreceding(a,d):a.childNodes.length<=b?!1:a.childNodes[b]===d?0<=c:f.isPreceding(a.childNodes[b],d)},isText:function(a){return null!=a?
3==a.nodeType:!1},getChildIndex:function(a){for(var b=0;a=a.previousSibling;)b++;return b}},e=this.Selection=function(){function a(a){this.win=a}a.prototype.hasSelection=function(){return a.hasSelection(this.win)};a.prototype.getOrigin=function(){return a.getOrigin(this.win)};a.prototype.getFocus=function(){return a.getFocus(this.win)};a.prototype.getStart=function(){return a.getStart(this.win)};a.prototype.getEnd=function(){return a.getEnd(this.win)};a.prototype.setSelection=function(b,d,c,e){return a.setSelection(this.win,
b,d,c,e)};a.prototype.clearSelection=function(){return a.clearSelection(this.win)};return a}();if(this.getSelection)e.supported=!0,e.hasSelection=function(a){var b;return(b=a.getSelection())&&null!=b.focusNode&&null!=b.anchorNode},e.getOrigin=function(a){var b;return!((b=a.getSelection())&&null!=b.anchorNode)?null:[b.anchorNode,b.anchorOffset]},e.getFocus=function(a){var b;return!((b=a.getSelection())&&null!=b.focusNode)?null:[b.focusNode,b.focusOffset]},e.getStart=function(a){var b,d,c;if(!e.hasSelection(a))return null;
d=e.getOrigin(a);b=d[0];d=d[1];c=e.getFocus(a);a=c[0];c=c[1];return f.isCursorPreceding(b,d,a,c)?[b,d]:[a,c]},e.getEnd=function(a){var b,d,c;if(!e.hasSelection(a))return null;d=e.getOrigin(a);b=d[0];d=d[1];c=e.getFocus(a);a=c[0];c=c[1];return f.isCursorPreceding(b,d,a,c)?[a,c]:[b,d]},e.setSelection=function(a,b,d,c,e){var f=a.getSelection();if(f)if(null==c&&(c=b),null==e&&(e=d),f.collapse&&f.extend)f.collapse(b,d),f.extend(c,e);else{r=a.document.createRange();r.setStart(b,d);r.setEnd(c,e);try{f.removeAllRanges()}catch(g){}f.addRange(r)}},
e.clearSelection=function(a){try{var b=a.getSelection();b&&b.removeAllRanges()}catch(d){}};else if(this.document.selection){var j=function(a,b,d){var c,e,a=a.createElement("a");c=b.duplicate();c.collapse(d);for(e=c.parentElement();!(e.insertBefore(a,a.previousSibling),c.moveToElementText(a),!(0<c.compareEndPoints(d?"StartToStart":"StartToEnd",b)&&null!=a.previousSibling)););-1===c.compareEndPoints(d?"StartToStart":"StartToEnd",b)&&a.nextSibling?(c.setEndPoint(d?"EndToStart":"EndToEnd",b),b=a.nextSibling,
d=c.text.length):(b=a.parentNode,d=f.getChildIndex(a));a.parentNode.removeChild(a);return[b,d]},k=function(a,b,d,c,e){var i,g,h;h=0;i=f.isText(c)?c:c.childNodes[e];g=f.isText(c)?c.parentNode:c;f.isText(c)&&(h=e);c=a.createElement("a");g.insertBefore(c,i||null);a=a.body.createTextRange();a.moveToElementText(c);c.parentNode.removeChild(c);b.setEndPoint(d?"StartToStart":"EndToEnd",a);return b[d?"moveStart":"moveEnd"]("character",h)};e.supported=!0;e.hasSelection=function(a){var b;a.focus();return!a.document.selection?
!1:(b=a.document.selection.createRange())&&b.parentElement().document===a.document};e.getStart=function(a){var b;a.focus();if(!e.hasSelection(a))return null;b=a.document.selection.createRange();return j(a.document,b,!0)};e.getEnd=function(a){var b;a.focus();if(!e.hasSelection(a))return null;b=a.document.selection.createRange();return j(a.document,b,!1)};e.getOrigin=function(a){return e.getStart(a)};e.getFocus=function(a){return e.getEnd(a)};e.setSelection=function(a,b,d,c,e){null==c&&(c=b);null==
e&&(e=d);var f=a.document.body.createTextRange();k(a.document,f,!1,c,e);k(a.document,f,!0,b,d);return f.select()};e.clearSelection=function(a){return a.document.selection.empty()}}else e.supported=!1}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment