Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

#7723 - Adding Support For '0x' Notation Color Format #13154

Merged
merged 17 commits into from
Mar 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/command/KeyBindingManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ define(function (require, exports, module) {
var normalizedKey = normalizeKeyDescriptorString(key);

if (!normalizedKey) {
console.log("Fail to nomalize " + key);
console.log("Failed to normalize " + key);
} else if (_isKeyAssigned(normalizedKey)) {
var binding = _keyMap[normalizedKey],
command = CommandManager.get(binding.commandID),
Expand Down
110 changes: 94 additions & 16 deletions src/extensions/default/InlineColorEditor/ColorEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,61 @@ define(function (require, exports, module) {
*/
var STEP_MULTIPLIER = 5;

/**
* Convert 0x notation into hex6 format for tinycolor
* compatibility: ("0xFFAACC" => "#FFFFFF")
* @param {string} str - String to ensure hex format for
* @returns {string} - str in hex format
*/
function ensureHexFormat(str) {
return (/^0x/).test(str) ? str.replace("0x","#") : str;
}

/**
* Converts a color to a 0x-prefixed string
* @param {tinycolor} color - color to convert
* @returns {string} - color as 0x-prefixed string
*/
function as0xString(color) {
return color.toHexString().replace("#", "0x");
}

/**
* Converts 0x-prefixed color to hex
* @param {string} color - Color to convert
* @param {boolean} convertToString - true if color should
* be returned as string
* @returns {tinycolor|string} - Hex color as a Tinycolor object
* or a hex string
*/
function _0xColorToHex(color, convertToStr) {
var hexColor = tinycolor(color.replace("0x", "#"));
hexColor._format = "0x";

if (convertToStr) {
return hexColor.toString();
}
return hexColor;
}

/**
* Ensures that a string is in Tinycolor supported format
* @param {string} color - Color to check the format for
* @param {boolean} convertToString - true if color should
* be returned as string
* @returns {tinycolor|string} - Color as a Tinycolor object
* or a hex string
*/
function checkSetFormat(color, convertToStr) {
if ((/^0x/).test(color)) {
return _0xColorToHex(color, convertToStr);
}
if (convertToStr) {
return tinycolor(color).toString();
}
return tinycolor(color);
}

/**
* Color picker control; may be used standalone or within an InlineColorEditor inline widget.
* @param {!jQuery} $parent DOM node into which to append the root of the color picker UI
Expand All @@ -64,8 +119,9 @@ define(function (require, exports, module) {
this._handleHueDrag = this._handleHueDrag.bind(this);
this._handleSelectionFieldDrag = this._handleSelectionFieldDrag.bind(this);

this._color = tinycolor(color);
this._originalColor = color;
this._color = checkSetFormat(color);

this._redoColor = null;
this._isUpperCase = PreferencesManager.get("uppercaseColors");
PreferencesManager.on("change", "uppercaseColors", function () {
Expand All @@ -77,6 +133,7 @@ define(function (require, exports, module) {
this.$rgbaButton = this.$element.find(".rgba");
this.$hexButton = this.$element.find(".hex");
this.$hslButton = this.$element.find(".hsla");
this.$0xButton = this.$element.find(".0x");
this.$currentColor = this.$element.find(".current-color");
this.$originalColor = this.$element.find(".original-color");
this.$selection = this.$element.find(".color-selection-field");
Expand All @@ -96,8 +153,9 @@ define(function (require, exports, module) {
this._addListeners();

// Initially selected color
this.$originalColor.css("background-color", this._originalColor);
this._commitColor(color);
this.$originalColor.css("background-color", checkSetFormat(this._originalColor));

this._commitColor(color);
}

/**
Expand Down Expand Up @@ -137,6 +195,7 @@ define(function (require, exports, module) {
this._bindColorFormatToRadioButton("rgba");
this._bindColorFormatToRadioButton("hex");
this._bindColorFormatToRadioButton("hsla");
this._bindColorFormatToRadioButton("0x");

this._bindInputHandlers();

Expand All @@ -158,14 +217,14 @@ define(function (require, exports, module) {
* Update all UI elements to reflect the selected color (_color and _hsv). It is usually
* incorrect to call this directly; use _commitColor() or setColorAsHsv() instead.
*/
ColorEditor.prototype._synchronize = function () {
var colorValue = this.getColor().getOriginalInput(),
colorObject = tinycolor(colorValue),
hueColor = "hsl(" + this._hsv.h + ", 100%, 50%)";

ColorEditor.prototype._synchronize = function () {
var colorValue = this.getColor().getOriginalInput();
var colorObject = checkSetFormat(colorValue);
var hueColor = "hsl(" + this._hsv.h + ", 100%, 50%)";
this._updateColorTypeRadioButtons(colorObject.getFormat());
this.$colorValue.val(colorValue);
this.$currentColor.css("background-color", colorValue);
this.$currentColor.css("background-color", checkSetFormat(colorValue, true));
this.$selection.css("background-color", hueColor);
this.$hueBase.css("background-color", hueColor);

Expand Down Expand Up @@ -228,6 +287,9 @@ define(function (require, exports, module) {
case "hsl":
this.$buttonList.find(".hsla").parent().addClass("selected");
break;
case "0x":
this.$buttonList.find(".0x").parent().addClass("selected");
break;
}
};

Expand All @@ -237,8 +299,9 @@ define(function (require, exports, module) {
self = this;
handler = function (event) {
var newFormat = $(event.currentTarget).html().toLowerCase().replace("%", "p"),
newColor = self.getColor().toString(),
colorObject = tinycolor(newColor);
newColor = self.getColor().toString();

var colorObject = checkSetFormat(newColor);

switch (newFormat) {
case "hsla":
Expand All @@ -254,6 +317,11 @@ define(function (require, exports, module) {
newColor = colorObject.toHexString();
self._hsv.a = 1;
break;
case "0x":
newColor = as0xString(colorObject);
self._hsv.a = 1;
self._format = "0x";
break;
}

// We need to run this again whenever RGB/HSL/Hex conversions
Expand Down Expand Up @@ -317,9 +385,10 @@ define(function (require, exports, module) {
/** Handle changes in text field */
ColorEditor.prototype._handleTextFieldInput = function (losingFocus) {
var newColor = $.trim(this.$colorValue.val()),
newColorObj = tinycolor(newColor),
newColorObj = checkSetFormat(newColor),
newColorOk = newColorObj.isValid();


// TinyColor will auto correct an incomplete rgb or hsl value into a valid color value.
// eg. rgb(0,0,0 -> rgb(0, 0, 0)
// We want to avoid having TinyColor do this, because we don't want to sync the color
Expand All @@ -328,7 +397,7 @@ define(function (require, exports, module) {
// TinyColor actually generates to see if it's different. If so, then we assume the color
// was incomplete to begin with.
if (newColorOk) {
newColorOk = (newColorObj.toString() === this._normalizeColorString(newColor));
newColorOk = (newColorObj.toString() === this._normalizeColorString(ensureHexFormat(newColor)));
}

// Restore to the previous valid color if the new color is invalid or incomplete.
Expand Down Expand Up @@ -363,10 +432,12 @@ define(function (require, exports, module) {

// Create swatches
swatches.forEach(function (swatch) {
var swatchValue = checkSetFormat(swatch.value, true);
var stringFormat = (swatch.count > 1) ? Strings.COLOR_EDITOR_USED_COLOR_TIP_PLURAL : Strings.COLOR_EDITOR_USED_COLOR_TIP_SINGULAR,
usedColorTip = StringUtils.format(stringFormat, swatch.value, swatch.count);

self.$swatches.append("<li tabindex='0'><div class='swatch-bg'><div class='swatch' style='background-color: " +
swatch.value + ";' title='" + usedColorTip + "'></div></div> <span class='value'" + " title='" +
swatchValue + ";' title='" + usedColorTip + "'></div></div> <span class='value'" + " title='" +
usedColorTip + "'>" + swatch.value + "</span></li>");
});

Expand All @@ -376,6 +447,7 @@ define(function (require, exports, module) {
event.keyCode === KeyEvent.DOM_VK_ENTER ||
event.keyCode === KeyEvent.DOM_VK_SPACE) {
// Enter/Space is same as clicking on swatch

self._commitColor($(event.currentTarget).find(".value").html());
} else if (event.keyCode === KeyEvent.DOM_VK_TAB) {
// Tab on last swatch loops back to color square
Expand Down Expand Up @@ -427,6 +499,9 @@ define(function (require, exports, module) {
case "name":
colorVal = this._hsv.a < 1 ? newColor.toRgbString() : newColor.toHexString();
break;
case "0x":
colorVal = as0xString(newColor);
break;
}
colorVal = this._isUpperCase ? colorVal.toUpperCase() : colorVal;
this._commitColor(colorVal, false);
Expand All @@ -439,11 +514,15 @@ define(function (require, exports, module) {
* @param {boolean=} resetHsv Pass false ONLY if hsv set already been modified to match colorVal. Default: true.
*/
ColorEditor.prototype._commitColor = function (colorVal, resetHsv) {

if (resetHsv === undefined) {
resetHsv = true;
}
this._callback(colorVal);
this._color = tinycolor(colorVal);

var colorObj = checkSetFormat(colorVal);
colorObj._originalInput = colorVal;
this._color = colorObj;

if (resetHsv) {
this._hsv = this._color.toHsv();
Expand Down Expand Up @@ -531,9 +610,8 @@ define(function (require, exports, module) {
*/
ColorEditor.prototype.undo = function () {
if (this._originalColor.toString() !== this._color.toString()) {
var curColor = this._color.toString();
this._commitColor(this._originalColor, true);
this._redoColor = curColor;
this._redoColor = this._color.toString();
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<li class="selected" title="{{COLOR_EDITOR_RGBA_BUTTON_TIP}}"><a href="#" tabindex="0" class="rgba">RGBa</a></li>
<li title="{{COLOR_EDITOR_HEX_BUTTON_TIP}}"><a href="#" tabindex="0" class="hex">Hex</a></li>
<li title="{{COLOR_EDITOR_HSLA_BUTTON_TIP}}"><a href="#" tabindex="0" class="hsla">HSLa</a></li>
<li title="{{COLOR_EDITOR_0X_BUTTON_TIP}}"><a href="#" tabindex="0" class="0x">0x</a></li>
</ul>
</footer>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ define(function (require, exports, module) {
var self = this;
if (colorString !== this._color) {
var range = this.getCurrentRange();

if (!range) {
return;
}
Expand Down
12 changes: 8 additions & 4 deletions src/extensions/default/QuickView/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ define(function (require, exports, module) {
function hasLengthInPixels(args) {
return (args.length > 1 && args[1].indexOf("px") > 0);
}

// Ensures that input is in usable hex format
function ensureHexFormat(str) {
return (/^0x/).test(str) ? str.replace("0x","#") : str;
}

// Normalizes px color stops to %
function normalizeGradientExpressionForQuickview(expression) {
Expand Down Expand Up @@ -400,15 +405,14 @@ define(function (require, exports, module) {
}
} else if (pos.ch <= match.index + match[0].length) {
// build the css for previewing the gradient from the regex result
var previewCSS = gradientMatch.prefix + (gradientMatch.colorValue || match[0]);
var previewCSS = gradientMatch.prefix + (gradientMatch.colorValue || match[0]);

// normalize the arguments to something that we can display to the user
// NOTE: we need both the div and the popover's _previewCSS member
// (used by unit tests) to match so normalize the css for both
previewCSS = normalizeGradientExpressionForQuickview(previewCSS);
previewCSS = normalizeGradientExpressionForQuickview(ensureHexFormat(previewCSS));

var preview = "<div class='color-swatch' style='background:" + previewCSS + "'>" +
"</div>";
var preview = "<div class='color-swatch' style='background:" + previewCSS + "'>" + "</div>";
var startPos = {line: pos.line, ch: match.index},
endPos = {line: pos.line, ch: match.index + match[0].length},
startCoords = cm.charCoords(startPos),
Expand Down
1 change: 1 addition & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ define({
"COLOR_EDITOR_RGBA_BUTTON_TIP" : "RGBa Format",
"COLOR_EDITOR_HEX_BUTTON_TIP" : "Hex Format",
"COLOR_EDITOR_HSLA_BUTTON_TIP" : "HSLa Format",
"COLOR_EDITOR_0X_BUTTON_TIP" : "Hex (0x) Format",
"COLOR_EDITOR_USED_COLOR_TIP_SINGULAR" : "{0} (Used {1} time)",
"COLOR_EDITOR_USED_COLOR_TIP_PLURAL" : "{0} (Used {1} times)",

Expand Down
4 changes: 2 additions & 2 deletions src/utils/ColorUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ define(function (require, exports, module) {

/**
* Regular expression that matches reasonably well-formed colors in hex format (3 or 6 digits),
* rgb()/rgba() function format, hsl()/hsla() function format,
* rgb()/rgba() function format, hsl()/hsla() function format, 0x notation 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 COLOR_REGEX = new RegExp(/0x([a-f0-9]{6})\b|#[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");

/*
* Adds a color swatch to code hints where this is supported.
Expand Down