From 320f414042c404205e451c9e79627890f9a5aebf Mon Sep 17 00:00:00 2001 From: feihaozi77 Date: Mon, 30 Oct 2017 17:01:26 -0400 Subject: [PATCH 01/23] Added all components for Border Radius Inline editor --- src/brackets.js | 1 + src/extensions/bramble-extensions.json | 11 + .../BorderRadiusEditor.js | 422 ++++++++++++++++++ .../BorderRadiusEditorTemplate.html | 43 ++ .../BorderRadiusProperties.json | 7 + .../InlineBorderRadiusEditor.js | 247 ++++++++++ .../InlineBorderRadiusEditor/css/main.less | 182 ++++++++ .../default/InlineBorderRadiusEditor/main.js | 209 +++++++++ src/utils/BorderRadiusUtils.js | 75 ++++ 9 files changed, 1197 insertions(+) create mode 100644 src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js create mode 100644 src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html create mode 100644 src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json create mode 100644 src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js create mode 100644 src/extensions/default/InlineBorderRadiusEditor/css/main.less create mode 100644 src/extensions/default/InlineBorderRadiusEditor/main.js create mode 100644 src/utils/BorderRadiusUtils.js diff --git a/src/brackets.js b/src/brackets.js index 430340803d8..391a870c326 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -107,6 +107,7 @@ define(function (require, exports, module) { require("utils/Resizer"); require("LiveDevelopment/main"); require("utils/ColorUtils"); + require("utils/BorderRadiusUtils"); require("view/ThemeManager"); require("thirdparty/lodash"); require("language/XMLUtils"); diff --git a/src/extensions/bramble-extensions.json b/src/extensions/bramble-extensions.json index 3f1f079ae60..e0ab10fec3e 100644 --- a/src/extensions/bramble-extensions.json +++ b/src/extensions/bramble-extensions.json @@ -51,6 +51,17 @@ "extensions/default/InlineColorEditor/img/*.png" ] }, + { + "path": "extensions/default/InlineBorderRadiusEditor", + "less": { + "dist/extensions/default/InlineBorderRadiusEditor/css/main.css": [ + "src/extensions/default/InlineBorderRadiusEditor/css/main.less" + ] + }, + "copy": [ + "extensions/default/InlineBorderRadiusEditor/img/*.png" + ] + }, { "path": "extensions/default/Inline3DParametersEditor", "less": { diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js new file mode 100644 index 00000000000..2704f5a901d --- /dev/null +++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js @@ -0,0 +1,422 @@ + +define(function(require, exports, module) { + "use strict"; + + var KeyEvent = brackets.getModule("utils/KeyEvent"), + PreferencesManager = brackets.getModule("preferences/PreferencesManager"), + StringUtils = brackets.getModule("utils/StringUtils"), + Strings = brackets.getModule("strings"), + Mustache = brackets.getModule("thirdparty/mustache/mustache"), + BorderRadiusUtils = brackets.getModule("utils/BorderRadiusUtils"); + + // tinycolor = require("thirdparty/tinycolor-min") + + /** Mustache template that forms the bare DOM structure of the UI */ + var BorderRadiusTemplate = require("text!BorderRadiusEditorTemplate.html"); + var DEFAULT_BORDER_RADIUS_VALUE = 0; + /** + * Box shadow editor control; may be used standalone or within an InlineBoxShadowEditor inline widget. + * @param {!jQuery} $parent DOM node into which to append the root of the box-shadow editor UI + * @param {!{horizontalOffset: string, verticalOffset: string, blurRadius: string, spreadRadius: string, color: string}} values Initial set of box-shadow values. + * @param {!function(string)} callback Called whenever values change + */ + function BorderRadiusEditor($parent, values, callback) { + // Create the DOM structure, filling in localized strings via Mustache + this.$element = $(Mustache.render(BorderRadiusTemplate, Strings)); + $parent.append(this.$element); + + this._callback = callback; + this._allCorners = (values.split("px").length===2); + this._values = values; + this._originalValues = values; + this._redoValues = null; + + this._tl=null; + this._tr=null; + this._br=null; + this._bl=null; + this._all=null; + + // Get references + this.$tlslider = this.$element.find("#top-left-slider"); + this.$trslider = this.$element.find("#top-right-slider"); + this.$blslider = this.$element.find("#bottom-left-slider"); + this.$brslider = this.$element.find("#bottom-right-slider"); + + //allcorner button reference + this.$allCornerButton = this.$element.find("#allCorners"); + + this.$allCornerSlider = this.$element.find("#all-corner-slider"); + this.$individualCorner = this.$element.find("#individualCorners"); + this.$individualCornerArea = this.$element.find("#individualCornerArea"); + this.$allCornersArea = this.$element.find("#allCornersArea"); + + // Attach event listeners to main UI elements + this._bindInputHandlers(); + //initialize individual corner editing to be disabled + if(this._allCorners){ + this.$allCornerButton.trigger("click"); + } + else{ + this.$individualCorner.trigger("click"); + } + // Set initial values in the box-shadow editor inputs. + this._setInputValues(); + } + + /** + * A string or tinycolor object representing the currently selected color + * TODO (#2201): type is unpredictable + * @type {tinycolor|string} + */ + BorderRadiusEditor.prototype._values = null; + + /** + * box shadow values that was selected before undo(), if undo was the last change made. Else null. + * @type {?string} + */ + BorderRadiusEditor.prototype._redoValues = null; + + /** + * Initial value the BoxShadow picker was opened with + * @type {!string} + */ + BorderRadiusEditor.prototype._originalValues = null; + + + /** Returns the root DOM node of the BoxShadowPicker UI */ + BorderRadiusEditor.prototype.getRootElement = function () { + return this.$element; + }; + + BorderRadiusEditor.prototype.getAllSliders = function() { + var sliders = { + tr : this.$trslider, + tl : this.$tlslider, + br : this.$brslider, + bl : this.$blslider, + all : this.$allCornerSlider + }; + return sliders; + }; + + BorderRadiusEditor.prototype.setValues = function(values) { + var result = values.replace(' ','').replace(";","").split("px"); + var finalValue = ""; + var count=0; + for(var i = 0; i < result.length; i++){ + if(!isNaN(parseFloat(result[i]))){ + result[i] = parseFloat(result[i])+"px"; + finalValue += result[i]; + count++; + } + } + this._allCorners=(count===1||count===0); + this._values = finalValue; + if(count===0){ + this._values = DEFAULT_BORDER_RADIUS_VALUE + "px"; + } + this._setInputValues(); + this._commitChanges(values); + }; + + BorderRadiusEditor.prototype._setInputValues = function() { + var values = this._values.split("px"); + //var tl,tr,bl,br,all; + if(!this._allCorners){ + if(values.length===2){ + + this._tr = parseFloat(values[0]); + this._tl = parseFloat(values[0]); + this._br = parseFloat(values[0]); + this._bl = parseFloat(values[0]); + } + else if(values.length===3){ + this._tl = parseFloat(values[0]); + this._tr = parseFloat(values[1]); + this._br = parseFloat(values[0]); + this._bl = parseFloat(values[1]); + } + else if(values.length===4){ + this._tl = parseFloat(values[0]); + this._tr = parseFloat(values[1]); + this._br = parseFloat(values[2]); + this._bl = parseFloat(values[1]); + } + else if(values.length===5){ + + this._tl = parseFloat(values[0]); + this._tr = parseFloat(values[1]); + this._br = parseFloat(values[2]); + this._bl = parseFloat(values[3]); + } + + } + else{ + this._tl = parseFloat(values[0]); + this._tr = parseFloat(values[0]); + this._br = parseFloat(values[0]); + this._bl = parseFloat(values[0]); + } + this._all = this._tl; + + /*this._tl = tl; + this._tr = tr; + this._br = br; + this._bl = bl; + this._all = all;*/ + this.$tlslider.val(this._tl); + this.$trslider.val(this._tr); + this.$blslider.val(this._bl); + this.$brslider.val(this._br); + this.$allCornerSlider.val(this._all); + + }; + + BorderRadiusEditor.prototype._bindInputHandlers = function() { + var self = this; + + this.$tlslider.bind("change", function(event){ + self._handleTLCHange(); + }); + + this.$trslider.bind("change", function(event){ + self._handleTRCHange(); + }); + + this.$blslider.bind("change", function(event){ + self._handleBLCHange(); + }); + + this.$brslider.bind("change", function(event){ + self._handleBRCHange(); + }); + this.$allCornerSlider.bind("change",function(event){ + self._handleALLCHange(); + }); + + this.$allCornerButton.bind("click",function(event){ + self.getButtonAllCorner().addClass("selected"); + self.getButtonIndividualCorner().removeClass("selected"); + var sliders = self.getAllSliders(); + + sliders['tl'].prop('disabled',true); + sliders['bl'].prop('disabled',true); + sliders['br'].prop('disabled',true); + sliders['tr'].prop('disabled',true); + sliders['all'].prop('disabled',false); + self.getAllCornerDiv().addClass("allCornersArea"); + self.getIndividualDiv().removeClass("individualCornerArea"); + self.setAllCornerBooleanFlag(true); + self._setInputValues(); + var result = self.getAllCornerValues(); + self._commitChanges(result["all"]+"px"); + }); + this.$individualCorner.bind("click",function(event){ + self.getButtonIndividualCorner().addClass("selected"); + self.getButtonAllCorner().removeClass("selected"); + var sliders = self.getAllSliders(); + + sliders['tl'].prop('disabled',false); + sliders['bl'].prop('disabled',false); + sliders['br'].prop('disabled',false); + sliders['tr'].prop('disabled',false); + sliders['all'].prop('disabled',true); + self.getAllCornerDiv().removeClass("allCornersArea"); + self.getIndividualDiv().addClass("individualCornerArea"); + self.setAllCornerBooleanFlag(false); + self._setInputValues(); + var result = self.getAllCornerValues(); + self._commitChanges(result["tl"]+"px "+result["tr"]+"px "+result["br"]+"px "+result["bl"]+"px"); + }); + }; + + BorderRadiusEditor.prototype.focus = function() { + this.$tlslider.focus(); + }; + + BorderRadiusEditor.prototype.setAllCornerBooleanFlag=function(flag){ + this._allCorners = flag; + }; + + BorderRadiusEditor.prototype.destroy = function() { + }; + + BorderRadiusEditor.prototype.getAllCornerValues=function(){ + var result = { + tl : this._tl, + tr : this._tr, + br : this._br, + bl : this._bl, + all : this._all + }; + return result; + }; + + BorderRadiusEditor.prototype.getValues = function() { + return this._values; + }; + + BorderRadiusEditor.prototype.getButtonAllCorner = function(){ + return this.$allCornerButton; + }; + + BorderRadiusEditor.prototype.getButtonIndividualCorner = function(){ + return this.$individualCorner; + }; + + BorderRadiusEditor.prototype.getAllCornerDiv = function(){ + return this.$allCornersArea; + }; + + BorderRadiusEditor.prototype.getIndividualDiv = function(){ + return this.$individualCornerArea; + }; + + // Utilty function to check if data is of correct format. + BorderRadiusEditor.prototype._isValidNumber = function(data) { + return (data.match(/\-?\d*/) !== null); + }; + + BorderRadiusEditor.prototype._isValidBorderRadiusString = function(string){ + var radiusValueRegEx = new RegExp(BorderRadiusUtils.BORDER_RADIUS_VALUE_REGEX); + return radiusValueRegEx.test(string); + }; + + BorderRadiusEditor.prototype.setBorderRadiusFromString = function(value) { + this.setValues(value); + }; + + function _handleChanges($inputElement, propertyName, value) { + var values = this._values.split("px"); + if(!this._isValidNumber(value)) { + if(!this._values[propertyName]) { + $inputElement.val(""); + return; + } + var curValue = parseFloat(this._values[propertyName]); + $inputElement.val(curValue); + } + + if(value === "") { + // This is to maintain the box-shadow property. + value = "0"; + $inputElement.val(value); + } + + var newValue; + + if(propertyName === "TL"){ + newValue = value+"px "+this._tr+"px "+this._br+"px "+this._bl+"px"; + this._values = value+"px"+this._tr+"px"+this._br+"px"+this._bl+"px"; + this._tl = value; + this._all = this._tl; + } + if(propertyName === "TR"){ + newValue = this._tl+"px "+value+"px "+this._br+"px "+this._bl+"px"; + this._values = this._tl+"px"+value+"px"+this._br+"px"+this._bl+"px"; + this._tr = value; + this._all = this._tl; + + } + if(propertyName === "BR"){ + newValue = this._tl+"px "+this._tr+"px "+value+"px "+this._bl+"px"; + this._values = this._tl+"px"+this._tr+"px"+value+"px"+this._bl+"px"; + this._br = value; + this._all = this._tl; + + } + if(propertyName === "BL"){ + newValue = this._tl+"px "+this._tr+"px "+this._br+"px "+value+"px"; + this._values = this._tl+"px"+this._tr+"px"+this._br+"px"+value+"px"; + this._bl = value; + this._all = this._tl; + + } + if(propertyName === "ALL"){ + newValue = value+"px"; + this._values = value+"px"+value+"px"+value+"px"+value+"px"; + this._bl=value; + this._br=value; + this._tl=value; + this._tr=value; + this._all = this._tl; + } + this._setInputValues(); + this._commitChanges( newValue); + }; + + BorderRadiusEditor.prototype._handleTLCHange = function() { + var self = this; + var newValue = this.$tlslider.val().trim(); + _handleChanges.call(self, this.$tlslider, "TL", newValue); + }; + + BorderRadiusEditor.prototype._handleTRCHange = function() { + var self = this; + var newValue = this.$trslider.val().trim(); + _handleChanges.call(self, this.$trslider, "TR", newValue); + }; + + BorderRadiusEditor.prototype._handleBLCHange = function() { + var self = this; + var newValue = this.$blslider.val().trim(); + _handleChanges.call(self, this.$blslider, "BL", newValue); + }; + + BorderRadiusEditor.prototype._handleBRCHange = function() { + var self = this; + var newValue = this.$brslider.val().trim(); + _handleChanges.call(self, this.$brslider, "BR", newValue); + }; + + BorderRadiusEditor.prototype._handleALLCHange = function() { + var self = this; + var newValue = this.$allCornerSlider.val().trim(); + _handleChanges.call(self, this.$allCornerSlider, "ALL", newValue); + }; + + BorderRadiusEditor.prototype._undo = function() { + + }; + + BorderRadiusEditor.prototype._redo = function() { + + }; + + /** + * Global handler for keys in the color editor. Catches undo/redo keys and traps + * arrow keys that would be handled by the scroller. + */ + BorderRadiusEditor.prototype._handleKeydown = function (event) { + var hasCtrl = (brackets.platform === "win") ? (event.ctrlKey) : (event.metaKey); + if (hasCtrl) { + switch (event.keyCode) { + case KeyEvent.DOM_VK_Z: + if (event.shiftKey) { + this.redo(); + } else { + this.undo(); + } + return false; + case KeyEvent.DOM_VK_Y: + this.redo(); + return false; + } + } + }; + + + BorderRadiusEditor.prototype._commitChanges = function(value) { + var result=""; + var _array = value.split(" "); + for(var i=0;i<_array.length;i++){ + result+=_array[i]; + } + this._values = result; + this._callback(value); + }; + + + exports.BorderRadiusEditor = BorderRadiusEditor; +}); diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html new file mode 100644 index 00000000000..0b9489d1e1d --- /dev/null +++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html @@ -0,0 +1,43 @@ +
+
+ +
+
+ : + px +
+
+ : + px +
+
+ : + px +
+
+ : + px +
+
+
+
+ : + px +
+
+ +
+
\ No newline at end of file diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json new file mode 100644 index 00000000000..a979ad5174b --- /dev/null +++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json @@ -0,0 +1,7 @@ +{ + "border-radius": {"values": ["inherit"]}, + "border-top-left-radius": {"values": ["inherit"]}, + "border-top-right-radius":{"values": ["inherit"]}, + "border-bottom-left-radius":{"values": ["inherit"]}, + "border-bottom-right-radius":{"values": ["inherit"]} +} diff --git a/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js b/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js new file mode 100644 index 00000000000..72321d432c5 --- /dev/null +++ b/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +define(function (require, exports, module) { + "use strict"; + + var InlineWidget = brackets.getModule("editor/InlineWidget").InlineWidget, + BorderRadiusEditor = require("BorderRadiusEditor").BorderRadiusEditor, + BorderRadiusUtils = brackets.getModule("utils/BorderRadiusUtils"); + + + /** @const @type {number} */ + var MAX_USED_COLORS = 7, + DEFAULT_BORDER_RADIUS = "30px"; + + /** @type {number} Global var used to provide a unique ID for each color editor instance's _origin field. */ + var lastOriginId = 1; + + /** + * Inline widget containing a ColorEditor control + * @param {!string} color Initially selected color + * @param {!CodeMirror.TextMarker} marker + */ + function InlineBorderRadiusEditor(borderRadius, marker) { + this._borderRadius = borderRadius; + this._marker = marker; + this._isOwnChange = false; + this._isHostChange = false; + this._origin = "+InlineBorderRadiusEditor_" + (lastOriginId++); + + this._handleBorderRadiusChange = this._handleBorderRadiusChange.bind(this); + this._handleHostDocumentChange = this._handleHostDocumentChange.bind(this); + + InlineWidget.call(this); + } + + InlineBorderRadiusEditor.prototype = Object.create(InlineWidget.prototype); + InlineBorderRadiusEditor.prototype.constructor = InlineBorderRadiusEditor; + InlineBorderRadiusEditor.prototype.parentClass = InlineWidget.prototype; + + /** @type {!ColorPicker} ColorPicker instance */ + InlineBorderRadiusEditor.prototype.borderRaidusEditor = null; + + /** @type {!string} Current value of the color picker control */ + InlineBorderRadiusEditor.prototype._color = null; + + /** + * Range of code we're attached to; _marker.find() may by null if sync is lost. + * @type {!CodeMirror.TextMarker} + */ + InlineBorderRadiusEditor.prototype._marker = null; + + /** @type {boolean} True while we're syncing a color picker change into the code editor */ + InlineBorderRadiusEditor.prototype._isOwnChange = null; + + /** @type {boolean} True while we're syncing a code editor change into the color picker */ + InlineBorderRadiusEditor.prototype._isHostChange = null; + + /** @type {number} ID used to identify edits coming from this inline widget for undo batching */ + InlineBorderRadiusEditor.prototype._origin = null; + + + /** + * Returns the current text range of the color we're attached to, or null if + * we've lost sync with what's in the code. + * @return {?{start:{line:number, ch:number}, end:{line:number, ch:number}}} + */ + InlineBorderRadiusEditor.prototype.getCurrentRange = function () { + var pos, start, end; + + pos = this._marker && this._marker.find(); + + start = pos && pos.from; + if (!start) { + return null; + } + + end = pos.to; + if (!end) { + end = {line: start.line}; + } + + // Even if we think we have a good range end, we want to run the + // regexp match to see if there's a valid match that extends past the marker. + // This can happen if the user deletes the end of the existing color and then + // types some more. + + //Manuelly find the position of the first occurance of radius value in the line + //because using this._maker.find() does not return expected value + //using this as a work around + var line = this.hostEditor.document.getLine(start.line); + for(var i = line.indexOf(":")+1; i end)); + + if(match){ + // Check if the cursorLine has a CSS rule of type color + var cssPropertyName, semiColonPos, colonPos, radiusValue, cursorLineSubstring, firstCharacterPos; + + // Get the css property name after removing spaces and ":" so that we can check for it in the file ColorProperties.json + cssPropertyName = cursorLine.split(':')[0].trim(); + + if (!cssPropertyName || !properties[cssPropertyName]) { + return null; + } + + if (properties[cssPropertyName]) { + colonPos = cursorLine.indexOf(":"); + semiColonPos = cursorLine.indexOf(";"); + cursorLineSubstring = cursorLine.substring(colonPos + 1, cursorLine.length); + radiusValue = cursorLineSubstring.replace(/ /g,"").replace(";", ""); + if (radiusValue) { + if (radiusValueRegEx.test(radiusValue)) { + // edit the radius value of an existing css rule + firstCharacterPos = cursorLineSubstring.search(/\S/); + pos.ch = colonPos + 1 + Math.min(firstCharacterPos,1); + if (semiColonPos !== -1) { + endPos = {line: pos.line, ch: semiColonPos}; + } else { + endPos = {line: pos.line, ch: cursorLine.length}; + } + } else { + return null; + } + } else { + // edit the radius value of a new css rule + var newText = " ", from, to; + newText = newText.concat(DEFAULT_RADIUS, ";"); + from = {line: pos.line, ch: colonPos + 1}; + to = {line: pos.line, ch: cursorLine.length}; + hostEditor._codeMirror.replaceRange(newText, from, to); + pos.ch = colonPos + 2; + endPos = {line: pos.line, ch: pos.ch + DEFAULT_RADIUS.length}; + radiusValue = DEFAULT_RADIUS; + } + + marker = hostEditor._codeMirror.markText(pos, endPos); + hostEditor.setSelection(pos, endPos); + + return { + radius: radiusValue, + marker: marker + }; + } + } + return null; + /*pos.ch = start; + endPos = {line: pos.line, ch: end}; + + marker = hostEditor._codeMirror.markText(pos, endPos); + hostEditor.setSelection(pos, endPos); + + return { + color: match[0], + marker: marker + };*/ + // Adjust pos to the beginning of the match so that the inline editor won't get + // dismissed while we're updating the color with the new values from user's inline editing + } + + /** + * Registered as an inline editor provider: creates an InlineEditorColor when the cursor + * is on a color value (in any flavor of code). + * + * @param {!Editor} hostEditor + * @param {!{line:Number, ch:Number}} pos + * @return {?$.Promise} synchronously resolved with an InlineWidget, or null if there's + * no color at pos. + */ + function inlineBorderRadiusEditorProvider(hostEditor, pos) { + var context = prepareEditorForProvider(hostEditor, pos), + inlineBorderRadiusEditor, + result; + + if (!context) { + return null; + } else { + inlineBorderRadiusEditor = new InlineBorderRadiusEditor(context.radius, context.marker); + inlineBorderRadiusEditor.load(hostEditor); + + result = new $.Deferred(); + result.resolve(inlineBorderRadiusEditor); + return result.promise(); + } + } + + function queryInlineBorderRadiusEditorPrivoder(hostEditor, pos) { + var borderRadiusRegEx, cursorLine, match, sel, start, end, endPos, marker; + var cssPropertyName, semiColonPos, colonPos, borderRadiusValue, cursorLineSubstring, firstCharacterPos; + + sel = hostEditor.getSelection(); + if (sel.start.line !== sel.end.line) { + return false; + } + + borderRadiusRegEx = new RegExp(BorderRadiusUtils.BORDER_RADIUS_REGEX); + cursorLine = hostEditor.document.getLine(pos.line); + + // Loop through each match of colorRegEx and stop when the one that contains pos is found. + do { + match = borderRadiusRegEx.exec(cursorLine); + if (match) { + start = match.index; + end = start + match[0].length; + } + } while (match && (pos.ch < start || pos.ch > end)); + + if (match) { + return true; + } + + // Get the css property name after removing spaces and ":" so that we can check for it in the file ColorProperties.json + cssPropertyName = cursorLine.split(':')[0].trim(); + + if (!cssPropertyName || !properties[cssPropertyName]) { + return false; + } + + if (properties[cssPropertyName]) { + colonPos = cursorLine.indexOf(":"); + semiColonPos = cursorLine.indexOf(";"); + cursorLineSubstring = cursorLine.substring(colonPos + 1, cursorLine.length); + borderRadiusValue = cursorLineSubstring.replace(/ /g,"").replace(";", ""); + if (borderRadiusValue) { + return borderRadiusRegEx.test(borderRadiusValue); + } + return true; + } + + return false; + } + + // Initialize extension + ExtensionUtils.loadStyleSheet(module, "css/main.less"); + + EditorManager.registerInlineEditProvider(inlineBorderRadiusEditorProvider, queryInlineBorderRadiusEditorPrivoder); + + exports.prepareEditorForProvider = prepareEditorForProvider; + + // for unit tests only + //exports.InlineBorderRadiusEditorProvider = InlineBorderRadiusEditorProvider; +}); + diff --git a/src/utils/BorderRadiusUtils.js b/src/utils/BorderRadiusUtils.js new file mode 100644 index 00000000000..7e86a917b8f --- /dev/null +++ b/src/utils/BorderRadiusUtils.js @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/** + * Utilities functions related to color matching + * + */ +define(function (require, exports, module) { + "use strict"; + + /** + * Sorted array of all the color names in the CSS Color Module Level 3 (http://www.w3.org/TR/css3-color/) + * and "rebeccapurple" from CSS Color Module Level 4 + * @const @type {Array} + */ + var COLOR_NAMES = ["aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "grey", "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"]; + + /** + * Regular expression that matches reasonably well-formed colors in hex format (3 or 6 digits), + * rgb()/rgba() function format, hsl()/hsla() function format, + * or color name format according to CSS Color Module Level 3 (http://www.w3.org/TR/css3-color/) + * or "rebeccapurple" from CSS Color Module Level 4. + * @const @type {RegExp} + */ + // use RegExp.source of the RegExp literal to avoid doubled backslashes + var COLOR_REGEX = new RegExp(/#[a-f0-9]{6}\b|#[a-f0-9]{3}\b|\brgb\(\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*\)|\brgb\(\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*\)|\brgba\(\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:[0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\brgba\(\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:[0-9]{1,2}%|100%)\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\bhsl\(\s*(?:[0-9]{1,3})\b\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:[0-9]{1,2}|100)\b%\s*\)|\bhsla\(\s*(?:[0-9]{1,3})\b\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:[0-9]{1,2}|100)\b%\s*,\s*(?:1|1\.0|0|0?\.[0-9]{1,3})\s*\)|\b/.source + COLOR_NAMES.join("\\b|\\b") + "\\b", "gi"); + var BORDER_RADIUS_REGEX = new RegExp('.*border-radius:.*'); + var BORDER_RADIUS_VALUE_REGEX = new RegExp('([0-9]{1,3}(px|em|ex|%|in|cm|mm|pt|pc)?){1,4}.*'); + + /* + * Adds a color swatch to code hints where this is supported. + * @param {!jQuery} $hintObj - list item where the swatch will be in + * @param {?string} color - color the swatch should have, or null to add extra left margin to + * align with the other hints + * @return {jQuery} jQuery object with the correct class and/or style applied + */ + function formatColorHint($hintObj, color) { + if (color) { + $hintObj.prepend($("") + .addClass("color-swatch") + .css("backgroundColor", color)); + } else { + $hintObj.addClass("no-swatch-margin"); + } + return $hintObj; + } + + + // Define public API + exports.COLOR_NAMES = COLOR_NAMES; + exports.COLOR_REGEX = COLOR_REGEX; + exports.BORDER_RADIUS_REGEX = BORDER_RADIUS_REGEX; + exports.BORDER_RADIUS_VALUE_REGEX = BORDER_RADIUS_VALUE_REGEX; + exports.formatColorHint = formatColorHint; +}); From 65ba628a1668ba9d95c724f3da1c9b639ebf78d1 Mon Sep 17 00:00:00 2001 From: feihaozi77 Date: Sat, 4 Nov 2017 01:54:52 -0400 Subject: [PATCH 02/23] remove text selection after closing inline border radius editor --- .../InlineBorderRadiusEditor/BorderRadiusEditor.js | 10 +++++----- .../BorderRadiusEditorTemplate.html | 10 +++++----- .../InlineBorderRadiusEditor.js | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js index 2704f5a901d..0964a002040 100644 --- a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js +++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditor.js @@ -176,22 +176,22 @@ define(function(require, exports, module) { BorderRadiusEditor.prototype._bindInputHandlers = function() { var self = this; - this.$tlslider.bind("change", function(event){ + this.$tlslider.bind("input", function(event){ self._handleTLCHange(); }); - this.$trslider.bind("change", function(event){ + this.$trslider.bind("input", function(event){ self._handleTRCHange(); }); - this.$blslider.bind("change", function(event){ + this.$blslider.bind("input", function(event){ self._handleBLCHange(); }); - this.$brslider.bind("change", function(event){ + this.$brslider.bind("input", function(event){ self._handleBRCHange(); }); - this.$allCornerSlider.bind("change",function(event){ + this.$allCornerSlider.bind("input",function(event){ self._handleALLCHange(); }); diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html index 0b9489d1e1d..dc68f79d5e6 100644 --- a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html +++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusEditorTemplate.html @@ -12,25 +12,25 @@
: - px + px
: - px + px
: - px + px
: - px + px
: - px + px