1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/lib/events", 7 "firebug/chrome/menu", 8 "firebug/lib/dom", 9 "firebug/lib/locale", 10 "firebug/lib/css", 11 "firebug/lib/options", 12 ], 13 function(Obj, Firebug, Events, Menu, Dom, Locale, Css, Options) { 14 15 // ********************************************************************************************* // 16 // Constants 17 18 var Cc = Components.classes; 19 var Ci = Components.interfaces; 20 var Cu = Components.utils; 21 22 var MODE_JAVASCRIPT = "js"; 23 var CONTEXT_MENU = ""; 24 var TEXT_CHANGED = ""; 25 26 try 27 { 28 // Introduced in Firefox 8 29 Cu["import"]("resource:///modules/source-editor.jsm"); 30 31 MODE_JAVASCRIPT = SourceEditor.MODES.JAVASCRIPT; 32 CONTEXT_MENU = SourceEditor.EVENTS.CONTEXT_MENU; 33 TEXT_CHANGED = SourceEditor.EVENTS.TEXT_CHANGED; 34 } 35 catch (err) 36 { 37 if (FBTrace.DBG_ERRORS) 38 FBTrace.sysout("commandEditor: EXCEPTION source-editors is not available!"); 39 } 40 41 // ********************************************************************************************* // 42 // Command Editor 43 44 Firebug.CommandEditor = Obj.extend(Firebug.Module, 45 { 46 dispatchName: "commandEditor", 47 48 editor: null, 49 50 initialize: function() 51 { 52 Firebug.Module.initialize.apply(this, arguments); 53 54 if (this.editor) 55 return; 56 57 // The current implementation of the SourceEditor (based on Orion) doesn't 58 // support zooming. So, the TextEditor (based on textarea) can be used 59 // by setting extensions.firebug.enableOrion pref to false. 60 // See issue 5678 61 if (typeof(SourceEditor) != "undefined" && Options.get("enableOrion")) 62 this.editor = new SourceEditor(); 63 else 64 this.editor = new TextEditor(); 65 66 var config = 67 { 68 mode: MODE_JAVASCRIPT, 69 showLineNumbers: false, 70 theme: "chrome://firebug/skin/orion-firebug.css" 71 }; 72 73 // Custom shortcuts for Orion editor 74 config.keys = [{ 75 action: "firebug-cmdEditor-execute", 76 code: KeyEvent.DOM_VK_RETURN, 77 accel: true, 78 callback: this.onExecute.bind(this), 79 },{ 80 action: "firebug-cmdEditor-escape", 81 code: KeyEvent.DOM_VK_ESCAPE, 82 callback: this.onEscape.bind(this), 83 }]; 84 85 // Initialize Orion editor. 86 this.parent = document.getElementById("fbCommandEditor"); 87 this.editor.init(this.parent, config, this.onEditorLoad.bind(this)); 88 89 if (FBTrace.DBG_COMMANDEDITOR) 90 FBTrace.sysout("commandEditor: SourceEditor initialized"); 91 }, 92 93 shutdown: function() 94 { 95 if (!this.editor) 96 return; 97 98 this.editor.removeEventListener(CONTEXT_MENU, this.onContextMenu); 99 this.editor.removeEventListener(TEXT_CHANGED, this.onTextChanged); 100 101 this.editor.destroy(); 102 this.editor = null; 103 }, 104 105 /** 106 * The load event handler for the source editor. This method does post-load 107 * editor initialization. 108 */ 109 onEditorLoad: function() 110 { 111 // xxxHonza: Context menu support is going to change in SourceEditor 112 this.editor.addEventListener(CONTEXT_MENU, this.onContextMenu); 113 this.editor.addEventListener(TEXT_CHANGED, this.onTextChanged); 114 115 this.editor.setCaretOffset(this.editor.getCharCount()); 116 117 Firebug.chrome.applyTextSize(Firebug.textSize); 118 119 if (FBTrace.DBG_COMMANDEDITOR) 120 FBTrace.sysout("commandEditor.onEditorLoad; SourceEditor loaded"); 121 }, 122 123 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 124 // Keyboard shortcuts 125 126 onExecute: function() 127 { 128 var context = Firebug.currentContext; 129 Firebug.CommandLine.update(context); 130 Firebug.CommandLine.enter(context); 131 return true; 132 }, 133 134 onEscape: function() 135 { 136 var context = Firebug.currentContext; 137 Firebug.CommandLine.update(context); 138 Firebug.CommandLine.cancel(context); 139 return true; 140 }, 141 142 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 143 // Other Events 144 145 onTextChanged: function(event) 146 { 147 // Ignore changes that are triggered by Firebug's restore logic. 148 if (Firebug.CommandEditor.ignoreChanges) 149 return; 150 151 var context = Firebug.currentContext; 152 Firebug.CommandLine.update(context); 153 }, 154 155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 156 // Context Menu 157 158 onContextMenu: function(event) 159 { 160 var popup = document.getElementById("fbCommandEditorPopup"); 161 Dom.eraseNode(popup); 162 163 var items = Firebug.CommandEditor.getContextMenuItems(); 164 Menu.createMenuItems(popup, items); 165 166 if (!popup.childNodes.length) 167 return; 168 169 popup.openPopupAtScreen(event.screenX, event.screenY, true); 170 }, 171 172 getContextMenuItems: function() 173 { 174 var items = []; 175 items.push({label: Locale.$STR("Cut"), commandID: "cmd_cut"}); 176 items.push({label: Locale.$STR("Copy"), commandID: "cmd_copy"}); 177 items.push({label: Locale.$STR("Paste"), commandID: "cmd_paste"}); 178 items.push({label: Locale.$STR("Delete"), commandID: "cmd_delete"}); 179 items.push("-"); 180 items.push({label: Locale.$STR("SelectAll"), commandID: "cmd_selectAll"}); 181 return items; 182 }, 183 184 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 185 // Public API 186 187 setText: function(text) 188 { 189 try 190 { 191 // When manually setting the text, ignore the TEXT_CHANGED event. 192 this.ignoreChanges = true; 193 194 if (this.editor) 195 this.editor.setText(text); 196 } 197 catch (err) 198 { 199 // No exception is really expected, we just need the finally clause. 200 } 201 finally 202 { 203 this.ignoreChanges = false; 204 } 205 }, 206 207 getText: function() 208 { 209 if (this.editor) 210 return this.editor.getText(); 211 }, 212 213 setSelectionRange: function(start, end) 214 { 215 if (this.editor) 216 this.editor.setSelection(start, end); 217 }, 218 219 select: function() 220 { 221 // TODO xxxHonza 222 }, 223 224 // returns the applicable commands 225 getExpression: function() 226 { 227 if (this.editor) 228 { 229 if (this.isCollapsed()) 230 return this.getText(); 231 else 232 return this.editor.getSelectedText(); 233 } 234 }, 235 236 isCollapsed: function() 237 { 238 var selection; 239 if (this.editor) 240 { 241 selection = this.editor.getSelection(); 242 return selection.start === selection.end; 243 } 244 return true; 245 }, 246 247 hasFocus: function() 248 { 249 try 250 { 251 if (this.editor) 252 return this.editor.hasFocus(); 253 } 254 catch (e) 255 { 256 } 257 }, 258 259 focus: function() 260 { 261 if (this.editor) 262 this.editor.focus(); 263 }, 264 265 fontSizeAdjust: function(adjust) 266 { 267 if (!this.editor || !this.editor._view) 268 return; 269 270 if (typeof(SourceEditor) != "undefined") 271 { 272 var doc = this.editor._view._frame.contentDocument; 273 274 // See issue 5488 275 //doc.body.style.fontSizeAdjust = adjust; 276 } 277 else 278 { 279 this.editor.textBox.style.fontSizeAdjust = adjust; 280 } 281 } 282 }); 283 284 // ********************************************************************************************* // 285 // Getters/setters 286 287 Firebug.CommandEditor.__defineGetter__("value", function() 288 { 289 return this.getText(); 290 }); 291 292 Firebug.CommandEditor.__defineSetter__("value", function(val) 293 { 294 this.setText(val); 295 }); 296 297 // ********************************************************************************************* // 298 // Text Editor 299 300 /** 301 * A simple <textbox> element is used in environments where the Orion SourceEditor is not 302 * available (such as SeaMonkey) 303 */ 304 function TextEditor() {} 305 TextEditor.prototype = 306 { 307 init: function(editorElement, config, callback) 308 { 309 var commandEditorBox = editorElement.parentNode; 310 311 this.textBox = commandEditorBox.ownerDocument.createElement("textbox"); 312 this.textBox.setAttribute("id", "fbCommandEditor"); 313 this.textBox.setAttribute("multiline", "true"); 314 this.textBox.setAttribute("flex", "1"); 315 this.textBox.setAttribute("newlines", "pasteintact"); 316 this.textBox.setAttribute("label", "CommandEditor"); 317 318 commandEditorBox.replaceChild(this.textBox, editorElement); 319 320 // The original source editor is also loaded asynchronously. 321 setTimeout(callback); 322 }, 323 324 destroy: function() 325 { 326 }, 327 328 addEventListener: function(type, callback) 329 { 330 if (!type) 331 return; 332 333 Events.addEventListener(this.textBox, type, callback, true); 334 }, 335 336 removeEventListener: function(type, callback) 337 { 338 if (!type) 339 return; 340 341 Events.removeEventListener(this.textBox, type, callback, true); 342 }, 343 344 setCaretOffset: function(offset) 345 { 346 }, 347 348 getCharCount: function() 349 { 350 return this.textBox.value ? this.textBox.value.length : 0; 351 }, 352 353 setText: function(text) 354 { 355 this.textBox.value = text; 356 }, 357 358 getText: function() 359 { 360 return this.textBox.value; 361 }, 362 363 setSelection: function(start, end) 364 { 365 this.textBox.setSelectionRange(start, end); 366 }, 367 368 getSelection: function() 369 { 370 return { 371 start: this.textBox.selectionStart, 372 end: this.textBox.selectionEnd 373 }; 374 }, 375 376 hasFocus: function() 377 { 378 return this.textBox.getAttribute("focused") == "true"; 379 }, 380 381 focus: function() 382 { 383 this.textBox.focus(); 384 }, 385 386 getSelectedText: function() 387 { 388 var start = this.textBox.selectionStart; 389 var end = this.textBox.selectionEnd; 390 391 return this.textBox.value.substring(start, end); 392 } 393 } 394 395 // ********************************************************************************************* // 396 // Registration 397 398 Firebug.registerModule(Firebug.CommandEditor); 399 400 return Firebug.CommandEditor; 401 402 // ********************************************************************************************* // 403 }); 404