1/* 2 * Copyright (C) 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.CodeMirrorGradientEditingController = function(codeMirror, marker) 27{ 28 WebInspector.CodeMirrorEditingController.call(this, codeMirror, marker); 29 30 if (!WebInspector.CodeMirrorGradientEditingController.GradientTypes) { 31 WebInspector.CodeMirrorGradientEditingController.GradientTypes = { 32 "linear-gradient": { 33 type: WebInspector.LinearGradient, 34 label: WebInspector.UIString("Linear Gradient"), 35 repeats: false 36 }, 37 38 "radial-gradient": { 39 type: WebInspector.RadialGradient, 40 label: WebInspector.UIString("Radial Gradient"), 41 repeats: false 42 }, 43 44 "repeating-linear-gradient": { 45 type: WebInspector.LinearGradient, 46 label: WebInspector.UIString("Repeating Linear Gradient"), 47 repeats: true 48 }, 49 50 "repeating-radial-gradient": { 51 type: WebInspector.RadialGradient, 52 label: WebInspector.UIString("Repeating Radial Gradient"), 53 repeats: true 54 } 55 }; 56 } 57} 58 59WebInspector.CodeMirrorGradientEditingController.StyleClassName = "gradient-editing-controller"; 60WebInspector.CodeMirrorGradientEditingController.EditsColorClassName = "edits-color"; 61WebInspector.CodeMirrorGradientEditingController.RadialGradientClassName = "radial-gradient"; 62 63// Lazily populated in the WebInspector.CodeMirrorGradientEditingController constructor. 64// It needs to be lazy to use UIString after Main.js and localizedStrings.js loads. 65WebInspector.CodeMirrorGradientEditingController.GradientTypes = null; 66 67WebInspector.CodeMirrorGradientEditingController.prototype = { 68 constructor: WebInspector.CodeMirrorGradientEditingController, 69 __proto__: WebInspector.CodeMirrorEditingController.prototype, 70 71 // Public 72 73 get initialValue() 74 { 75 return WebInspector.Gradient.fromString(this.text); 76 }, 77 78 get cssClassName() 79 { 80 return "gradient"; 81 }, 82 83 get popoverPreferredEdges() 84 { 85 // Since the gradient editor can resize to be quite tall, let's avoid displaying the popover 86 // above the edited value so that it may not change which edge it attaches to upon editing a stop. 87 return [WebInspector.RectEdge.MIN_X, WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]; 88 }, 89 90 popoverTargetFrameWithRects: function(rects) 91 { 92 // If a gradient is defined across several lines, we probably want to use the first line only 93 // as a target frame for the editor since we may reformat the gradient value to fit on a single line. 94 return rects[0]; 95 }, 96 97 popoverWillPresent: function(popover) 98 { 99 this._container = document.createElement("div"); 100 this._container.className = WebInspector.CodeMirrorGradientEditingController.StyleClassName; 101 102 this._gradientTypePicker = this._container.appendChild(document.createElement("select")); 103 for (var type in WebInspector.CodeMirrorGradientEditingController.GradientTypes) { 104 var option = this._gradientTypePicker.appendChild(document.createElement("option")); 105 option.value = type; 106 option.innerText = WebInspector.CodeMirrorGradientEditingController.GradientTypes[type].label; 107 } 108 this._gradientTypePicker.addEventListener("change", this); 109 110 this._gradientSlider = new WebInspector.GradientSlider; 111 this._container.appendChild(this._gradientSlider.element); 112 113 this._colorPicker = new WebInspector.ColorPicker; 114 this._colorPicker.colorWheel.dimension = 190; 115 this._colorPicker.addEventListener(WebInspector.ColorPicker.Event.ColorChanged, this._colorPickerColorChanged, this); 116 117 var angleLabel = this._container.appendChild(document.createElement("label")); 118 angleLabel.textContent = WebInspector.UIString("Angle"); 119 120 this._angleInput = document.createElement("input"); 121 this._angleInput.type = "text"; 122 this._angleInput.size = 3; 123 this._angleInput.addEventListener("input", this); 124 angleLabel.appendChild(this._angleInput); 125 126 var dragToAdjustController = new WebInspector.DragToAdjustController(this); 127 dragToAdjustController.element = angleLabel; 128 dragToAdjustController.enabled = true; 129 130 this._updateCSSClassForGradientType(); 131 132 popover.content = this._container; 133 }, 134 135 popoverDidPresent: function(popover) 136 { 137 this._gradientSlider.stops = this.value.stops; 138 139 if (this.value instanceof WebInspector.LinearGradient) { 140 this._gradientTypePicker.value = this.value.repeats ? "repeating-linear-gradient" : "linear-gradient"; 141 this._angleInput.value = this.value.angle + "\u00B0"; 142 } else 143 this._gradientTypePicker.value = this.value.repeats ? "repeating-radial-gradient" : "radial-gradient" 144 145 this._gradientSlider.delegate = this; 146 }, 147 148 // Protected 149 150 handleEvent: function(event) 151 { 152 if (event.type === "input") 153 this._handleInputEvent(event); 154 else if (event.type === "change") 155 this._handleChangeEvent(event); 156 }, 157 158 gradientSliderStopsDidChange: function(gradientSlider) 159 { 160 this.text = this.value.toString(); 161 }, 162 163 gradientSliderStopWasSelected: function(gradientSlider, stop) 164 { 165 var selectedStop = gradientSlider.selectedStop; 166 167 if (selectedStop && !this._container.classList.contains(WebInspector.CodeMirrorGradientEditingController.EditsColorClassName)) { 168 this._container.appendChild(this._colorPicker.element); 169 this._container.classList.add(WebInspector.CodeMirrorGradientEditingController.EditsColorClassName); 170 this._colorPicker.color = selectedStop.color; 171 } else if (!selectedStop) { 172 this._colorPicker.element.remove(); 173 this._container.classList.remove(WebInspector.CodeMirrorGradientEditingController.EditsColorClassName); 174 } 175 176 // Ensure the angle input is not focused since, if it were, it'd make a scrollbar appear as we 177 // animate the popover's frame to fit its new content. 178 this._angleInput.blur(); 179 180 this.popover.update(); 181 }, 182 183 dragToAdjustControllerWasAdjustedByAmount: function(dragToAdjustController, amount) 184 { 185 var angle = parseFloat(this._angleInput.value) + amount; 186 if (Math.round(angle) !== angle) 187 angle = angle.toFixed(1); 188 189 this._angleInput.value = angle; 190 this._angleInputValueDidChange(angle); 191 }, 192 193 // Private 194 195 _handleInputEvent: function(event) 196 { 197 var angle = parseFloat(this._angleInput.value); 198 if (isNaN(angle)) 199 return; 200 201 this._angleInputValueDidChange(angle); 202 }, 203 204 _angleInputValueDidChange: function(angle) 205 { 206 this.value.angle = angle; 207 this.text = this.value.toString(); 208 209 var matches = this._angleInput.value.match(/\u00B0/g); 210 if (!matches || matches.length !== 1) { 211 var selectionStart = this._angleInput.selectionStart; 212 this._angleInput.value = angle + "\u00B0"; 213 this._angleInput.selectionStart = selectionStart; 214 this._angleInput.selectionEnd = selectionStart; 215 } 216 }, 217 218 _handleChangeEvent: function(event) 219 { 220 var descriptor = WebInspector.CodeMirrorGradientEditingController.GradientTypes[this._gradientTypePicker.value]; 221 if (!(this.value instanceof descriptor.type)) { 222 if (descriptor.type === WebInspector.LinearGradient) { 223 this.value = new WebInspector.LinearGradient(180, this.value.stops); 224 this._angleInput.value = "180\u00B0"; 225 } else 226 this.value = new WebInspector.RadialGradient("", this.value.stops); 227 228 this._updateCSSClassForGradientType(); 229 this.popover.update(); 230 } 231 this.value.repeats = descriptor.repeats; 232 this.text = this.value.toString(); 233 }, 234 235 _colorPickerColorChanged: function(event) 236 { 237 this._gradientSlider.selectedStop.color = event.target.color; 238 this._gradientSlider.stops = this.value.stops; 239 this.text = this.value.toString(); 240 }, 241 242 _updateCSSClassForGradientType: function() 243 { 244 if (this.value instanceof WebInspector.LinearGradient) 245 this._container.classList.remove(WebInspector.CodeMirrorGradientEditingController.RadialGradientClassName); 246 else 247 this._container.classList.add(WebInspector.CodeMirrorGradientEditingController.RadialGradientClassName); 248 } 249} 250