Files
galaxy/static/scripts/libs/jquery/jquery.wymeditor.js
T

4704 lines
137 KiB
JavaScript

/**
* @version 0.5-rc1
*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
* Dual licensed under the MIT (MIT-license.txt)
* and GPL (GPL-license.txt) licenses.
*
* For further information visit:
* http://www.wymeditor.org/
*
* File: jquery.wymeditor.js
*
* Main JS file with core classes and functions.
* See the documentation for more info.
*
* About: authors
*
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
* Volker Mische (vmx a-t gmx dotde)
* Scott Lewis (lewiscot a-t gmail dotcom)
* Bermi Ferrer (wymeditor a-t bermi dotorg)
* Daniel Reszka (d.reszka a-t wymeditor dotorg)
* Jonatan Lundin (jonatan.lundin _at_ gmail.com)
*/
/*
Namespace: WYMeditor
Global WYMeditor namespace.
*/
if(!WYMeditor) var WYMeditor = {};
//Wrap the Firebug console in WYMeditor.console
(function() {
if ( !window.console || !console.firebug ) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
WYMeditor.console = {};
for (var i = 0; i < names.length; ++i)
WYMeditor.console[names[i]] = function() {}
} else WYMeditor.console = window.console;
})();
jQuery.extend(WYMeditor, {
/*
Constants: Global WYMeditor constants.
VERSION - Defines WYMeditor version.
INSTANCES - An array of loaded WYMeditor.editor instances.
STRINGS - An array of loaded WYMeditor language pairs/values.
SKINS - An array of loaded WYMeditor skins.
NAME - The "name" attribute.
INDEX - A string replaced by the instance index.
WYM_INDEX - A string used to get/set the instance index.
BASE_PATH - A string replaced by WYMeditor's base path.
SKIN_PATH - A string replaced by WYMeditor's skin path.
WYM_PATH - A string replaced by WYMeditor's main JS file path.
SKINS_DEFAULT_PATH - The skins default base path.
SKINS_DEFAULT_CSS - The skins default CSS file.
LANG_DEFAULT_PATH - The language files default path.
IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path.
IFRAME_DEFAULT - The iframe's default base path.
JQUERY_PATH - A string replaced by the computed jQuery path.
DIRECTION - A string replaced by the text direction (rtl or ltr).
LOGO - A string replaced by WYMeditor logo.
TOOLS - A string replaced by the toolbar's HTML.
TOOLS_ITEMS - A string replaced by the toolbar items.
TOOL_NAME - A string replaced by a toolbar item's name.
TOOL_TITLE - A string replaced by a toolbar item's title.
TOOL_CLASS - A string replaced by a toolbar item's class.
CLASSES - A string replaced by the classes panel's HTML.
CLASSES_ITEMS - A string replaced by the classes items.
CLASS_NAME - A string replaced by a class item's name.
CLASS_TITLE - A string replaced by a class item's title.
CONTAINERS - A string replaced by the containers panel's HTML.
CONTAINERS_ITEMS - A string replaced by the containers items.
CONTAINER_NAME - A string replaced by a container item's name.
CONTAINER_TITLE - A string replaced by a container item's title.
CONTAINER_CLASS - A string replaced by a container item's class.
HTML - A string replaced by the HTML view panel's HTML.
IFRAME - A string replaced by the designmode iframe.
STATUS - A string replaced by the status panel's HTML.
DIALOG_TITLE - A string replaced by a dialog's title.
DIALOG_BODY - A string replaced by a dialog's HTML body.
BODY - The BODY element.
STRING - The "string" type.
BODY,DIV,P,
H1,H2,H3,H4,H5,H6,
PRE,BLOCKQUOTE,
A,BR,IMG,
TABLE,TD,TH,
UL,OL,LI - HTML elements string representation.
CLASS,HREF,SRC,
TITLE,ALT - HTML attributes string representation.
DIALOG_LINK - A link dialog type.
DIALOG_IMAGE - An image dialog type.
DIALOG_TABLE - A table dialog type.
DIALOG_PASTE - A 'Paste from Word' dialog type.
BOLD - Command: (un)set selection to <strong>.
ITALIC - Command: (un)set selection to <em>.
CREATE_LINK - Command: open the link dialog or (un)set link.
INSERT_IMAGE - Command: open the image dialog or insert an image.
INSERT_TABLE - Command: open the table dialog.
PASTE - Command: open the paste dialog.
INDENT - Command: nest a list item.
OUTDENT - Command: unnest a list item.
TOGGLE_HTML - Command: display/hide the HTML view.
FORMAT_BLOCK - Command: set a block element to another type.
PREVIEW - Command: open the preview dialog.
UNLINK - Command: unset a link.
INSERT_UNORDEREDLIST- Command: insert an unordered list.
INSERT_ORDEREDLIST - Command: insert an ordered list.
MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor.
BLOCKS - An array of the HTML block elements.
KEY - Standard key codes.
NODE - Node types.
*/
VERSION : "0.5-rc1",
INSTANCES : [],
STRINGS : [],
SKINS : [],
NAME : "name",
INDEX : "{Wym_Index}",
WYM_INDEX : "wym_index",
BASE_PATH : "{Wym_Base_Path}",
CSS_PATH : "{Wym_Css_Path}",
WYM_PATH : "{Wym_Wym_Path}",
SKINS_DEFAULT_PATH : "skins/",
SKINS_DEFAULT_CSS : "skin.css",
SKINS_DEFAULT_JS : "skin.js",
LANG_DEFAULT_PATH : "lang/",
IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}",
IFRAME_DEFAULT : "iframe/default/",
JQUERY_PATH : "{Wym_Jquery_Path}",
DIRECTION : "{Wym_Direction}",
LOGO : "{Wym_Logo}",
TOOLS : "{Wym_Tools}",
TOOLS_ITEMS : "{Wym_Tools_Items}",
TOOL_NAME : "{Wym_Tool_Name}",
TOOL_TITLE : "{Wym_Tool_Title}",
TOOL_CLASS : "{Wym_Tool_Class}",
CLASSES : "{Wym_Classes}",
CLASSES_ITEMS : "{Wym_Classes_Items}",
CLASS_NAME : "{Wym_Class_Name}",
CLASS_TITLE : "{Wym_Class_Title}",
CONTAINERS : "{Wym_Containers}",
CONTAINERS_ITEMS : "{Wym_Containers_Items}",
CONTAINER_NAME : "{Wym_Container_Name}",
CONTAINER_TITLE : "{Wym_Containers_Title}",
CONTAINER_CLASS : "{Wym_Container_Class}",
HTML : "{Wym_Html}",
IFRAME : "{Wym_Iframe}",
STATUS : "{Wym_Status}",
DIALOG_TITLE : "{Wym_Dialog_Title}",
DIALOG_BODY : "{Wym_Dialog_Body}",
STRING : "string",
BODY : "body",
DIV : "div",
P : "p",
H1 : "h1",
H2 : "h2",
H3 : "h3",
H4 : "h4",
H5 : "h5",
H6 : "h6",
PRE : "pre",
BLOCKQUOTE : "blockquote",
A : "a",
BR : "br",
IMG : "img",
TABLE : "table",
TD : "td",
TH : "th",
UL : "ul",
OL : "ol",
LI : "li",
CLASS : "class",
HREF : "href",
SRC : "src",
TITLE : "title",
ALT : "alt",
DIALOG_LINK : "Link",
DIALOG_IMAGE : "Image",
DIALOG_TABLE : "Table",
DIALOG_PASTE : "Paste_From_Word",
BOLD : "Bold",
ITALIC : "Italic",
CREATE_LINK : "CreateLink",
INSERT_IMAGE : "InsertImage",
INSERT_TABLE : "InsertTable",
INSERT_HTML : "InsertHTML",
PASTE : "Paste",
INDENT : "Indent",
OUTDENT : "Outdent",
TOGGLE_HTML : "ToggleHtml",
FORMAT_BLOCK : "FormatBlock",
PREVIEW : "Preview",
UNLINK : "Unlink",
INSERT_UNORDEREDLIST: "InsertUnorderedList",
INSERT_ORDEREDLIST : "InsertOrderedList",
MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),
BLOCKS : new Array("address", "blockquote", "div", "dl",
"fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr",
"noscript", "ol", "p", "pre", "table", "ul", "dd", "dt",
"li", "tbody", "td", "tfoot", "th", "thead", "tr"),
KEY : {
BACKSPACE: 8,
ENTER: 13,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
CURSOR: new Array(37, 38, 39, 40),
DELETE: 46
},
NODE : {
ELEMENT: 1,
ATTRIBUTE: 2,
TEXT: 3
},
/*
Class: WYMeditor.editor
WYMeditor editor main class, instanciated for each editor occurrence.
*/
editor : function(elem, options) {
/*
Constructor: WYMeditor.editor
Initializes main values (index, elements, paths, ...)
and call WYMeditor.editor.init which initializes the editor.
Parameters:
elem - The HTML element to be replaced by the editor.
options - The hash of options.
Returns:
Nothing.
See Also:
<WYMeditor.editor.init>
*/
//store the instance in the INSTANCES array and store the index
this._index = WYMeditor.INSTANCES.push(this) - 1;
//store the element replaced by the editor
this._element = elem;
//store the options
this._options = options;
//store the element's inner value
this._html = jQuery(elem).val();
//store the HTML option, if any
if(this._options.html) this._html = this._options.html;
//get or compute the base path (where the main JS file is located)
this._options.basePath = this._options.basePath
|| this.computeBasePath();
//get or set the skin path (where the skin files are located)
this._options.skinPath = this._options.skinPath
|| this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH
+ this._options.skin + '/';
//get or compute the main JS file location
this._options.wymPath = this._options.wymPath
|| this.computeWymPath();
//get or set the language files path
this._options.langPath = this._options.langPath
|| this._options.basePath + WYMeditor.LANG_DEFAULT_PATH;
//get or set the designmode iframe's base path
this._options.iframeBasePath = this._options.iframeBasePath
|| this._options.basePath + WYMeditor.IFRAME_DEFAULT;
//get or compute the jQuery JS file location
this._options.jQueryPath = this._options.jQueryPath
|| this.computeJqueryPath();
//initialize the editor instance
this.init();
}
});
/********** JQUERY **********/
/**
* Replace an HTML element by WYMeditor
*
* @example jQuery(".wymeditor").wymeditor(
* {
*
* }
* );
* @desc Example description here
*
* @name WYMeditor
* @description WYMeditor is a web-based WYSIWYM XHTML editor
* @param Hash hash A hash of parameters
* @option Integer iExample Description here
* @option String sExample Description here
*
* @type jQuery
* @cat Plugins/WYMeditor
* @author Jean-Francois Hovinne
*/
jQuery.fn.wymeditor = function(options) {
options = jQuery.extend({
html: "",
basePath: false,
skinPath: false,
wymPath: false,
iframeBasePath: false,
jQueryPath: false,
styles: false,
stylesheet: false,
skin: "default",
initSkin: true,
loadSkin: true,
lang: "en",
direction: "ltr",
boxHtml: "<div class='wym_box'>"
+ "<div class='wym_area_top'>"
+ WYMeditor.TOOLS
+ "</div>"
+ "<div class='wym_area_left'></div>"
+ "<div class='wym_area_right'>"
+ WYMeditor.CONTAINERS
+ WYMeditor.CLASSES
+ "</div>"
+ "<div class='wym_area_main'>"
+ WYMeditor.HTML
+ WYMeditor.IFRAME
+ WYMeditor.STATUS
+ "</div>"
+ "<div class='wym_area_bottom'>"
+ WYMeditor.LOGO
+ "</div>"
+ "</div>",
logoHtml: "<a class='wym_wymeditor_link' "
+ "href='http://www.wymeditor.org/'>WYMeditor</a>",
iframeHtml:"<div class='wym_iframe wym_section'>"
+ "<iframe "
+ "src='"
+ "/page/get_editor_iframe' "
//+ WYMeditor.IFRAME_BASE_PATH
//+ "wymiframe.html' "
+ "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
+ WYMeditor.INDEX + "].initIframe(this)'"
+ "></iframe>"
+ "</div>",
editorStyles: [],
toolsHtml: "<div class='wym_tools wym_section'>"
+ "<h2>{Tools}</h2>"
+ "<ul>"
+ WYMeditor.TOOLS_ITEMS
+ "</ul>"
+ "</div>",
toolsItemHtml: "<li class='"
+ WYMeditor.TOOL_CLASS
+ "'><a href='#' name='"
+ WYMeditor.TOOL_NAME
+ "' title='"
+ WYMeditor.TOOL_TITLE
+ "'>"
+ WYMeditor.TOOL_TITLE
+ "</a></li>",
toolsItems: [
{'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'},
{'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
{'name': 'Superscript', 'title': 'Superscript',
'css': 'wym_tools_superscript'},
{'name': 'Subscript', 'title': 'Subscript',
'css': 'wym_tools_subscript'},
{'name': 'InsertOrderedList', 'title': 'Ordered_List',
'css': 'wym_tools_ordered_list'},
{'name': 'InsertUnorderedList', 'title': 'Unordered_List',
'css': 'wym_tools_unordered_list'},
{'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'},
{'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'},
{'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
{'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
{'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
{'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
{'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
{'name': 'Paste', 'title': 'Paste_From_Word',
'css': 'wym_tools_paste'},
{'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'},
{'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}
],
containersHtml: "<div class='wym_containers wym_section'>"
+ "<h2>{Containers}</h2>"
+ "<ul>"
+ WYMeditor.CONTAINERS_ITEMS
+ "</ul>"
+ "</div>",
containersItemHtml:"<li class='"
+ WYMeditor.CONTAINER_CLASS
+ "'>"
+ "<a href='#' name='"
+ WYMeditor.CONTAINER_NAME
+ "'>"
+ WYMeditor.CONTAINER_TITLE
+ "</a></li>",
containersItems: [
{'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
{'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
{'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
{'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
{'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
{'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
{'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
{'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
{'name': 'BLOCKQUOTE', 'title': 'Blockquote',
'css': 'wym_containers_blockquote'},
{'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}
],
classesHtml: "<div class='wym_classes wym_section'>"
+ "<h2>{Classes}</h2><ul>"
+ WYMeditor.CLASSES_ITEMS
+ "</ul></div>",
classesItemHtml: "<li><a href='#' name='"
+ WYMeditor.CLASS_NAME
+ "'>"
+ WYMeditor.CLASS_TITLE
+ "</a></li>",
classesItems: [],
statusHtml: "<div class='wym_status wym_section'>"
+ "<h2>{Status}</h2>"
+ "</div>",
htmlHtml: "<div class='wym_html wym_section'>"
+ "<h2>{Source_Code}</h2>"
+ "<textarea class='wym_html_val'></textarea>"
+ "</div>",
boxSelector: ".wym_box",
toolsSelector: ".wym_tools",
toolsListSelector: " ul",
containersSelector:".wym_containers",
classesSelector: ".wym_classes",
htmlSelector: ".wym_html",
iframeSelector: ".wym_iframe iframe",
iframeBodySelector:".wym_iframe",
statusSelector: ".wym_status",
toolSelector: ".wym_tools a",
containerSelector: ".wym_containers a",
classSelector: ".wym_classes a",
htmlValSelector: ".wym_html_val",
hrefSelector: ".wym_href",
srcSelector: ".wym_src",
titleSelector: ".wym_title",
altSelector: ".wym_alt",
textSelector: ".wym_text",
rowsSelector: ".wym_rows",
colsSelector: ".wym_cols",
captionSelector: ".wym_caption",
summarySelector: ".wym_summary",
submitSelector: ".wym_submit",
cancelSelector: ".wym_cancel",
previewSelector: "",
dialogTypeSelector: ".wym_dialog_type",
dialogLinkSelector: ".wym_dialog_link",
dialogImageSelector: ".wym_dialog_image",
dialogTableSelector: ".wym_dialog_table",
dialogPasteSelector: ".wym_dialog_paste",
dialogPreviewSelector: ".wym_dialog_preview",
updateSelector: ".wymupdate",
updateEvent: "click",
dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ ",width=560,height=300,top=0,left=0",
dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ ",scrollbars=yes,width=560,height=300,top=0,left=0",
dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
+ " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
+ "<html dir='"
+ WYMeditor.DIRECTION
+ "'><head>"
+ "<link rel='stylesheet' type='text/css' media='screen'"
+ " href='"
+ WYMeditor.CSS_PATH
+ "' />"
+ "<title>"
+ WYMeditor.DIALOG_TITLE
+ "</title>"
+ "<script type='text/javascript'"
+ " src='"
+ WYMeditor.JQUERY_PATH
+ "'></script>"
+ "<script type='text/javascript'"
+ " src='"
+ WYMeditor.WYM_PATH
+ "'></script>"
+ "</head>"
+ WYMeditor.DIALOG_BODY
+ "</html>",
dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'"
+ " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ ">"
+ "<form>"
+ "<fieldset>"
+ "<input type='hidden' class='wym_dialog_type' value='"
+ WYMeditor.DIALOG_LINK
+ "' />"
+ "<legend>{Link}</legend>"
+ "<div class='row'>"
+ "<label>{URL}</label>"
+ "<input type='text' class='wym_href' value='' size='40' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Title}</label>"
+ "<input type='text' class='wym_title' value='' size='40' />"
+ "</div>"
+ "<div class='row row-indent'>"
+ "<input class='wym_submit' type='button'"
+ " value='{Submit}' />"
+ "<input class='wym_cancel' type='button'"
+ "value='{Cancel}' />"
+ "</div>"
+ "</fieldset>"
+ "</form>"
+ "</body>",
dialogImageHtml: "<body class='wym_dialog wym_dialog_image'"
+ " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ ">"
+ "<form>"
+ "<fieldset>"
+ "<input type='hidden' class='wym_dialog_type' value='"
+ WYMeditor.DIALOG_IMAGE
+ "' />"
+ "<legend>{Image}</legend>"
+ "<div class='row'>"
+ "<label>{URL}</label>"
+ "<input type='text' class='wym_src' value='' size='40' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Alternative_Text}</label>"
+ "<input type='text' class='wym_alt' value='' size='40' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Title}</label>"
+ "<input type='text' class='wym_title' value='' size='40' />"
+ "</div>"
+ "<div class='row row-indent'>"
+ "<input class='wym_submit' type='button'"
+ " value='{Submit}' />"
+ "<input class='wym_cancel' type='button'"
+ "value='{Cancel}' />"
+ "</div>"
+ "</fieldset>"
+ "</form>"
+ "</body>",
dialogTableHtml: "<body class='wym_dialog wym_dialog_table'"
+ " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ ">"
+ "<form>"
+ "<fieldset>"
+ "<input type='hidden' class='wym_dialog_type' value='"
+ WYMeditor.DIALOG_TABLE
+ "' />"
+ "<legend>{Table}</legend>"
+ "<div class='row'>"
+ "<label>{Caption}</label>"
+ "<input type='text' class='wym_caption' value='' size='40' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Summary}</label>"
+ "<input type='text' class='wym_summary' value='' size='40' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Number_Of_Rows}</label>"
+ "<input type='text' class='wym_rows' value='3' size='3' />"
+ "</div>"
+ "<div class='row'>"
+ "<label>{Number_Of_Cols}</label>"
+ "<input type='text' class='wym_cols' value='2' size='3' />"
+ "</div>"
+ "<div class='row row-indent'>"
+ "<input class='wym_submit' type='button'"
+ " value='{Submit}' />"
+ "<input class='wym_cancel' type='button'"
+ "value='{Cancel}' />"
+ "</div>"
+ "</fieldset>"
+ "</form>"
+ "</body>",
dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'"
+ " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ ">"
+ "<form>"
+ "<input type='hidden' class='wym_dialog_type' value='"
+ WYMeditor.DIALOG_PASTE
+ "' />"
+ "<fieldset>"
+ "<legend>{Paste_From_Word}</legend>"
+ "<div class='row'>"
+ "<textarea class='wym_text' rows='10' cols='50'></textarea>"
+ "</div>"
+ "<div class='row'>"
+ "<input class='wym_submit' type='button'"
+ " value='{Submit}' />"
+ "<input class='wym_cancel' type='button'"
+ "value='{Cancel}' />"
+ "</div>"
+ "</fieldset>"
+ "</form>"
+ "</body>",
dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'"
+ " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ "></body>",
dialogStyles: [],
stringDelimiterLeft: "{",
stringDelimiterRight:"}",
preInit: null,
preBind: null,
postInit: null,
preInitDialog: null,
postInitDialog: null
}, options);
return this.each(function() {
new WYMeditor.editor(jQuery(this),options);
});
};
/* @name extend
* @description Returns the WYMeditor instance based on its index
*/
jQuery.extend({
wymeditors: function(i) {
return (WYMeditor.INSTANCES[i]);
}
});
/********** WYMeditor **********/
/* @name Wymeditor
* @description WYMeditor class
*/
/* @name init
* @description Initializes a WYMeditor instance
*/
WYMeditor.editor.prototype.init = function() {
//load subclass - browser specific
//unsupported browsers: do nothing
if (jQuery.browser.msie) {
var WymClass = new WYMeditor.WymClassExplorer(this);
}
else if (jQuery.browser.mozilla) {
var WymClass = new WYMeditor.WymClassMozilla(this);
}
else if (jQuery.browser.opera) {
var WymClass = new WYMeditor.WymClassOpera(this);
}
else if (jQuery.browser.safari) {
var WymClass = new WYMeditor.WymClassSafari(this);
}
if(WymClass) {
if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this);
var SaxListener = new WYMeditor.XhtmlSaxListener();
jQuery.extend(SaxListener, WymClass);
this.parser = new WYMeditor.XhtmlParser(SaxListener);
if(this._options.styles || this._options.stylesheet){
this.configureEditorUsingRawCss();
}
this.helper = new WYMeditor.XmlHelper();
//extend the Wymeditor object
//don't use jQuery.extend since 1.1.4
//jQuery.extend(this, WymClass);
for (var prop in WymClass) { this[prop] = WymClass[prop]; }
//load wymbox
this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index);
//store the instance index in wymbox and element replaced by editor instance
//but keep it compatible with jQuery < 1.2.3, see #122
if( jQuery.isFunction( jQuery.fn.data ) ) {
jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index);
jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index);
}
var h = WYMeditor.Helper;
//construct the iframe
var iframeHtml = this._options.iframeHtml;
iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index);
iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath);
//construct wymbox
var boxHtml = jQuery(this._box).html();
boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml);
//construct tools list
var aTools = eval(this._options.toolsItems);
var sTools = "";
for(var i = 0; i < aTools.length; i++) {
var oTool = aTools[i];
if(oTool.name && oTool.title)
var sTool = this._options.toolsItemHtml;
var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name);
sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
+ oTool.title
+ this._options.stringDelimiterRight);
sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css);
sTools += sTool;
}
boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
//construct classes list
var aClasses = eval(this._options.classesItems);
var sClasses = "";
for(var i = 0; i < aClasses.length; i++) {
var oClass = aClasses[i];
if(oClass.name && oClass.title)
var sClass = this._options.classesItemHtml;
sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name);
sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title);
sClasses += sClass;
}
boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses);
//construct containers list
var aContainers = eval(this._options.containersItems);
var sContainers = "";
for(var i = 0; i < aContainers.length; i++) {
var oContainer = aContainers[i];
if(oContainer.name && oContainer.title)
var sContainer = this._options.containersItemHtml;
sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name);
sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE,
this._options.stringDelimiterLeft
+ oContainer.title
+ this._options.stringDelimiterRight);
sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
sContainers += sContainer;
}
boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers);
//l10n
boxHtml = this.replaceStrings(boxHtml);
//load html in wymbox
jQuery(this._box).html(boxHtml);
//hide the html value
jQuery(this._box).find(this._options.htmlSelector).hide();
//enable the skin
this.loadSkin();
}
};
WYMeditor.editor.prototype.bindEvents = function() {
//copy the instance
var wym = this;
//handle click event on tools buttons
jQuery(this._box).find(this._options.toolSelector).click(function() {
wym._iframe.contentWindow.focus(); //See #154
wym.exec(jQuery(this).attr(WYMeditor.NAME));
return(false);
});
//handle click event on containers buttons
jQuery(this._box).find(this._options.containerSelector).click(function() {
wym.container(jQuery(this).attr(WYMeditor.NAME));
return(false);
});
//handle keyup event on html value: set the editor value
//handle focus/blur events to check if the element has focus, see #147
jQuery(this._box).find(this._options.htmlValSelector)
.keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());})
.focus(function() { jQuery(this).toggleClass('hasfocus'); })
.blur(function() { jQuery(this).toggleClass('hasfocus'); });
//handle click event on classes buttons
jQuery(this._box).find(this._options.classSelector).click(function() {
var aClasses = eval(wym._options.classesItems);
var sName = jQuery(this).attr(WYMeditor.NAME);
var oClass = WYMeditor.Helper.findByName(aClasses, sName);
if(oClass) {
var jqexpr = oClass.expr;
wym.toggleClass(sName, jqexpr);
}
wym._iframe.contentWindow.focus(); //See #154
return(false);
});
//handle event on update element
jQuery(this._options.updateSelector)
.bind(this._options.updateEvent, function() {
wym.update();
});
};
WYMeditor.editor.prototype.ready = function() {
return(this._doc != null);
};
/********** METHODS **********/
/* @name box
* @description Returns the WYMeditor container
*/
WYMeditor.editor.prototype.box = function() {
return(this._box);
};
/* @name html
* @description Get/Set the html value
*/
WYMeditor.editor.prototype.html = function(html) {
if(typeof html === 'string') jQuery(this._doc.body).html(html);
else return(jQuery(this._doc.body).html());
};
/* @name xhtml
* @description Cleans up the HTML
*/
WYMeditor.editor.prototype.xhtml = function() {
return this.parser.parse(this.html());
};
/* @name exec
* @description Executes a button command
*/
WYMeditor.editor.prototype.exec = function(cmd) {
//base function for execCommand
//open a dialog or exec
switch(cmd) {
case WYMeditor.CREATE_LINK:
var container = this.container();
if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK);
break;
case WYMeditor.INSERT_IMAGE:
this.dialog(WYMeditor.DIALOG_IMAGE);
break;
case WYMeditor.INSERT_TABLE:
this.dialog(WYMeditor.DIALOG_TABLE);
break;
case WYMeditor.PASTE:
this.dialog(WYMeditor.DIALOG_PASTE);
break;
case WYMeditor.TOGGLE_HTML:
this.update();
this.toggleHtml();
//partially fixes #121 when the user manually inserts an image
if(!jQuery(this._box).find(this._options.htmlSelector).is(':visible'))
this.listen();
break;
case WYMeditor.PREVIEW:
this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
break;
default:
this._exec(cmd);
break;
}
};
/* @name container
* @description Get/Set the selected container
*/
WYMeditor.editor.prototype.container = function(sType) {
if(sType) {
var container = null;
if(sType.toLowerCase() == WYMeditor.TH) {
container = this.container();
//find the TD or TH container
switch(container.tagName.toLowerCase()) {
case WYMeditor.TD: case WYMeditor.TH:
break;
default:
var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
container = this.findUp(this.container(), aTypes);
break;
}
//if it exists, switch
if(container!=null) {
sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
this.switchTo(container,sType);
this.update();
}
} else {
//set the container type
var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,
WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);
container = this.findUp(this.container(), aTypes);
if(container) {
var newNode = null;
//blockquotes must contain a block level element
if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) {
var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE);
if(blockquote == null) {
newNode = this._doc.createElement(sType);
container.parentNode.insertBefore(newNode,container);
newNode.appendChild(container);
this.setFocusToNode(newNode.firstChild);
} else {
var nodes = blockquote.childNodes;
var lgt = nodes.length;
var firstNode = null;
if(lgt > 0) firstNode = nodes.item(0);
for(var x=0; x<lgt; x++) {
blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
}
blockquote.parentNode.removeChild(blockquote);
if(firstNode) this.setFocusToNode(firstNode);
}
}
else this.switchTo(container,sType);
this.update();
}
}
}
else return(this.selected());
};
/* @name toggleClass
* @description Toggles class on selected element, or one of its parents
*/
WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) {
var container = (this._selected_image
? this._selected_image
: jQuery(this.selected()));
container = jQuery(container).parentsOrSelf(jqexpr);
jQuery(container).toggleClass(sClass);
if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class);
};
/* @name findUp
* @description Returns the first parent or self container, based on its type
*/
WYMeditor.editor.prototype.findUp = function(node, filter) {
//filter is a string or an array of strings
if(node) {
var tagname = node.tagName.toLowerCase();
if(typeof(filter) == WYMeditor.STRING) {
while(tagname != filter && tagname != WYMeditor.BODY) {
node = node.parentNode;
tagname = node.tagName.toLowerCase();
}
} else {
var bFound = false;
while(!bFound && tagname != WYMeditor.BODY) {
for(var i = 0; i < filter.length; i++) {
if(tagname == filter[i]) {
bFound = true;
break;
}
}
if(!bFound) {
node = node.parentNode;
tagname = node.tagName.toLowerCase();
}
}
}
if(tagname != WYMeditor.BODY) return(node);
else return(null);
} else return(null);
};
/* @name switchTo
* @description Switch the node's type
*/
WYMeditor.editor.prototype.switchTo = function(node,sType) {
var newNode = this._doc.createElement(sType);
var html = jQuery(node).html();
node.parentNode.replaceChild(newNode,node);
jQuery(newNode).html(html);
this.setFocusToNode(newNode);
};
WYMeditor.editor.prototype.replaceStrings = function(sVal) {
//check if the language file has already been loaded
//if not, get it via a synchronous ajax call
if(!WYMeditor.STRINGS[this._options.lang]) {
try {
eval(jQuery.ajax({url:this._options.langPath
+ this._options.lang + '.js', async:false}).responseText);
} catch(e) {
WYMeditor.console.error("WYMeditor: error while parsing language file.");
return sVal;
}
}
//replace all the strings in sVal and return it
for (var key in WYMeditor.STRINGS[this._options.lang]) {
sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key
+ this._options.stringDelimiterRight,
WYMeditor.STRINGS[this._options.lang][key]);
};
return(sVal);
};
WYMeditor.editor.prototype.encloseString = function(sVal) {
return(this._options.stringDelimiterLeft
+ sVal
+ this._options.stringDelimiterRight);
};
/* @name status
* @description Prints a status message
*/
WYMeditor.editor.prototype.status = function(sMessage) {
//print status message
jQuery(this._box).find(this._options.statusSelector).html(sMessage);
};
/* @name update
* @description Updates the element and textarea values
*/
WYMeditor.editor.prototype.update = function() {
var html = this.xhtml();
jQuery(this._element).val(html);
jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147
};
/* @name dialog
* @description Opens a dialog box
*/
WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
var features = dialogFeatures || this._wym._options.dialogFeatures;
var wDialog = window.open('', 'dialog', features);
if(wDialog) {
var sBodyHtml = "";
switch( dialogType ) {
case(WYMeditor.DIALOG_LINK):
sBodyHtml = this._options.dialogLinkHtml;
break;
case(WYMeditor.DIALOG_IMAGE):
sBodyHtml = this._options.dialogImageHtml;
break;
case(WYMeditor.DIALOG_TABLE):
sBodyHtml = this._options.dialogTableHtml;
break;
case(WYMeditor.DIALOG_PASTE):
sBodyHtml = this._options.dialogPasteHtml;
break;
case(WYMeditor.PREVIEW):
sBodyHtml = this._options.dialogPreviewHtml;
break;
default:
sBodyHtml = bodyHtml;
}
var h = WYMeditor.Helper;
//construct the dialog
var dialogHtml = this._options.dialogHtml;
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType ));
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml);
dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index);
dialogHtml = this.replaceStrings(dialogHtml);
var doc = wDialog.document;
doc.write(dialogHtml);
doc.close();
}
};
/* @name toggleHtml
* @description Shows/Hides the HTML
*/
WYMeditor.editor.prototype.toggleHtml = function() {
jQuery(this._box).find(this._options.htmlSelector).toggle();
};
WYMeditor.editor.prototype.uniqueStamp = function() {
var now = new Date();
return("wym-" + now.getTime());
};
WYMeditor.editor.prototype.paste = function(sData) {
var sTmp;
var container = this.selected();
//split the data, using double newlines as the separator
var aP = sData.split(this._newLine + this._newLine);
var rExp = new RegExp(this._newLine, "g");
//add a P for each item
if(container && container.tagName.toLowerCase() != WYMeditor.BODY) {
for(x = aP.length - 1; x >= 0; x--) {
sTmp = aP[x];
//simple newlines are replaced by a break
sTmp = sTmp.replace(rExp, "<br />");
jQuery(container).after("<p>" + sTmp + "</p>");
}
} else {
for(x = 0; x < aP.length; x++) {
sTmp = aP[x];
//simple newlines are replaced by a break
sTmp = sTmp.replace(rExp, "<br />");
jQuery(this._doc.body).append("<p>" + sTmp + "</p>");
}
}
};
WYMeditor.editor.prototype.insert = function(html) {
// Do we have a selection?
if (this._iframe.contentWindow.getSelection().focusNode != null) {
// Overwrite selection with provided html
this._exec( WYMeditor.INSERT_HTML, html);
} else {
// Fall back to the internal paste function if there's no selection
this.paste(html)
}
};
WYMeditor.editor.prototype.wrap = function(left, right) {
// Do we have a selection?
if (this._iframe.contentWindow.getSelection().focusNode != null) {
// Wrap selection with provided html
this._exec( WYMeditor.INSERT_HTML, left + this._iframe.contentWindow.getSelection().toString() + right);
}
};
WYMeditor.editor.prototype.unwrap = function() {
// Do we have a selection?
if (this._iframe.contentWindow.getSelection().focusNode != null) {
// Unwrap selection
this._exec( WYMeditor.INSERT_HTML, this._iframe.contentWindow.getSelection().toString() );
}
};
WYMeditor.editor.prototype.addCssRules = function(doc, aCss) {
var styles = doc.styleSheets[0];
if(styles) {
for(var i = 0; i < aCss.length; i++) {
var oCss = aCss[i];
if(oCss.name && oCss.css) this.addCssRule(styles, oCss);
}
}
};
/********** CONFIGURATION **********/
WYMeditor.editor.prototype.computeBasePath = function() {
return jQuery(jQuery.grep(jQuery('script'), function(s){
return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
})).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, '');
};
WYMeditor.editor.prototype.computeWymPath = function() {
return jQuery(jQuery.grep(jQuery('script'), function(s){
return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
})).attr('src');
};
WYMeditor.editor.prototype.computeJqueryPath = function() {
return jQuery(jQuery.grep(jQuery('script'), function(s){
return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
})).attr('src');
};
WYMeditor.editor.prototype.computeCssPath = function() {
return jQuery(jQuery.grep(jQuery('link'), function(s){
return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ ))
})).attr('href');
};
WYMeditor.editor.prototype.configureEditorUsingRawCss = function() {
var CssParser = new WYMeditor.WymCssParser();
if(this._options.stylesheet){
CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText);
}else{
CssParser.parse(this._options.styles, false);
}
if(this._options.classesItems.length == 0) {
this._options.classesItems = CssParser.css_settings.classesItems;
}
if(this._options.editorStyles.length == 0) {
this._options.editorStyles = CssParser.css_settings.editorStyles;
}
if(this._options.dialogStyles.length == 0) {
this._options.dialogStyles = CssParser.css_settings.dialogStyles;
}
};
/********** EVENTS **********/
WYMeditor.editor.prototype.listen = function() {
//don't use jQuery.find() on the iframe body
//because of MSIE + jQuery + expando issue (#JQ1143)
//jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup);
jQuery(this._doc.body).bind("mousedown", this.mousedown);
var images = this._doc.body.getElementsByTagName("img");
for(var i=0; i < images.length; i++) {
jQuery(images[i]).bind("mousedown", this.mousedown);
}
};
WYMeditor.editor.prototype.mousedown = function(evt) {
var wym = WYMeditor.INSTANCES[this.ownerDocument.title];
wym._selected_image = (this.tagName.toLowerCase() == WYMeditor.IMG) ? this : null;
evt.stopPropagation();
};
/********** SKINS **********/
/*
* Function: WYMeditor.loadCss
* Loads a stylesheet in the document.
*
* Parameters:
* href - The CSS path.
*/
WYMeditor.loadCss = function(href) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
var head = jQuery('head').get(0);
head.appendChild(link);
};
/*
* Function: WYMeditor.editor.loadSkin
* Loads the skin CSS and initialization script (if needed).
*/
WYMeditor.editor.prototype.loadSkin = function() {
//does the user want to automatically load the CSS (default: yes)?
//we also test if it hasn't been already loaded by another instance
//see below for a better (second) test
if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) {
//check if it hasn't been already loaded
//so we don't load it more than once
//(we check the existing <link> elements)
var found = false;
var rExp = new RegExp(this._options.skin
+ '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$');
jQuery('link').each( function() {
if(this.href.match(rExp)) found = true;
});
//load it, using the skin path
if(!found) WYMeditor.loadCss( this._options.skinPath
+ WYMeditor.SKINS_DEFAULT_CSS );
}
//put the classname (ex. wym_skin_default) on wym_box
jQuery(this._box).addClass( "wym_skin_" + this._options.skin );
//does the user want to use some JS to initialize the skin (default: yes)?
//also check if it hasn't already been loaded by another instance
if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) {
eval(jQuery.ajax({url:this._options.skinPath
+ WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText);
}
//init the skin, if needed
if(WYMeditor.SKINS[this._options.skin]
&& WYMeditor.SKINS[this._options.skin].init)
WYMeditor.SKINS[this._options.skin].init(this);
};
/********** DIALOGS **********/
WYMeditor.INIT_DIALOG = function(index) {
var wym = window.opener.WYMeditor.INSTANCES[index];
var doc = window.document;
var selected = wym.selected();
var dialogType = jQuery(wym._options.dialogTypeSelector).val();
var sStamp = wym.uniqueStamp();
switch(dialogType) {
case WYMeditor.DIALOG_LINK:
//ensure that we select the link to populate the fields
if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A)
selected = jQuery(selected).parentsOrSelf(WYMeditor.A);
//fix MSIE selection if link image has been clicked
if(!selected && wym._selected_image)
selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A);
break;
}
//pre-init functions
if(jQuery.isFunction(wym._options.preInitDialog))
wym._options.preInitDialog(wym,window);
//add css rules from options
var styles = doc.styleSheets[0];
var aCss = eval(wym._options.dialogStyles);
wym.addCssRules(doc, aCss);
//auto populate fields if selected container (e.g. A)
if(selected) {
jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));
jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));
jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));
jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT));
}
//auto populate image fields if selected image
if(wym._selected_image) {
jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector)
.val(jQuery(wym._selected_image).attr(WYMeditor.SRC));
jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector)
.val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));
jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector)
.val(jQuery(wym._selected_image).attr(WYMeditor.ALT));
}
jQuery(wym._options.dialogLinkSelector + " "
+ wym._options.submitSelector).click(function() {
var sUrl = jQuery(wym._options.hrefSelector).val();
if(sUrl.length > 0) {
wym._exec(WYMeditor.CREATE_LINK, sStamp);
jQuery("a[href=" + sStamp + "]", wym._doc.body)
.attr(WYMeditor.HREF, sUrl)
.attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val());
}
window.close();
});
jQuery(wym._options.dialogImageSelector + " "
+ wym._options.submitSelector).click(function() {
var sUrl = jQuery(wym._options.srcSelector).val();
if(sUrl.length > 0) {
wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
jQuery("img[src$=" + sStamp + "]", wym._doc.body)
.attr(WYMeditor.SRC, sUrl)
.attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
.attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val());
}
window.close();
});
jQuery(wym._options.dialogTableSelector + " "
+ wym._options.submitSelector).click(function() {
var iRows = jQuery(wym._options.rowsSelector).val();
var iCols = jQuery(wym._options.colsSelector).val();
if(iRows > 0 && iCols > 0) {
var table = wym._doc.createElement(WYMeditor.TABLE);
var newRow = null;
var newCol = null;
var sCaption = jQuery(wym._options.captionSelector).val();
//we create the caption
var newCaption = table.createCaption();
newCaption.innerHTML = sCaption;
//we create the rows and cells
for(x=0; x<iRows; x++) {
newRow = table.insertRow(x);
for(y=0; y<iCols; y++) {newRow.insertCell(y);}
}
//set the summary attr
jQuery(table).attr('summary',
jQuery(wym._options.summarySelector).val());
//append the table after the selected container
var node = jQuery(wym.findUp(wym.container(),
WYMeditor.MAIN_CONTAINERS)).get(0);
if(!node || !node.parentNode) jQuery(wym._doc.body).append(table);
else jQuery(node).after(table);
}
window.close();
});
jQuery(wym._options.dialogPasteSelector + " "
+ wym._options.submitSelector).click(function() {
var sText = jQuery(wym._options.textSelector).val();
wym.paste(sText);
window.close();
});
jQuery(wym._options.dialogPreviewSelector + " "
+ wym._options.previewSelector)
.html(wym.xhtml());
//cancel button
jQuery(wym._options.cancelSelector).mousedown(function() {
window.close();
});
//pre-init functions
if(jQuery.isFunction(wym._options.postInitDialog))
wym._options.postInitDialog(wym,window);
};
/********** XHTML LEXER/PARSER **********/
/*
* @name xml
* @description Use these methods to generate XML and XHTML compliant tags and
* escape tag attributes correctly
* @author Bermi Ferrer - http://bermi.org
* @author David Heinemeier Hansson http://loudthinking.com
*/
WYMeditor.XmlHelper = function()
{
this._entitiesDiv = document.createElement('div');
return this;
};
/*
* @name tag
* @description
* Returns an empty HTML tag of type *name* which by default is XHTML
* compliant. Setting *open* to true will create an open tag compatible
* with HTML 4.0 and below. Add HTML attributes by passing an attributes
* array to *options*. For attributes with no value like (disabled and
* readonly), give it a value of true in the *options* array.
*
* Examples:
*
* this.tag('br')
* # => <br />
* this.tag ('br', false, true)
* # => <br>
* this.tag ('input', jQuery({type:'text',disabled:true }) )
* # => <input type="text" disabled="disabled" />
*/
WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
{
options = options || false;
open = open || false;
return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
};
/*
* @name contentTag
* @description
* Returns a XML block tag of type *name* surrounding the *content*. Add
* XML attributes by passing an attributes array to *options*. For attributes
* with no value like (disabled and readonly), give it a value of true in
* the *options* array. You can use symbols or strings for the attribute names.
*
* this.contentTag ('p', 'Hello world!' )
* # => <p>Hello world!</p>
* this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"}))
* # => <div class="strong"><p>Hello world!</p></div>
* this.contentTag("select", options, jQuery({multiple : true}))
* # => <select multiple="multiple">...options...</select>
*/
WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
{
options = options || false;
return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
};
/*
* @name cdataSection
* @description
* Returns a CDATA section for the given +content+. CDATA sections
* are used to escape blocks of text containing characters which would
* otherwise be recognized as markup. CDATA sections begin with the string
* <tt>&lt;![CDATA[</tt> and } with (and may not contain) the string
* <tt>]]></tt>.
*/
WYMeditor.XmlHelper.prototype.cdataSection = function(content)
{
return '<![CDATA['+content+']]>';
};
/*
* @name escapeOnce
* @description
* Returns the escaped +xml+ without affecting existing escaped entities.
*
* this.escapeOnce( "1 > 2 &amp; 3")
* # => "1 &gt; 2 &amp; 3"
*/
WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
{
return this._fixDoubleEscape(this.escapeEntities(xml));
};
/*
* @name _fixDoubleEscape
* @description
* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
*/
WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
{
return escaped.replace(/&amp;([a-z]+|(#\d+));/ig, "&$1;");
};
/*
* @name tagOptions
* @description
* Takes an array like the one generated by Tag.parseAttributes
* [["src", "http://www.editam.com/?a=b&c=d&amp;f=g"], ["title", "Editam, <Simplified> CMS"]]
* or an object like {src:"http://www.editam.com/?a=b&c=d&amp;f=g", title:"Editam, <Simplified> CMS"}
* and returns a string properly escaped like
* ' src = "http://www.editam.com/?a=b&amp;c=d&amp;f=g" title = "Editam, &lt;Simplified&gt; CMS"'
* which is valid for strict XHTML
*/
WYMeditor.XmlHelper.prototype.tagOptions = function(options)
{
var xml = this;
xml._formated_options = '';
for (var key in options) {
var formated_options = '';
var value = options[key];
if(typeof value != 'function' && value.length > 0) {
if(parseInt(key) == key && typeof value == 'object'){
key = value.shift();
value = value.pop();
}
if(key != '' && value != ''){
xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
}
}
}
return xml._formated_options;
};
/*
* @name escapeEntities
* @description
* Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
* will not escape ". If set to true it will also escape '
*/
WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
{
this._entitiesDiv.innerHTML = string;
this._entitiesDiv.textContent = string;
var result = this._entitiesDiv.innerHTML;
if(typeof escape_quotes == 'undefined'){
if(escape_quotes != false) result = result.replace('"', '&quot;');
if(escape_quotes == true) result = result.replace('"', '&#039;');
}
return result;
};
/*
* Parses a string conatining tag attributes and values an returns an array formated like
* [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
*/
WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
{
// Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
var result = [];
var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
if(matches.toString() != tag_attributes){
for (var k in matches) {
var v = matches[k];
if(typeof v != 'function' && v.length != 0){
var re = new RegExp('(\\w+)\\s*'+v);
if(match = tag_attributes.match(re) ){
var value = v.replace(/^[\s=]+/, "");
var delimiter = value.charAt(0);
delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
if(delimiter != ''){
value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
}
tag_attributes = tag_attributes.replace(match[0],'');
result.push([match[1] , value]);
}
}
}
}
return result;
};
/**
* XhtmlValidator for validating tag attributes
*
* @author Bermi Ferrer - http://bermi.org
*/
WYMeditor.XhtmlValidator = {
"_attributes":
{
"core":
{
"except":[
"base",
"head",
"html",
"meta",
"param",
"script",
"style",
"title"
],
"attributes":[
"class",
"id",
"style",
"title",
"accesskey",
"tabindex"
]
},
"language":
{
"except":[
"base",
"br",
"hr",
"iframe",
"param",
"script"
],
"attributes":
{
"dir":[
"ltr",
"rtl"
],
"0":"lang",
"1":"xml:lang"
}
},
"keyboard":
{
"attributes":
{
"accesskey":/^(\w){1}$/,
"tabindex":/^(\d)+$/
}
}
},
"_events":
{
"window":
{
"only":[
"body"
],
"attributes":[
"onload",
"onunload"
]
},
"form":
{
"only":[
"form",
"input",
"textarea",
"select",
"a",
"label",
"button"
],
"attributes":[
"onchange",
"onsubmit",
"onreset",
"onselect",
"onblur",
"onfocus"
]
},
"keyboard":
{
"except":[
"base",
"bdo",
"br",
"frame",
"frameset",
"head",
"html",
"iframe",
"meta",
"param",
"script",
"style",
"title"
],
"attributes":[
"onkeydown",
"onkeypress",
"onkeyup"
]
},
"mouse":
{
"except":[
"base",
"bdo",
"br",
"head",
"html",
"meta",
"param",
"script",
"style",
"title"
],
"attributes":[
"onclick",
"ondblclick",
"onmousedown",
"onmousemove",
"onmouseover",
"onmouseout",
"onmouseup"
]
}
},
"_tags":
{
"a":
{
"attributes":
{
"0":"charset",
"1":"coords",
"2":"href",
"3":"hreflang",
"4":"name",
"rel":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
"rev":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
"shape":/^(rect|rectangle|circ|circle|poly|polygon)$/,
"5":"type"
}
},
"0":"abbr",
"1":"acronym",
"2":"address",
"area":
{
"attributes":
{
"0":"alt",
"1":"coords",
"2":"href",
"nohref":/^(true|false)$/,
"shape":/^(rect|rectangle|circ|circle|poly|polygon)$/
},
"required":[
"alt"
]
},
"3":"b",
"base":
{
"attributes":[
"href"
],
"required":[
"href"
]
},
"bdo":
{
"attributes":
{
"dir":/^(ltr|rtl)$/
},
"required":[
"dir"
]
},
"4":"big",
"blockquote":
{
"attributes":[
"cite"
]
},
"5":"body",
"6":"br",
"button":
{
"attributes":
{
"disabled":/^(disabled)$/,
"type":/^(button|reset|submit)$/,
"0":"value"
},
"inside":"form"
},
"7":"caption",
"8":"cite",
"9":"code",
"col":
{
"attributes":
{
"align":/^(right|left|center|justify)$/,
"0":"char",
"1":"charoff",
"span":/^(\d)+$/,
"valign":/^(top|middle|bottom|baseline)$/,
"2":"width"
},
"inside":"colgroup"
},
"colgroup":
{
"attributes":
{
"align":/^(right|left|center|justify)$/,
"0":"char",
"1":"charoff",
"span":/^(\d)+$/,
"valign":/^(top|middle|bottom|baseline)$/,
"2":"width"
}
},
"10":"dd",
"del":
{
"attributes":
{
"0":"cite",
"datetime":/^([0-9]){8}/
}
},
"11":"div",
"12":"dfn",
"13":"dl",
"14":"dt",
"15":"em",
"fieldset":
{
"inside":"form"
},
"form":
{
"attributes":
{
"0":"action",
"1":"accept",
"2":"accept-charset",
"3":"enctype",
"method":/^(get|post)$/
},
"required":[
"action"
]
},
"head":
{
"attributes":[
"profile"
]
},
"16":"h1",
"17":"h2",
"18":"h3",
"19":"h4",
"20":"h5",
"21":"h6",
"22":"hr",
"html":
{
"attributes":[
"xmlns"
]
},
"23":"i",
"img":
{
"attributes":[
"alt",
"src",
"height",
"ismap",
"longdesc",
"usemap",
"width"
],
"required":[
"alt",
"src"
]
},
"input":
{
"attributes":
{
"0":"accept",
"1":"alt",
"checked":/^(checked)$/,
"disabled":/^(disabled)$/,
"maxlength":/^(\d)+$/,
"2":"name",
"readonly":/^(readonly)$/,
"size":/^(\d)+$/,
"3":"src",
"type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,
"4":"value"
},
"inside":"form"
},
"ins":
{
"attributes":
{
"0":"cite",
"datetime":/^([0-9]){8}/
}
},
"24":"kbd",
"label":
{
"attributes":[
"for"
],
"inside":"form"
},
"25":"legend",
"26":"li",
"link":
{
"attributes":
{
"0":"charset",
"1":"href",
"2":"hreflang",
"media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i,
//next comment line required by Opera!
/*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/
"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
"rev":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
"3":"type"
},
"inside":"head"
},
"map":
{
"attributes":[
"id",
"name"
],
"required":[
"id"
]
},
"meta":
{
"attributes":
{
"0":"content",
"http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i,
"1":"name",
"2":"scheme"
},
"required":[
"content"
]
},
"27":"noscript",
"object":
{
"attributes":[
"archive",
"classid",
"codebase",
"codetype",
"data",
"declare",
"height",
"name",
"standby",
"type",
"usemap",
"width"
]
},
"28":"ol",
"optgroup":
{
"attributes":
{
"0":"label",
"disabled": /^(disabled)$/
},
"required":[
"label"
]
},
"option":
{
"attributes":
{
"0":"label",
"disabled":/^(disabled)$/,
"selected":/^(selected)$/,
"1":"value"
},
"inside":"select"
},
"29":"p",
"param":
{
"attributes":
{
"0":"type",
"valuetype":/^(data|ref|object)$/,
"1":"valuetype",
"2":"value"
},
"required":[
"name"
]
},
"30":"pre",
"q":
{
"attributes":[
"cite"
]
},
"31":"samp",
"script":
{
"attributes":
{
"type":/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/,
"0":"charset",
"defer":/^(defer)$/,
"1":"src"
},
"required":[
"type"
]
},
"select":
{
"attributes":
{
"disabled":/^(disabled)$/,
"multiple":/^(multiple)$/,
"0":"name",
"1":"size"
},
"inside":"form"
},
"32":"small",
"33":"span",
"34":"strong",
"style":
{
"attributes":
{
"0":"type",
"media":/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/
},
"required":[
"type"
]
},
"35":"sub",
"36":"sup",
"table":
{
"attributes":
{
"0":"border",
"1":"cellpadding",
"2":"cellspacing",
"frame":/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/,
"rules":/^(none|groups|rows|cols|all)$/,
"3":"summary",
"4":"width"
}
},
"tbody":
{
"attributes":
{
"align":/^(right|left|center|justify)$/,
"0":"char",
"1":"charoff",
"valign":/^(top|middle|bottom|baseline)$/
}
},
"td":
{
"attributes":
{
"0":"abbr",
"align":/^(left|right|center|justify|char)$/,
"1":"axis",
"2":"char",
"3":"charoff",
"colspan":/^(\d)+$/,
"4":"headers",
"rowspan":/^(\d)+$/,
"scope":/^(col|colgroup|row|rowgroup)$/,
"valign":/^(top|middle|bottom|baseline)$/
}
},
"textarea":
{
"attributes":[
"cols",
"rows",
"disabled",
"name",
"readonly"
],
"required":[
"cols",
"rows"
],
"inside":"form"
},
"tfoot":
{
"attributes":
{
"align":/^(right|left|center|justify)$/,
"0":"char",
"1":"charoff",
"valign":/^(top|middle|bottom)$/,
"2":"baseline"
}
},
"th":
{
"attributes":
{
"0":"abbr",
"align":/^(left|right|center|justify|char)$/,
"1":"axis",
"2":"char",
"3":"charoff",
"colspan":/^(\d)+$/,
"4":"headers",
"rowspan":/^(\d)+$/,
"scope":/^(col|colgroup|row|rowgroup)$/,
"valign":/^(top|middle|bottom|baseline)$/
}
},
"thead":
{
"attributes":
{
"align":/^(right|left|center|justify)$/,
"0":"char",
"1":"charoff",
"valign":/^(top|middle|bottom|baseline)$/
}
},
"37":"title",
"tr":
{
"attributes":
{
"align":/^(right|left|center|justify|char)$/,
"0":"char",
"1":"charoff",
"valign":/^(top|middle|bottom|baseline)$/
}
},
"38":"tt",
"39":"ul",
"40":"var"
},
// Temporary skiped attributes
skiped_attributes : [],
skiped_attribute_values : [],
getValidTagAttributes: function(tag, attributes)
{
var valid_attributes = {};
var possible_attributes = this.getPossibleTagAttributes(tag);
for(var attribute in attributes) {
var value = attributes[attribute];
var h = WYMeditor.Helper;
if(!h.contains(this.skiped_attributes, attribute) && !h.contains(this.skiped_attribute_values, value)){
if (typeof value != 'function' && h.contains(possible_attributes, attribute)) {
if (this.doesAttributeNeedsValidation(tag, attribute)) {
if(this.validateAttribute(tag, attribute, value)){
valid_attributes[attribute] = value;
}
}else{
valid_attributes[attribute] = value;
}
}
}
}
return valid_attributes;
},
getUniqueAttributesAndEventsForTag : function(tag)
{
var result = [];
if (this._tags[tag] && this._tags[tag]['attributes']) {
for (k in this._tags[tag]['attributes']) {
result.push(parseInt(k) == k ? this._tags[tag]['attributes'][k] : k);
}
}
return result;
},
getDefaultAttributesAndEventsForTags : function()
{
var result = [];
for (var key in this._events){
result.push(this._events[key]);
}
for (var key in this._attributes){
result.push(this._attributes[key]);
}
return result;
},
isValidTag : function(tag)
{
if(this._tags[tag]){
return true;
}
for(var key in this._tags){
if(this._tags[key] == tag){
return true;
}
}
return false;
},
getDefaultAttributesAndEventsForTag : function(tag)
{
var default_attributes = [];
if (this.isValidTag(tag)) {
var default_attributes_and_events = this.getDefaultAttributesAndEventsForTags();
for(var key in default_attributes_and_events) {
var defaults = default_attributes_and_events[key];
if(typeof defaults == 'object'){
var h = WYMeditor.Helper;
if ((defaults['except'] && h.contains(defaults['except'], tag)) || (defaults['only'] && !h.contains(defaults['only'], tag))) {
continue;
}
var tag_defaults = defaults['attributes'] ? defaults['attributes'] : defaults['events'];
for(var k in tag_defaults) {
default_attributes.push(typeof tag_defaults[k] != 'string' ? k : tag_defaults[k]);
}
}
}
}
return default_attributes;
},
doesAttributeNeedsValidation: function(tag, attribute)
{
return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] &&
WYMeditor.Helper.contains(this._tags[tag]['required'], attribute)));
},
validateAttribute : function(tag, attribute, value)
{
if ( this._tags[tag] &&
(this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute] && value.length > 0 && !value.match(this._tags[tag]['attributes'][attribute])) || // invalid format
(this._tags[tag] && this._tags[tag]['required'] && WYMeditor.Helper.contains(this._tags[tag]['required'], attribute) && value.length == 0) // required attribute
) {
return false;
}
return typeof this._tags[tag] != 'undefined';
},
getPossibleTagAttributes : function(tag)
{
if (!this._possible_tag_attributes) {
this._possible_tag_attributes = {};
}
if (!this._possible_tag_attributes[tag]) {
this._possible_tag_attributes[tag] = this.getUniqueAttributesAndEventsForTag(tag).concat(this.getDefaultAttributesAndEventsForTag(tag));
}
return this._possible_tag_attributes[tag];
}
};
/**
* Compounded regular expression. Any of
* the contained patterns could match and
* when one does, it's label is returned.
*
* Constructor. Starts with no patterns.
* @param boolean case True for case sensitive, false
* for insensitive.
* @access public
* @author Marcus Baker (http://lastcraft.com)
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.ParallelRegex = function(case_sensitive)
{
this._case = case_sensitive;
this._patterns = [];
this._labels = [];
this._regex = null;
return this;
};
/**
* Adds a pattern with an optional label.
* @param string pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string label Label of regex to be returned
* on a match.
* @access public
*/
WYMeditor.ParallelRegex.prototype.addPattern = function(pattern, label)
{
label = label || true;
var count = this._patterns.length;
this._patterns[count] = pattern;
this._labels[count] = label;
this._regex = null;
};
/**
* Attempts to match all patterns at once against
* a string.
* @param string subject String to match against.
*
* @return boolean True on success.
* @return string match First matched portion of
* subject.
* @access public
*/
WYMeditor.ParallelRegex.prototype.match = function(subject)
{
if (this._patterns.length == 0) {
return [false, ''];
}
var matches = subject.match(this._getCompoundedRegex());
if(!matches){
return [false, ''];
}
var match = matches[0];
for (var i = 1; i < matches.length; i++) {
if (matches[i]) {
return [this._labels[i-1], match];
}
}
return [true, matches[0]];
};
/**
* Compounds the patterns into a single
* regular expression separated with the
* "or" operator. Caches the regex.
* Will automatically escape (, ) and / tokens.
* @param array patterns List of patterns in order.
* @access private
*/
WYMeditor.ParallelRegex.prototype._getCompoundedRegex = function()
{
if (this._regex == null) {
for (var i = 0, count = this._patterns.length; i < count; i++) {
this._patterns[i] = '(' + this._untokenizeRegex(this._tokenizeRegex(this._patterns[i]).replace(/([\/\(\)])/g,'\\$1')) + ')';
}
this._regex = new RegExp(this._patterns.join("|") ,this._getPerlMatchingFlags());
}
return this._regex;
};
/**
* Escape lookahead/lookbehind blocks
*/
WYMeditor.ParallelRegex.prototype._tokenizeRegex = function(regex)
{
return regex.
replace(/\(\?(i|m|s|x|U)\)/, '~~~~~~Tk1\$1~~~~~~').
replace(/\(\?(\-[i|m|s|x|U])\)/, '~~~~~~Tk2\$1~~~~~~').
replace(/\(\?\=(.*)\)/, '~~~~~~Tk3\$1~~~~~~').
replace(/\(\?\!(.*)\)/, '~~~~~~Tk4\$1~~~~~~').
replace(/\(\?\<\=(.*)\)/, '~~~~~~Tk5\$1~~~~~~').
replace(/\(\?\<\!(.*)\)/, '~~~~~~Tk6\$1~~~~~~').
replace(/\(\?\:(.*)\)/, '~~~~~~Tk7\$1~~~~~~');
};
/**
* Unscape lookahead/lookbehind blocks
*/
WYMeditor.ParallelRegex.prototype._untokenizeRegex = function(regex)
{
return regex.
replace(/~~~~~~Tk1(.{1})~~~~~~/, "(?\$1)").
replace(/~~~~~~Tk2(.{2})~~~~~~/, "(?\$1)").
replace(/~~~~~~Tk3(.*)~~~~~~/, "(?=\$1)").
replace(/~~~~~~Tk4(.*)~~~~~~/, "(?!\$1)").
replace(/~~~~~~Tk5(.*)~~~~~~/, "(?<=\$1)").
replace(/~~~~~~Tk6(.*)~~~~~~/, "(?<!\$1)").
replace(/~~~~~~Tk7(.*)~~~~~~/, "(?:\$1)");
};
/**
* Accessor for perl regex mode flags to use.
* @return string Perl regex flags.
* @access private
*/
WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags = function()
{
return (this._case ? "m" : "mi");
};
/**
* States for a stack machine.
*
* Constructor. Starts in named state.
* @param string start Starting state name.
* @access public
* @author Marcus Baker (http://lastcraft.com)
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.StateStack = function(start)
{
this._stack = [start];
return this;
};
/**
* Accessor for current state.
* @return string State.
* @access public
*/
WYMeditor.StateStack.prototype.getCurrent = function()
{
return this._stack[this._stack.length - 1];
};
/**
* Adds a state to the stack and sets it
* to be the current state.
* @param string state New state.
* @access public
*/
WYMeditor.StateStack.prototype.enter = function(state)
{
this._stack.push(state);
};
/**
* Leaves the current state and reverts
* to the previous one.
* @return boolean False if we drop off
* the bottom of the list.
* @access public
*/
WYMeditor.StateStack.prototype.leave = function()
{
if (this._stack.length == 1) {
return false;
}
this._stack.pop();
return true;
};
// GLOBALS
WYMeditor.LEXER_ENTER = 1;
WYMeditor.LEXER_MATCHED = 2;
WYMeditor.LEXER_UNMATCHED = 3;
WYMeditor.LEXER_EXIT = 4;
WYMeditor.LEXER_SPECIAL = 5;
/**
* Accepts text and breaks it into tokens.
* Some optimisation to make the sure the
* content is only scanned by the PHP regex
* parser once. Lexer modes must not start
* with leading underscores.
*
* Sets up the lexer in case insensitive matching
* by default.
* @param Parser parser Handling strategy by reference.
* @param string start Starting handler.
* @param boolean case True for case sensitive.
* @access public
* @author Marcus Baker (http://lastcraft.com)
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.Lexer = function(parser, start, case_sensitive)
{
start = start || 'accept';
this._case = case_sensitive || false;
this._regexes = {};
this._parser = parser;
this._mode = new WYMeditor.StateStack(start);
this._mode_handlers = {};
this._mode_handlers[start] = start;
return this;
};
/**
* Adds a token search pattern for a particular
* parsing mode. The pattern does not change the
* current mode.
* @param string pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string mode Should only apply this
* pattern when dealing with
* this type of input.
* @access public
*/
WYMeditor.Lexer.prototype.addPattern = function(pattern, mode)
{
var mode = mode || "accept";
if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern);
if (typeof this._mode_handlers[mode] == 'undefined') {
this._mode_handlers[mode] = mode;
}
};
/**
* Adds a pattern that will enter a new parsing
* mode. Useful for entering parenthesis, strings,
* tags, etc.
* @param string pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string new_mode Change parsing to this new
* nested mode.
* @access public
*/
WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode)
{
if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, new_mode);
if (typeof this._mode_handlers[new_mode] == 'undefined') {
this._mode_handlers[new_mode] = new_mode;
}
};
/**
* Adds a pattern that will exit the current mode
* and re-enter the previous one.
* @param string pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string mode Mode to leave.
* @access public
*/
WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode)
{
if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, "__exit");
if (typeof this._mode_handlers[mode] == 'undefined') {
this._mode_handlers[mode] = mode;
}
};
/**
* Adds a pattern that has a special mode. Acts as an entry
* and exit pattern in one go, effectively calling a special
* parser handler for this token only.
* @param string pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string special Use this mode for this one token.
* @access public
*/
WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special)
{
if (typeof this._regexes[mode] == 'undefined') {
this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
}
this._regexes[mode].addPattern(pattern, '_'+special);
if (typeof this._mode_handlers[special] == 'undefined') {
this._mode_handlers[special] = special;
}
};
/**
* Adds a mapping from a mode to another handler.
* @param string mode Mode to be remapped.
* @param string handler New target handler.
* @access public
*/
WYMeditor.Lexer.prototype.mapHandler = function(mode, handler)
{
this._mode_handlers[mode] = handler;
};
/**
* Splits the page text into tokens. Will fail
* if the handlers report an error or if no
* content is consumed. If successful then each
* unparsed and parsed token invokes a call to the
* held listener.
* @param string raw Raw HTML text.
* @return boolean True on success, else false.
* @access public
*/
WYMeditor.Lexer.prototype.parse = function(raw)
{
if (typeof this._parser == 'undefined') {
return false;
}
var length = raw.length;
var parsed;
while (typeof (parsed = this._reduce(raw)) == 'object') {
var raw = parsed[0];
var unmatched = parsed[1];
var matched = parsed[2];
var mode = parsed[3];
if (! this._dispatchTokens(unmatched, matched, mode)) {
return false;
}
if (raw == '') {
return true;
}
if (raw.length == length) {
return false;
}
length = raw.length;
}
if (! parsed ) {
return false;
}
return this._invokeParser(raw, WYMeditor.LEXER_UNMATCHED);
};
/**
* Sends the matched token and any leading unmatched
* text to the parser changing the lexer to a new
* mode if one is listed.
* @param string unmatched Unmatched leading portion.
* @param string matched Actual token match.
* @param string mode Mode after match. A boolean
* false mode causes no change.
* @return boolean False if there was any error
* from the parser.
* @access private
*/
WYMeditor.Lexer.prototype._dispatchTokens = function(unmatched, matched, mode)
{
mode = mode || false;
if (! this._invokeParser(unmatched, WYMeditor.LEXER_UNMATCHED)) {
return false;
}
if (typeof mode == 'boolean') {
return this._invokeParser(matched, WYMeditor.LEXER_MATCHED);
}
if (this._isModeEnd(mode)) {
if (! this._invokeParser(matched, WYMeditor.LEXER_EXIT)) {
return false;
}
return this._mode.leave();
}
if (this._isSpecialMode(mode)) {
this._mode.enter(this._decodeSpecial(mode));
if (! this._invokeParser(matched, WYMeditor.LEXER_SPECIAL)) {
return false;
}
return this._mode.leave();
}
this._mode.enter(mode);
return this._invokeParser(matched, WYMeditor.LEXER_ENTER);
};
/**
* Tests to see if the new mode is actually to leave
* the current mode and pop an item from the matching
* mode stack.
* @param string mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
WYMeditor.Lexer.prototype._isModeEnd = function(mode)
{
return (mode === "__exit");
};
/**
* Test to see if the mode is one where this mode
* is entered for this token only and automatically
* leaves immediately afterwoods.
* @param string mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
WYMeditor.Lexer.prototype._isSpecialMode = function(mode)
{
return (mode.substring(0,1) == "_");
};
/**
* Strips the magic underscore marking single token
* modes.
* @param string mode Mode to decode.
* @return string Underlying mode name.
* @access private
*/
WYMeditor.Lexer.prototype._decodeSpecial = function(mode)
{
return mode.substring(1);
};
/**
* Calls the parser method named after the current
* mode. Empty content will be ignored. The lexer
* has a parser handler for each mode in the lexer.
* @param string content Text parsed.
* @param boolean is_match Token is recognised rather
* than unparsed data.
* @access private
*/
WYMeditor.Lexer.prototype._invokeParser = function(content, is_match)
{
if (!/ +/.test(content) && ((content === '') || (content == false))) {
return true;
}
var current = this._mode.getCurrent();
var handler = this._mode_handlers[current];
var result;
eval('result = this._parser.' + handler + '(content, is_match);');
return result;
};
/**
* Tries to match a chunk of text and if successful
* removes the recognised chunk and any leading
* unparsed data. Empty strings will not be matched.
* @param string raw The subject to parse. This is the
* content that will be eaten.
* @return array/boolean Three item list of unparsed
* content followed by the
* recognised token and finally the
* action the parser is to take.
* True if no match, false if there
* is a parsing error.
* @access private
*/
WYMeditor.Lexer.prototype._reduce = function(raw)
{
var matched = this._regexes[this._mode.getCurrent()].match(raw);
var match = matched[1];
var action = matched[0];
if (action) {
var unparsed_character_count = raw.indexOf(match);
var unparsed = raw.substr(0, unparsed_character_count);
raw = raw.substring(unparsed_character_count + match.length);
return [raw, unparsed, match, action];
}
return true;
};
/**
* This are the rules for breaking the XHTML code into events
* handled by the provided parser.
*
* @author Marcus Baker (http://lastcraft.com)
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.XhtmlLexer = function(parser)
{
jQuery.extend(this, new WYMeditor.Lexer(parser, 'Text'));
this.mapHandler('Text', 'Text');
this.addTokens();
this.init();
return this;
};
WYMeditor.XhtmlLexer.prototype.init = function()
{
};
WYMeditor.XhtmlLexer.prototype.addTokens = function()
{
this.addCommentTokens('Text');
this.addScriptTokens('Text');
this.addCssTokens('Text');
this.addTagTokens('Text');
};
WYMeditor.XhtmlLexer.prototype.addCommentTokens = function(scope)
{
this.addEntryPattern("<!--", scope, 'Comment');
this.addExitPattern("-->", 'Comment');
};
WYMeditor.XhtmlLexer.prototype.addScriptTokens = function(scope)
{
this.addEntryPattern("<script", scope, 'Script');
this.addExitPattern("</script>", 'Script');
};
WYMeditor.XhtmlLexer.prototype.addCssTokens = function(scope)
{
this.addEntryPattern("<style", scope, 'Css');
this.addExitPattern("</style>", 'Css');
};
WYMeditor.XhtmlLexer.prototype.addTagTokens = function(scope)
{
this.addSpecialPattern("<\\s*[a-z0-9:\-]+\\s*>", scope, 'OpeningTag');
this.addEntryPattern("<[a-z0-9:\-]+"+'[\\\/ \\\>]+', scope, 'OpeningTag');
this.addInTagDeclarationTokens('OpeningTag');
this.addSpecialPattern("</\\s*[a-z0-9:\-]+\\s*>", scope, 'ClosingTag');
};
WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens = function(scope)
{
this.addSpecialPattern('\\s+', scope, 'Ignore');
this.addAttributeTokens(scope);
this.addExitPattern('/>', scope);
this.addExitPattern('>', scope);
};
WYMeditor.XhtmlLexer.prototype.addAttributeTokens = function(scope)
{
this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?=\=)\\s*", scope, 'TagAttributes');
this.addEntryPattern('=\\s*"', scope, 'DoubleQuotedAttribute');
this.addPattern("\\\\\"", 'DoubleQuotedAttribute');
this.addExitPattern('"', 'DoubleQuotedAttribute');
this.addEntryPattern("=\\s*'", scope, 'SingleQuotedAttribute');
this.addPattern("\\\\'", 'SingleQuotedAttribute');
this.addExitPattern("'", 'SingleQuotedAttribute');
this.addSpecialPattern('=\\s*[^>\\s]*', scope, 'UnquotedAttribute');
};
/**
* XHTML Parser.
*
* This XHTML parser will trigger the events available on on
* current SaxListener
*
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.XhtmlParser = function(Listener, mode)
{
var mode = mode || 'Text';
this._Lexer = new WYMeditor.XhtmlLexer(this);
this._Listener = Listener;
this._mode = mode;
this._matches = [];
this._last_match = '';
this._current_match = '';
return this;
};
WYMeditor.XhtmlParser.prototype.parse = function(raw)
{
this._Lexer.parse(this.beforeParsing(raw));
return this.afterParsing(this._Listener.getResult());
};
WYMeditor.XhtmlParser.prototype.beforeParsing = function(raw)
{
if(raw.match(/class="MsoNormal"/) || raw.match(/ns = "urn:schemas-microsoft-com/)){
// Usefull for cleaning up content pasted from other sources (MSWord)
this._Listener.avoidStylingTagsAndAttributes();
}
return this._Listener.beforeParsing(raw);
};
WYMeditor.XhtmlParser.prototype.afterParsing = function(parsed)
{
if(this._Listener._avoiding_tags_implicitly){
this._Listener.allowStylingTagsAndAttributes();
}
return this._Listener.afterParsing(parsed);
};
WYMeditor.XhtmlParser.prototype.Ignore = function(match, state)
{
return true;
};
WYMeditor.XhtmlParser.prototype.Text = function(text)
{
this._Listener.addContent(text);
return true;
};
WYMeditor.XhtmlParser.prototype.Comment = function(match, status)
{
return this._addNonTagBlock(match, status, 'addComment');
};
WYMeditor.XhtmlParser.prototype.Script = function(match, status)
{
return this._addNonTagBlock(match, status, 'addScript');
};
WYMeditor.XhtmlParser.prototype.Css = function(match, status)
{
return this._addNonTagBlock(match, status, 'addCss');
};
WYMeditor.XhtmlParser.prototype._addNonTagBlock = function(match, state, type)
{
switch (state){
case WYMeditor.LEXER_ENTER:
this._non_tag = match;
break;
case WYMeditor.LEXER_UNMATCHED:
this._non_tag += match;
break;
case WYMeditor.LEXER_EXIT:
switch(type) {
case 'addComment':
this._Listener.addComment(this._non_tag+match);
break;
case 'addScript':
this._Listener.addScript(this._non_tag+match);
break;
case 'addCss':
this._Listener.addCss(this._non_tag+match);
break;
}
}
return true;
};
WYMeditor.XhtmlParser.prototype.OpeningTag = function(match, state)
{
switch (state){
case WYMeditor.LEXER_ENTER:
this._tag = this.normalizeTag(match);
this._tag_attributes = {};
break;
case WYMeditor.LEXER_SPECIAL:
this._callOpenTagListener(this.normalizeTag(match));
break;
case WYMeditor.LEXER_EXIT:
this._callOpenTagListener(this._tag, this._tag_attributes);
}
return true;
};
WYMeditor.XhtmlParser.prototype.ClosingTag = function(match, state)
{
this._callCloseTagListener(this.normalizeTag(match));
return true;
};
WYMeditor.XhtmlParser.prototype._callOpenTagListener = function(tag, attributes)
{
var attributes = attributes || {};
this.autoCloseUnclosedBeforeNewOpening(tag);
if(this._Listener.isBlockTag(tag)){
this._Listener._tag_stack.push(tag);
this._Listener.fixNestingBeforeOpeningBlockTag(tag, attributes);
this._Listener.openBlockTag(tag, attributes);
this._increaseOpenTagCounter(tag);
}else if(this._Listener.isInlineTag(tag)){
this._Listener.inlineTag(tag, attributes);
}else{
this._Listener.openUnknownTag(tag, attributes);
this._increaseOpenTagCounter(tag);
}
this._Listener.last_tag = tag;
this._Listener.last_tag_opened = true;
this._Listener.last_tag_attributes = attributes;
};
WYMeditor.XhtmlParser.prototype._callCloseTagListener = function(tag)
{
if(this._decreaseOpenTagCounter(tag)){
this.autoCloseUnclosedBeforeTagClosing(tag);
if(this._Listener.isBlockTag(tag)){
var expected_tag = this._Listener._tag_stack.pop();
if(expected_tag == false){
return;
}else if(expected_tag != tag){
tag = expected_tag;
}
this._Listener.closeBlockTag(tag);
}else{
this._Listener.closeUnknownTag(tag);
}
}else{
this._Listener.closeUnopenedTag(tag);
}
this._Listener.last_tag = tag;
this._Listener.last_tag_opened = false;
};
WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter = function(tag)
{
this._Listener._open_tags[tag] = this._Listener._open_tags[tag] || 0;
this._Listener._open_tags[tag]++;
};
WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter = function(tag)
{
if(this._Listener._open_tags[tag]){
this._Listener._open_tags[tag]--;
if(this._Listener._open_tags[tag] == 0){
this._Listener._open_tags[tag] = undefined;
}
return true;
}
return false;
};
WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening = function(new_tag)
{
this._autoCloseUnclosed(new_tag, false);
};
WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing = function(tag)
{
this._autoCloseUnclosed(tag, true);
};
WYMeditor.XhtmlParser.prototype._autoCloseUnclosed = function(new_tag, closing)
{
var closing = closing || false;
if(this._Listener._open_tags){
for (var tag in this._Listener._open_tags) {
var counter = this._Listener._open_tags[tag];
if(counter > 0 && this._Listener.shouldCloseTagAutomatically(tag, new_tag, closing)){
this._callCloseTagListener(tag, true);
}
}
}
};
WYMeditor.XhtmlParser.prototype.getTagReplacements = function()
{
return this._Listener.getTagReplacements();
};
WYMeditor.XhtmlParser.prototype.normalizeTag = function(tag)
{
tag = tag.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,'').toLowerCase();
var tags = this._Listener.getTagReplacements();
if(tags[tag]){
return tags[tag];
}
return tag;
};
WYMeditor.XhtmlParser.prototype.TagAttributes = function(match, state)
{
if(WYMeditor.LEXER_SPECIAL == state){
this._current_attribute = match;
}
return true;
};
WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute = function(match, state)
{
if(WYMeditor.LEXER_UNMATCHED == state){
this._tag_attributes[this._current_attribute] = match;
}
return true;
};
WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute = function(match, state)
{
if(WYMeditor.LEXER_UNMATCHED == state){
this._tag_attributes[this._current_attribute] = match;
}
return true;
};
WYMeditor.XhtmlParser.prototype.UnquotedAttribute = function(match, state)
{
this._tag_attributes[this._current_attribute] = match.replace(/^=/,'');
return true;
};
/**
* XHTML Sax parser.
*
* @author Bermi Ferrer (http://bermi.org)
*/
WYMeditor.XhtmlSaxListener = function()
{
this.output = '';
this.helper = new WYMeditor.XmlHelper();
this._open_tags = {};
this.validator = WYMeditor.XhtmlValidator;
this._tag_stack = [];
this.avoided_tags = [];
this.entities = {
'&nbsp;':'&#160;','&iexcl;':'&#161;','&cent;':'&#162;',
'&pound;':'&#163;','&curren;':'&#164;','&yen;':'&#165;',
'&brvbar;':'&#166;','&sect;':'&#167;','&uml;':'&#168;',
'&copy;':'&#169;','&ordf;':'&#170;','&laquo;':'&#171;',
'&not;':'&#172;','&shy;':'&#173;','&reg;':'&#174;',
'&macr;':'&#175;','&deg;':'&#176;','&plusmn;':'&#177;',
'&sup2;':'&#178;','&sup3;':'&#179;','&acute;':'&#180;',
'&micro;':'&#181;','&para;':'&#182;','&middot;':'&#183;',
'&cedil;':'&#184;','&sup1;':'&#185;','&ordm;':'&#186;',
'&raquo;':'&#187;','&frac14;':'&#188;','&frac12;':'&#189;',
'&frac34;':'&#190;','&iquest;':'&#191;','&Agrave;':'&#192;',
'&Aacute;':'&#193;','&Acirc;':'&#194;','&Atilde;':'&#195;',
'&Auml;':'&#196;','&Aring;':'&#197;','&AElig;':'&#198;',
'&Ccedil;':'&#199;','&Egrave;':'&#200;','&Eacute;':'&#201;',
'&Ecirc;':'&#202;','&Euml;':'&#203;','&Igrave;':'&#204;',
'&Iacute;':'&#205;','&Icirc;':'&#206;','&Iuml;':'&#207;',
'&ETH;':'&#208;','&Ntilde;':'&#209;','&Ograve;':'&#210;',
'&Oacute;':'&#211;','&Ocirc;':'&#212;','&Otilde;':'&#213;',
'&Ouml;':'&#214;','&times;':'&#215;','&Oslash;':'&#216;',
'&Ugrave;':'&#217;','&Uacute;':'&#218;','&Ucirc;':'&#219;',
'&Uuml;':'&#220;','&Yacute;':'&#221;','&THORN;':'&#222;',
'&szlig;':'&#223;','&agrave;':'&#224;','&aacute;':'&#225;',
'&acirc;':'&#226;','&atilde;':'&#227;','&auml;':'&#228;',
'&aring;':'&#229;','&aelig;':'&#230;','&ccedil;':'&#231;',
'&egrave;':'&#232;','&eacute;':'&#233;','&ecirc;':'&#234;',
'&euml;':'&#235;','&igrave;':'&#236;','&iacute;':'&#237;',
'&icirc;':'&#238;','&iuml;':'&#239;','&eth;':'&#240;',
'&ntilde;':'&#241;','&ograve;':'&#242;','&oacute;':'&#243;',
'&ocirc;':'&#244;','&otilde;':'&#245;','&ouml;':'&#246;',
'&divide;':'&#247;','&oslash;':'&#248;','&ugrave;':'&#249;',
'&uacute;':'&#250;','&ucirc;':'&#251;','&uuml;':'&#252;',
'&yacute;':'&#253;','&thorn;':'&#254;','&yuml;':'&#255;',
'&OElig;':'&#338;','&oelig;':'&#339;','&Scaron;':'&#352;',
'&scaron;':'&#353;','&Yuml;':'&#376;','&fnof;':'&#402;',
'&circ;':'&#710;','&tilde;':'&#732;','&Alpha;':'&#913;',
'&Beta;':'&#914;','&Gamma;':'&#915;','&Delta;':'&#916;',
'&Epsilon;':'&#917;','&Zeta;':'&#918;','&Eta;':'&#919;',
'&Theta;':'&#920;','&Iota;':'&#921;','&Kappa;':'&#922;',
'&Lambda;':'&#923;','&Mu;':'&#924;','&Nu;':'&#925;',
'&Xi;':'&#926;','&Omicron;':'&#927;','&Pi;':'&#928;',
'&Rho;':'&#929;','&Sigma;':'&#931;','&Tau;':'&#932;',
'&Upsilon;':'&#933;','&Phi;':'&#934;','&Chi;':'&#935;',
'&Psi;':'&#936;','&Omega;':'&#937;','&alpha;':'&#945;',
'&beta;':'&#946;','&gamma;':'&#947;','&delta;':'&#948;',
'&epsilon;':'&#949;','&zeta;':'&#950;','&eta;':'&#951;',
'&theta;':'&#952;','&iota;':'&#953;','&kappa;':'&#954;',
'&lambda;':'&#955;','&mu;':'&#956;','&nu;':'&#957;',
'&xi;':'&#958;','&omicron;':'&#959;','&pi;':'&#960;',
'&rho;':'&#961;','&sigmaf;':'&#962;','&sigma;':'&#963;',
'&tau;':'&#964;','&upsilon;':'&#965;','&phi;':'&#966;',
'&chi;':'&#967;','&psi;':'&#968;','&omega;':'&#969;',
'&thetasym;':'&#977;','&upsih;':'&#978;','&piv;':'&#982;',
'&ensp;':'&#8194;','&emsp;':'&#8195;','&thinsp;':'&#8201;',
'&zwnj;':'&#8204;','&zwj;':'&#8205;','&lrm;':'&#8206;',
'&rlm;':'&#8207;','&ndash;':'&#8211;','&mdash;':'&#8212;',
'&lsquo;':'&#8216;','&rsquo;':'&#8217;','&sbquo;':'&#8218;',
'&ldquo;':'&#8220;','&rdquo;':'&#8221;','&bdquo;':'&#8222;',
'&dagger;':'&#8224;','&Dagger;':'&#8225;','&bull;':'&#8226;',
'&hellip;':'&#8230;','&permil;':'&#8240;','&prime;':'&#8242;',
'&Prime;':'&#8243;','&lsaquo;':'&#8249;','&rsaquo;':'&#8250;',
'&oline;':'&#8254;','&frasl;':'&#8260;','&euro;':'&#8364;',
'&image;':'&#8465;','&weierp;':'&#8472;','&real;':'&#8476;',
'&trade;':'&#8482;','&alefsym;':'&#8501;','&larr;':'&#8592;',
'&uarr;':'&#8593;','&rarr;':'&#8594;','&darr;':'&#8595;',
'&harr;':'&#8596;','&crarr;':'&#8629;','&lArr;':'&#8656;',
'&uArr;':'&#8657;','&rArr;':'&#8658;','&dArr;':'&#8659;',
'&hArr;':'&#8660;','&forall;':'&#8704;','&part;':'&#8706;',
'&exist;':'&#8707;','&empty;':'&#8709;','&nabla;':'&#8711;',
'&isin;':'&#8712;','&notin;':'&#8713;','&ni;':'&#8715;',
'&prod;':'&#8719;','&sum;':'&#8721;','&minus;':'&#8722;',
'&lowast;':'&#8727;','&radic;':'&#8730;','&prop;':'&#8733;',
'&infin;':'&#8734;','&ang;':'&#8736;','&and;':'&#8743;',
'&or;':'&#8744;','&cap;':'&#8745;','&cup;':'&#8746;',
'&int;':'&#8747;','&there4;':'&#8756;','&sim;':'&#8764;',
'&cong;':'&#8773;','&asymp;':'&#8776;','&ne;':'&#8800;',
'&equiv;':'&#8801;','&le;':'&#8804;','&ge;':'&#8805;',
'&sub;':'&#8834;','&sup;':'&#8835;','&nsub;':'&#8836;',
'&sube;':'&#8838;','&supe;':'&#8839;','&oplus;':'&#8853;',
'&otimes;':'&#8855;','&perp;':'&#8869;','&sdot;':'&#8901;',
'&lceil;':'&#8968;','&rceil;':'&#8969;','&lfloor;':'&#8970;',
'&rfloor;':'&#8971;','&lang;':'&#9001;','&rang;':'&#9002;',
'&loz;':'&#9674;','&spades;':'&#9824;','&clubs;':'&#9827;',
'&hearts;':'&#9829;','&diams;':'&#9830;'};
this.block_tags = ["a", "abbr", "acronym", "address", "area", "b",
"base", "bdo", "big", "blockquote", "body", "button",
"caption", "cite", "code", "col", "colgroup", "dd", "del", "div",
"dfn", "dl", "dt", "em", "fieldset", "form", "head", "h1", "h2",
"h3", "h4", "h5", "h6", "html", "i", "ins",
"kbd", "label", "legend", "li", "map", "noscript",
"object", "ol", "optgroup", "option", "p", "param", "pre", "q",
"samp", "script", "select", "small", "span", "strong", "style",
"sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th",
"thead", "title", "tr", "tt", "ul", "var", "extends"];
this.inline_tags = ["br", "hr", "img", "input"];
return this;
};
WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically = function(tag, now_on_tag, closing)
{
var closing = closing || false;
if(tag == 'td'){
if((closing && now_on_tag == 'tr') || (!closing && now_on_tag == 'td')){
return true;
}
}
if(tag == 'option'){
if((closing && now_on_tag == 'select') || (!closing && now_on_tag == 'option')){
return true;
}
}
return false;
};
WYMeditor.XhtmlSaxListener.prototype.beforeParsing = function(raw)
{
this.output = '';
return raw;
};
WYMeditor.XhtmlSaxListener.prototype.afterParsing = function(xhtml)
{
xhtml = this.replaceNamedEntities(xhtml);
xhtml = this.joinRepeatedEntities(xhtml);
xhtml = this.removeEmptyTags(xhtml);
xhtml = this.removeBrInPre(xhtml);
return xhtml;
};
WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities = function(xhtml)
{
for (var entity in this.entities) {
xhtml = xhtml.replace(new RegExp(entity, 'g'), this.entities[entity]);
}
return xhtml;
};
WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities = function(xhtml)
{
var tags = 'em|strong|sub|sup|acronym|pre|del|address';
return xhtml.replace(new RegExp('<\/('+tags+')><\\1>' ,''),'').
replace(new RegExp('(\s*<('+tags+')>\s*){2}(.*)(\s*<\/\\2>\s*){2}' ,''),'<\$2>\$3<\$2>');
};
WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags = function(xhtml)
{
return xhtml.replace(new RegExp('<('+this.block_tags.join("|").replace(/\|td/,'').replace(/\|th/, '')+')>(<br \/>|&#160;|&nbsp;|\\s)*<\/\\1>' ,'g'),'');
};
WYMeditor.XhtmlSaxListener.prototype.removeBrInPre = function(xhtml)
{
var matches = xhtml.match(new RegExp('<pre[^>]*>(.*?)<\/pre>','gmi'));
if(matches) {
for(var i=0; i<matches.length; i++) {
xhtml = xhtml.replace(matches[i], matches[i].replace(new RegExp('<br \/>', 'g'), String.fromCharCode(13,10)));
}
}
return xhtml;
};
WYMeditor.XhtmlSaxListener.prototype.getResult = function()
{
return this.output;
};
WYMeditor.XhtmlSaxListener.prototype.getTagReplacements = function()
{
return {'b':'strong', 'i':'em'};
};
WYMeditor.XhtmlSaxListener.prototype.addContent = function(text)
{
this.output += text;
};
WYMeditor.XhtmlSaxListener.prototype.addComment = function(text)
{
if(this.remove_comments){
this.output += text;
}
};
WYMeditor.XhtmlSaxListener.prototype.addScript = function(text)
{
if(!this.remove_scripts){
this.output += text;
}
};
WYMeditor.XhtmlSaxListener.prototype.addCss = function(text)
{
if(!this.remove_embeded_styles){
this.output += text;
}
};
WYMeditor.XhtmlSaxListener.prototype.openBlockTag = function(tag, attributes)
{
this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes), true);
};
WYMeditor.XhtmlSaxListener.prototype.inlineTag = function(tag, attributes)
{
this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes));
};
WYMeditor.XhtmlSaxListener.prototype.openUnknownTag = function(tag, attributes)
{
//this.output += this.helper.tag(tag, attributes, true);
};
WYMeditor.XhtmlSaxListener.prototype.closeBlockTag = function(tag)
{
this.output = this.output.replace(/<br \/>$/, '')+this._getClosingTagContent('before', tag)+"</"+tag+">"+this._getClosingTagContent('after', tag);
};
WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag = function(tag)
{
//this.output += "</"+tag+">";
};
WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag = function(tag)
{
this.output += "</"+tag+">";
};
WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes = function()
{
this.avoided_tags = ['div','span'];
this.validator.skiped_attributes = ['style'];
this.validator.skiped_attribute_values = ['MsoNormal','main1']; // MS Word attributes for class
this._avoiding_tags_implicitly = true;
};
WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes = function()
{
this.avoided_tags = [];
this.validator.skiped_attributes = [];
this.validator.skiped_attribute_values = [];
this._avoiding_tags_implicitly = false;
};
WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag)
{
return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag);
};
WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag)
{
return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag);
};
WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content)
{
this._insertContentWhenClosingTag('after', tag, content);
};
WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag = function(tag, content)
{
this._insertContentWhenClosingTag('before', tag, content);
};
WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag = function(tag, attributes)
{
if(tag != 'li' && (tag == 'ul' || tag == 'ol') && this.last_tag && !this.last_tag_opened && this.last_tag == 'li'){
this.output = this.output.replace(/<\/li>$/, '');
this.insertContentAfterClosingTag(tag, '</li>');
}
};
WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag = function(position, tag, content)
{
if(!this['_insert_'+position+'_closing']){
this['_insert_'+position+'_closing'] = [];
}
if(!this['_insert_'+position+'_closing'][tag]){
this['_insert_'+position+'_closing'][tag] = [];
}
this['_insert_'+position+'_closing'][tag].push(content);
};
WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent = function(position, tag)
{
if( this['_insert_'+position+'_closing'] &&
this['_insert_'+position+'_closing'][tag] &&
this['_insert_'+position+'_closing'][tag].length > 0){
return this['_insert_'+position+'_closing'][tag].pop();
}
return '';
};
/********** CSS PARSER **********/
WYMeditor.WymCssLexer = function(parser, only_wym_blocks)
{
var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks);
jQuery.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss')));
this.mapHandler('WymCss', 'Ignore');
if(only_wym_blocks == true){
this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/", 'Ignore', 'WymCss');
this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss');
}
this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+", 'WymCss', 'WymCssStyleDeclaration');
this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment');
this.addExitPattern("\\\x2a/", 'WymCssComment');
this.addEntryPattern("\x7b", 'WymCss', 'WymCssStyle');
this.addExitPattern("\x7d", 'WymCssStyle');
this.addEntryPattern("/\\\x2a", 'WymCssStyle', 'WymCssFeedbackStyle');
this.addExitPattern("\\\x2a/", 'WymCssFeedbackStyle');
return this;
};
WYMeditor.WymCssParser = function()
{
this._in_style = false;
this._has_title = false;
this.only_wym_blocks = true;
this.css_settings = {'classesItems':[], 'editorStyles':[], 'dialogStyles':[]};
return this;
};
WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks)
{
var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks);
this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks);
this._Lexer.parse(raw);
};
WYMeditor.WymCssParser.prototype.Ignore = function(match, state)
{
return true;
};
WYMeditor.WymCssParser.prototype.WymCssComment = function(text, status)
{
if(text.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){
return false;
}
if(status == WYMeditor.LEXER_UNMATCHED){
if(!this._in_style){
this._has_title = true;
this._current_item = {'title':WYMeditor.Helper.trim(text)};
}else{
if(this._current_item[this._current_element]){
if(!this._current_item[this._current_element].expressions){
this._current_item[this._current_element].expressions = [text];
}else{
this._current_item[this._current_element].expressions.push(text);
}
}
}
this._in_style = true;
}
return true;
};
WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status)
{
if(status == WYMeditor.LEXER_UNMATCHED){
match = WYMeditor.Helper.trim(match);
if(match != ''){
this._current_item[this._current_element].style = match;
}
}else if (status == WYMeditor.LEXER_EXIT){
this._in_style = false;
this._has_title = false;
this.addStyleSetting(this._current_item);
}
return true;
};
WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle = function(match, status)
{
if(status == WYMeditor.LEXER_UNMATCHED){
this._current_item[this._current_element].feedback_style = match.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,'');
}
return true;
};
WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration = function(match)
{
match = match.replace(/^([\s\.]*)|([\s\.*]*)$/gm, '');
var tag = '';
if(match.indexOf('.') > 0){
var parts = match.split('.');
this._current_element = parts[1];
var tag = parts[0];
}else{
this._current_element = match;
}
if(!this._has_title){
this._current_item = {'title':(!tag?'':tag.toUpperCase()+': ')+this._current_element};
this._has_title = true;
}
if(!this._current_item[this._current_element]){
this._current_item[this._current_element] = {'name':this._current_element};
}
if(tag){
if(!this._current_item[this._current_element].tags){
this._current_item[this._current_element].tags = [tag];
}else{
this._current_item[this._current_element].tags.push(tag);
}
}
return true;
};
WYMeditor.WymCssParser.prototype.addStyleSetting = function(style_details)
{
for (var name in style_details){
var details = style_details[name];
if(typeof details == 'object' && name != 'title'){
this.css_settings.classesItems.push({
'name': WYMeditor.Helper.trim(details.name),
'title': style_details.title,
'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', '))
});
if(details.feedback_style){
this.css_settings.editorStyles.push({
'name': '.'+ WYMeditor.Helper.trim(details.name),
'css': details.feedback_style
});
}
if(details.style){
this.css_settings.dialogStyles.push({
'name': '.'+ WYMeditor.Helper.trim(details.name),
'css': details.style
});
}
}
}
};
/********** HELPERS **********/
// Returns true if it is a text node with whitespaces only
jQuery.fn.isPhantomNode = function() {
if (this[0].nodeType == 3)
return !(/[^\t\n\r ]/.test(this[0].data));
return false;
};
WYMeditor.isPhantomNode = function(n) {
if (n.nodeType == 3)
return !(/[^\t\n\r ]/.test(n.data));
return false;
};
WYMeditor.isPhantomString = function(str) {
return !(/[^\t\n\r ]/.test(str));
};
// Returns the Parents or the node itself
// jqexpr = a jQuery expression
jQuery.fn.parentsOrSelf = function(jqexpr) {
var n = this;
if (n[0].nodeType == 3)
n = n.parents().slice(0,1);
// if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
if (n.filter(jqexpr).size() == 1)
return n;
else
return n.parents(jqexpr).slice(0,1);
};
// String & array helpers
WYMeditor.Helper = {
//replace all instances of 'old' by 'rep' in 'str' string
replaceAll: function(str, old, rep) {
var rExp = new RegExp(old, "g");
return(str.replace(rExp, rep));
},
//insert 'inserted' at position 'pos' in 'str' string
insertAt: function(str, inserted, pos) {
return(str.substr(0,pos) + inserted + str.substring(pos));
},
//trim 'str' string
trim: function(str) {
return str.replace(/^(\s*)|(\s*)$/gm,'');
},
//return true if 'arr' array contains 'elem', or false
contains: function(arr, elem) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === elem) return true;
}
return false;
},
//return 'item' position in 'arr' array, or -1
indexOf: function(arr, item) {
var ret=-1;
for(var i = 0; i < arr.length; i++) {
if (arr[i] == item) {
ret = i;
break;
}
}
return(ret);
},
//return 'item' object in 'arr' array, checking its 'name' property, or null
findByName: function(arr, name) {
for(var i = 0; i < arr.length; i++) {
var item = arr[i];
if(item.name == name) return(item);
}
return(null);
}
};
/*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
* Dual licensed under the MIT (MIT-license.txt)
* and GPL (GPL-license.txt) licenses.
*
* For further information visit:
* http://www.wymeditor.org/
*
* File Name:
* jquery.wymeditor.explorer.js
* MSIE specific class and functions.
* See the documentation for more info.
*
* File Authors:
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
* Bermi Ferrer (wymeditor a-t bermi dotorg)
* Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
* Jonatan Lundin (jonatan.lundin _at_ gmail.com)
*/
WYMeditor.WymClassExplorer = function(wym) {
this._wym = wym;
this._class = "className";
this._newLine = "\r\n";
};
WYMeditor.WymClassExplorer.prototype.initIframe = function(iframe) {
//This function is executed twice, though it is called once!
//But MSIE needs that, otherwise designMode won't work.
//Weird.
this._iframe = iframe;
this._doc = iframe.contentWindow.document;
//add css rules from options
var styles = this._doc.styleSheets[0];
var aCss = eval(this._options.editorStyles);
this.addCssRules(this._doc, aCss);
this._doc.title = this._wym._index;
//set the text direction
jQuery('html', this._doc).attr('dir', this._options.direction);
//init html value
jQuery(this._doc.body).html(this._wym._html);
//handle events
var wym = this;
this._doc.body.onfocus = function()
{wym._doc.designMode = "on"; wym._doc = iframe.contentWindow.document;};
this._doc.onbeforedeactivate = function() {wym.saveCaret();};
this._doc.onkeyup = function() {
wym.saveCaret();
wym.keyup();
};
this._doc.onclick = function() {wym.saveCaret();};
this._doc.body.onbeforepaste = function() {
wym._iframe.contentWindow.event.returnValue = false;
};
this._doc.body.onpaste = function() {
wym._iframe.contentWindow.event.returnValue = false;
wym.paste(window.clipboardData.getData("Text"));
};
//callback can't be executed twice, so we check
if(this._initialized) {
//pre-bind functions
if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
//bind external events
this._wym.bindEvents();
//post-init functions
if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
//add event listeners to doc elements, e.g. images
this.listen();
}
this._initialized = true;
//init designMode
this._doc.designMode="on";
try{
// (bermi's note) noticed when running unit tests on IE6
// Is this really needed, it trigger an unexisting property on IE6
this._doc = iframe.contentWindow.document;
}catch(e){}
};
WYMeditor.WymClassExplorer.prototype._exec = function(cmd,param) {
switch(cmd) {
case WYMeditor.INDENT: case WYMeditor.OUTDENT:
var container = this.findUp(this.container(), WYMeditor.LI);
if(container) {
var ancestor = container.parentNode.parentNode;
if(container.parentNode.childNodes.length>1
|| ancestor.tagName.toLowerCase() == WYMeditor.OL
|| ancestor.tagName.toLowerCase() == WYMeditor.UL)
this._doc.execCommand(cmd);
}
break;
default:
if(param) this._doc.execCommand(cmd,false,param);
else this._doc.execCommand(cmd);
break;
}
this.listen();
};
WYMeditor.WymClassExplorer.prototype.selected = function() {
var caretPos = this._iframe.contentWindow.document.caretPos;
if(caretPos!=null) {
if(caretPos.parentElement!=undefined)
return(caretPos.parentElement());
}
};
WYMeditor.WymClassExplorer.prototype.saveCaret = function() {
this._doc.caretPos = this._doc.selection.createRange();
};
WYMeditor.WymClassExplorer.prototype.addCssRule = function(styles, oCss) {
styles.addRule(oCss.name, oCss.css);
};
WYMeditor.WymClassExplorer.prototype.insert = function(html) {
// Get the current selection
var range = this._doc.selection.createRange();
// Check if the current selection is inside the editor
if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
try {
// Overwrite selection with provided html
range.pasteHTML(html);
} catch (e) { }
} else {
// Fall back to the internal paste function if there's no selection
this.paste(html);
}
};
WYMeditor.WymClassExplorer.prototype.wrap = function(left, right) {
// Get the current selection
var range = this._doc.selection.createRange();
// Check if the current selection is inside the editor
if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
try {
// Overwrite selection with provided html
range.pasteHTML(left + range.text + right);
} catch (e) { }
}
};
WYMeditor.WymClassExplorer.prototype.unwrap = function() {
// Get the current selection
var range = this._doc.selection.createRange();
// Check if the current selection is inside the editor
if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
try {
// Unwrap selection
var text = range.text;
this._exec( 'Cut' );
range.pasteHTML( text );
} catch (e) { }
}
};
//keyup handler
WYMeditor.WymClassExplorer.prototype.keyup = function() {
this._selected_image = null;
};
WYMeditor.WymClassExplorer.prototype.setFocusToNode = function(node) {
var range = this._doc.selection.createRange();
range.moveToElementText(node);
range.collapse(false);
range.move('character',-1);
range.select();
node.focus();
};
/*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
* Dual licensed under the MIT (MIT-license.txt)
* and GPL (GPL-license.txt) licenses.
*
* For further information visit:
* http://www.wymeditor.org/
*
* File Name:
* jquery.wymeditor.mozilla.js
* Gecko specific class and functions.
* See the documentation for more info.
*
* File Authors:
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
* Volker Mische (vmx a-t gmx dotde)
* Bermi Ferrer (wymeditor a-t bermi dotorg)
* Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
*/
WYMeditor.WymClassMozilla = function(wym) {
this._wym = wym;
this._class = "class";
this._newLine = "\n";
};
WYMeditor.WymClassMozilla.prototype.initIframe = function(iframe) {
this._iframe = iframe;
this._doc = iframe.contentDocument;
//add css rules from options
var styles = this._doc.styleSheets[0];
var aCss = eval(this._options.editorStyles);
this.addCssRules(this._doc, aCss);
this._doc.title = this._wym._index;
//set the text direction
jQuery('html', this._doc).attr('dir', this._options.direction);
//init html value
this.html(this._wym._html);
//init designMode
this.enableDesignMode();
//pre-bind functions
if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
//bind external events
this._wym.bindEvents();
//bind editor keydown events
jQuery(this._doc).bind("keydown", this.keydown);
//bind editor keyup events
jQuery(this._doc).bind("keyup", this.keyup);
//bind editor focus events (used to reset designmode - Gecko bug)
jQuery(this._doc).bind("focus", this.enableDesignMode);
//post-init functions
if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
//add event listeners to doc elements, e.g. images
this.listen();
};
/* @name html
* @description Get/Set the html value
*/
WYMeditor.WymClassMozilla.prototype.html = function(html) {
if(typeof html === 'string') {
//disable designMode
try { this._doc.designMode = "off"; } catch(e) { };
//replace em by i and strong by bold
//(designMode issue)
html = html.replace(/<em(\b[^>]*)>/gi, "<i$1>")
.replace(/<\/em>/gi, "</i>")
.replace(/<strong(\b[^>]*)>/gi, "<b$1>")
.replace(/<\/strong>/gi, "</b>");
//update the html body
jQuery(this._doc.body).html(html);
//re-init designMode
this.enableDesignMode();
}
else return(jQuery(this._doc.body).html());
};
WYMeditor.WymClassMozilla.prototype._exec = function(cmd,param) {
if(!this.selected()) return(false);
switch(cmd) {
case WYMeditor.INDENT: case WYMeditor.OUTDENT:
var focusNode = this.selected();
var sel = this._iframe.contentWindow.getSelection();
var anchorNode = sel.anchorNode;
if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
if(focusNode && focusNode == anchorNode
&& focusNode.tagName.toLowerCase() == WYMeditor.LI) {
var ancestor = focusNode.parentNode.parentNode;
if(focusNode.parentNode.childNodes.length>1
|| ancestor.tagName.toLowerCase() == WYMeditor.OL
|| ancestor.tagName.toLowerCase() == WYMeditor.UL)
this._doc.execCommand(cmd,'',null);
}
break;
default:
if(param) this._doc.execCommand(cmd,'',param);
else this._doc.execCommand(cmd,'',null);
}
//set to P if parent = BODY
var container = this.selected();
if(container.tagName.toLowerCase() == WYMeditor.BODY)
this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
//add event handlers on doc elements
this.listen();
};
/* @name selected
* @description Returns the selected container
*/
WYMeditor.WymClassMozilla.prototype.selected = function() {
var sel = this._iframe.contentWindow.getSelection();
var node = sel.focusNode;
if(node) {
if(node.nodeName == "#text") return(node.parentNode);
else return(node);
} else return(null);
};
WYMeditor.WymClassMozilla.prototype.addCssRule = function(styles, oCss) {
styles.insertRule(oCss.name + " {" + oCss.css + "}",
styles.cssRules.length);
};
//keydown handler, mainly used for keyboard shortcuts
WYMeditor.WymClassMozilla.prototype.keydown = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
var container = null;
if(evt.ctrlKey){
if(evt.keyCode == 66){
//CTRL+b => STRONG
wym._exec(WYMeditor.BOLD);
return false;
}
if(evt.keyCode == 73){
//CTRL+i => EMPHASIS
wym._exec(WYMeditor.ITALIC);
return false;
}
}
else if(evt.keyCode == 13) {
if(!evt.shiftKey){
//fix PRE bug #73
container = wym.selected();
if(container && container.tagName.toLowerCase() == WYMeditor.PRE) {
evt.preventDefault();
wym.insert('<p></p>');
}
}
}
};
//keyup handler, mainly used for cleanups
WYMeditor.WymClassMozilla.prototype.keyup = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
wym._selected_image = null;
var container = null;
if(evt.keyCode == 13 && !evt.shiftKey) {
//RETURN key
//cleanup <br><br> between paragraphs
jQuery(wym._doc.body).children(WYMeditor.BR).remove();
}
else if(evt.keyCode != 8
&& evt.keyCode != 17
&& evt.keyCode != 46
&& evt.keyCode != 224
&& !evt.metaKey
&& !evt.ctrlKey) {
//NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
//text nodes replaced by P
container = wym.selected();
var name = container.tagName.toLowerCase();
//fix forbidden main containers
if(
name == "strong" ||
name == "b" ||
name == "em" ||
name == "i" ||
name == "sub" ||
name == "sup" ||
name == "a"
) name = container.parentNode.tagName.toLowerCase();
if(name == WYMeditor.BODY) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
}
};
WYMeditor.WymClassMozilla.prototype.enableDesignMode = function() {
if(this.designMode == "off") {
try {
this.designMode = "on";
this.execCommand("styleWithCSS", '', false);
} catch(e) { }
}
};
WYMeditor.WymClassMozilla.prototype.setFocusToNode = function(node) {
var range = document.createRange();
range.selectNode(node);
var selected = this._iframe.contentWindow.getSelection();
selected.addRange(range);
selected.collapse(node, node.childNodes.length);
this._iframe.contentWindow.focus();
};
WYMeditor.WymClassMozilla.prototype.openBlockTag = function(tag, attributes)
{
var attributes = this.validator.getValidTagAttributes(tag, attributes);
// Handle Mozilla styled spans
if(tag == 'span' && attributes.style){
var new_tag = this.getTagForStyle(attributes.style);
if(new_tag){
this._tag_stack.pop();
var tag = new_tag;
this._tag_stack.push(new_tag);
attributes.style = '';
}else{
return;
}
}
this.output += this.helper.tag(tag, attributes, true);
};
WYMeditor.WymClassMozilla.prototype.getTagForStyle = function(style) {
if(/bold/.test(style)) return 'strong';
if(/italic/.test(style)) return 'em';
if(/sub/.test(style)) return 'sub';
if(/sub/.test(style)) return 'super';
return false;
};
/*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
* Dual licensed under the MIT (MIT-license.txt)
* and GPL (GPL-license.txt) licenses.
*
* For further information visit:
* http://www.wymeditor.org/
*
* File Name:
* jquery.wymeditor.opera.js
* Opera specific class and functions.
* See the documentation for more info.
*
* File Authors:
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
*/
WYMeditor.WymClassOpera = function(wym) {
this._wym = wym;
this._class = "class";
this._newLine = "\r\n";
};
WYMeditor.WymClassOpera.prototype.initIframe = function(iframe) {
this._iframe = iframe;
this._doc = iframe.contentWindow.document;
//add css rules from options
var styles = this._doc.styleSheets[0];
var aCss = eval(this._options.editorStyles);
this.addCssRules(this._doc, aCss);
this._doc.title = this._wym._index;
//set the text direction
jQuery('html', this._doc).attr('dir', this._options.direction);
//init designMode
this._doc.designMode = "on";
//init html value
this.html(this._wym._html);
//pre-bind functions
if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
//bind external events
this._wym.bindEvents();
//bind editor keydown events
jQuery(this._doc).bind("keydown", this.keydown);
//bind editor events
jQuery(this._doc).bind("keyup", this.keyup);
//post-init functions
if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
//add event listeners to doc elements, e.g. images
this.listen();
};
WYMeditor.WymClassOpera.prototype._exec = function(cmd,param) {
if(param) this._doc.execCommand(cmd,false,param);
else this._doc.execCommand(cmd);
this.listen();
};
WYMeditor.WymClassOpera.prototype.selected = function() {
var sel=this._iframe.contentWindow.getSelection();
var node=sel.focusNode;
if(node) {
if(node.nodeName=="#text")return(node.parentNode);
else return(node);
} else return(null);
};
WYMeditor.WymClassOpera.prototype.addCssRule = function(styles, oCss) {
styles.insertRule(oCss.name + " {" + oCss.css + "}",
styles.cssRules.length);
};
//keydown handler
WYMeditor.WymClassOpera.prototype.keydown = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
var sel = wym._iframe.contentWindow.getSelection();
startNode = sel.getRangeAt(0).startContainer;
//Get a P instead of no container
if(!jQuery(startNode).parentsOrSelf(
WYMeditor.MAIN_CONTAINERS.join(","))[0]
&& !jQuery(startNode).parentsOrSelf('li')
&& evt.keyCode != WYMeditor.KEY.ENTER
&& evt.keyCode != WYMeditor.KEY.LEFT
&& evt.keyCode != WYMeditor.KEY.UP
&& evt.keyCode != WYMeditor.KEY.RIGHT
&& evt.keyCode != WYMeditor.KEY.DOWN
&& evt.keyCode != WYMeditor.KEY.BACKSPACE
&& evt.keyCode != WYMeditor.KEY.DELETE)
wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
};
//keyup handler
WYMeditor.WymClassOpera.prototype.keyup = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
wym._selected_image = null;
};
// TODO: implement me
WYMeditor.WymClassOpera.prototype.setFocusToNode = function(node) {
};
/*
* WYMeditor : what you see is What You Mean web-based editor
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
* Dual licensed under the MIT (MIT-license.txt)
* and GPL (GPL-license.txt) licenses.
*
* For further information visit:
* http://www.wymeditor.org/
*
* File Name:
* jquery.wymeditor.safari.js
* Safari specific class and functions.
* See the documentation for more info.
*
* File Authors:
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
* Scott Lewis (lewiscot a-t gmail dotcom)
*/
WYMeditor.WymClassSafari = function(wym) {
this._wym = wym;
this._class = "class";
this._newLine = "\n";
};
WYMeditor.WymClassSafari.prototype.initIframe = function(iframe) {
this._iframe = iframe;
this._doc = iframe.contentDocument;
//add css rules from options
var styles = this._doc.styleSheets[0];
var aCss = eval(this._options.editorStyles);
this.addCssRules(this._doc, aCss);
this._doc.title = this._wym._index;
//set the text direction
jQuery('html', this._doc).attr('dir', this._options.direction);
//init designMode
this._doc.designMode = "on";
//init html value
this.html(this._wym._html);
//pre-bind functions
if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
//bind external events
this._wym.bindEvents();
//bind editor keydown events
jQuery(this._doc).bind("keydown", this.keydown);
//bind editor keyup events
jQuery(this._doc).bind("keyup", this.keyup);
//post-init functions
if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
//add event listeners to doc elements, e.g. images
this.listen();
};
WYMeditor.WymClassSafari.prototype._exec = function(cmd,param) {
if(!this.selected()) return(false);
switch(cmd) {
case WYMeditor.INDENT: case WYMeditor.OUTDENT:
var focusNode = this.selected();
var sel = this._iframe.contentWindow.getSelection();
var anchorNode = sel.anchorNode;
if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
if(focusNode && focusNode == anchorNode
&& focusNode.tagName.toLowerCase() == WYMeditor.LI) {
var ancestor = focusNode.parentNode.parentNode;
if(focusNode.parentNode.childNodes.length>1
|| ancestor.tagName.toLowerCase() == WYMeditor.OL
|| ancestor.tagName.toLowerCase() == WYMeditor.UL)
this._doc.execCommand(cmd,'',null);
}
break;
case WYMeditor.INSERT_ORDEREDLIST: case WYMeditor.INSERT_UNORDEREDLIST:
this._doc.execCommand(cmd,'',null);
//Safari creates lists in e.g. paragraphs.
//Find the container, and remove it.
var focusNode = this.selected();
var container = this.findUp(focusNode, WYMeditor.MAIN_CONTAINERS);
if(container) jQuery(container).replaceWith(jQuery(container).html());
break;
default:
if(param) this._doc.execCommand(cmd,'',param);
else this._doc.execCommand(cmd,'',null);
}
//set to P if parent = BODY
var container = this.selected();
if(container && container.tagName.toLowerCase() == WYMeditor.BODY)
this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
//add event handlers on doc elements
this.listen();
};
/* @name selected
* @description Returns the selected container
*/
WYMeditor.WymClassSafari.prototype.selected = function() {
var sel = this._iframe.contentWindow.getSelection();
var node = sel.focusNode;
if(node) {
if(node.nodeName == "#text") return(node.parentNode);
else return(node);
} else return(null);
};
WYMeditor.WymClassSafari.prototype.addCssRule = function(styles, oCss) {
styles.insertRule(oCss.name + " {" + oCss.css + "}",
styles.cssRules.length);
};
//keydown handler, mainly used for keyboard shortcuts
WYMeditor.WymClassSafari.prototype.keydown = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
if(evt.ctrlKey){
if(evt.keyCode == 66){
//CTRL+b => STRONG
wym._exec(WYMeditor.BOLD);
return false;
}
if(evt.keyCode == 73){
//CTRL+i => EMPHASIS
wym._exec(WYMeditor.ITALIC);
return false;
}
}
};
//keyup handler, mainly used for cleanups
WYMeditor.WymClassSafari.prototype.keyup = function(evt) {
//'this' is the doc
var wym = WYMeditor.INSTANCES[this.title];
wym._selected_image = null;
var container = null;
if(evt.keyCode == 13 && !evt.shiftKey) {
//RETURN key
//cleanup <br><br> between paragraphs
jQuery(wym._doc.body).children(WYMeditor.BR).remove();
//fix PRE bug #73
container = wym.selected();
if(container && container.tagName.toLowerCase() == WYMeditor.PRE)
wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //create P after PRE
}
//fix #112
if(evt.keyCode == 13 && evt.shiftKey) {
wym._exec('InsertLineBreak');
}
if(evt.keyCode != 8
&& evt.keyCode != 17
&& evt.keyCode != 46
&& evt.keyCode != 224
&& !evt.metaKey
&& !evt.ctrlKey) {
//NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
//text nodes replaced by P
container = wym.selected();
var name = container.tagName.toLowerCase();
//fix forbidden main containers
if(
name == "strong" ||
name == "b" ||
name == "em" ||
name == "i" ||
name == "sub" ||
name == "sup" ||
name == "a" ||
name == "span" //fix #110
) name = container.parentNode.tagName.toLowerCase();
if(name == WYMeditor.BODY || name == WYMeditor.DIV) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //fix #110 for DIV
}
};
WYMeditor.WymClassSafari.prototype.setFocusToNode = function(node) {
var range = this._iframe.contentDocument.createRange();
range.selectNode(node);
var selected = this._iframe.contentWindow.getSelection();
selected.addRange(range);
selected.collapse(node, node.childNodes.length);
this._iframe.contentWindow.focus();
};
WYMeditor.WymClassSafari.prototype.openBlockTag = function(tag, attributes)
{
var attributes = this.validator.getValidTagAttributes(tag, attributes);
// Handle Safari styled spans
if(tag == 'span' && attributes.style) {
var new_tag = this.getTagForStyle(attributes.style);
if(new_tag){
this._tag_stack.pop();
var tag = new_tag;
this._tag_stack.push(new_tag);
attributes.style = '';
//should fix #125 - also removed the xhtml() override
if(typeof attributes['class'] == 'string')
attributes['class'] = attributes['class'].replace(/apple-style-span/gi, '');
} else {
return;
}
}
this.output += this.helper.tag(tag, attributes, true);
};
WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) {
if(/bold/.test(style)) return 'strong';
if(/italic/.test(style)) return 'em';
if(/sub/.test(style)) return 'sub';
if(/super/.test(style)) return 'sup';
return false;
};