1 /* 2 * Copyright (C) 2012 Google 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 are 6 * met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @constructor 31 * @extends {WebInspector.SourceFrame} 32 * @param {!WebInspector.UISourceCode} uiSourceCode 33 */ 34 WebInspector.UISourceCodeFrame = function(uiSourceCode) 35 { 36 this._uiSourceCode = uiSourceCode; 37 WebInspector.SourceFrame.call(this, this._uiSourceCode); 38 WebInspector.settings.textEditorAutocompletion.addChangeListener(this._enableAutocompletionIfNeeded, this); 39 this._enableAutocompletionIfNeeded(); 40 41 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyChanged, this); 42 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyCommitted, this); 43 this._updateStyle(); 44 } 45 46 WebInspector.UISourceCodeFrame.prototype = { 47 /** 48 * @return {!WebInspector.UISourceCode} 49 */ 50 uiSourceCode: function() 51 { 52 return this._uiSourceCode; 53 }, 54 55 _enableAutocompletionIfNeeded: function() 56 { 57 this.textEditor.setCompletionDictionary(WebInspector.settings.textEditorAutocompletion.get() ? new WebInspector.SampleCompletionDictionary() : null); 58 }, 59 60 wasShown: function() 61 { 62 WebInspector.SourceFrame.prototype.wasShown.call(this); 63 this._boundWindowFocused = this._windowFocused.bind(this); 64 window.addEventListener("focus", this._boundWindowFocused, false); 65 this._checkContentUpdated(); 66 }, 67 68 willHide: function() 69 { 70 WebInspector.SourceFrame.prototype.willHide.call(this); 71 window.removeEventListener("focus", this._boundWindowFocused, false); 72 delete this._boundWindowFocused; 73 this._uiSourceCode.removeWorkingCopyGetter(); 74 }, 75 76 /** 77 * @return {boolean} 78 */ 79 canEditSource: function() 80 { 81 var projectType = this._uiSourceCode.project().type(); 82 if (projectType === WebInspector.projectTypes.Debugger || projectType === WebInspector.projectTypes.Formatter) 83 return false; 84 if (projectType === WebInspector.projectTypes.Network && this._uiSourceCode.contentType() === WebInspector.resourceTypes.Document) 85 return false; 86 return true; 87 }, 88 89 _windowFocused: function(event) 90 { 91 this._checkContentUpdated(); 92 }, 93 94 _checkContentUpdated: function() 95 { 96 if (!this.loaded || !this.isShowing()) 97 return; 98 this._uiSourceCode.checkContentUpdated(); 99 }, 100 101 commitEditing: function() 102 { 103 if (!this._uiSourceCode.isDirty()) 104 return; 105 106 this._muteSourceCodeEvents = true; 107 this._uiSourceCode.commitWorkingCopy(); 108 delete this._muteSourceCodeEvents; 109 }, 110 111 onTextChanged: function(oldRange, newRange) 112 { 113 WebInspector.SourceFrame.prototype.onTextChanged.call(this, oldRange, newRange); 114 if (this._isSettingContent) 115 return; 116 this._muteSourceCodeEvents = true; 117 if (this._textEditor.isClean()) 118 this._uiSourceCode.resetWorkingCopy(); 119 else 120 this._uiSourceCode.setWorkingCopyGetter(this._textEditor.text.bind(this._textEditor)); 121 delete this._muteSourceCodeEvents; 122 }, 123 124 /** 125 * @param {!WebInspector.Event} event 126 */ 127 _onWorkingCopyChanged: function(event) 128 { 129 if (this._muteSourceCodeEvents) 130 return; 131 this._innerSetContent(this._uiSourceCode.workingCopy()); 132 this.onUISourceCodeContentChanged(); 133 }, 134 135 /** 136 * @param {!WebInspector.Event} event 137 */ 138 _onWorkingCopyCommitted: function(event) 139 { 140 if (!this._muteSourceCodeEvents) { 141 this._innerSetContent(this._uiSourceCode.workingCopy()); 142 this.onUISourceCodeContentChanged(); 143 } 144 this._textEditor.markClean(); 145 this._updateStyle(); 146 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { 147 action: WebInspector.UserMetrics.UserActionNames.FileSaved, 148 url: this._uiSourceCode.url 149 }); 150 }, 151 152 _updateStyle: function() 153 { 154 this.element.classList.toggle("source-frame-unsaved-committed-changes", this._uiSourceCode.hasUnsavedCommittedChanges()); 155 }, 156 157 onUISourceCodeContentChanged: function() 158 { 159 }, 160 161 /** 162 * @param {string} content 163 */ 164 _innerSetContent: function(content) 165 { 166 this._isSettingContent = true; 167 this.setContent(content); 168 delete this._isSettingContent; 169 }, 170 171 populateTextAreaContextMenu: function(contextMenu, lineNumber) 172 { 173 WebInspector.SourceFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber); 174 contextMenu.appendApplicableItems(this._uiSourceCode); 175 contextMenu.appendSeparator(); 176 }, 177 178 /** 179 * @param {!Array.<!WebInspector.UISourceCodeFrame.Infobar|undefined>} infobars 180 */ 181 attachInfobars: function(infobars) 182 { 183 for (var i = infobars.length - 1; i >= 0; --i) { 184 var infobar = infobars[i]; 185 if (!infobar) 186 continue; 187 this.element.insertBefore(infobar.element, this.element.children[0]); 188 infobar._attached(this); 189 } 190 this.doResize(); 191 }, 192 193 dispose: function() 194 { 195 WebInspector.settings.textEditorAutocompletion.removeChangeListener(this._enableAutocompletionIfNeeded, this); 196 this._textEditor.dispose(); 197 this.detach(); 198 }, 199 200 __proto__: WebInspector.SourceFrame.prototype 201 } 202 203 /** 204 * @constructor 205 * @param {!WebInspector.UISourceCodeFrame.Infobar.Level} level 206 * @param {string} message 207 */ 208 WebInspector.UISourceCodeFrame.Infobar = function(level, message) 209 { 210 this.element = document.createElementWithClass("div", "source-frame-infobar source-frame-infobar-" + level); 211 this._mainRow = this.element.createChild("div", "source-frame-infobar-main-row"); 212 this._detailsContainer = this.element.createChild("span", "source-frame-infobar-details-container"); 213 214 this._mainRow.createChild("span", "source-frame-infobar-icon"); 215 this._mainRow.createChild("span", "source-frame-infobar-row-message").textContent = message; 216 217 this._toggleElement = this._mainRow.createChild("div", "source-frame-infobar-toggle source-frame-infobar-link"); 218 this._toggleElement.addEventListener("click", this._onToggleDetails.bind(this), false); 219 220 this._closeElement = this._mainRow.createChild("div", "close-button"); 221 this._closeElement.addEventListener("click", this._onClose.bind(this), false); 222 223 this._updateToggleElement(); 224 } 225 226 /** 227 * @enum {string} 228 */ 229 WebInspector.UISourceCodeFrame.Infobar.Level = { 230 Info: "info", 231 Warning: "warning", 232 Error: "error", 233 }; 234 235 WebInspector.UISourceCodeFrame.Infobar.prototype = { 236 _onResize: function() 237 { 238 if (this._uiSourceCodeFrame) 239 this._uiSourceCodeFrame.doResize(); 240 }, 241 242 _onToggleDetails: function() 243 { 244 this._toggled = !this._toggled; 245 this._updateToggleElement(); 246 this._onResize(); 247 }, 248 249 _onClose: function() 250 { 251 this.dispose(); 252 }, 253 254 _updateToggleElement: function() 255 { 256 this._toggleElement.textContent = this._toggled ? WebInspector.UIString("less") : WebInspector.UIString("more"); 257 this._detailsContainer.classList.toggle("hidden", !this._toggled); 258 }, 259 260 /** 261 * @param {!WebInspector.UISourceCodeFrame} uiSourceCodeFrame 262 */ 263 _attached: function(uiSourceCodeFrame) 264 { 265 this._uiSourceCodeFrame = uiSourceCodeFrame; 266 }, 267 268 /** 269 * @param {string=} message 270 * @return {!Element} 271 */ 272 createDetailsRowMessage: function(message) 273 { 274 var infobarDetailsRow = this._detailsContainer.createChild("div", "source-frame-infobar-details-row"); 275 var detailsRowMessage = infobarDetailsRow.createChild("span", "source-frame-infobar-row-message"); 276 detailsRowMessage.textContent = message || ""; 277 return detailsRowMessage; 278 }, 279 280 dispose: function() 281 { 282 this.element.remove(); 283 this._onResize(); 284 delete this._uiSourceCodeFrame; 285 } 286 } 287