diff options
Diffstat (limited to 'WebCore/inspector/front-end/StylesSidebarPane.js')
-rw-r--r-- | WebCore/inspector/front-end/StylesSidebarPane.js | 393 |
1 files changed, 253 insertions, 140 deletions
diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index 5399953..c89e452 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -704,6 +704,26 @@ WebInspector.StylePropertiesSection.prototype = { return true; }, + nextEditableSibling: function() + { + var curSection = this; + do { + curSection = curSection.nextSibling; + } while (curSection && !curSection.editable); + + return curSection; + }, + + previousEditableSibling: function() + { + var curSection = this; + do { + curSection = curSection.previousSibling; + } while (curSection && !curSection.editable); + + return curSection; + }, + update: function(full) { if (full) { @@ -795,6 +815,7 @@ WebInspector.StylePropertiesSection.prototype = { this.propertiesTreeOutline.appendChild(item); item.listItemElement.textContent = ""; item._newProperty = true; + item.updateTitle(); return item; }, @@ -875,22 +896,35 @@ WebInspector.StylePropertiesSection.prototype = { if (WebInspector.isBeingEdited(element)) return; - WebInspector.startEditing(this._selectorElement, this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this), null); + WebInspector.startEditing(this._selectorElement, { + context: null, + commitHandler: this.editingSelectorCommitted.bind(this), + cancelHandler: this.editingSelectorCancelled.bind(this) + }); window.getSelection().setBaseAndExtent(element, 0, element, 1); }, editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection) { function moveToNextIfNeeded() { - if (!moveDirection || moveDirection !== "forward") + if (!moveDirection) return; - this.expand(); - if (this.propertiesTreeOutline.children.length === 0) - this.addNewBlankProperty().startEditing(); - else { - var item = this.propertiesTreeOutline.children[0] - item.startEditing(item.valueElement); + if (moveDirection === "forward") { + this.expand(); + if (this.propertiesTreeOutline.children.length === 0) + this.addNewBlankProperty().startEditing(); + else { + var item = this.propertiesTreeOutline.children[0] + item.startEditing(item.nameElement); + } + } else { + var previousSection = this.previousEditableSibling(); + if (!previousSection) + return; + + previousSection.expand(); + previousSection.addNewBlankProperty().startEditing(); } } @@ -1202,8 +1236,6 @@ WebInspector.StylePropertyTreeElement.prototype = { this.valueElement = valueElement; if (value) { - var self = this; - function processValue(regex, processor, nextProcessor, valueText) { var container = document.createDocumentFragment(); @@ -1227,19 +1259,10 @@ WebInspector.StylePropertyTreeElement.prototype = { function linkifyURL(url) { - var hrefUrl = url; - var match = hrefUrl.match(/['"]?([^'"]+)/); - if (match) - hrefUrl = match[1]; var container = document.createDocumentFragment(); container.appendChild(document.createTextNode("url(")); - if (self._styleRule.sourceURL) - hrefUrl = WebInspector.completeURL(self._styleRule.sourceURL, hrefUrl); - else if (WebInspector.panels.elements.focusedDOMNode) - hrefUrl = WebInspector.resourceURLForRelatedNode(WebInspector.panels.elements.focusedDOMNode, hrefUrl); - var hasResource = !!WebInspector.resourceForURL(hrefUrl); - // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI. - container.appendChild(WebInspector.linkifyURLAsNode(hrefUrl, url, null, hasResource)); + var hasResource = !!WebInspector.resourceForURL(url); + container.appendChild(WebInspector.linkifyURLAsNode(url, url, null, hasResource)); container.appendChild(document.createTextNode(")")); return container; } @@ -1341,7 +1364,7 @@ WebInspector.StylePropertyTreeElement.prototype = { var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g; var colorProcessor = processValue.bind(window, colorRegex, processColor, null); - valueElement.appendChild(processValue(/url\(\s*([^)\s]+)\s*\)/g, linkifyURL, colorProcessor, value)); + valueElement.appendChild(processValue(/url\(([^)]+)\)/g, linkifyURL, colorProcessor, value)); } this.listItemElement.removeChildren(); @@ -1357,8 +1380,11 @@ WebInspector.StylePropertyTreeElement.prototype = { this.listItemElement.appendChild(valueElement); this.listItemElement.appendChild(document.createTextNode(";")); - if (!this.parsedOk) + if (!this.parsedOk) { + // Avoid having longhands under an invalid shorthand. + this.hasChildren = false; this.listItemElement.addStyleClass("not-parsed-ok"); + } if (this.property.inactive) this.listItemElement.addStyleClass("inactive"); @@ -1471,60 +1497,147 @@ WebInspector.StylePropertyTreeElement.prototype = { if (this.parent.shorthand) return; - if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutline.section && !this.treeOutline.section.editable)) + if (this.treeOutline.section && !this.treeOutline.section.editable) + return; + + if (!selectElement) + selectElement = this.nameElement; // No arguments passed in - edit the name element by default. + else + selectElement = selectElement.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement.enclosingNodeOrSelfWithClass("value"); + + var isEditingName = selectElement === this.nameElement; + if (!isEditingName && selectElement !== this.valueElement) { + // Double-click in the LI - start editing value. + isEditingName = false; + selectElement = this.valueElement; + } + + if (WebInspector.isBeingEdited(selectElement)) return; var context = { expanded: this.expanded, hasChildren: this.hasChildren, - keyDownListener: this.editingKeyDown.bind(this), - keyPressListener: this.editingKeyPress.bind(this) + keyDownListener: isEditingName ? this.editingNameKeyDown.bind(this) : this.editingValueKeyDown.bind(this), + keyPressListener: isEditingName ? this.editingNameKeyPress.bind(this) : this.editingValueKeyPress.bind(this), + isEditingName: isEditingName, }; // Lie about our children to prevent expanding on double click and to collapse shorthands. this.hasChildren = false; - if (!selectElement) - selectElement = this.listItemElement; + selectElement.addEventListener("keydown", context.keyDownListener, false); + selectElement.addEventListener("keypress", context.keyPressListener, false); + if (selectElement.parentElement) + selectElement.parentElement.addStyleClass("child-editing"); + selectElement.textContent = selectElement.textContent; // remove color swatch and the like + + function shouldCommitValueSemicolon(text, cursorPosition) + { + // FIXME: should this account for semicolons inside comments? + var openQuote = ""; + for (var i = 0; i < cursorPosition; ++i) { + var ch = text[i]; + if (ch === "\\" && openQuote !== "") + ++i; // skip next character inside string + else if (!openQuote && (ch === "\"" || ch === "'")) + openQuote = ch; + else if (openQuote === ch) + openQuote = ""; + } + return !openQuote; + } - this.listItemElement.addEventListener("keydown", context.keyDownListener, false); - this.listItemElement.addEventListener("keypress", context.keyPressListener, false); + function nameValueFinishHandler(context, isEditingName, event) + { + // FIXME: the ":"/";" detection does not work for non-US layouts due to the event being keydown rather than keypress. + var isFieldInputTerminated = (event.keyCode === WebInspector.KeyboardShortcut.Keys.Semicolon.code) && + (isEditingName ? event.shiftKey : (!event.shiftKey && shouldCommitValueSemicolon(event.target.textContent, event.target.selectionLeftOffset))); + if (isEnterKey(event) || isFieldInputTerminated) { + // Enter or colon (for name)/semicolon outside of string (for value). + event.preventDefault(); + return "move-forward"; + } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) + return "cancel"; + else if (event.keyIdentifier === "U+0009") // Tab key. + return "move-" + (event.shiftKey ? "backward" : "forward"); + } - WebInspector.startEditing(this.listItemElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context); + WebInspector.startEditing(selectElement, { + context: context, + commitHandler: this.editingCommitted.bind(this), + cancelHandler: this.editingCancelled.bind(this), + customFinishHandler: nameValueFinishHandler.bind(this, context, isEditingName) + }); window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1); }, - editingKeyPress: function(event) + editingNameKeyPress: function(event) { - var selection = window.getSelection(); - var colonIndex = this.listItemElement.textContent.indexOf(":"); - var selectionLeftOffset = event.target.selectionLeftOffset; - - if (colonIndex < 0 || selectionLeftOffset <= colonIndex) { - // Complete property names. - var character = event.data.toLowerCase(); - if (character && /[a-z-]/.test(character)) { - var prefix = selection.anchorNode.textContent.substring(0, selection.anchorOffset); - var property = WebInspector.CSSCompletions.firstStartsWith(prefix + character); - - if (!selection.isCollapsed) - selection.deleteFromDocument(); - - this.restoreNameElement(); - - if (property) { - if (property !== this.nameElement.textContent) - this.nameElement.textContent = property; - this.nameElement.firstChild.select(prefix.length + 1); - event.preventDefault(); - } + // Complete property names. + var character = event.data.toLowerCase(); + if (character && /[a-z-]/.test(character)) { + var selection = window.getSelection(); + var prefix = selection.anchorNode.textContent.substring(0, selection.anchorOffset); + var property = WebInspector.cssNameCompletions.firstStartsWith(prefix + character); + + if (!selection.isCollapsed) + selection.deleteFromDocument(); + + this.restoreNameElement(); + + if (property) { + if (property !== this.nameElement.textContent) + this.nameElement.textContent = property; + this.nameElement.firstChild.select(prefix.length + 1); + event.preventDefault(); } - } else { - // FIXME: This should complete property values. } }, - editingKeyDown: function(event) + editingValueKeyPress: function(event) + { + // FIXME: This should complete property values. + }, + + editingNameKeyDown: function(event) + { + var showNext; + if (event.keyIdentifier === "Up") + showNext = false; + else if (event.keyIdentifier === "Down") + showNext = true; + else + return; + + var selection = window.getSelection(); + if (!selection.rangeCount) + return; + + var selectionRange = selection.getRangeAt(0); + if (selectionRange.commonAncestorContainer !== this.nameElement && !selectionRange.commonAncestorContainer.isDescendant(this.nameElement)) + return; + + const styleValueDelimeters = " \t\n\"':;,/()"; + var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.nameElement); + var wordString = wordRange.toString(); + var cursorPosition = selectionRange.startOffset != selectionRange.endOffset ? selectionRange.startOffset : 0; + var prefix = selectionRange.startContainer.textContent.substring(0, cursorPosition); + var property; + + if (showNext) + property = WebInspector.cssNameCompletions.next(wordString, prefix); + else + property = WebInspector.cssNameCompletions.previous(wordString, prefix); + + if (property) { + this.nameElement.textContent = property; + this.nameElement.firstChild.select(cursorPosition); + } + event.preventDefault(); + }, + + editingValueKeyDown: function(event) { var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down"); var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown"); @@ -1536,33 +1649,11 @@ WebInspector.StylePropertyTreeElement.prototype = { return; var selectionRange = selection.getRangeAt(0); - if (selectionRange.commonAncestorContainer !== this.listItemElement && !selectionRange.commonAncestorContainer.isDescendant(this.listItemElement)) + if (selectionRange.commonAncestorContainer !== this.valueElement && !selectionRange.commonAncestorContainer.isDescendant(this.valueElement)) return; - // If there are several properties in the text, do not handle increments/decrements. - var text = event.target.textContent.trim(); - var openQuote; - var wasEscape = false; - // Exclude the last character from the check since it is allowed to be ";". - for (var i = 0; i < text.length - 1; ++i) { - var ch = text.charAt(i); - if (ch === "\\") { - wasEscape = true; - continue; - } - if (ch === ";" && !openQuote) - return; // Do not handle name/value shifts if the property is compound. - if ((ch === "'" || ch === "\"") && !wasEscape) { - if (!openQuote) - openQuote = ch; - else if (ch === openQuote) - openQuote = null; - } - wasEscape = false; - } - const styleValueDelimeters = " \t\n\"':;,/()"; - var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.listItemElement); + var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.valueElement); var wordString = wordRange.toString(); var replacementString = wordString; @@ -1604,22 +1695,6 @@ WebInspector.StylePropertyTreeElement.prototype = { } replacementString = prefix + number + suffix; - } else if (selection.containsNode(this.nameElement, true)) { - var prefix = selectionRange.startContainer.textContent.substring(0, selectionRange.startOffset); - var property; - - if (event.keyIdentifier === "Up") - property = WebInspector.CSSCompletions.previous(wordString, prefix); - else if (event.keyIdentifier === "Down") - property = WebInspector.CSSCompletions.next(wordString, prefix); - - var startOffset = selectionRange.startOffset; - if (property) { - this.nameElement.textContent = property; - this.nameElement.firstChild.select(startOffset); - } - event.preventDefault(); - return; } else { // FIXME: this should cycle through known keywords for the current property value. } @@ -1643,7 +1718,9 @@ WebInspector.StylePropertyTreeElement.prototype = { // if the editing is canceled and before each apply. this.originalPropertyText = this.property.propertyText; } - this.applyStyleText(this.listItemElement.textContent); + + // Synthesize property text disregarding any comments, custom whitespace etc. + this.applyStyleText(this.nameElement.textContent + ": " + this.valueElement.textContent); }, editingEnded: function(context) @@ -1651,8 +1728,12 @@ WebInspector.StylePropertyTreeElement.prototype = { this.hasChildren = context.hasChildren; if (context.expanded) this.expand(); - this.listItemElement.removeEventListener("keydown", context.keyDownListener, false); - this.listItemElement.removeEventListener("keypress", context.keyPressListener, false); + var editedElement = context.isEditingName ? this.nameElement : this.valueElement; + editedElement.removeEventListener("keydown", context.keyDownListener, false); + editedElement.removeEventListener("keypress", context.keyPressListener, false); + if (editedElement.parentElement) + editedElement.parentElement.removeStyleClass("child-editing"); + delete this.originalPropertyText; }, @@ -1672,61 +1753,96 @@ WebInspector.StylePropertyTreeElement.prototype = { editingCommitted: function(element, userInput, previousContent, context, moveDirection) { this.editingEnded(context); + var isEditingName = context.isEditingName; // Determine where to move to before making changes - var newProperty, moveToPropertyName, moveToSelector; + var createNewProperty, moveToPropertyName, moveToSelector; var moveTo = this; - do { - moveTo = (moveDirection === "forward" ? moveTo.nextSibling : moveTo.previousSibling); - } while(moveTo && !moveTo.selectable); - - if (moveTo) - moveToPropertyName = moveTo.name; - else if (moveDirection === "forward") - newProperty = true; - else if (moveDirection === "backward" && this.treeOutline.section.rule) - moveToSelector = true; + var moveToOther = (isEditingName ^ (moveDirection === "forward")); + var abandonNewProperty = this._newProperty && !userInput && (moveToOther || isEditingName); + if (moveDirection === "forward" && !isEditingName || moveDirection === "backward" && isEditingName) { + do { + moveTo = (moveDirection === "forward" ? moveTo.nextSibling : moveTo.previousSibling); + } while(moveTo && !moveTo.selectable); + + if (moveTo) + moveToPropertyName = moveTo.name; + else if (moveDirection === "forward" && (!this._newProperty || userInput)) + createNewProperty = true; + else if (moveDirection === "backward" && this.treeOutline.section.rule) + moveToSelector = true; + } - // Make the Changes and trigger the moveToNextCallback after updating + // Make the Changes and trigger the moveToNextCallback after updating. var blankInput = /^\s*$/.test(userInput); - if (userInput !== previousContent || (this._newProperty && blankInput)) { // only if something changed, or adding a new style and it was blank - this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput); - this.applyStyleText(userInput, true); - } else - moveToNextCallback(this._newProperty, false, this.treeOutline.section, false); + var shouldCommitNewProperty = this._newProperty && (moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput)); - // The Callback to start editing the next property + if ((userInput !== previousContent && !this._newProperty) || shouldCommitNewProperty) { + this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, this.treeOutline.section); + var propertyText; + if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent))) + propertyText = ""; + else { + if (isEditingName) + propertyText = userInput + ": " + this.valueElement.textContent; + else + propertyText = this.nameElement.textContent + ": " + userInput; + } + this.applyStyleText(propertyText, true); + } else { + if (!this._newProperty) + this.updateTitle(); + moveToNextCallback(this._newProperty, false, this.treeOutline.section); + } + + var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1; + + // The Callback to start editing the next/previous property/selector. function moveToNextCallback(alreadyNew, valueChanged, section) { if (!moveDirection) return; - // User just tabbed through without changes + // User just tabbed through without changes. if (moveTo && moveTo.parent) { - moveTo.startEditing(moveTo.valueElement); + moveTo.startEditing(!isEditingName ? moveTo.nameElement : moveTo.valueElement); return; } - // User has made a change then tabbed, wiping all the original treeElements, - // recalculate the new treeElement for the same property we were going to edit next - // FIXME(apavlov): this will not work for multiple same-named properties in a style - // (the first one will always be returned). + // User has made a change then tabbed, wiping all the original treeElements. + // Recalculate the new treeElement for the same property we were going to edit next. if (moveTo && !moveTo.parent) { - var treeElement = section.findTreeElementWithName(moveToPropertyName); - if (treeElement) - treeElement.startEditing(treeElement.valueElement); - return; + var propertyElements = section.propertiesTreeOutline.children; + if (moveDirection === "forward" && blankInput && !isEditingName) + --moveToIndex; + if (moveToIndex >= propertyElements.length && !this._newProperty) + createNewProperty = true; + else { + var treeElement = moveToIndex >= 0 ? propertyElements[moveToIndex] : null; + if (treeElement) { + treeElement.startEditing(!isEditingName ? treeElement.nameElement : treeElement.valueElement); + return; + } else if (!alreadyNew) + moveToSelector = true; + } } - // Create a new attribute in this section - if (newProperty) { - if (alreadyNew && !valueChanged) + // Create a new attribute in this section (or move to next editable selector if possible). + if (createNewProperty) { + if (alreadyNew && !valueChanged && (isEditingName ^ (moveDirection === "backward"))) return; section.addNewBlankProperty().startEditing(); return; } + if (abandonNewProperty) { + var sectionToEdit = moveDirection === "backward" ? section : section.nextEditableSibling(); + if (sectionToEdit && sectionToEdit.rule) + sectionToEdit.startEditingSelector(); + return; + } + if (moveToSelector) section.startEditingSelector(); } @@ -1736,16 +1852,13 @@ WebInspector.StylePropertyTreeElement.prototype = { { var section = this.treeOutline.section; var elementsPanel = WebInspector.panels.elements; - styleText = styleText.replace(/\s/g, " ").trim(); // replace with whitespace. + styleText = styleText.replace(/\s/g, " ").trim(); // Replace with whitespace. var styleTextLength = styleText.length; - if (!styleTextLength && updateInterface) { - if (this._newProperty) { - // The user deleted everything, so remove the tree element and update. - this.parent.removeChild(this); - section.afterUpdate(); - return; - } else - delete section._afterUpdate; + if (!styleTextLength && updateInterface && this._newProperty) { + // The user deleted everything, so remove the tree element and update. + this.parent.removeChild(this); + section.afterUpdate(); + return; } function callback(newStyle) |