OSDN Git Service

if is IE use openWYSIWYG, is is NOT ie use WYMeditor.
[feedblog/feedgenerator.git] / wymeditor / jquery.wymeditor.back.js
diff --git a/wymeditor/jquery.wymeditor.back.js b/wymeditor/jquery.wymeditor.back.js
new file mode 100644 (file)
index 0000000..b13e2d0
--- /dev/null
@@ -0,0 +1,4707 @@
+/**
+ * @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='"
+              + 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;
+};