* Feature/Cleanup: Acronym plugin of htmlArea RTE using new plugin API and enabled in IE7
* Fixed issue 6154: Plugin Acronym enables class abbr for span
* Added feature 1927: Activate Acronym Plugin for IE
+ * (major) Feature: Improved behaviour and configuration options for inline elements and text styling
+ * Added feature 3708 (complete): support for <q>
+ * Fixed issue 5952 (complete): Usability Issue with classes wrapped with <> in select lists
+ * Added feature 6034: Improvements of inlineCSS: adding classes to inline elements without additional span
+ * Fixed issue 6839: sub and sup are added via CSS instead of tags in Safari
+
2008-01-05 Stanislas Rolland <stanislas.rolland@fructifor.ca>
* Feature/Cleanup: Acronym plugin of htmlArea RTE using new plugin API and enabled in IE7
* Fixed issue 6154: Plugin Acronym enables class abbr for span
* Added feature 1927: Activate Acronym Plugin for IE
+ * (major) Feature: Improved behaviour and configuration options for inline elements and text styling
+ * Added feature 3708 (complete): support for <q>
+ * Fixed issue 5952 (complete): Usability Issue with classes wrapped with <> in select lists
+ * Added feature 6034: Improvements of inlineCSS: adding classes to inline elements without additional span
+ * Fixed issue 6839: sub and sup are added via CSS instead of tags in Safari
2008-01-05 Stanislas Rolland <stanislas.rolland@fructifor.ca>
$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['Acronym']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/Acronym/class.tx_rtehtmlarea_acronym.php:&tx_rtehtmlarea_acronym';
$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['Acronym']['addIconsToSkin'] = 0;
$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['Acronym']['disableInFE'] = 1;
-//$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['InlineElements'] = array();
-//$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['InlineElements']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/InlineElements/class.tx_rtehtmlarea_inlineelements.php:&tx_rtehtmlarea_inlineelements';
-//$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['TextStyle'] = array();
-//$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['TextStyle']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/TextStyle/class.tx_rtehtmlarea_textstyle.php:&tx_rtehtmlarea_textstyle';
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['InlineElements'] = array();
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['InlineElements']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/InlineElements/class.tx_rtehtmlarea_inlineelements.php:&tx_rtehtmlarea_inlineelements';
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['TextStyle'] = array();
+$TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['plugins']['TextStyle']['objectReference'] = 'EXT:'.$_EXTKEY.'/extensions/TextStyle/class.tx_rtehtmlarea_textstyle.php:&tx_rtehtmlarea_textstyle';
$_EXTCONF = unserialize($_EXTCONF); // unserializing the configuration so we can use it here:
--- /dev/null
+<?php
+/***************************************************************
+* Copyright notice
+*
+* (c) 2007-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+* All rights reserved
+*
+* This script is part of the Typo3 project. The Typo3 project is
+* free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* The GNU General Public License can be found at
+* http://www.gnu.org/copyleft/gpl.html.
+*
+* This script is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * InlineElements plugin for htmlArea RTE
+ *
+ * @author Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+ *
+ * TYPO3 CVS ID: $Id: class.tx_rtehtmlarea_pi1.php 2449 2007-08-15 08:12:19Z ingorenner $
+ *
+ */
+
+require_once(t3lib_extMgm::extPath('rtehtmlarea').'class.tx_rtehtmlareaapi.php');
+
+class tx_rtehtmlarea_inlineelements extends tx_rtehtmlareaapi {
+
+ protected $extensionKey = 'rtehtmlarea'; // The key of the extension that is extending htmlArea RTE
+ protected $pluginName = 'InlineElements'; // The name of the plugin registered by the extension
+ protected $relativePathToLocallangFile = 'extensions/InlineElements/locallang.xml'; // Path to this main locallang file of the extension relative to the extension dir.
+ protected $relativePathToSkin = 'extensions/InlineElements/skin/htmlarea.css'; // Path to the skin (css) file relative to the extension dir.
+ protected $htmlAreaRTE; // Reference to the invoking object
+ protected $thisConfig; // Reference to RTE PageTSConfig
+ protected $toolbar; // Reference to RTE toolbar array
+ protected $LOCAL_LANG; // Frontend language array
+
+ protected $pluginButtons = 'formattext, bidioverride, big, bold, citation, code, definition, deletedtext, emphasis, insertedtext, italic, keyboard, quotation, sample, small, span, strikethrough, strong, subscript, superscript, underline, variable';
+
+ protected $convertToolbarForHtmlAreaArray = array (
+ 'formattext' => 'FormatText',
+ 'bidioverride' => 'BiDiOverride',
+ 'big' => 'Big',
+ 'bold' => 'Bold',
+ 'citation' => 'Citation',
+ 'code' => 'Code',
+ 'definition' => 'Definition',
+ 'deletedtext' => 'DeletedText',
+ 'emphasis' => 'Emphasis',
+ 'insertedtext' => 'InsertedText',
+ 'italic' => 'Italic',
+ 'keyboard' => 'Keyboard',
+ 'monospaced' => 'MonoSpaced',
+ 'quotation' => 'Quotation',
+ 'sample' => 'Sample',
+ 'small' => 'Small',
+ 'span' => 'Span',
+ 'strikethrough' => 'StrikeThrough',
+ 'strong' => 'Strong',
+ 'subscript' => 'Subscript',
+ 'superscript' => 'Superscript',
+ 'underline' => 'Underline',
+ 'variable' => 'Variable',
+ );
+
+ private $defaultInlineElements = array(
+ 'none' => 'No markup',
+ 'b' => 'Bold',
+ 'bdo' => 'BiDi override',
+ 'big' => 'Large text',
+ 'cite' => 'Citation',
+ 'code' => 'Code',
+ 'del' => 'Deleted text',
+ 'dfn' => 'Definition',
+ 'em' => 'Emphasis',
+ 'i' => 'Italic',
+ 'ins' => 'Inserted text',
+ 'kbd' => 'Keyboard',
+ //'label' => 'Label',
+ 'q' => 'Quotation',
+ 'samp' => 'Sample',
+ 'small' => 'Small text',
+ 'span' => 'Style container',
+ 'strike' => 'Strike-through',
+ 'strong' => 'Strong emphasis',
+ 'sub' => 'Subscript',
+ 'sup' => 'Superscript',
+ 'tt' => 'Monospaced text',
+ 'u' => 'Underline',
+ 'var' => 'Variable',
+ );
+
+ private $defaultInlineElementsOrder = 'none, bidioverride, big, bold, citation, code, definition, deletedtext, emphasis, insertedtext, italic, keyboard,
+ monospaced, quotation, sample, small, span, strikethrough, strong, subscript, superscript, underline, variable';
+
+ private $buttonToInlineElement = array(
+ 'none' => 'none',
+ 'bidioverride' => 'bdo',
+ 'big' => 'big',
+ 'bold' => 'b',
+ 'citation' => 'cite',
+ 'code' => 'code',
+ 'definition' => 'dfn',
+ 'deletedtext' => 'del',
+ 'emphasis' => 'em',
+ 'insertedtext' => 'ins',
+ 'italic' => 'i',
+ 'keyboard' => 'kbd',
+ //'label' => 'label',
+ 'monospaced' => 'tt',
+ 'quotation' => 'q',
+ 'sample' => 'samp',
+ 'small' => 'small',
+ 'span' => 'span',
+ 'strikethrough' => 'strike',
+ 'strong' => 'strong',
+ 'subscript' => 'sub',
+ 'superscript' => 'sup',
+ 'underline' => 'u',
+ 'variable' => 'var',
+ );
+
+ /**
+ * Return JS configuration of the htmlArea plugins registered by the extension
+ *
+ * @param integer Relative id of the RTE editing area in the form
+ *
+ * @return string JS configuration for registered plugins
+ *
+ * The returned string will be a set of JS instructions defining the configuration that will be provided to the plugin(s)
+ * Each of the instructions should be of the form:
+ * RTEarea['.$RTEcounter.']["buttons"]["button-id"]["property"] = "value";
+ */
+ public function buildJavascriptConfiguration($RTEcounter) {
+ global $TSFE, $LANG;
+
+ $registerRTEinJavascriptString = '';
+ if (in_array('formattext', $this->toolbar)) {
+ if (!is_array( $this->thisConfig['buttons.']) || !is_array( $this->thisConfig['buttons.']['formattext.'])) {
+ $registerRTEinJavascriptString .= '
+ RTEarea['.$RTEcounter.']["buttons"]["formattext"] = new Object();';
+ }
+
+ // Default inline elements
+ $hideItems = array();
+ $restrictTo = array('*');
+ $inlineElementsOrder = $this->defaultInlineElementsOrder;
+ $prefixLabelWithTag = false;
+ $postfixLabelWithTag = false;
+
+ // Processing PageTSConfig
+ if (is_array($this->thisConfig['buttons.']) && is_array($this->thisConfig['buttons.']['formattext.'])) {
+ // Removing elements
+ if ($this->thisConfig['buttons.']['formattext.']['removeItems']) {
+ $hideItems = t3lib_div::trimExplode(',', $this->htmlAreaRTE->cleanList($this->thisConfig['buttons.']['formattext.']['removeItems']), 1);
+ }
+ // Restriction clause
+ if ($this->thisConfig['buttons.']['formattext.']['restrictToItems']) {
+ $restrictTo = t3lib_div::trimExplode(',', $this->htmlAreaRTE->cleanList('none,'.$this->thisConfig['buttons.']['formattext.']['restrictTo']), 1);
+ }
+ // Elements order
+ if ($this->thisConfig['buttons.']['formattext.']['orderItems']) {
+ $inlineElementsOrder = 'none,'.$this->thisConfig['buttons.']['formattext.']['orderItems'];
+ }
+ $prefixLabelWithTag = ($this->thisConfig['buttons.']['formattext.']['prefixLabelWithTag'])?true:$prefixLabelWithTag;
+ $postfixLabelWithTag = ($this->thisConfig['buttons.']['formattext.']['postfixLabelWithTag'])?true:$postfixLabelWithTag;
+ }
+
+ $inlineElementsOrder = array_diff(t3lib_div::trimExplode(',', $this->htmlAreaRTE->cleanList($inlineElementsOrder), 1), $hideItems);
+ if (!in_array('*', $restrictTo)) {
+ $inlineElementsOrder = array_intersect($inlineElementsOrder, $restrictTo);
+ }
+
+ // Localizing the options
+ $inlineElementsOptions = array();
+ foreach ($inlineElementsOrder as $item) {
+ if ($this->htmlAreaRTE->is_FE()) {
+ $inlineElementsOptions[$this->buttonToInlineElement[$item]] = $TSFE->csConvObj->conv($TSFE->getLLL($this->defaultInlineElements[$this->buttonToInlineElement[$item]],$this->LOCAL_LANG), $TSFE->labelsCharset, $TSFE->renderCharset);
+ } else {
+ $inlineElementsOptions[$this->buttonToInlineElement[$item]] = $LANG->getLL($this->defaultInlineElements[$this->buttonToInlineElement[$item]]);
+ }
+ $inlineElementsOptions[$this->buttonToInlineElement[$item]] = (($prefixLabelWithTag && $item != 'none')?($this->buttonToInlineElement[$item].' - '):'') . $inlineElementsOptions[$this->buttonToInlineElement[$item]] . (($postfixLabelWithTag && $item != 'none')?(' - '.$this->buttonToInlineElement[$item]):'');
+ }
+
+ $first = array_shift($inlineElementsOptions);
+ // Sorting the options
+ if (!is_array($this->thisConfig['buttons.']) || !is_array($this->thisConfig['buttons.']['formattext.']) || !$this->thisConfig['buttons.']['formattext.']['orderItems']) {
+ asort($inlineElementsOptions);
+ }
+ // Generating the javascript options
+ $JSInlineElements = '{
+ "'. $first.'" : "none"';
+ foreach ($inlineElementsOptions as $item => $label) {
+ $JSInlineElements .= ',
+ "' . $label . '" : "' . $item . '"';
+ }
+ $JSInlineElements .= '};';
+
+ $registerRTEinJavascriptString .= '
+ RTEarea['.$RTEcounter.'].buttons.formattext.dropDownOptions = '. $JSInlineElements;
+ }
+ return $registerRTEinJavascriptString;
+ }
+
+
+} // end of class
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/InlineElements/class.tx_rtehtmlarea_inlineelements.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/InlineElements/class.tx_rtehtmlarea_inlineelements.php']);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+ <meta type="array">
+ <description>Labels for Inline Elements plugin of htmlArea RTE</description>
+ <type>module</type>
+ </meta>
+ <data type="array">
+ <languageKey index="default" type="array">
+ <label index="No markup">No text format</label>
+ <label index="Bold">Bold</label>
+ <label index="BiDi override">BiDi override</label>
+ <label index="Large text">Large text</label>
+ <label index="Citation">Citation</label>
+ <label index="Code">Code</label>
+ <label index="Definition">Definition</label>
+ <label index="Deleted text">Deleted text</label>
+ <label index="Emphasis">Emphasis</label>
+ <label index="Inserted text">Inserted text</label>
+ <label index="Italic">Italic</label>
+ <label index="Keyboard">Keyboard</label>
+ <label index="Label">Label</label>
+ <label index="Monospaced text">Monospaced text</label>
+ <label index="Quotation">Quotation</label>
+ <label index="Sample">Sample</label>
+ <label index="Small text">Small text</label>
+ <label index="Style container">Style container</label>
+ <label index="Strike-through">Strike-through</label>
+ <label index="Strong emphasis">Strong emphasis</label>
+ <label index="Subscript">Subscript</label>
+ <label index="Superscript">Superscript</label>
+ <label index="Underline">Underline</label>
+ <label index="Variable">Variable</label>
+ </languageKey>
+ </data>
+ <orig_hash type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_hash>
+ <orig_text type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_text>
+</T3locallang>
\ No newline at end of file
--- /dev/null
+/* Selectors for the InlineElements plugin of htmlArea RTE */
+.htmlarea .toolbar .BiDiOverride {background-image:url("images/bidioverride.gif");}
+.htmlarea .toolbar .Big {background-image:url("images/big.gif");}
+.htmlarea .toolbar .Bold {background-image:url("images/bold.gif");}
+.htmlarea .toolbar .Citation {background-image:url("images/citation.gif");}
+.htmlarea .toolbar .Code {background-image:url("images/code.gif");}
+.htmlarea .toolbar .Definition {background-image:url("images/definition.gif");}
+.htmlarea .toolbar .DeletedText {background-image:url("images/deletedtext.gif");}
+.htmlarea .toolbar .Emphasis {background-image:url("images/emphasis.gif");}
+.htmlarea .toolbar .InsertedText {background-image:url("images/insertedtext.gif");}
+.htmlarea .toolbar .Italic {background-image:url("images/italic.gif");}
+.htmlarea .toolbar .Keyboard {background-image:url("images/keyboard.gif");}
+.htmlarea .toolbar .MonoSpaced {background-image:url("images/monospaced.gif");}
+.htmlarea .toolbar .Quotation {background-image:url("images/quotation.gif");}
+.htmlarea .toolbar .Sample {background-image:url("images/sample.gif");}
+.htmlarea .toolbar .Small {background-image:url("images/small.gif");}
+.htmlarea .toolbar .Span {background-image:url("images/span.gif");}
+.htmlarea .toolbar .StrikeThrough {background-image:url("images/strikethrough.gif");}
+.htmlarea .toolbar .Strong {background-image:url("images/strong.gif");}
+.htmlarea .toolbar .Subscript {background-image:url("images/subscript.gif");}
+.htmlarea .toolbar .Superscript {background-image:url("images/superscript.gif");}
+.htmlarea .toolbar .Underline {background-image:url("images/underline.gif");}
+.htmlarea .toolbar .Variable {background-image:url("images/variable.gif");}
--- /dev/null
+<?php\r
+/***************************************************************\r
+* Copyright notice\r
+*\r
+* (c) 2007-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>\r
+* All rights reserved\r
+*\r
+* This script is part of the Typo3 project. The Typo3 project is\r
+* free software; you can redistribute it and/or modify\r
+* it under the terms of the GNU General Public License as published by\r
+* the Free Software Foundation; either version 2 of the License, or\r
+* (at your option) any later version.\r
+*\r
+* The GNU General Public License can be found at\r
+* http://www.gnu.org/copyleft/gpl.html.\r
+*\r
+* This script is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+* GNU General Public License for more details.\r
+*\r
+* This copyright notice MUST APPEAR in all copies of the script!\r
+***************************************************************/\r
+/**\r
+ * TextStyle plugin for htmlArea RTE\r
+ *\r
+ * @author Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>\r
+ *\r
+ * TYPO3 SVN ID: $Id$\r
+ *\r
+ */\r
+\r
+require_once(t3lib_extMgm::extPath('rtehtmlarea').'class.tx_rtehtmlareaapi.php');\r
+\r
+class tx_rtehtmlarea_textstyle extends tx_rtehtmlareaapi {\r
+\r
+ protected $extensionKey = 'rtehtmlarea'; // The key of the extension that is extending htmlArea RTE\r
+ protected $pluginName = 'TextStyle'; // The name of the plugin registered by the extension\r
+ protected $relativePathToLocallangFile = 'extensions/TextStyle/locallang.xml'; // Path to this main locallang file of the extension relative to the extension dir.\r
+ protected $relativePathToSkin = ''; // Path to the skin (css) file relative to the extension dir.\r
+ protected $htmlAreaRTE; // Reference to the invoking object\r
+ protected $thisConfig; // Reference to RTE PageTSConfig\r
+ protected $toolbar; // Reference to RTE toolbar array\r
+ protected $LOCAL_LANG; // Frontend language array\r
+ \r
+ protected $pluginButtons = 'textstyle'; // The comma-seperated list of button names that the extension id adding to the htmlArea RTE tollbar\r
+ protected $pluginLabels = 'textstylelabel'; // The comma-seperated list of label names that the extension id adding to the htmlArea RTE tollbar\r
+ // The name-converting array, converting the button names used in the RTE PageTSConfing to the button id's used by the JS scripts\r
+ protected $convertToolbarForHtmlAreaArray = array (\r
+ 'textstylelabel' => 'I[text_style]',\r
+ 'textstyle' => 'TextStyle',\r
+ );\r
+ protected $requiresClassesConfiguration = true; // True if the extension requires the PageTSConfig Classes configuration\r
+\r
+} // end of class\r
+\r
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/TextStyle/class.tx_rtehtmlarea_textstyle.php']) {\r
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/rtehtmlarea/extensions/TextStyle/class.tx_rtehtmlarea_textstyle.php']);\r
+}\r
+\r
+?>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+ <meta type="array">
+ <description>Labels for Text Style plugin of htmlArea RTE</description>
+ <type>module</type>
+ </meta>
+ <data type="array">
+ <languageKey index="default" type="array">
+ <label index="No style">No text style</label>
+ </languageKey>
+ </data>
+ <orig_hash type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_hash>
+ <orig_text type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_text>
+</T3locallang>
\ No newline at end of file
--- /dev/null
+/***************************************************************
+* Copyright notice
+*
+* (c) 2007-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+* All rights reserved
+*
+* This script is part of the TYPO3 project. The TYPO3 project is
+* free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* The GNU General Public License can be found at
+* http://www.gnu.org/copyleft/gpl.html.
+* A copy is found in the textfile GPL.txt and important notices to the license
+* from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+* This script is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*
+* This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/*
+ * Inline Elements Plugin for TYPO3 htmlArea RTE
+ *
+ * TYPO3 SVN ID: $Id$
+ */
+/*
+ * Creation of the class of InlineElements plugins
+ */
+InlineElements = HTMLArea.Plugin.extend({
+ /*
+ * Let the base class do some initialization work
+ */
+ constructor : function(editor, pluginName) {
+ this.base(editor, pluginName);
+ },
+
+ /*
+ * This function gets called by the base constructor
+ */
+ configurePlugin : function (editor) {
+
+ this.allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", (HTMLArea.is_gecko?"class":"className"));
+
+ if (this.editorConfiguration.buttons.textstyle) {
+ this.tags = this.editorConfiguration.buttons.textstyle.tags;
+ }
+
+ /*
+ * Registering plugin "About" information
+ */
+ var pluginInformation = {
+ version : "1.0",
+ developer : "Stanislas Rolland",
+ developerUrl : "http://www.fructifor.ca/",
+ copyrightOwner : "Stanislas Rolland",
+ sponsor : this.localize("Technische Universitat Ilmenau"),
+ sponsorUrl : "http://www.tu-ilmenau.de/",
+ license : "GPL"
+ };
+ this.registerPluginInformation(pluginInformation);
+
+ /*
+ * Registering the dropdown list
+ */
+ var buttonId = "FormatText";
+ var dropDownConfiguration = {
+ id : buttonId,
+ tooltip : this.localize(buttonId + "-Tooltip"),
+ options : (this.editorConfiguration.buttons[buttonId.toLowerCase()]?this.editorConfiguration.buttons[buttonId.toLowerCase()]["dropDownOptions"]:null),
+ action : "onChange",
+ refresh : null
+ };
+ this.registerDropDown(dropDownConfiguration);
+
+ /*
+ * Registering the buttons
+ */
+ var n = this.buttonList.length;
+ for (var i = 0; i < n; ++i) {
+ var button = this.buttonList[i];
+ buttonId = button[0];
+ var buttonConfiguration = {
+ id : buttonId,
+ tooltip : this.localize(buttonId + "-Tooltip"),
+ action : "onButtonPress",
+ context : button[1],
+ hide : false,
+ selection : false
+ };
+ this.registerButton(buttonConfiguration);
+ }
+ },
+
+ /*
+ * The list of buttons added by this plugin
+ */
+ buttonList : [
+ ["BiDiOverride", null],
+ ["Big", null],
+ ["Bold", null],
+ ["Citation", null],
+ ["Code", null],
+ ["Definition", null],
+ ["DeletedText", null],
+ ["Emphasis", null],
+ ["InsertedText", null],
+ ["Italic", null],
+ ["Keyboard", null],
+ //["Label", null],
+ ["MonoSpaced", null],
+ ["Quotation", null],
+ ["Sample", null],
+ ["Small", null],
+ ["Span", null],
+ ["StrikeThrough", null],
+ ["Strong", null],
+ ["Subscript", null],
+ ["Superscript", null],
+ ["Underline", null],
+ ["Variable", null]
+ ],
+
+ /*
+ * Conversion object: button names to corresponding tag names
+ */
+ convertBtn : {
+ BiDiOverride : "bdo",
+ Big : "big",
+ Bold : "b",
+ Citation : "cite",
+ Code : "code",
+ Definition : "dfn",
+ DeletedText : "del",
+ Emphasis : "em",
+ InsertedText : "ins",
+ Italic : "i",
+ Keyboard : "kbd",
+ //Label : "label",
+ MonoSpaced : "tt",
+ Quotation : "q",
+ Sample : "samp",
+ Small : "small",
+ Span : "span",
+ StrikeThrough : "strike",
+ Strong : "strong",
+ Subscript : "sub",
+ Superscript : "sup",
+ Underline : "u",
+ Variable : "var"
+ },
+
+ /*
+ * Regular expression to check if an element is an inline elment
+ */
+ REInlineElements : /^(b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|label|q|samp|small|span|strike|strong|sub|sup|tt|u|var)$/,
+
+ /*
+ * Function to check if an element is an inline elment
+ */
+ isInlineElement : function (el) {
+ return el && (el.nodeType === 1) && this.REInlineElements.test(el.nodeName.toLowerCase());
+ },
+
+ /*
+ * This function gets called when some inline element button was pressed.
+ */
+ onButtonPress : function (editor, id) {
+ // Could be a button or its hotkey
+ var buttonId = this.translateHotKey(id);
+ buttonId = buttonId ? buttonId : id;
+ var obj = editor._toolbarObjects[buttonId];
+ var element = this.convertBtn[buttonId];
+ if (element) {
+ this.applyInlineElement(editor, element);
+ return false;
+ } else {
+ this.appendToLog("onButtonPress", "No element corresponding to button: " + buttonId);
+ }
+ },
+
+ /*
+ * This function gets called when some inline element was selected in the drop-down list
+ */
+ onChange : function (editor, buttonId) {
+ var tbobj = editor._toolbarObjects[buttonId];
+ var element = document.getElementById(tbobj.elementId).value;
+ this.applyInlineElement(editor, element, false);
+ },
+
+ /*
+ * This function applies to the selection the markup chosen in the drop-down list or corresponding to the button pressed
+ */
+ applyInlineElement : function (editor, element) {
+ editor.focusEditor();
+ var selection = editor._getSelection();
+ var range = editor._createRange(selection);
+ var parent = editor.getParentElement(selection, range);
+ var ancestors = editor.getAllAncestors();
+ var elementIsAncestor = false;
+ var selectionEmpty = editor._selectionEmpty(selection);
+ if (HTMLArea.is_ie) {
+ var bookmark = range.getBookmark();
+ }
+ // Check if the chosen element is among the ancestors
+ for (var i = 0; i < ancestors.length; ++i) {
+ if ((ancestors[i].nodeType == 1) && (ancestors[i].nodeName.toLowerCase() == element)) {
+ elementIsAncestor = true;
+ var elementAncestorIndex = i;
+ break;
+ }
+ }
+ if (!selectionEmpty) {
+ // The selection is not empty.
+ for (var i = 0; i < ancestors.length; ++i) {
+ fullNodeSelected = (HTMLArea.is_ie && ((editor._statusBarTree.selected === ancestors[i] && ancestors[i].innerText === range.text) || (!editor._statusBarTree.selected && ancestors[i].innerText === range.text)))
+ || (HTMLArea.is_gecko && ((editor._statusBarTree.selected === ancestors[i] && ancestors[i].textContent === range.toString()) || (!editor._statusBarTree.selected && ancestors[i].textContent === range.toString())));
+ if (fullNodeSelected) {
+ if (!HTMLArea.isBlockElement(ancestors[i])) {
+ parent = ancestors[i];
+ }
+ break;
+ }
+ }
+ // Working around bug in Safari selectNodeContents
+ if (!fullNodeSelected && HTMLArea.is_safari && this.editor._statusBarTree.selected && this.isInlineElement(this.editor._statusBarTree.selected) && this.editor._statusBarTree.selected.textContent === range.toString()) {
+ fullNodeSelected = true;
+ parent = this.editor._statusBarTree.selected;
+ }
+
+ var fullNodeTextSelected = (HTMLArea.is_gecko && parent.textContent === range.toString())
+ || (HTMLArea.is_ie && parent.innerText === range.text);
+ if (fullNodeTextSelected && elementIsAncestor) {
+ fullNodeSelected = true;
+ }
+ if (element !== "none" && !(fullNodeSelected && elementIsAncestor)) {
+ // Add markup
+ if (HTMLArea.is_gecko) {
+ if (fullNodeSelected && editor._statusBarTree.selected) {
+ if (HTMLArea.is_safari) {
+ this.editor.selectNode(parent);
+ range = this.editor._createRange(this.editor._getSelection());
+ } else {
+ range.selectNode(parent);
+ }
+ }
+ var newElement = this.editor._doc.createElement(element);
+ if (element === "bdo") {
+ newElement.setAttribute("dir", "rtl");
+ }
+ // Sometimes Opera 9.25 raises a bad boundary points error
+ if (HTMLArea.is_opera) {
+ try {
+ range.surroundContents(newElement);
+ } catch(e) {
+ newElement.appendChild(range.extractContents());
+ range.insertNode(newElement);
+ }
+ } else {
+ range.surroundContents(newElement);
+ }
+ // Sometimes Firefox inserts empty elements just outside the boundaries of the range
+ var neighbour = newElement.previousSibling;
+ if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
+ HTMLArea.removeFromParent(neighbour);
+ }
+ neighbour = newElement.nextSibling;
+ if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
+ HTMLArea.removeFromParent(neighbour);
+ }
+ if (fullNodeSelected && editor._statusBarTree.selected && !HTMLArea.is_safari) {
+ this.editor.selectNodeContents(newElement.lastChild, false);
+ } else {
+ this.editor.selectNodeContents(newElement, false);
+ }
+ range.detach();
+ } else {
+ var tagopen = "<" + element + ">";
+ var tagclose = "</" + element + ">";
+ if (fullNodeSelected) {
+ if (!editor._statusBarTree.selected) {
+ parent.innerHTML = tagopen + parent.innerHTML + tagclose;
+ if (element === "bdo") {
+ parent.firstChild.setAttribute("dir", "rtl");
+ }
+ editor.selectNodeContents(parent, false);
+ } else {
+ var content = parent.outerHTML;
+ var newElement = this.remapMarkup(parent, element);
+ newElement.innerHTML = content;
+ editor.selectNodeContents(newElement, false);
+ }
+ } else {
+ var rangeStart = range.duplicate();
+ rangeStart.collapse(true);
+ var parentStart = rangeStart.parentElement();
+ var rangeEnd = range.duplicate();
+ rangeEnd.collapse(true);
+ var newRange = editor._createRange();
+
+ var parentEnd = rangeEnd.parentElement();
+ var upperParentStart = parentStart;
+ if (parentStart !== parent) {
+ while (upperParentStart.parentNode !== parent) {
+ upperParentStart = upperParentStart.parentNode;
+ }
+ }
+
+ var newElement = editor._doc.createElement(element);
+ newElement.innerHTML = range.htmlText;
+ // IE eats spaces on the start boundary
+ if (range.htmlText.charAt(0) === "\x20") {
+ newElement.innerHTML = " " + newElement.innerHTML;
+ }
+ var newElementClone = newElement.cloneNode(true);
+ range.pasteHTML(newElement.outerHTML);
+ // IE inserts the element as the last child of the start container
+ if (parentStart !== parent
+ && parentStart.lastChild
+ && parentStart.lastChild.nodeType === 1
+ && parentStart.lastChild.nodeName.toLowerCase() === element) {
+ parent.insertBefore(newElementClone, upperParentStart.nextSibling);
+ parentStart.removeChild(parentStart.lastChild);
+ // Sometimes an empty previous sibling was created
+ if (newElementClone.previousSibling
+ && newElementClone.previousSibling.nodeType === 1
+ && !newElementClone.previousSibling.innerText) {
+ parent.removeChild(newElementClone.previousSibling);
+ }
+ // The bookmark will not work anymore
+ newRange.moveToElementText(newElementClone);
+ newRange.collapse(false);
+ newRange.select();
+ } else {
+ // Working around IE boookmark bug
+ if (parentStart != parentEnd) {
+ var newRange = editor._createRange();
+ if (newRange.moveToBookmark(bookmark)) {
+ newRange.collapse(false);
+ newRange.select();
+ }
+ } else {
+ range.collapse(false);
+ }
+ }
+ parent.normalize();
+ }
+ }
+ } else {
+ // A complete node is selected: remove the markup
+ if (fullNodeSelected) {
+ if (elementIsAncestor) {
+ parent = ancestors[elementAncestorIndex];
+ }
+ this.removeMarkup(parent);
+ }
+ }
+ } else {
+ // Remove or remap markup when the selection is collapsed
+ if (parent && !HTMLArea.isBlockElement(parent)) {
+ if ((element === "none") || elementIsAncestor) {
+ if (elementIsAncestor) {
+ parent = ancestors[elementAncestorIndex];
+ }
+ this.removeMarkup(parent);
+ } else {
+ var bookmark = this.editor.getBookmark(range);
+ var newElement = this.remapMarkup(parent, element);
+ this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+ }
+ }
+ }
+ },
+
+ /*
+ * This function remaps the given element to the specified tagname
+ */
+ remapMarkup : function(element, tagName) {
+ var attributeValue;
+ var newElement = this.editor.convertNode(element, tagName);
+ if (tagName === "bdo") {
+ newElement.setAttribute("dir", "ltr");
+ }
+ for (var i = 0; i < this.allowedAttributes.length; ++i) {
+ if (attributeValue = element.getAttribute(this.allowedAttributes[i])) {
+ newElement.setAttribute(this.allowedAttributes[i], attributeValue);
+ }
+ }
+
+ if (this.tags && this.tags[tagName] && this.tags[tagName].allowedClasses) {
+ if (newElement.className && /\S/.test(newElement.className)) {
+ var allowedClasses = new RegExp( "^(" + this.tags[tagName].allowedClasses.trim().split(",").join("|") + ")$");
+ classNames = newElement.className.trim().split(" ");
+ for (var i = 0; i < classNames.length; ++i) {
+ if (!allowedClasses.test(classNames[i])) {
+ HTMLArea._removeClass(newElement, classNames[i]);
+ }
+ }
+ }
+ }
+ return newElement;
+ },
+
+ /*
+ * This function removes the given markup element
+ */
+ removeMarkup : function(element) {
+ var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
+ var parent = element.parentNode;
+ while (element.firstChild) {
+ parent.insertBefore(element.firstChild, element);
+ }
+ parent.removeChild(element);
+ this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+ },
+
+ /*
+ * This function gets called when the toolbar is updated
+ */
+ onUpdateToolbar : function () {
+ var editor = this.editor;
+ if (editor._editMode !== "textmode") {
+ var id, activeButton;
+ var tagName = false, endPointsInSameBlock = true, fullNodeSelected = false;
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ var parent = editor.getParentElement(sel);
+ if (parent && !HTMLArea.isBlockElement(parent)) {
+ tagName = parent.nodeName.toLowerCase();
+ }
+ var selectionEmpty = editor._selectionEmpty(sel);
+ if (!selectionEmpty) {
+ var ancestors = editor.getAllAncestors();
+ for (var i = 0; i < ancestors.length; ++i) {
+ fullNodeSelected = (editor._statusBarTree.selected === ancestors[i])
+ && ((HTMLArea.is_gecko && ancestors[i].textContent === range.toString()) || (HTMLArea.is_ie && ancestors[i].innerText === range.text));
+ if (fullNodeSelected) {
+ if (!HTMLArea.isBlockElement(ancestors[i])) {
+ tagName = ancestors[i].nodeName.toLowerCase();
+ }
+ break;
+ }
+ }
+ // Working around bug in Safari selectNodeContents
+ if (!fullNodeSelected && HTMLArea.is_safari && this.editor._statusBarTree.selected && this.isInlineElement(this.editor._statusBarTree.selected) && this.editor._statusBarTree.selected.textContent === range.toString()) {
+ fullNodeSelected = true;
+ tagName = this.editor._statusBarTree.selected.nodeName.toLowerCase();
+ }
+ }
+ var selectionInInlineElement = tagName && this.REInlineElements.test(tagName);
+ var disabled = !this.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
+
+ var obj = editor.config.customSelects["FormatText"];
+ if ((typeof(obj) !== "undefined") && (typeof(editor._toolbarObjects[obj.id]) !== "undefined")) {
+ this.updateValue(editor, obj, tagName, selectionEmpty, fullNodeSelected, disabled);
+ }
+
+ var ancestors = editor.getAllAncestors();
+ var bl = this.buttonList;
+ for (var i = 0; i < bl.length; ++i) {
+ var btn = bl[i];
+ id = btn[0];
+ var obj = editor._toolbarObjects[id];
+ if ((typeof(obj) !== "undefined")) {
+ activeButton = false;
+ for (var j = ancestors.length; --j >= 0;) {
+ var el = ancestors[j];
+ if (!el) { continue; }
+ if (this.convertBtn[id] === el.nodeName.toLowerCase()) {
+ activeButton = true;
+ }
+ }
+ obj.state("active", activeButton);
+ obj.state("enabled", !disabled);
+ }
+ }
+ }
+ },
+
+ /*
+ * This function determines if the end poins of the current selection are within the same block
+ */
+ endPointsInSameBlock : function() {
+ var selection = this.editor._getSelection();
+ if (this.editor._selectionEmpty(selection)) {
+ return true;
+ } else {
+ var parent = this.editor.getParentElement(selection);
+ var endBlocks = this.editor.getEndBlocks(selection);
+ return (endBlocks.start === endBlocks.end && !/^(body|table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
+ }
+ },
+
+ /*
+ * This function updates the drop-down list of inline elemenents
+ */
+ updateValue : function (editor, obj, tagName, selectionEmpty, fullNodeSelected, disabled) {
+ var select = document.getElementById(editor._toolbarObjects[obj.id]["elementId"]);
+ var options = select.options;
+ for (var i = options.length; --i >= 0;) {
+ options[i].selected = false;
+ }
+ select.selectedIndex = 0;
+ options[0].selected = true;
+ select.options[0].text = this.localize("No markup");
+ for (i = options.length; --i >= 0;) {
+ if (tagName === options[i].value) {
+ if (selectionEmpty || fullNodeSelected) {
+ options[i].selected = true;
+ select.selectedIndex = i;
+ select.options[0].text = this.localize("Remove markup");
+ }
+ break;
+ }
+ }
+
+ select.disabled = !(options.length>1) || disabled;
+ select.className = "";
+ if (select.disabled) {
+ select.className = "buttonDisabled";
+ }
+ }
+});
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<!-- TYPO3 SVN ID: $Id$ -->
+<T3locallang>
+ <meta type="array">
+ <description>Labels for Inline Elements plugin of htmlArea RTE</description>
+ <type>module</type>
+ </meta>
+ <data type="array">
+ <languageKey index="default" type="array">
+ <label index="No markup">No text format</label>
+ <label index="Remove markup">Remove text format</label>
+ <label index="FormatText-Tooltip">Apply format to selected text</label>
+ <label index="BiDiOverride-Tooltip">Bidirectionality override</label>
+ <label index="Big-Tooltip">Large text</label>
+ <label index="Bold-Tooltip">Bold</label>
+ <label index="Citation-Tooltip">Citation</label>
+ <label index="Code-Tooltip">Computer code fragment</label>
+ <label index="Definition-Tooltip">Definition</label>
+ <label index="DeletedText-Tooltip">Deleted text</label>
+ <label index="Emphasis-Tooltip">Emphasis</label>
+ <label index="Keyboard-Tooltip">Text to be entered by the user</label>
+ <label index="InsertedText-Tooltip">Inserted text</label>
+ <label index="Italic-Tooltip">Italic</label>
+ <label index="MonoSpaced-Tooltip">Monospaced text</label>
+ <label index="Quotation-Tooltip">Short inline quotation</label>
+ <label index="Sample-Tooltip">Sample program output or script</label>
+ <label index="Small-Tooltip">Small text</label>
+ <label index="Span-Tooltip">Style container</label>
+ <label index="StrikeThrough-Tooltip">Strike-through text</label>
+ <label index="Strong-Tooltip">Strong emphasis</label>
+ <label index="Subscript-Tooltip">Subscript</label>
+ <label index="Superscript-Tooltip">Superscript</label>
+ <label index="Underline-Tooltip">Underline</label>
+ <label index="Variable-Tooltip">Variable or program argument</label>
+ <label index="Technische Universitat Ilmenau">Technische Universität Ilmenau</label>
+ </languageKey>
+ </data>
+ <orig_hash type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_hash>
+ <orig_text type="array">
+ <languageKey index="default" type="array">
+ </languageKey>
+ </orig_text>
+</T3locallang>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>\r
+<!-- TYPO3 SVN ID: $Id$ -->\r
+<T3locallang>\r
+ <meta type="array">\r
+ <description>Labels for Text Style plugin of htmlArea RTE</description>\r
+ <type>module</type>\r
+ </meta>\r
+ <data type="array">\r
+ <languageKey index="default" type="array">\r
+ <label index="TextStyle-Tooltip">Apply style to the selected text</label>\r
+ <label index="Technische Universitat Ilmenau">Technische Universität Ilmenau</label>\r
+ <label index="No style">No text style</label>\r
+ <label index="Remove style">Remove text style</label>\r
+ <label index="Unknown style">Unknown text style</label>\r
+ <label index="Element style">Inline element style</label>\r
+ </languageKey>\r
+ </data>\r
+ <orig_hash type="array">\r
+ <languageKey index="default" type="array">\r
+ </languageKey>\r
+ </orig_hash>\r
+ <orig_text type="array">\r
+ <languageKey index="default" type="array">\r
+ </languageKey>\r
+ </orig_text>\r
+</T3locallang>
\ No newline at end of file
--- /dev/null
+/***************************************************************
+* Copyright notice
+*
+* (c) 2007 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+* All rights reserved
+*
+* This script is part of the TYPO3 project. The TYPO3 project is
+* free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* The GNU General Public License can be found at
+* http://www.gnu.org/copyleft/gpl.html.
+* A copy is found in the textfile GPL.txt and important notices to the license
+* from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+* This script is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*
+* This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/*
+ * Text Style Plugin for TYPO3 htmlArea RTE
+ *
+ * TYPO3 SVN ID: $Id$
+ */
+/*
+ * Creation of the class of TextStyle plugins
+ */
+TextStyle = HTMLArea.Plugin.extend({
+ /*
+ * Let the base class do some initialization work
+ */
+ constructor : function(editor, pluginName) {
+ this.base(editor, pluginName);
+ },
+
+ /*
+ * This function gets called by the class constructor
+ */
+ configurePlugin : function (editor) {
+
+ this.cssLoaded = false;
+ this.cssTimeout = null;
+ this.cssParseCount = 0;
+ this.cssArray = new Object();
+
+ this.classesUrl = this.editorConfiguration.classesUrl;
+ this.pageTSconfiguration = this.editorConfiguration.buttons.textstyle;
+ this.tags = this.pageTSconfiguration.tags;
+ if (!this.tags) {
+ this.tags = new Object();
+ }
+ if (typeof(this.editorConfiguration.classesTag) !== "undefined") {
+ if (this.editorConfiguration.classesTag.span) {
+ if (!this.tags.span) {
+ this.tags.span = new Object();
+ }
+ if (!this.tags.span.allowedClasses) {
+ this.tags.span.allowedClasses = this.editorConfiguration.classesTag.span;
+ }
+ }
+ }
+ this.showTagFreeClasses = this.pageTSconfiguration.showTagFreeClasses || this.editorConfiguration.showTagFreeClasses;
+ this.prefixLabelWithClassName = this.pageTSconfiguration.prefixLabelWithClassName;
+ this.postfixLabelWithClassName = this.pageTSconfiguration.postfixLabelWithClassName;
+
+ /*
+ * Regular expression to check if an element is an inline elment
+ */
+ this.REInlineTags = /^(abbr|acronym|b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|q|samp|small|span|strike|strong|sub|sup|tt|u|var)$/;
+
+ /*
+ * Registering plugin "About" information
+ */
+ var pluginInformation = {
+ version : "1.0",
+ developer : "Stanislas Rolland",
+ developerUrl : "http://www.fructifor.ca/",
+ copyrightOwner : "Stanislas Rolland",
+ sponsor : this.localize("Technische Universitat Ilmenau"),
+ sponsorUrl : "http://www.tu-ilmenau.de/",
+ license : "GPL"
+ };
+ this.registerPluginInformation(pluginInformation);
+
+ /*
+ * Registering the dropdown list
+ */
+ var buttonId = "TextStyle";
+ var dropDownConfiguration = {
+ id : buttonId,
+ tooltip : this.localize(buttonId + "-Tooltip"),
+ textMode : false,
+ options : {"":""},
+ action : "onChange",
+ refresh : "generate",
+ context : null
+ };
+ this.registerDropDown(dropDownConfiguration);
+
+ return true;
+ },
+
+ isInlineElement : function (el) {
+ return el && (el.nodeType === 1) && this.REInlineTags.test(el.nodeName.toLowerCase());
+ },
+
+ /*
+ * This function gets called when some style in the drop-down list applies it to the highlighted textt
+ */
+ onChange : function (editor, buttonId) {
+ var select = document.getElementById(this.editor._toolbarObjects[buttonId].elementId);
+ var className = select.value;
+ var classNames = null;
+ var fullNodeSelected = false;
+
+ this.editor.focusEditor();
+ var selection = this.editor._getSelection();
+ var range = this.editor._createRange(selection);
+ var parent = this.editor.getParentElement();
+ var selectionEmpty = this.editor._selectionEmpty(selection);
+ var ancestors = this.editor.getAllAncestors();
+ if (HTMLArea.is_ie) {
+ var bookmark = range.getBookmark();
+ }
+
+ if (!selectionEmpty) {
+ // The selection is not empty
+ for (var i = 0; i < ancestors.length; ++i) {
+ fullNodeSelected = (HTMLArea.is_ie && ((this.editor._statusBarTree.selected === ancestors[i] && ancestors[i].innerText === range.text) || (!this.editor._statusBarTree.selected && ancestors[i].innerText === range.text)))
+ || (HTMLArea.is_gecko && ((this.editor._statusBarTree.selected === ancestors[i] && ancestors[i].textContent === range.toString()) || (!this.editor._statusBarTree.selected && ancestors[i].textContent === range.toString())));
+ if (fullNodeSelected) {
+ if (this.isInlineElement(ancestors[i])) {
+ parent = ancestors[i];
+ }
+ break;
+ }
+ }
+ // Working around bug in Safari selectNodeContents
+ if (!fullNodeSelected && HTMLArea.is_safari && this.editor._statusBarTree.selected && this.isInlineElement(this.editor._statusBarTree.selected) && this.editor._statusBarTree.selected.textContent === range.toString()) {
+ fullNodeSelected = true;
+ parent = this.editor._statusBarTree.selected;
+ }
+ }
+ if (!selectionEmpty && !fullNodeSelected) {
+ // The selection is not empty, nor full element
+ if (className !== "none") {
+ // Add span element with class attribute
+ if (HTMLArea.is_gecko) {
+ var newElement = this.editor._doc.createElement("span");
+ HTMLArea._addClass(newElement, className);
+ range.surroundContents(newElement);
+ newElement.normalize();
+ parent.normalize();
+ // Firefox sometimes inserts empty elements just outside the boundaries of the range
+ var neighbour = newElement.previousSibling;
+ if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
+ HTMLArea.removeFromParent(neighbour);
+ }
+ neighbour = newElement.nextSibling;
+ if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
+ HTMLArea.removeFromParent(neighbour);
+ }
+ this.editor.selectNodeContents(newElement, false);
+ range.detach();
+ } else {
+ var rangeStart = range.duplicate();
+ rangeStart.collapse(true);
+ var parentStart = rangeStart.parentElement();
+ var rangeEnd = range.duplicate();
+ rangeEnd.collapse(true);
+ var parentEnd = rangeEnd.parentElement();
+ var newRange = editor._createRange();
+
+ var upperParentStart = parentStart;
+ if (parentStart !== parent) {
+ while (upperParentStart.parentNode !== parent) {
+ upperParentStart = upperParentStart.parentNode;
+ }
+ }
+
+ var newElement = editor._doc.createElement("span");
+ HTMLArea._addClass(newElement, className);
+ newElement.innerHTML = range.htmlText;
+ // IE eats spaces on the start boundary
+ if (range.htmlText.charAt(0) === "\x20") {
+ newElement.innerHTML = " " + newElement.innerHTML;
+ }
+ var newElementClone = newElement.cloneNode(true);
+ range.pasteHTML(newElement.outerHTML);
+ // IE inserts the element as the last child of the start container
+ if (parentStart !== parent
+ && parentStart.lastChild
+ && parentStart.lastChild.nodeType === 1
+ && parentStart.lastChild.nodeName.toLowerCase() === "span") {
+ parent.insertBefore(newElementClone, upperParentStart.nextSibling);
+ parentStart.removeChild(parentStart.lastChild);
+ // Sometimes an empty previous sibling was created
+ if (newElementClone.previousSibling
+ && newElementClone.previousSibling.nodeType === 1
+ && !newElementClone.previousSibling.innerText) {
+ parent.removeChild(newElementClone.previousSibling);
+ }
+ // The bookmark will not work anymore
+ newRange.moveToElementText(newElementClone);
+ newRange.collapse(false);
+ newRange.select();
+ } else {
+ // Working around IE boookmark bug
+ if (parentStart != parentEnd) {
+ var newRange = editor._createRange();
+ if (newRange.moveToBookmark(bookmark)) {
+ newRange.collapse(false);
+ newRange.select();
+ }
+ } else {
+ range.collapse(false);
+ }
+ }
+ parent.normalize();
+ }
+ }
+ } else {
+ // Add or remove class
+ if (parent && !HTMLArea.isBlockElement(parent)) {
+ if (className === "none" && parent.className && /\S/.test(parent.className)) {
+ classNames = parent.className.trim().split(" ");
+ HTMLArea._removeClass(parent, classNames[classNames.length-1]);
+ }
+ if (className !== "none") {
+ HTMLArea._addClass(parent, className);
+ }
+ // Remove the span tag if it has no more attribute
+ if ((parent.nodeName.toLowerCase() === "span") && !this.hasAllowedAttributes(parent)) {
+ this.removeMarkup(parent);
+ }
+ }
+ }
+ },
+
+ /*
+ * This function verifies if the element has any of the allowed attributes
+ */
+ hasAllowedAttributes : function(element) {
+ var allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", "class", "className");
+ for (var i = 0; i < allowedAttributes.length; ++i) {
+ if (element.getAttribute(allowedAttributes[i])) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /*
+ * This function removes the given markup element
+ */
+ removeMarkup : function(element) {
+ var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
+ var parent = element.parentNode;
+ while (element.firstChild) {
+ parent.insertBefore(element.firstChild, element);
+ }
+ parent.removeChild(element);
+ this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+ },
+
+ /*
+ * This function gets called when the plugin is generated
+ * Get the classes configuration and initiate the parsing of the style sheets
+ */
+ onGenerate : function() {
+ this.generate(this.editor, "TextStyle");
+ },
+
+ /*
+ * This function gets called on plugin generation, on toolbar update and on change mode
+ * Re-initiate the parsing of the style sheets, if not yet completed, and refresh our toolbar components
+ */
+ generate : function(editor, dropDownId) {
+ if (this.cssLoaded) {
+ this.updateToolbar(dropDownId);
+ } else {
+ if (this.cssTimeout) {
+ if (editor._iframe.contentWindow) {
+ editor._iframe.contentWindow.clearTimeout(this.cssTimeout);
+ } else {
+ window.clearTimeout(this.cssTimeout);
+ }
+ this.cssTimeout = null;
+ }
+ if (this.classesUrl && (typeof(HTMLArea.classesLabels) === "undefined")) {
+ this.getJavascriptFile(this.classesUrl);
+ }
+ this.buildCssArray(editor, dropDownId);
+ }
+ },
+
+ buildCssArray : function(editor, dropDownId) {
+ this.cssArray = this.parseStyleSheet();
+ if (!this.cssLoaded && (this.cssParseCount < 17)) {
+ var buildCssArrayLaterFunctRef = this.makeFunctionReference("buildCssArray");
+ this.cssTimeout = editor._iframe.contentWindow ? editor._iframe.contentWindow.setTimeout(buildCssArrayLaterFunctRef, 200) : window.setTimeout(buildCssArrayLaterFunctRef, 200);
+ this.cssParseCount++;
+ } else {
+ this.cssTimeout = null;
+ this.cssLoaded = true;
+ this.cssArray = this.sortCssArray(this.cssArray);
+ this.updateToolbar(dropDownId);
+ }
+ },
+
+ parseStyleSheet : function() {
+ var iframe = this.editor._iframe.contentWindow ? this.editor._iframe.contentWindow.document : this.editor._iframe.contentDocument;
+ var newCssArray = new Object();
+ this.cssLoaded = true;
+ for (var i = 0; i < iframe.styleSheets.length; i++) {
+ if (HTMLArea.is_gecko) {
+ try {
+ newCssArray = this.parseCssRule(iframe.styleSheets[i].cssRules, newCssArray);
+ } catch(e) {
+ this.cssLoaded = false;
+ }
+ } else {
+ try{
+ // @import StyleSheets (IE)
+ if (iframe.styleSheets[i].imports) {
+ newCssArray = this.parseCssIEImport(iframe.styleSheets[i].imports, newCssArray);
+ }
+ if (iframe.styleSheets[i].rules) {
+ newCssArray = this.parseCssRule(iframe.styleSheets[i].rules, newCssArray);
+ }
+ } catch(e) {
+ this.cssLoaded = false;
+ }
+ }
+ }
+ return newCssArray;
+ },
+
+ parseCssIEImport : function(cssIEImport, cssArray) {
+ var newCssArray = new Object();
+ newCssArray = cssArray;
+ for (var i=0; i < cssIEImport.length; i++) {
+ if (cssIEImport[i].imports) {
+ newCssArray = this.parseCssIEImport(cssIEImport[i].imports, newCssArray);
+ }
+ if (cssIEImport[i].rules) {
+ newCssArray = this.parseCssRule(cssIEImport[i].rules, newCssArray);
+ }
+ }
+ return newCssArray;
+ },
+
+ parseCssRule : function(cssRules, cssArray) {
+ var newCssArray = new Object();
+ newCssArray = cssArray;
+ for (var rule = 0; rule < cssRules.length; rule++) {
+ // StyleRule
+ if (cssRules[rule].selectorText) {
+ newCssArray = this.parseSelectorText(cssRules[rule].selectorText, newCssArray);
+ } else {
+ // ImportRule (Mozilla)
+ if (cssRules[rule].styleSheet) {
+ newCssArray = this.parseCssRule(cssRules[rule].styleSheet.cssRules, newCssArray);
+ }
+ // MediaRule (Mozilla)
+ if (cssRules[rule].cssRules) {
+ newCssArray = this.parseCssRule(cssRules[rule].cssRules, newCssArray);
+ }
+ }
+ }
+ return newCssArray;
+ },
+
+ parseSelectorText : function(selectorText, cssArray) {
+ var cssElements = new Array();
+ var cssElement = new Array();
+ var tagName, className;
+ var newCssArray = new Object();
+ newCssArray = cssArray;
+ if (selectorText.search(/:+/) == -1) {
+ // split equal Styles (Mozilla-specific) e.q. head, body {border:0px}
+ // for ie not relevant. returns allways one element
+ cssElements = selectorText.split(",");
+ for (var k = 0; k < cssElements.length; k++) {
+ cssElement = cssElements[k].split(".");
+ tagName = cssElement[0].toLowerCase().trim();
+ if (!tagName) {
+ tagName = 'all';
+ }
+ className = cssElement[1];
+ if (!HTMLArea.reservedClassNames.test(className)) {
+ if (((tagName != "all") && (!this.tags || !this.tags[tagName]))
+ || ((tagName == "all") && (!this.tags || !this.tags[tagName]) && this.showTagFreeClasses)
+ || (this.tags && this.tags[tagName] && this.tags[tagName].allowedClasses.indexOf(className) != -1)) {
+ if (!newCssArray[tagName]) {
+ newCssArray[tagName] = new Object();
+ }
+ if (className) {
+ cssName = className;
+ if (HTMLArea.classesLabels && HTMLArea.classesLabels[className]) {
+ cssName = this.prefixLabelWithClassName ? (className + " - " + HTMLArea.classesLabels[className]) : HTMLArea.classesLabels[className];
+ cssName = this.postfixLabelWithClassName ? (cssName + " - " + className) : cssName;
+ }
+ } else {
+ className = 'none';
+ cssName = this.localize("Element style");
+ }
+ newCssArray[tagName][className] = cssName;
+ }
+ }
+ }
+ }
+ return newCssArray;
+ },
+
+ sortCssArray : function(cssArray) {
+ var newCssArray = new Object();
+ for (var tagName in cssArray) {
+ if (cssArray.hasOwnProperty(tagName)) {
+ newCssArray[tagName] = new Object();
+ var tagArrayKeys = new Array();
+ for (var cssClass in cssArray[tagName]) {
+ if (cssArray[tagName].hasOwnProperty(cssClass)) {
+ tagArrayKeys.push(cssClass);
+ }
+ }
+ function compare(a, b) {
+ x = cssArray[tagName][a];
+ y = cssArray[tagName][b];
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ }
+ tagArrayKeys = tagArrayKeys.sort(compare);
+ for (var i = 0; i < tagArrayKeys.length; ++i) {
+ newCssArray[tagName][tagArrayKeys[i]] = cssArray[tagName][tagArrayKeys[i]];
+ }
+ }
+ }
+ return newCssArray;
+ },
+
+ /*
+ * This function gets called when the toolbar is being updated
+ */
+ onUpdateToolbar : function() {
+ if (this.editor._editMode !== "textmode") {
+ this.generate(this.editor, "TextStyle");
+ }
+ },
+
+ /*
+ * This function gets called when the drop-down list needs to be refreshed
+ */
+ updateToolbar : function(dropDownId) {
+ var editor = this.editor;
+ if (editor._editMode !== "textmode") {
+ var tagName = false, classNames = Array(), fullNodeSelected = false;
+ var selection = editor._getSelection();
+ var range = editor._createRange(selection);
+ var parent = editor.getParentElement(selection);
+ var ancestors = editor.getAllAncestors();
+ if (parent && !HTMLArea.isBlockElement(parent)) {
+ tagName = parent.nodeName.toLowerCase();
+ if (parent.className && /\S/.test(parent.className)) {
+ classNames = parent.className.trim().split(" ");
+ }
+ }
+ var selectionEmpty = editor._selectionEmpty(selection);
+ if (!selectionEmpty) {
+ for (var i = 0; i < ancestors.length; ++i) {
+ fullNodeSelected = (editor._statusBarTree.selected === ancestors[i])
+ && ((HTMLArea.is_gecko && ancestors[i].textContent === range.toString()) || (HTMLArea.is_ie && ancestors[i].innerText === range.text));
+ if (fullNodeSelected) {
+ if (!HTMLArea.isBlockElement(ancestors[i])) {
+ tagName = ancestors[i].nodeName.toLowerCase();
+ if (ancestors[i].className && /\S/.test(ancestors[i].className)) {
+ classNames = ancestors[i].className.trim().split(" ");
+ }
+ }
+ break;
+ }
+ }
+ // Working around bug in Safari selectNodeContents
+ if (!fullNodeSelected && HTMLArea.is_safari && this.editor._statusBarTree.selected && this.isInlineElement(this.editor._statusBarTree.selected) && this.editor._statusBarTree.selected.textContent === range.toString()) {
+ fullNodeSelected = true;
+ tagName = this.editor._statusBarTree.selected.nodeName.toLowerCase();
+ if (this.editor._statusBarTree.selected.className && /\S/.test(this.editor._statusBarTree.selected.className)) {
+ classNames = this.editor._statusBarTree.selected.className.trim().split(" ");
+ }
+ }
+ }
+ var selectionInInlineElement = tagName && this.REInlineTags.test(tagName);
+ var disabled = !this.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
+ if (!disabled && !tagName) {
+ tagName = "span";
+ }
+
+ this.updateValue(dropDownId, tagName, classNames, selectionEmpty, fullNodeSelected, disabled);
+ }
+ },
+
+ /*
+ * This function determines if the end poins of the current selection are within the same block
+ */
+ endPointsInSameBlock : function() {
+ var selection = this.editor._getSelection();
+ if (this.editor._selectionEmpty(selection)) {
+ return true;
+ } else {
+ var parent = this.editor.getParentElement(selection);
+ var endBlocks = this.editor.getEndBlocks(selection);
+ return (endBlocks.start === endBlocks.end && !/^(body|table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
+ }
+ },
+
+ updateValue : function(dropDownId, tagName, classNames, selectionEmpty, fullNodeSelected, disabled) {
+ var editor = this.editor;
+ var select = document.getElementById(editor._toolbarObjects[dropDownId]["elementId"]);
+ var cssArray = new Array();
+
+ while(select.options.length > 0) {
+ select.options[select.length-1] = null;
+ }
+ select.options[0] = new Option(this.localize("No style"),"none");
+ if (this.REInlineTags.test(tagName)) {
+ // Get classes allowed for all tags
+ if (typeof(this.cssArray["all"]) !== "undefined") {
+ if (this.tags && this.tags[tagName]) {
+ var allowedClasses = this.tags[tagName].allowedClasses;
+ for (cssClass in this.cssArray["all"]) {
+ if (allowedClasses.indexOf(cssClass) !== -1) {
+ cssArray[cssClass] = this.cssArray["all"][cssClass];
+ }
+ }
+ } else {
+ for (cssClass in this.cssArray["all"]) {
+ if (this.cssArray["all"].hasOwnProperty(cssClass)) {
+ cssArray[cssClass] = this.cssArray["all"][cssClass];
+ }
+ }
+ }
+ }
+ // Merge classes allowed for tagName and sort the array
+ if (typeof(this.cssArray[tagName]) !== "undefined") {
+ if (this.tags && this.tags[tagName]) {
+ var allowedClasses = this.tags[tagName].allowedClasses;
+ for (var cssClass in this.cssArray[tagName]) {
+ if (allowedClasses.indexOf(cssClass) !== -1) {
+ cssArray[cssClass] = this.cssArray[tagName][cssClass];
+ }
+ }
+ } else {
+ for (var cssClass in this.cssArray[tagName]) {
+ if (this.cssArray[tagName].hasOwnProperty(cssClass)) {
+ cssArray[cssClass] = this.cssArray[tagName][cssClass];
+ }
+ }
+ }
+ var sortedCssArray = new Object();
+ var cssArrayKeys = new Array();
+ for (var cssClass in cssArray) {
+ if (cssArray.hasOwnProperty(cssClass)) {
+ cssArrayKeys.push(cssClass);
+ }
+ }
+ function compare(a, b) {
+ x = cssArray[a];
+ y = cssArray[b];
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ }
+ cssArrayKeys = cssArrayKeys.sort(compare);
+ for (var i = 0; i < cssArrayKeys.length; ++i) {
+ sortedCssArray[cssArrayKeys[i]] = cssArray[cssArrayKeys[i]];
+ }
+ cssArray = sortedCssArray;
+ }
+ for (var cssClass in cssArray) {
+ if (cssArray.hasOwnProperty(cssClass) && cssArray[cssClass]) {
+ if (cssClass == "none") {
+ select.options[0] = new Option(cssArray[cssClass], cssClass);
+ } else {
+ select.options[select.options.length] = new Option(cssArray[cssClass], cssClass);
+ if (!editor.config.disablePCexamples && HTMLArea.classesValues && HTMLArea.classesValues[cssClass] && !HTMLArea.classesNoShow[cssClass]) {
+ select.options[select.options.length-1].setAttribute("style", HTMLArea.classesValues[cssClass]);
+ }
+ }
+ }
+ }
+
+ select.selectedIndex = 0;
+ if (classNames.length && (selectionEmpty || fullNodeSelected)) {
+ for (i = select.options.length; --i >= 0;) {
+ if (classNames[classNames.length-1] == select.options[i].value) {
+ select.options[i].selected = true;
+ select.selectedIndex = i;
+ select.options[0].text = this.localize("Remove style");
+ break;
+ }
+ }
+ if (select.selectedIndex == 0) {
+ select.options[select.options.length] = new Option(this.localize("Unknown style"), classNames[classNames.length-1]);
+ select.options[select.options.length-1].selected = true;
+ select.selectedIndex = select.options.length-1;
+ }
+ for (i = select.options.length; --i >= 0;) {
+ if (("," + classNames.join(",") + ",").indexOf("," + select.options[i].value + ",") !== -1) {
+ if (select.selectedIndex != i) {
+ select.options[i] = null;
+ }
+ }
+ }
+ }
+ }
+ select.disabled = !(select.options.length>1) || disabled;
+ select.className = "";
+ if (select.disabled) {
+ select.className = "buttonDisabled";
+ }
+ },
+
+ /*
+ * This function gets called when the editor has changed its mode to "wysiwyg"
+ */
+ onMode : function(mode) {
+ if (mode === "wysiwyg") {
+ this.generate(this.editor, "TextStyle");
+ }
+ }
+});
+
## This is a list of additional attributes to keep
keepPDIVattribs = id, title, dir, lang, xml:lang
+ ## DO NOT REMAP BOLD AND ITALIC TO STRONG AND EMPHASIS AND VICE VERSA
+ transformBoldAndItalicTags = 0
+
## CONTENT TO DATABASE
entryHTMLparser_db = 1
entryHTMLparser_db {
q.allowedAttribs = id, title, dir, lang, xml:lang, class
samp.allowedAttribs = id, title, dir, lang, xml:lang, class
small.allowedAttribs = id, title, dir, lang, xml:lang, class
+ strike.allowedAttribs = id, title, dir, lang, xml:lang, class
strong.allowedAttribs = id, title, dir, lang, xml:lang, class
sub.allowedAttribs = id, title, dir, lang, xml:lang, class
sup.allowedAttribs = id, title, dir, lang, xml:lang, class
tt.allowedAttribs = id, title, dir, lang, xml:lang, class
+ u.allowedAttribs = id, title, dir, lang, xml:lang, class
var.allowedAttribs = id, title, dir, lang, xml:lang, class
}
exitHTMLparser_db = 1
exitHTMLparser_db {
- ## REMAP B AND I TAGS
- ## b and i tags are used by Mozilla/Firefox in editing mode.
- ## This must be done on exit because the default HTMLparser_db parsing executes the reverse mapping.
- tags.b.remap = strong
- tags.i.remap = em
-
## KEEP ALL TAGS
## Unwanted tags were removed on entry.
## Without this rule, the parser will remove all tags! Presumably, this rule will be more efficient than repeating the allowTags rule