+
+
+
diff --git a/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusProperties.json
new file mode 100644
index 00000000000..89b8bda0543
--- /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/BorderRadiusUtils.js b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusUtils.js
new file mode 100644
index 00000000000..ef39198b656
--- /dev/null
+++ b/src/extensions/default/InlineBorderRadiusEditor/BorderRadiusUtils.js
@@ -0,0 +1,57 @@
+/*
+ * 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 regular expressions related to border-radius rule matching
+ *
+ */
+define(function (require, exports, module) {
+ "use strict";
+ /**
+ * Regular expression that matches the css rule for border-radius values after the
+ * colon is optional
+ * @const @type {RegExp}
+ */
+ var BORDER_RADIUS_REGEX = new RegExp('.*border-radius:.*');
+
+ /**
+ * Regular expression that matches the reasonable format of css value for border-radius,
+ * starting with a number or decimal followed by any scalable units listed in the
+ * expression. Such pattern may occur up to 4 times since maximum of 4 corners can be used.
+ * We use a regex as detailed below:
+ * (\d+\.?\d*) matches a decimal or integer number
+ * (px|em|%)? matches the unit which is optional (mainly for 0)
+ * {1,4} makes sure that the above two groups together are only present
+ * between 1 to 4 times (both inclusive).
+ * @const @type {RegExp}
+ */
+ var BORDER_RADIUS_VALUE_REGEX = new RegExp(/((\d+\.?\d*)(px|em|%)?){1,4}.*/);
+ // Matches a single value and captures the number and unit. Use it with exec()
+ // to find successive values in a valid border radius value string.
+ var BORDER_RADIUS_SINGLE_VALUE_REGEX = new RegExp(/(\d+\.?\d*)(px|em|%)?/, "g");
+
+ // Define public API
+ exports.BORDER_RADIUS_REGEX = BORDER_RADIUS_REGEX;
+ exports.BORDER_RADIUS_VALUE_REGEX = BORDER_RADIUS_VALUE_REGEX;
+ exports.BORDER_RADIUS_SINGLE_VALUE_REGEX = BORDER_RADIUS_SINGLE_VALUE_REGEX;
+});
diff --git a/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js b/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js
new file mode 100644
index 00000000000..a7665d5045f
--- /dev/null
+++ b/src/extensions/default/InlineBorderRadiusEditor/InlineBorderRadiusEditor.js
@@ -0,0 +1,230 @@
+/*
+ * 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;
+ var BorderRadiusUtils = require("BorderRadiusUtils");
+ var BorderRadiusEditor = require("BorderRadiusEditor").BorderRadiusEditor;
+
+
+ /** @const @type {number} */
+ var DEFAULT_BORDER_RADIUS = "30px";
+
+ /** @type {number} Global var used to provide a unique ID for each borderRadius editor instance's _origin field. */
+ var lastOriginId = 1;
+
+ /**
+ * Inline widget containing a BorderRadiusEditor control
+ * @param {!string} borderRadius Initially selected borderRadius
+ * @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 {!borderRaidusEditor} borderRadiusEditor instance */
+ InlineBorderRadiusEditor.prototype.borderRaidusEditor = 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 borderRadiusEditor change into the code editor */
+ InlineBorderRadiusEditor.prototype._isOwnChange = null;
+
+ /** @type {boolean} True while we're syncing a code editor change into the borderRadiusEditor*/
+ 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 border-radius value 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 border-radius value 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 border-radius
+ 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 BorderRadiusProperties.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;
+ // Adjust pos to the beginning of the match so that the inline editor won't get
+ // dismissed while we're updating the border-radius with the new values from user's inline editing
+ }
+
+ /**
+ * Registered as an inline editor provider: creates an InlineBorderRadiusEditor when the cursor
+ * is on a border-radius 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 border-radius 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 queryInlineBorderRadiusEditorProvider(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 borderRadiusRegEx 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 BorderRadiusProperties.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, queryInlineBorderRadiusEditorProvider);
+ exports.prepareEditorForProvider = prepareEditorForProvider;
+});