| /* |
| * Copyright (C) 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.VisualStylePropertyCombiner = class VisualStylePropertyCombiner extends WI.Object |
| { |
| constructor(propertyName, propertyEditors, spreadNumberValues) |
| { |
| super(); |
| |
| this._style = null; |
| this._propertyName = propertyName; |
| this._propertyMissing = false; |
| this._propertyEditors = propertyEditors || []; |
| this._spreadNumberValues = !!spreadNumberValues && this._propertyEditors.length >= 4; |
| |
| for (let editor of this._propertyEditors) { |
| editor.addEventListener(WI.VisualStylePropertyEditor.Event.ValueDidChange, this._handlePropertyEditorValueChanged, this); |
| editor.suppressStyleTextUpdate = true; |
| } |
| |
| this._textContainsNameRegExp = new RegExp("(?:(?:^|;)\\s*" + this._propertyName + "\\s*:)"); |
| this._replacementRegExp = new RegExp("((?:^|;)\\s*)(" + this._propertyName + ")(.+?(?:;|$))"); |
| this._valueRegExp = /([^\s]+\(.+\)|[^\s]+)(?:;?)/g; |
| } |
| |
| get style() |
| { |
| return this._style; |
| } |
| |
| get synthesizedValue() |
| { |
| let value = ""; |
| let oneEditorHasValue = false; |
| for (let editor of this._propertyEditors) { |
| let editorValue = editor.synthesizedValue; |
| if (editorValue && editorValue.length) |
| oneEditorHasValue = true; |
| else if (editor.optionalProperty) |
| continue; |
| |
| if (editor.masterProperty && editor.valueIsSupportedKeyword(editor.value)) { |
| this._markEditors(editor, true); |
| return editorValue; |
| } |
| |
| if (editor !== this._propertyEditors[0]) |
| value += " "; |
| |
| value += editorValue || (editor.colorProperty ? "transparent" : 0); |
| } |
| |
| this._markEditors(); |
| return value.length && oneEditorHasValue ? value : null; |
| } |
| |
| modifyPropertyText(text, value) |
| { |
| if (this._textContainsNameRegExp.test(text)) |
| text = text.replace(this._replacementRegExp, value !== null ? "$1$2: " + value + ";" : "$1"); |
| else if (value !== null) |
| text += WI.VisualStylePropertyEditor.generateFormattedTextForNewProperty(text, this._propertyName, value); |
| |
| return text; |
| } |
| |
| update(style) |
| { |
| if (style) |
| this._style = style; |
| else if (this._ignoreNextUpdate) { |
| this._ignoreNextUpdate = false; |
| return; |
| } |
| |
| if (!this._style || !this._valueRegExp) |
| return; |
| |
| let property = this._style.propertyForName(this._propertyName, true); |
| let propertyMissing = !property; |
| if (propertyMissing && this._style.nodeStyles) |
| property = this._style.nodeStyles.computedStyle.propertyForName(this._propertyName); |
| |
| if (!property) |
| return; |
| |
| this.updateValuesFromText(property.value, propertyMissing); |
| this._propertyMissing = propertyMissing; |
| } |
| |
| updateValuesFromText(styleText, propertyMissing) |
| { |
| if (styleText === this.synthesizedValue) |
| return; |
| |
| for (let editor of this._propertyEditors) |
| editor[WI.VisualStylePropertyCombiner.EditorUpdatedSymbol] = false; |
| |
| function updateEditor(editor, value) { |
| let updatedValues = editor.getValuesFromText(value || "", propertyMissing); |
| if (!updatedValues) |
| return; |
| |
| editor.updateEditorValues(updatedValues); |
| editor[WI.VisualStylePropertyCombiner.EditorUpdatedSymbol] = true; |
| } |
| |
| if (this._spreadNumberValues) { |
| let numberValues = styleText.match(/\d+[\w%]*/g); |
| let count = numberValues && numberValues.length; |
| if (count === 1) { |
| for (let editor of this._propertyEditors) |
| updateEditor(editor, numberValues[0]); |
| } else if (count === 2) { |
| for (let i = 0; i < count; ++i) { |
| updateEditor(this._propertyEditors[i], numberValues[i]); |
| updateEditor(this._propertyEditors[i + 2], numberValues[i]); |
| } |
| } else if (count === 3) { |
| updateEditor(this._propertyEditors[0], numberValues[0]); |
| updateEditor(this._propertyEditors[1], numberValues[1]); |
| updateEditor(this._propertyEditors[2], numberValues[2]); |
| updateEditor(this._propertyEditors[3], numberValues[1]); |
| } |
| } |
| |
| function updateCompatibleEditor(value) { |
| for (let editor of this._propertyEditors) { |
| if (value && !editor.valueIsCompatible(value) || editor[WI.VisualStylePropertyCombiner.EditorUpdatedSymbol]) |
| continue; |
| |
| if (this._currentValueIsKeyword && editor.disabled) |
| continue; |
| |
| updateEditor(editor, value); |
| if (value) |
| return; |
| } |
| } |
| |
| let matches = styleText.match(this._valueRegExp); |
| for (let i = 0; i < this._propertyEditors.length; ++i) |
| updateCompatibleEditor.call(this, matches && matches[i]); |
| } |
| |
| get propertyMissing() |
| { |
| return this._propertyMissing; |
| } |
| |
| resetEditorValues(value) |
| { |
| this._ignoreNextUpdate = false; |
| this.updateValuesFromText(value || ""); |
| } |
| |
| // Private |
| |
| _markEditors(ignoredEditor, disabled) |
| { |
| this._currentValueIsKeyword = disabled || false; |
| for (let editor of this._propertyEditors) { |
| if (ignoredEditor && editor === ignoredEditor) |
| continue; |
| |
| editor.disabled = this._currentValueIsKeyword; |
| } |
| } |
| |
| _handlePropertyEditorValueChanged() |
| { |
| this._ignoreNextUpdate = true; |
| let value = this.synthesizedValue; |
| if (this._style) |
| this._style.text = this.modifyPropertyText(this._style.text, value); |
| |
| this._propertyMissing = !value; |
| this.dispatchEventToListeners(WI.VisualStylePropertyEditor.Event.ValueDidChange); |
| } |
| }; |
| |
| WI.VisualStylePropertyCombiner.EditorUpdatedSymbol = Symbol("visual-style-property-combiner-editor-updated"); |