/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is CaScadeS, a stylesheet editor for Composer. * * The Initial Developer of the Original Code is * Daniel Glazman. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Original author: Daniel Glazman * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const SHEET = 1; const STYLE_RULE = 2; const IMPORT_RULE = 3; const MEDIA_RULE = 4; const CHARSET_RULE = 5; const PAGE_RULE = 6; const OWNER_NODE = 7; // const COMPATIBILITY_TAB = 1; const GENERAL_TAB = 2; const TEXT_TAB = 3; const BACKGROUND_TAB = 4; const BORDER_TAB = 5; const BOX_TAB = 6; const AURAL_TAB = 7; const GENERIC_SELECTOR = 0; const TYPE_ELEMENT_SELECTOR = 1; const CLASS_SELECTOR = 2; const kAsyncTimeout = 1500; // 1.5 second var objectsArray = null; var gTimerID; var gAsyncLoadingTimerID; // needed for commonCssProps.js var gHaveDocumentUrl = false; var gInsertIndex = -1; // * dialog initialization code function Startup() { // are we in a pre-1.3 Mozilla ? if (typeof window.InitEditorShell == "function") { // yes, so let's get an editorshell if (!InitEditorShell()) return; } else if (typeof window.GetCurrentEditor != "function" || !GetCurrentEditor()) { window.close(); return; } // gDialog is declared in EdDialogCommon.js gDialog.selectionBased = false; // Set commonly-used widgets like this: gDialog.selectedTab = TEXT_TAB; gDialog.sheetsTreechildren = document.getElementById("stylesheetsTree"); gDialog.sheetsTree = document.getElementById("sheetsTree"); gDialog.sheetInfoTab = document.getElementById("sheetInfoTab"); gDialog.atimportButton = document.getElementById("atimportButton"); gDialog.atmediaButton = document.getElementById("atmediaButton"); gDialog.linkButton = document.getElementById("linkButton"); gDialog.styleButton = document.getElementById("styleButton"); gDialog.ruleButton = document.getElementById("ruleButton"); gDialog.removeButton = document.getElementById("removeButton"); gDialog.upButton = document.getElementById("upButton"); gDialog.downButton = document.getElementById("downButton"); gDialog.selectedTab = GENERAL_TAB; gDialog.sheetInfoTabPanelTitle = document.getElementById("sheetInfoTabPanelTitle"); gDialog.textTab = document.getElementById("textTab"); gDialog.brownFoxLabel = document.getElementById("brownFoxLabel"); gDialog.backgroundImageInput = document.getElementById("backgroundImageInput"); gDialog.backgroundPreview = document.getElementById("backgroundPreview"); gDialog.sheetTabbox = document.getElementById("sheetTabbox"); gDialog.backgroundColorInput = document.getElementById("backgroundColorInput"); gDialog.textColorInput = document.getElementById("textColorInput"); gDialog.backgroundRepeatMenulist = document.getElementById("backgroundRepeatMenulist"); gDialog.backgroundAttachmentCheckbox = document.getElementById("backgroundAttachmentCheckbox"); gDialog.xBackgroundPositionRadiogroup = document.getElementById("xBackgroundPositionRadiogroup"); gDialog.yBackgroundPositionRadiogroup = document.getElementById("yBackgroundPositionRadiogroup"); gDialog.fontFamilyRadiogroup = document.getElementById("fontFamilyRadiogroup"); gDialog.customFontFamilyInput = document.getElementById("customFontFamilyInput"); gDialog.predefFontFamilyMenulist = document.getElementById("predefFontFamilyMenulist"); gDialog.fontSizeInput = document.getElementById("fontSizeInput"); gDialog.lineHeightInput = document.getElementById("lineHeightInput"); gDialog.textUnderlineCheckbox = document.getElementById("underlineTextDecorationCheckbox"); gDialog.textOverlineCheckbox = document.getElementById("overlineTextDecorationCheckbox"); gDialog.textLinethroughCheckbox = document.getElementById("linethroughTextDecorationCheckbox"); gDialog.textBlinkCheckbox = document.getElementById("blinkTextDecorationCheckbox"); gDialog.noDecorationCheckbox = document.getElementById("noneTextDecorationCheckbox"); gDialog.topBorderStyleMenulist = document.getElementById("topBorderStyleMenulist"); gDialog.topBorderWidthInput = document.getElementById("topBorderWidthInput"); gDialog.topBorderColorInput = document.getElementById("topBorderColorInput"); gDialog.leftBorderStyleMenulist = document.getElementById("leftBorderStyleMenulist"); gDialog.leftBorderWidthInput = document.getElementById("leftBorderWidthInput"); gDialog.leftBorderColorInput = document.getElementById("leftBorderColorInput"); gDialog.rightBorderStyleMenulist = document.getElementById("rightBorderStyleMenulist"); gDialog.rightBorderWidthInput = document.getElementById("rightBorderWidthInput"); gDialog.rightBorderColorInput = document.getElementById("rightBorderColorInput"); gDialog.bottomBorderStyleMenulist = document.getElementById("bottomBorderStyleMenulist"); gDialog.bottomBorderWidthInput = document.getElementById("bottomBorderWidthInput"); gDialog.bottomBorderColorInput = document.getElementById("bottomBorderColorInput"); gDialog.allFourBordersSame = document.getElementById("allFourBordersSame"); gDialog.borderPreview = document.getElementById("borderPreview"); gDialog.volumeScrollbar = document.getElementById("volumeScrollbar"); gDialog.volumeMenulist = document.getElementById("volumeMenulist"); gDialog.muteVolumeCheckbox = document.getElementById("muteVolumeCheckbox"); gDialog.opacityScrollbar = document.getElementById("opacityScrollbar"); gDialog.opacityLabel = document.getElementById("opacityLabel"); gDialog.sheetInfoTabGridRows = document.getElementById("sheetInfoTabGridRows"); gDialog.sheetInfoTabGrid = document.getElementById("sheetInfoTabGrid"); gDialog.expertMode = true; gDialog.modified = false; gDialog.selectedIndex = -1; gHaveDocumentUrl = GetDocumentBaseUrl(); // Initialize all dialog widgets here, // e.g., get attributes from an element for property dialog InitSheetsTree(gDialog.sheetsTreechildren); // Set window location relative to parent window (based on persisted attributes) SetWindowLocation(); } // * Toggles on/off expert mode. In expert mode, all buttons are enabled // including buttons for stylesheet creation. When the mode is off, only // the "create rule" button is enabled, a stylesheet being created to contain // the new rule if necessary function toggleExpertMode() { // toggle the boolean gDialog.expertMode = !gDialog.expertMode; if (gDialog.expertMode) { if (gDialog.selectedIndex == -1) { // if expert mode is on but no selection in the tree, only // sheet creation buttons are enabled UpdateButtons(false, false, true, true, false, false); } else { // if expert mode is on and we have something selected in the tree, // the state of the buttons depend on the type of the selection var external = objectsArray[gDialog.selectedIndex].external; var type = objectsArray[gDialog.selectedIndex].type; UpdateButtons(!external, !external, true, true, !external, (!external || (type == SHEET)) ); } } else { // if we're not in expert mode, allow only rule creation button UpdateButtons(!gDialog.atimportButton.hasAttribute("disabled"), !gDialog.atmediaButton.hasAttribute("disabled"), !gDialog.linkButton.hasAttribute("disabled"), !gDialog.styleButton.hasAttribute("disabled"), !gDialog.ruleButton.hasAttribute("disabled"), !gDialog.removeButton.hasAttribute("disabled")); } } // * This function recreates the contents of the STYLE elements and // of the stylesheets local to the filesystem function FlushChanges() { if (gDialog.modified) { // let's make sure the editor is going to require save on exit getCurrentEditor().incrementModificationCount(1); } // Validate all user data and set attributes and possibly insert new element here // If there's an error the user must correct, return false to keep dialog open. var sheet; for (var i = 0; i < objectsArray.length; i++) { if (objectsArray[i].modified && !objectsArray[i].external && objectsArray[i].type == SHEET) { /* let's serialize this stylesheet ! */ sheet = objectsArray[i].cssElt; if (sheet.ownerNode.nodeName.toLowerCase() == "link") SerializeExternalSheet(sheet, null); else SerializeEmbeddedSheet(sheet); } } SaveWindowLocation(); return true; // do close the window } // * removes all the content in a tree // param XULElement sheetsTree function CleanSheetsTree(sheetsTreeChildren) { // we need to clear the selection in the tree otherwise the onselect // action on the tree will be fired when we removed the selected entry ClearTreeSelection(gDialog.sheetsTree); var elt = sheetsTreeChildren.firstChild; while (elt) { var tmp = elt.nextSibling; sheetsTreeChildren.removeChild(elt); elt = tmp; } } function AddSheetEntryToTree(sheetsTree, ownerNode) { if (ownerNode.nodeType == Node.ELEMENT_NODE) { var ownerTag = ownerNode.nodeName.toLowerCase() var relType = ownerNode.getAttribute("rel"); if (relType) relType = relType.toLowerCase(); if (ownerTag == "style" || (ownerTag == "link" && relType.indexOf("stylesheet") != -1)) { var treeitem = document.createElementNS(XUL_NS, "treeitem"); var treerow = document.createElementNS(XUL_NS, "treerow"); var treecell = document.createElementNS(XUL_NS, "treecell"); // what kind of owner node do we have here ? // a style element indicates an embedded stylesheet, // while a link element indicates an external stylesheet; // the case of an XML Processing Instruction is not handled, we // are supposed to be in HTML 4 var external = false; if (ownerTag == "style") { treecell.setAttribute("label", "internal stylesheet"); } else if (ownerTag == "link") { // external stylesheet, let's present its URL to user treecell.setAttribute("label", ownerNode.href); external = true; if ( /(\w*):.*/.test(ownerNode.href) ) { if (RegExp.$1 == "file") { external = false; } } else external = false; } // add a new entry to the tree var o = newObject( treeitem, external, SHEET, ownerNode.sheet, false, 0 ); PushInObjectsArray(o); treerow.appendChild(treecell); treeitem.appendChild(treerow); treeitem.setAttribute("container", "true"); // add enties to the tree for the rules in the current stylesheet var rules = null; if (ownerNode.sheet) rules = ownerNode.sheet.cssRules; AddRulesToTreechildren(treeitem, rules, external, 1); sheetsTree.appendChild(treeitem); } } } function PushInObjectsArray(o) { if (gInsertIndex == -1) objectsArray.push(o); else { objectsArray.splice(gInsertIndex, 0, o); gInsertIndex++; } } // * populates the tree in the dialog with entries // corresponding to all stylesheets and css rules attached to // document // param XULElement sheetsTree function InitSheetsTree(sheetsTree) { // remove all entries in the tree CleanSheetsTree(sheetsTree); // Look for the stylesheets attached to the current document // Get them from the STYLE and LINK elements because of async sheet loading : // the LINK element is always here while the corresponding sheet might be // delayed by network var headNode = GetHeadElement(); if ( headNode && headNode.hasChildNodes() ) { var ssn = headNode.childNodes.length; objectsArray = new Array(); if (ssn) { var i; gInsertIndex = -1; for (i=0; i= 0 && objectsArray[index].type != SHEET) { index--; } return index; } // * declares that the stylesheet containing the selected tree entry // has been modified function SetModifiedFlagOnStylesheet() { var index = GetSheetContainer(); if (index != -1) { objectsArray[index].modified = true; gDialog.modified = true; } } // * we are about to put some info about the selected entry into // the Info tab // return XULElement function PrepareInfoGridForCreation() { gDialog.sheetTabbox.selectedTab = gDialog.sheetInfoTab; var gridrows = gDialog.sheetInfoTabGridRows; var grid = gDialog.sheetInfoTabGrid; if (gridrows) { grid.removeChild(gridrows); } gridrows = document.createElementNS(XUL_NS, "rows"); gDialog.sheetInfoTabGridRows = gridrows; gridrows.setAttribute("id", "sheetInfoTabGridRows"); grid.appendChild(gridrows); grid.removeAttribute("style"); return gridrows; } // * user wants to create a @import rule function CreateNewAtimportRule() { var gridrows = PrepareInfoGridForCreation(); gDialog.newType = IMPORT_RULE; gDialog.newMediaList = ""; gDialog.newURL = ""; gDialog.sheetInfoTabPanelTitle.setAttribute("value", "New Import At-rule"); AddEditableZoneToInfobox(gridrows, "URL:", gDialog.newURL, "onNewURLChange", true); AddEditableZoneToInfobox(gridrows, "Media list:", gDialog.newMediaList, "onNewMediaListChange", false); AddSingleButtonToInfobox(gridrows, "Create Import At-rule", "onConfirmCreateNewObject", false); } // * user wants to create a new style rule function CreateNewStyleRule() { var gridrows = PrepareInfoGridForCreation(); gDialog.newExternal = false; gDialog.newType = STYLE_RULE; gDialog.newSelector = ""; gDialog.sheetInfoTabPanelTitle.setAttribute("value", "New Style Rule"); gDialog.newSelectorType = CLASS_SELECTOR; var radiogroup = AddRadioGroupToInfoBox(gridrows, "Create a new:"); // offer choice between class selector and type element selector AddRadioToRadioGroup(radiogroup, "named style (enter class name below)", "onCreationStyleRuleTypeChange", CLASS_SELECTOR, true); AddRadioToRadioGroup(radiogroup, "style applied to all elements of type (enter type below)", "onCreationStyleRuleTypeChange", TYPE_ELEMENT_SELECTOR, false); // oh, and in expert mode, allow of course any selector if (gDialog.expertMode) { AddRadioToRadioGroup(radiogroup, "style applied to all elements matching the following selector", "onCreationStyleRuleTypeChange", GENERIC_SELECTOR, false); } AddEditableZoneToInfobox(gridrows, " ", gDialog.newSelector, "onNewSelectorChange", true); AddSingleButtonToInfobox(gridrows, "Create Style Rule", "onConfirmCreateNewObject"); } // * user changed the type of style rule (s)he wants to create // param integer type function onCreationStyleRuleTypeChange(type) { gDialog.newSelectorType = type; } // * user wants to create a new embedded stylesheet function CreateNewStyleElement() { var gridrows = PrepareInfoGridForCreation(); gDialog.newExternal = false; gDialog.newType = SHEET; gDialog.newMediaList = ""; gDialog.newTitle = ""; gDialog.sheetInfoTabPanelTitle.setAttribute("value", "New Stylesheet"); AddLabelToInfobox(gridrows, "Type:", "text/css", null, false); AddEditableZoneToInfobox(gridrows, "Media list:", gDialog.newMediaList, "onNewMediaListChange", true); AddEditableZoneToInfobox(gridrows, "Title:", gDialog.newTitle, "onNewTitleChange", false); AddSingleButtonToInfobox(gridrows, "Create Stylesheet", "onConfirmCreateNewObject") } // * user wants to attach an external stylesheet function CreateNewLinkedSheet() { var gridrows = PrepareInfoGridForCreation(); gDialog.newExternal = true; gDialog.newType = SHEET; gDialog.newAlternate = false; gDialog.newURL = ""; gDialog.newMediaList = ""; gDialog.newTitle = ""; gDialog.sheetInfoTabPanelTitle.setAttribute("value", "New Linked Stylesheet"); AddLabelToInfobox(gridrows, "Type:", "text/css", null, false); // alternate stylesheet ? AddCheckboxToInfobox(gridrows, "Alternate:", "check to create alternate stylesheet", gDialog.newAlternate, "onNewAlternateChange"); gDialog.URLtextbox = AddEditableZoneToInfobox(gridrows, "URL:", gDialog.newURL, "onNewURLChange", true); AddSingleButtonToInfobox(gridrows, "Choose file", "onChooseLocalFile") AddEditableZoneToInfobox(gridrows, "Media list:", gDialog.newMediaList, "onNewMediaListChange", false); AddEditableZoneToInfobox(gridrows, "Title:", gDialog.newTitle, "onNewTitleChange", false); AddSingleButtonToInfobox(gridrows, "Create Stylesheet", "onConfirmCreateNewObject") // the two following labels are unfortunately useful... AddLabelToInfobox(gridrows, "", "(Warning : save document *before* attaching local stylesheet)", null, false); AddLabelToInfobox(gridrows, "", "(use Refresh button if stylesheet is not immediately downloaded)", null, false); } // * forget about everything, and let's redo it function Restart() { // delete all objects we keep track of var l = objectsArray.length; for (var i=0; i= 0 ; i--) { if (objectsArray[i].type == SHEET && ! objectsArray[i].external) { indexLastEmbeddedStylesheet = i; break; } } if (indexLastEmbeddedStylesheet != -1) { gDialog.selectedIndex = indexLastEmbeddedStylesheet; } else { // there is no stylesheet ! let's create one that will contain our rule gDialog.newExternal = false; gDialog.newMediaList = ""; gDialog.newTitle = ""; gDialog.newType = SHEET; var selectorType = gDialog.newSelectorType; selector = gDialog.newSelector; onConfirmCreateNewObject(); // now, create the rule... gDialog.newType = STYLE_RULE; gDialog.newSelectorType = selectorType; gDialog.newSelector = selector; } } var containerIndex, sheetIndex; var cssObject; var l; var ruleIndex; var newSheetOwnerNode; var headNode; var newCssRule; switch (gDialog.newType) { case STYLE_RULE: if (gDialog.newSelector != "") { containerIndex = gDialog.selectedIndex; while (objectsArray[containerIndex].type != SHEET && objectsArray[containerIndex].type != MEDIA_RULE) containerIndex--; switch (gDialog.newSelectorType) { case TYPE_ELEMENT_SELECTOR: case GENERIC_SELECTOR: selector = gDialog.newSelector; break; case CLASS_SELECTOR: selector = "." + gDialog.newSelector; break; } cssObject = objectsArray[containerIndex].cssElt; l = cssObject.cssRules.length; cssObject.insertRule(selector + " { }", l); if (cssObject.cssRules.length > l) { // hmmm, there's always the bad case of a wrong rule, dropped by the // parser ; that's why we need to check we really inserted something /* find inserted rule's index in objectsArray */ var depth = objectsArray[containerIndex].depth; var external = objectsArray[containerIndex].external; ruleIndex = containerIndex + 1; while (ruleIndex < objectsArray.length && objectsArray[ruleIndex].depth > depth) { ruleIndex++; } var subtreechildren = GetSubtreeChildren(objectsArray[containerIndex].xulElt); gInsertIndex = ruleIndex; var subtreeitem = AddStyleRuleToTreeChildren(cssObject.cssRules[l], external, depth); subtreechildren.appendChild(subtreeitem); selectTreeItem(subtreeitem); SetModifiedFlagOnStylesheet(); } } break; case IMPORT_RULE: if (gDialog.newURL != "") { containerIndex = GetSheetContainer(); // **must** clear the selection before changing the tree ClearTreeSelection(gDialog.sheetsTree); var containerCssObject = objectsArray[containerIndex].cssElt; var containerDepth = objectsArray[containerIndex].depth; var containerExternal = objectsArray[containerIndex].external; var cssRuleIndex = -1; if (containerCssObject.cssRules) for (i=0; i < containerCssObject.cssRules.length; i++) if (containerCssObject.cssRules[i].type != CSSRule.IMPORT_RULE && containerCssObject.cssRules[i].type != CSSRule.CHARSET_RULE) { cssRuleIndex = i; break; } if (cssRuleIndex == -1) { // no rule in the sheet for the moment or only charset and import rules containerCssObject.insertRule('@import url("'+ gDialog.newURL + '") ' + gDialog.newMediaList + ";", containerCssObject.cssRules.length); newCssRule = containerCssObject.cssRules[containerCssObject.cssRules.length - 1]; subtreechildren = GetSubtreeChildren(objectsArray[containerIndex].xulElt); gInsertIndex = ruleIndex; subtreeitem = AddImportRuleToTreeChildren(newCssRule, containerExternal, containerDepth + 1); subtreechildren.appendChild(subtreeitem); ruleIndex = FindObjectIndexInObjectsArray(newCssRule); } else { // cssRuleIndex is the index of the first not charset and not import rule in the sheet ruleIndex = FindObjectIndexInObjectsArray(containerCssObject.cssRules[cssRuleIndex]); // and ruleIndex represents the index of the corresponding object in objectsArray var refObject = objectsArray[ruleIndex]; containerCssObject.insertRule('@import url("'+ gDialog.newURL + '") ' + gDialog.newMediaList + ";", cssRuleIndex); newCssRule = containerCssObject.cssRules[cssRuleIndex]; gInsertIndex = ruleIndex; subtreeitem = AddImportRuleToTreeChildren(newCssRule, containerExternal, containerDepth + 1); var refNode = refObject.xulElt; refNode.parentNode.insertBefore(subtreeitem, refNode); } selectTreeItem(subtreeitem); SetModifiedFlagOnStylesheet(); if (gAsyncLoadingTimerID) clearTimeout(gAsyncLoadingTimerID); if (!newCssRule.styleSheet) gAsyncLoadingTimerID = setTimeout("sheetLoadedTimeoutCallback(" + ruleIndex + ")", kAsyncTimeout); } break; case SHEET: gInsertIndex = -1; ClearTreeSelection(gDialog.sheetsTree); if (gDialog.newExternal && gDialog.newURL != "") { subtreeitem = document.createElementNS(XUL_NS, "treeitem"); var subtreerow = document.createElementNS(XUL_NS, "treerow"); var subtreecell = document.createElementNS(XUL_NS, "treecell"); subtreeitem.setAttribute("container", "true"); subtreerow.appendChild(subtreecell); subtreeitem.appendChild(subtreerow); gDialog.sheetsTreechildren.appendChild(subtreeitem); newSheetOwnerNode = getCurrentEditor().document.createElement("link"); newSheetOwnerNode.setAttribute("type", "text/css"); newSheetOwnerNode.setAttribute("href", gDialog.newURL); if (gDialog.newAlternate) { newSheetOwnerNode.setAttribute("rel", "alternate stylesheet"); } else { newSheetOwnerNode.setAttribute("rel", "stylesheet"); } if (gDialog.newMediaList != "") { newSheetOwnerNode.setAttribute("media", gDialog.newMediaList); } if (gDialog.newTitle != "") { newSheetOwnerNode.setAttribute("title", gDialog.newTitle); } headNode = GetHeadElement(); headNode.appendChild(newSheetOwnerNode); subtreecell.setAttribute("label", gDialog.newURL); external = true; if ( /(\w*):.*/.test(gDialog.newURL) ) { if (RegExp.$1 == "file") { external = false; } } if (external) subtreecell.setAttribute("properties", "external"); if (!newSheetOwnerNode.sheet) { /* hack due to asynchronous load of external stylesheet */ var o = newObject( subtreeitem, external, OWNER_NODE, newSheetOwnerNode, false, 0 ); PushInObjectsArray(o) if (gAsyncLoadingTimerID) clearTimeout(gAsyncLoadingTimerID); sheetIndex = objectsArray.length - 1; gAsyncLoadingTimerID = setTimeout("sheetLoadedTimeoutCallback(" + sheetIndex + ")", kAsyncTimeout); } else { o = newObject( subtreeitem, external, SHEET, newSheetOwnerNode.sheet, false, 0 ); PushInObjectsArray(o) AddRulesToTreechildren(subtreeitem, newSheetOwnerNode.sheet.cssRules, external, 1); } } else if (!gDialog.newExternal) { newSheetOwnerNode = getCurrentEditor().document.createElement("style"); newSheetOwnerNode.setAttribute("type", "text/css"); if (gDialog.newMediaList != "") { newSheetOwnerNode.setAttribute("media", gDialog.newMediaList); } if (gDialog.newTitle != "") { newSheetOwnerNode.setAttribute("title", gDialog.newTitle); } headNode = GetHeadElement(); headNode.appendChild(newSheetOwnerNode); AddSheetEntryToTree(gDialog.sheetsTreechildren, newSheetOwnerNode); selectTreeItem(objectsArray[objectsArray.length - 1].xulElt); } selectTreeItem(subtreeitem); break; } } // * we need that to refresh the tree after async sheet load // param integer index function sheetLoadedTimeoutCallback(index) { var subtreeitem = objectsArray[index].xulElt; gInsertIndex = index+1; ClearTreeSelection(gDialog.sheetsTree); if (objectsArray[index].type == OWNER_NODE && objectsArray[index].cssElt.sheet != null) { var sheet = objectsArray[index].cssElt.sheet; AddRulesToTreechildren(subtreeitem , sheet.cssRules, objectsArray[index].external, objectsArray[index].depth+1); objectsArray[index].type = SHEET; objectsArray[index].cssElt = sheet; } else if (objectsArray[index].type == IMPORT_RULE && objectsArray[index].cssElt.styleSheet != null) { AddRulesToTreechildren(subtreeitem , objectsArray[index].cssElt.styleSheet.cssRules, true, objectsArray[index].depth+1) } else return; selectTreeItem(subtreeitem); } // * gets the object's index corresponding to an entry in the tree // param XULElement object // return integer function FindObjectIndexInObjectsArray(object) { var i, l = objectsArray.length; for (i=0; i depth; i++); toSplice = i - objectIndex; break; case IMPORT_RULE: case MEDIA_RULE: for (ruleIndexInTree=objectIndex-1; objectsArray[ruleIndexInTree].depth >= depth; ruleIndexInTree--); objectsArray[ruleIndexInTree].modified = true; ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentStyleSheet.cssRules, gDialog.selectedObject); if (ruleIndex != -1) { gDialog.selectedObject.parentStyleSheet.deleteRule(ruleIndex); } for (i=objectIndex+1; i depth; i++); toSplice = i - objectIndex; break; case STYLE_RULE: for (ruleIndexInTree=objectIndex-1; objectsArray[ruleIndexInTree].depth; ruleIndexInTree--); objectsArray[ruleIndexInTree].modified = true; if (gDialog.selectedObject.parentRule) { /* this style rule is contained in an at-rule */ /* need to remove the rule only from the at-rule listing it */ ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentRule.cssRules, gDialog.selectedObject); if (ruleIndex != -1) { gDialog.selectedObject.parentRule.deleteRule(ruleIndex); } } else { ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentStyleSheet.cssRules, gDialog.selectedObject); if (ruleIndex != -1) { gDialog.selectedObject.parentStyleSheet.deleteRule(ruleIndex); } } toSplice = 1; break; } // let's remove the whole treeitem gDialog.treeItem.parentNode.removeChild(gDialog.treeItem); // and then remove the objects from our array objectsArray.splice(objectIndex, toSplice); // can we select an item ? if (objectsArray.length) selectTreeItem(objectsArray[Math.min(objectIndex, objectsArray.length - 1)].xulElt); } // * moves a sheet/rule up in the tree function MoveObjectUp() { GetSelectedItemData(); var index = gDialog.selectedIndex; if (index <= 0) return; var sheetIndex = GetSheetContainer(); switch (gDialog.selectedType) { case SHEET: var ownerNode = gDialog.selectedObject.ownerNode; ClearTreeSelection(gDialog.sheetsTree) index--; while (index && objectsArray[index].type != SHEET) index--; if (index == -1) return; ownerNode.parentNode.insertBefore(ownerNode, objectsArray[index].cssElt.ownerNode); Restart(); selectTreeItem(objectsArray[index].xulElt); gDialog.modified = true; break; case OWNER_NODE: ownerNode = gDialog.selectedObject; ClearTreeSelection(gDialog.sheetsTree) index--; while (index && objectsArray[index].type != SHEET) index--; if (index == -1) return; ownerNode.parentNode.insertBefore(ownerNode, objectsArray[index].cssElt.ownerNode); Restart(); selectTreeItem(objectsArray[index].xulElt); gDialog.modified = true; break; case STYLE_RULE: case PAGE_RULE: var rule = gDialog.selectedObject; objectsArray[sheetIndex].modified = true; ClearTreeSelection(gDialog.sheetsTree) var ruleText = rule.cssText; var subtreeitem; var newRule; if (rule.parentRule) { var ruleIndex = getRuleIndexInRulesList(rule.parentRule.cssRules, rule); var parentRule = rule.parentRule; if (ruleIndex == -1) return; if (!ruleIndex) { // we have to move the rule just before its parent rule parentRule.deleteRule(0); var parentRuleIndex; parentRuleIndex = getRuleIndexInRulesList(parentRule.parentStyleSheet.cssRules, parentRule); parentRule.parentStyleSheet.insertRule(ruleText, parentRuleIndex); newRule = parentRule.parentStyleSheet.cssRules[parentRuleIndex]; } else { // we just move the rule in its parentRule parentRule.deleteRule(ruleIndex); parentRule.insertRule(ruleText, ruleIndex - 1); newRule = parentRule.cssRules.item(ruleIndex - 1); } // remove the tree entry objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt); // delete the object objectsArray.splice(index, 1); // position the insertion index gInsertIndex = index - 1; subtreeitem = AddStyleRuleToTreeChildren(newRule, objectsArray[index-1].external, objectsArray[index-1].depth); // make the new tree entry objectsArray[index].xulElt.parentNode.insertBefore(subtreeitem, objectsArray[index].xulElt); selectTreeItem(subtreeitem); } else { // standard case, the parent of the rule is the stylesheet itself ruleIndex = getRuleIndexInRulesList(rule.parentStyleSheet.cssRules, rule); var refStyleSheet = rule.parentStyleSheet; if (ruleIndex == -1) return; if (ruleIndex) { // we just move the rule in the sheet refStyleSheet.deleteRule(ruleIndex); var targetRule = refStyleSheet.cssRules.item(ruleIndex - 1); if (targetRule.type == CSSRule.MEDIA_RULE) { targetRule.insertRule(ruleText, targetRule.cssRules.length); var targetRuleIndex = FindObjectIndexInObjectsArray(targetRule); newRule = targetRule.cssRules.item(targetRule.cssRules.length - 1); var subtreechildren = GetSubtreeChildren(objectsArray[targetRuleIndex].xulElt); // in this case, the target treeitem is at same location but one level deeper // remove the tree entry objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt); // delete the object objectsArray.splice(index, 1); // position the insertion index gInsertIndex = index; subtreeitem = AddStyleRuleToTreeChildren(newRule, objectsArray[targetRuleIndex].external, objectsArray[targetRuleIndex].depth + 1); // make the new tree entry subtreechildren.appendChild(subtreeitem); selectTreeItem(subtreeitem); return; } else if (targetRule.type != CSSRule.IMPORT_RULE && targetRule.type != CSSRule.CHARSET_RULE) { // we can move the rule before its predecessor only if that one is // not an @import rule nor an @charset rule refStyleSheet.insertRule(ruleText, ruleIndex - 1); newRule = refStyleSheet.cssRules[ruleIndex - 1]; // remove the tree entry objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt); // delete the object objectsArray.splice(index, 1); // position the insertion index gInsertIndex = index - 1; subtreeitem = AddStyleRuleToTreeChildren(newRule, objectsArray[index-1].external, objectsArray[index-1].depth); // make the new tree entry objectsArray[index].xulElt.parentNode.insertBefore(subtreeitem, objectsArray[index].xulElt); selectTreeItem(subtreeitem); return; } } // At this point, we need to move the rule from one sheet to another one, if it // exists... // First, let's if there is another candidate stylesheet before the current one // for the style rule's ownership var refSheetIndex = FindObjectIndexInObjectsArray(refStyleSheet); sheetIndex = refSheetIndex; if (!sheetIndex) return; // early way out sheetIndex--; while (sheetIndex && (objectsArray[sheetIndex].type != SHEET || objectsArray[sheetIndex])) { sheetIndex--; } if (sheetIndex == -1) return; // no embedded or local stylesheet available var newStyleSheet = objectsArray[sheetIndex].cssElt; // we need to check the type of the last rule in the sheet, if it exists if (newStyleSheet.cssRules.length && newStyleSheet.cssRules[newStyleSheet.cssRules.length - 1].type == CSSRule.MEDIA_RULE) { // this media rule is our target var refMediaRule = newStyleSheet.cssRules[newStyleSheet.cssRules.length - 1]; var refMediaRuleIndex = FindObjectIndexInObjectsArray(refMediaRule ); var refRuleIndex = refMediaRuleIndex++; while (refRuleIndex < objectsArray.length && objectsArray[refRuleIndex].depth > 1) refRuleIndex++; // refRuleIndex represents where we should insert the new object } else { // we just append the rule to the list of rules of the sheet } } break; } } // * moves a sheet/rule down in the tree function MoveObjectDown() { /* NOT YET IMPLEMENTED */ var objectIndex = FindObjectIndexInObjectsArray(gDialog.selectedObject); if (objectIndex == -1) return; } // * opens a file picker and returns the file:/// URL in gDialog.newURL function onChooseLocalFile() { // Get a local file, converted into URL format var fileName = getLocalFileURL(true); if (fileName) { gDialog.URLtextbox.setAttribute("value", fileName); gDialog.newURL = fileName; } } // * opens a file picker for a *.css filename, exports the selected stylesheet // in that file, and replace the selected embedded sheet by its external, but // local to the filesystem, new counterpart function onExportStylesheet() { var fileName = getLocalFileURL(false); SerializeExternalSheet(gDialog.selectedObject, fileName); var ownerNode = gDialog.selectedObject.ownerNode; var newSheetOwnerNode = ownerNode.ownerDocument.createElement("link"); newSheetOwnerNode.setAttribute("type", "text/css"); newSheetOwnerNode.setAttribute("href", fileName); newSheetOwnerNode.setAttribute("rel", "stylesheet"); var mediaList = ownerNode.getAttribute("media"); if (mediaList && mediaList != "") newSheetOwnerNode.setAttribute("media", mediaList); ownerNode.parentNode.insertBefore(newSheetOwnerNode, ownerNode); ownerNode.parentNode.removeChild(ownerNode); // we still have to wait for async sheet loading's completion if (gAsyncLoadingTimerID) clearTimeout(gAsyncLoadingTimerID); gAsyncLoadingTimerID = setTimeout("Refresh()", 500); } function getCurrentEditor() { if (typeof window.InitEditorShell == "function") return editorShell.editor; else return GetCurrentEditor(); } function AddCSSLevelChoice(rows) { var labelLabel = document.createElementNS(XUL_NS, "label"); var row = document.createElementNS(XUL_NS, "row"); row.setAttribute("align", "center"); labelLabel.setAttribute("value", "CSS Level"); row.appendChild(labelLabel); var level; for (level = 1; level < 4; level++) { var levelCheckbox = document.createElementNS(XUL_NS, "checkbox"); levelCheckbox.setAttribute("label", level); row.appendChild(levelCheckbox); } rows.appendChild(row); }