зеркало из https://github.com/mozilla/pjs.git
Fixing bug 92686. Return inserts line break, should insert paragraph break. Patch by daniel@glazman.org, r=brade@comcast.netbrade@comcast.net, sr=jst@mozilla.org
This commit is contained in:
Родитель
50ae79dc39
Коммит
371985eba8
|
@ -51,7 +51,7 @@ interface nsIContentFilter;
|
|||
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_EDITOR, 1)
|
||||
|
||||
%}
|
||||
[scriptable, uuid(4b0fd0d0-1dd2-11b2-bf2e-ef20fbca2c88)]
|
||||
[scriptable, uuid(afc36593-5787-4420-93d9-b2c0ccbf0cad)]
|
||||
|
||||
interface nsIHTMLEditor : nsISupports
|
||||
{
|
||||
|
@ -600,5 +600,13 @@ interface nsIHTMLEditor : nsISupports
|
|||
void checkSelectionStateForAnonymousButtons(in nsISelection aSelection);
|
||||
|
||||
boolean isAnonymousElement(in nsIDOMElement aElement);
|
||||
|
||||
/**
|
||||
* A boolean indicating if a return key pressed in a paragraph creates
|
||||
* another paragraph or just inserts a <br> at the caret
|
||||
*
|
||||
* @return true if CR in a paragraph creates a new paragraph
|
||||
*/
|
||||
attribute boolean returnInParagraphCreatesNewParagraph;
|
||||
};
|
||||
|
||||
|
|
|
@ -6288,10 +6288,18 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
|||
*aCancel = PR_FALSE;
|
||||
*aHandled = PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> sibling;
|
||||
nsresult res = NS_OK;
|
||||
nsCOMPtr<nsIDOMNode> parent;
|
||||
PRInt32 offset;
|
||||
nsresult res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
PRBool doesCRCreateNewP;
|
||||
res = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph(&doesCRCreateNewP);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
PRBool newBRneeded = PR_FALSE;
|
||||
nsCOMPtr<nsIDOMNode> sibling;
|
||||
|
||||
// easy case, in a text node:
|
||||
if (mHTMLEditor->IsTextNode(aNode))
|
||||
{
|
||||
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
|
||||
|
@ -6304,46 +6312,37 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
|||
{
|
||||
// is there a BR prior to it?
|
||||
mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
|
||||
if (!sibling)
|
||||
if (!sibling ||
|
||||
!mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
|
||||
{
|
||||
// no previous sib, so
|
||||
// just fall out to default of inserting a BR
|
||||
return res;
|
||||
newBRneeded = PR_TRUE;
|
||||
}
|
||||
if (mHTMLEditor->IsVisBreak(sibling) && !nsTextEditUtils::HasMozAttr(sibling))
|
||||
{
|
||||
// split para
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
*aHandled = PR_TRUE;
|
||||
res = SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
|
||||
}
|
||||
// else just fall out to default of inserting a BR
|
||||
return res;
|
||||
}
|
||||
// at end of text node?
|
||||
if (aOffset == (PRInt32)strLength)
|
||||
else if (aOffset == (PRInt32)strLength)
|
||||
{
|
||||
// we're at the end of text node...
|
||||
// is there a BR after to it?
|
||||
res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
|
||||
if (!sibling)
|
||||
if (!sibling ||
|
||||
!mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
|
||||
{
|
||||
// no next sib, so
|
||||
// just fall out to default of inserting a BR
|
||||
return res;
|
||||
newBRneeded = PR_TRUE;
|
||||
offset++;
|
||||
}
|
||||
if (mHTMLEditor->IsVisBreak(sibling) && !nsTextEditUtils::HasMozAttr(sibling))
|
||||
{
|
||||
// split para
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
*aHandled = PR_TRUE;
|
||||
res = SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
|
||||
}
|
||||
// else just fall out to default of inserting a BR
|
||||
return res;
|
||||
}
|
||||
// inside text node
|
||||
// just fall out to default of inserting a BR
|
||||
return res;
|
||||
else
|
||||
{
|
||||
if (doesCRCreateNewP)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> tmp;
|
||||
res = mEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
|
||||
if (NS_FAILED(res)) return res;
|
||||
aNode = tmp;
|
||||
}
|
||||
|
||||
newBRneeded = PR_TRUE;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -6359,19 +6358,27 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
|||
if (NS_FAILED(res)) return res;
|
||||
if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
|
||||
{
|
||||
// just fall out to default of inserting a BR
|
||||
return res;
|
||||
newBRneeded = PR_TRUE;
|
||||
}
|
||||
}
|
||||
// else split para
|
||||
*aHandled = PR_TRUE;
|
||||
res = SplitParagraph(aPara, nearNode, aSelection, address_of(selNode), &aOffset);
|
||||
if (!newBRneeded)
|
||||
sibling = nearNode;
|
||||
}
|
||||
if (newBRneeded)
|
||||
{
|
||||
// if CR does not create a new P, default to BR creation
|
||||
if (!doesCRCreateNewP)
|
||||
return NS_OK;
|
||||
|
||||
return res;
|
||||
nsCOMPtr<nsIDOMNode> brNode;
|
||||
res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
|
||||
sibling = brNode;
|
||||
}
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
*aHandled = PR_TRUE;
|
||||
return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// SplitParagraph: split a paragraph at selection point, possibly deleting a br
|
||||
//
|
||||
|
|
|
@ -151,8 +151,9 @@ nsHTMLEditor::nsHTMLEditor()
|
|||
: nsPlaintextEditor()
|
||||
, mIgnoreSpuriousDragEvent(PR_FALSE)
|
||||
, mTypeInState(nsnull)
|
||||
, mSelectedCellIndex(0)
|
||||
, mCRInParagraphCreatesParagraph(PR_FALSE)
|
||||
, mHTMLCSSUtils(nsnull)
|
||||
, mSelectedCellIndex(0)
|
||||
, mIsObjectResizingEnabled(PR_TRUE)
|
||||
, mIsResizing(PR_FALSE)
|
||||
, mIsAbsolutelyPositioningEnabled(PR_TRUE)
|
||||
|
@ -163,11 +164,6 @@ nsHTMLEditor::nsHTMLEditor()
|
|||
, mIsInlineTableEditingEnabled(PR_TRUE)
|
||||
, mGridSize(0)
|
||||
{
|
||||
mBoldAtom = do_GetAtom("b");
|
||||
mItalicAtom = do_GetAtom("i");
|
||||
mUnderlineAtom = do_GetAtom("u");
|
||||
mFontAtom = do_GetAtom("font");
|
||||
mLinkAtom = do_GetAtom("a");
|
||||
}
|
||||
|
||||
nsHTMLEditor::~nsHTMLEditor()
|
||||
|
@ -6114,3 +6110,16 @@ nsHTMLEditor::IsAnonymousElement(nsIDOMElement * aElement, PRBool * aReturn)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(PRBool aCreatesNewParagraph)
|
||||
{
|
||||
mCRInParagraphCreatesParagraph = aCreatesNewParagraph;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(PRBool *aCreatesNewParagraph)
|
||||
{
|
||||
*aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -768,21 +768,12 @@ protected:
|
|||
|
||||
TypeInState* mTypeInState;
|
||||
|
||||
nsCOMPtr<nsIAtom> mBoldAtom;
|
||||
nsCOMPtr<nsIAtom> mItalicAtom;
|
||||
nsCOMPtr<nsIAtom> mUnderlineAtom;
|
||||
nsCOMPtr<nsIAtom> mFontAtom;
|
||||
nsCOMPtr<nsIAtom> mLinkAtom;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mCachedNode;
|
||||
|
||||
PRBool mCachedBoldStyle;
|
||||
PRBool mCachedItalicStyle;
|
||||
PRBool mCachedUnderlineStyle;
|
||||
nsString mCachedFontName;
|
||||
PRPackedBool mCRInParagraphCreatesParagraph;
|
||||
|
||||
// Used to disable HTML formatting commands during HTML source editing
|
||||
PRBool mCanEditHTML;
|
||||
PRPackedBool mCSSAware;
|
||||
nsHTMLCSSUtils *mHTMLCSSUtils;
|
||||
|
||||
// Used by GetFirstSelectedCell and GetNextSelectedCell
|
||||
PRInt32 mSelectedCellIndex;
|
||||
|
@ -790,13 +781,9 @@ protected:
|
|||
nsString mLastStyleSheetURL;
|
||||
nsString mLastOverrideStyleSheetURL;
|
||||
|
||||
PRBool mCSSAware;
|
||||
nsHTMLCSSUtils *mHTMLCSSUtils;
|
||||
|
||||
// Maintain a list of associated style sheets and their urls.
|
||||
nsStringArray mStyleSheetURLs;
|
||||
nsCOMArray<nsICSSStyleSheet> mStyleSheets;
|
||||
PRInt32 mNumStyleSheets;
|
||||
|
||||
// an array for holding default style settings
|
||||
nsVoidArray mDefaultStyles;
|
||||
|
|
|
@ -134,3 +134,5 @@ pref("editor.disable_spell_checker", false);
|
|||
pref("editor.dont_lock_spell_files", true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
pref("editor.CR_creates_new_p", false);
|
||||
|
|
|
@ -78,6 +78,8 @@ var gColorObj = { LastTextColor:"", LastBackgroundColor:"", LastHighlightColor:"
|
|||
var gDefaultTextColor = "";
|
||||
var gDefaultBackgroundColor = "";
|
||||
var gCSSPrefListener;
|
||||
var gEditorToolbarPrefListener;
|
||||
var gReturnInParagraphPrefListener;
|
||||
var gPrefs;
|
||||
var gLocalFonts = null;
|
||||
|
||||
|
@ -90,6 +92,8 @@ var gFontSizeNames = ["xx-small","x-small","small","medium","large","x-large","x
|
|||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
|
||||
const kEditorToolbarPrefs = "editor.toolbars.showbutton.";
|
||||
const kUseCssPref = "editor.use_css";
|
||||
const kCRInParagraphsPref = "editor.CR_creates_new_p";
|
||||
|
||||
function ShowHideToolbarSeparators(toolbar) {
|
||||
var childNodes = toolbar.childNodes;
|
||||
|
@ -123,55 +127,18 @@ function ShowHideToolbarButtons()
|
|||
ShowHideToolbarSeparators(document.getElementById("FormatToolbar"));
|
||||
}
|
||||
|
||||
function AddToolbarPrefListener()
|
||||
function nsPrefListener(prefName)
|
||||
{
|
||||
try {
|
||||
var pbi = GetPrefs().QueryInterface(Components.interfaces.nsIPrefBranchInternal);
|
||||
pbi.addObserver(kEditorToolbarPrefs, gEditorToolbarPrefListener, false);
|
||||
} catch(ex) {
|
||||
dump("Failed to observe prefs: " + ex + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
function RemoveToolbarPrefListener()
|
||||
{
|
||||
try {
|
||||
var pbi = GetPrefs().QueryInterface(Components.interfaces.nsIPrefBranchInternal);
|
||||
pbi.removeObserver(kEditorToolbarPrefs, gEditorToolbarPrefListener);
|
||||
} catch(ex) {
|
||||
dump("Failed to remove pref observer: " + ex + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Pref listener constants
|
||||
const gEditorToolbarPrefListener =
|
||||
{
|
||||
observe: function(subject, topic, prefName)
|
||||
{
|
||||
// verify that we're changing a button pref
|
||||
if (topic != "nsPref:changed")
|
||||
return;
|
||||
|
||||
var id = prefName.substr(kEditorToolbarPrefs.length) + "Button";
|
||||
var button = document.getElementById(id);
|
||||
if (button) {
|
||||
button.hidden = !gPrefs.getBoolPref(prefName);
|
||||
ShowHideToolbarSeparators(button.parentNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function nsButtonPrefListener()
|
||||
{
|
||||
this.startup();
|
||||
this.startup(prefName);
|
||||
}
|
||||
|
||||
// implements nsIObserver
|
||||
nsButtonPrefListener.prototype =
|
||||
nsPrefListener.prototype =
|
||||
{
|
||||
domain: "editor.use_css",
|
||||
startup: function()
|
||||
domain: "",
|
||||
startup: function(prefName)
|
||||
{
|
||||
this.domain = prefName;
|
||||
try {
|
||||
var pbi = pref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
|
||||
pbi.addObserver(this.domain, this, false);
|
||||
|
@ -194,26 +161,44 @@ nsButtonPrefListener.prototype =
|
|||
return;
|
||||
// verify that we're changing a button pref
|
||||
if (topic != "nsPref:changed") return;
|
||||
if (prefName.substr(0, this.domain.length) != this.domain) return;
|
||||
|
||||
var cmd = document.getElementById("cmd_highlight");
|
||||
if (cmd) {
|
||||
var prefs = GetPrefs();
|
||||
var useCSS = prefs.getBoolPref(prefName);
|
||||
if (prefName.substr(0, kUseCssPref.length) == kUseCssPref)
|
||||
{
|
||||
var cmd = document.getElementById("cmd_highlight");
|
||||
if (cmd) {
|
||||
var prefs = GetPrefs();
|
||||
var useCSS = prefs.getBoolPref(prefName);
|
||||
var editor = GetCurrentEditor();
|
||||
if (useCSS && editor) {
|
||||
var mixedObj = {};
|
||||
var state = editor.getHighlightColorState(mixedObj);
|
||||
cmd.setAttribute("state", state);
|
||||
cmd.collapsed = false;
|
||||
}
|
||||
else {
|
||||
cmd.setAttribute("state", "transparent");
|
||||
cmd.collapsed = true;
|
||||
}
|
||||
|
||||
if (editor)
|
||||
editor.isCSSEnabled = useCSS;
|
||||
}
|
||||
}
|
||||
else if (prefName.substr(0, kEditorToolbarPrefs.length) == kEditorToolbarPrefs)
|
||||
{
|
||||
var id = prefName.substr(kEditorToolbarPrefs.length) + "Button";
|
||||
var button = document.getElementById(id);
|
||||
if (button) {
|
||||
button.hidden = !gPrefs.getBoolPref(prefName);
|
||||
ShowHideToolbarSeparators(button.parentNode);
|
||||
}
|
||||
}
|
||||
else if (prefName.substr(0, kCRInParagraphsPref.length) == kCRInParagraphsPref)
|
||||
{
|
||||
var crInParagraphCreatesParagraph = gPrefs.getBoolPref(prefName);
|
||||
var editor = GetCurrentEditor();
|
||||
if (useCSS && editor) {
|
||||
var mixedObj = {};
|
||||
var state = editor.getHighlightColorState(mixedObj);
|
||||
cmd.setAttribute("state", state);
|
||||
cmd.collapsed = false;
|
||||
}
|
||||
else {
|
||||
cmd.setAttribute("state", "transparent");
|
||||
cmd.collapsed = true;
|
||||
}
|
||||
|
||||
if (editor)
|
||||
editor.isCSSEnabled = useCSS;
|
||||
editor.returnInParagraphCreatesNewParagraph = crInParagraphCreatesParagraph;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -418,6 +403,9 @@ var gEditorDocumentObserver =
|
|||
// Things for just the Web Composer application
|
||||
if (IsWebComposer())
|
||||
{
|
||||
var prefs = GetPrefs();
|
||||
editor.returnInParagraphCreatesNewParagraph = prefs.getBoolPref(kCRInParagraphsPref);
|
||||
|
||||
// Set focus to content window if not a mail composer
|
||||
// Race conditions prevent us from setting focus here
|
||||
// when loading a url into blank window
|
||||
|
@ -566,15 +554,17 @@ function EditorStartup()
|
|||
SetupComposerWindowCommands();
|
||||
|
||||
ShowHideToolbarButtons();
|
||||
AddToolbarPrefListener();
|
||||
gEditorToolbarPrefListener = new nsPrefListener(kEditorToolbarPrefs);
|
||||
|
||||
gCSSPrefListener = new nsButtonPrefListener();
|
||||
gCSSPrefListener = new nsPrefListener(kUseCssPref);
|
||||
gReturnInParagraphPrefListener = new nsPrefListener(kCRInParagraphsPref);
|
||||
|
||||
// hide Highlight button if we are in an HTML editor with CSS mode off
|
||||
// and tell the editor if a CR in a paragraph creates a new paragraph
|
||||
var prefs = GetPrefs();
|
||||
var cmd = document.getElementById("cmd_highlight");
|
||||
if (cmd) {
|
||||
var prefs = GetPrefs();
|
||||
var useCSS = prefs.getBoolPref("editor.use_css");
|
||||
var useCSS = prefs.getBoolPref(kUseCssPref);
|
||||
if (!useCSS && is_HTMLEditor) {
|
||||
cmd.collapsed = true;
|
||||
}
|
||||
|
@ -692,8 +682,9 @@ function EditorResetFontAndColorAttributes()
|
|||
|
||||
function EditorShutdown()
|
||||
{
|
||||
RemoveToolbarPrefListener();
|
||||
gEditorToolbarPrefListener.shutdown();
|
||||
gCSSPrefListener.shutdown();
|
||||
gReturnInParagraphPrefListener.shutdown();
|
||||
|
||||
try {
|
||||
var commandManager = GetCurrentCommandManager();
|
||||
|
@ -1332,7 +1323,7 @@ function GetBackgroundElementWithColor()
|
|||
else
|
||||
{
|
||||
var prefs = GetPrefs();
|
||||
var IsCSSPrefChecked = prefs.getBoolPref("editor.use_css");
|
||||
var IsCSSPrefChecked = prefs.getBoolPref(kUseCssPref);
|
||||
if (IsCSSPrefChecked && IsHTMLEditor())
|
||||
{
|
||||
var selection = editor.selection;
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<script type="application/x-javascript">
|
||||
<!-- Add "shouldAutoSave", "autoSaveAmount" to _elementIDs when implemented -->
|
||||
<![CDATA[
|
||||
var _elementIDs = ["maintainTableStructure", "preserveFormatting", "saveAssociatedFiles", "showPublishDialog", "recentFiles", "useCSS"];
|
||||
var _elementIDs = ["maintainTableStructure", "preserveFormatting", "saveAssociatedFiles", "showPublishDialog", "recentFiles", "useCSS", "crInPCreatesNewP"];
|
||||
]]>
|
||||
</script>
|
||||
<script type="application/x-javascript" src="chrome://editor/content/EdDialogCommon.js"/>
|
||||
|
@ -187,9 +187,18 @@
|
|||
<groupbox>
|
||||
<caption label="&cssEditing.label;"/>
|
||||
<checkbox
|
||||
label = "&useCSS.label;"
|
||||
id = "useCSS"
|
||||
prefstring= "editor.use_css"
|
||||
label = "&useCSS.label;"
|
||||
id = "useCSS"
|
||||
prefstring = "editor.use_css"
|
||||
/>
|
||||
</groupbox>
|
||||
|
||||
<groupbox>
|
||||
<caption label="&carriageReturns.label;"/>
|
||||
<checkbox
|
||||
label = "&crInPCreatesNewP.label;"
|
||||
id = "crInPCreatesNewP"
|
||||
prefstring = "editor.CR_creates_new_p"
|
||||
/>
|
||||
</groupbox>
|
||||
</page>
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
<!-- extracted from content/pref-composer.xul -->
|
||||
|
||||
<!--LOCALIZATION NOTE : FILE 'Composer' prefs dialog. Similar to Communcator 4.x Document Properties/Colors and Background -->
|
||||
|
||||
<!--LOCALIZATION NOTE (title.label): DONT_TRANSLATE -->
|
||||
<!ENTITY title.label "Composer">
|
||||
<!ENTITY lHeader "Composer">
|
||||
<!ENTITY rHeader "Settings for authoring of new Web pages">
|
||||
|
||||
<!ENTITY tableEditing.label "Table Editing">
|
||||
<!ENTITY maintainTableStructure.label "Maintain table layout when inserting or deleting cells">
|
||||
<!ENTITY maintainTableStructure.tooltip "Preserves table's rectangular shape by automatically adding cells after inserting or deleting cells">
|
||||
<!ENTITY maintainStructure.accesskey "m">
|
||||
|
||||
<!ENTITY savingFiles.title "When Saving or Publishing Pages">
|
||||
<!ENTITY preserveExistingFormatting "Retain original source formatting">
|
||||
<!ENTITY preserveExistingFormatting.accesskey "o">
|
||||
<!ENTITY preserveExistingFormatting.tooltip "Preserves line breaks and page's original formatting">
|
||||
<!ENTITY reformat.label "Reformat HTML source">
|
||||
<!ENTITY reformat.accesskey "r">
|
||||
<!ENTITY reformat.tooltip "Reformat HTML tags using line breaks and indentation">
|
||||
|
||||
<!ENTITY saveAssociatedFiles.label "Save images and other associated files when saving pages">
|
||||
<!ENTITY showPublishDialog.label "Always show Publish dialog when publishing pages">
|
||||
|
||||
<!ENTITY saving "Saving">
|
||||
<!ENTITY AutoSaveCheck "Automatically save every">
|
||||
<!ENTITY minText "minutes">
|
||||
|
||||
<!ENTITY exterLegend.label "External Editors">
|
||||
<!ENTITY htmlSource "HTML Source:">
|
||||
<!ENTITY imageeditor "Images:">
|
||||
<!ENTITY chooseButton.label "Choose">
|
||||
|
||||
<!ENTITY recentFiles.title "Recent Pages Menu">
|
||||
<!ENTITY documentsInMenu.accesskey "n">
|
||||
<!ENTITY documentsInMenu "Maximum number of pages listed:">
|
||||
|
||||
|
||||
<!ENTITY cssEditing.label "Cascading Style Sheets (CSS) Editing">
|
||||
<!ENTITY useCSS.label "Use CSS styles instead of HTML elements and attributes">
|
Загрузка…
Ссылка в новой задаче