1 /* See license.txt for terms of usage */ 2 3 // ************************************************************************************************ 4 // Constants 5 6 const Cc = Components.classes; 7 const Ci = Components.interfaces; 8 const Cu = Components.utils; 9 10 const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); 11 const names = ["label", "executable", "cmdline", "image"]; 12 13 // ************************************************************************************************ 14 // Globals 15 16 var gEditorManager = 17 { 18 _tree : null, 19 _data : [], 20 _removeButton : null, 21 _changeButton : null, 22 _moveUpButton : null, 23 24 init: function() 25 { 26 var args = window.arguments[0]; 27 this._FBL = args.FBL; 28 this._prefName = args.prefName; 29 30 (this._removeButton = document.getElementById("removeEditor")).disabled = true; 31 (this._changeButton = document.getElementById("changeEditor")).disabled = true; 32 (this._moveUpButton = document.getElementById("moveUpEditor")).disabled = true; 33 34 this._tree = document.getElementById("editorsList"); 35 36 this._treeView = 37 { 38 data: this._data, 39 selection: null, 40 41 get rowCount() { return this.data.length; }, 42 getCellText: function(row, column) 43 { 44 switch(column.id) 45 { 46 case "editorName": 47 return " "+this.data[row].label; 48 case "editorExecutable": 49 return this.data[row].executable; 50 case "editorParams": 51 return this.data[row].cmdline; 52 } 53 return ""; 54 }, 55 setTree: function(treebox){ this.treebox = treebox; }, 56 isContainer: function(row) { return false; }, 57 isContainerOpen: function(row) { return false; }, 58 isContainerEmpty: function(row) { return false; }, 59 isSeparator: function(row) { return false; }, 60 isSorted: function() { return false; }, 61 getLevel: function(row) { return 0; }, 62 getImageSrc: function(row,column) { return column.id=="editorName" ? this.data[row].image : null; }, 63 getRowProperties: function(row,props) {}, 64 getCellProperties: function(row,column,props) {}, 65 getColumnProperties: function(colid,column,props) {} 66 }; 67 68 this._load(); 69 this._tree.view = this._treeView; 70 71 this.internationalizeUI(document); 72 }, 73 74 uninit: function() 75 { 76 }, 77 78 internationalizeUI: function(doc) 79 { 80 var elements = doc.getElementsByClassName("fbInternational"); 81 var attributes = ["title", "label", "value"]; 82 for (var i=0; i<elements.length; i++) 83 { 84 for(var j=0; j<attributes.length; j++) 85 { 86 if (elements[i].hasAttribute(attributes[j])) 87 this._FBL.internationalize(elements[i], attributes[j]); 88 } 89 } 90 }, 91 92 onSelectionChanged: function() 93 { 94 var selection = this._tree.view.selection, disabled = (selection.count != 1); 95 this._removeButton.disabled = disabled; 96 this._changeButton.disabled = disabled; 97 this._moveUpButton.disabled = disabled || (selection.currentIndex == 0); 98 }, 99 100 addEditorHandler: function() 101 { 102 var item = { label: "", executable: null, cmdline: "" }; 103 var result = {}; 104 var args = { 105 item: item, 106 FBL: this._FBL 107 }; 108 109 openDialog("chrome://firebug/content/firefox/external-editors/changeeditor.xul", 110 "_blank", "modal,centerscreen,resizable", args, result); 111 112 if (result.saveChanges) 113 { 114 item.id = item.label.replace(/\W/g, "_"); 115 this._saveItem(item); 116 117 this._loadItem(item); 118 this._data.push(item); 119 this._tree.view = this._treeView; 120 121 var editors = []; 122 try { 123 editors = prefs.getCharPref(this._prefName).split(","); 124 for( var i = 0; i < editors.length; ++i ) 125 { 126 if ( editors[i].replace(/^\s+|\s+$/,"") == "" ) 127 editors.splice(i, 1); 128 } 129 } 130 catch(exc) 131 { 132 this._FBL.ERROR(exc); 133 } 134 editors.push(item.id); 135 prefs.setCharPref(this._prefName, editors.join(",")); 136 } 137 }, 138 139 removeEditorHandler: function() 140 { 141 var selection = this._tree.view.selection; 142 if (selection.count < 1) 143 return; 144 var item = this._data[selection.currentIndex]; 145 this._data.splice(selection.currentIndex, 1); 146 this._tree.view = this._treeView; 147 148 try { 149 var editors = prefs.getCharPref(this._prefName).split(","); 150 this._FBL.remove(editors, item.id); 151 prefs.setCharPref(this._prefName, editors.join(",")); 152 prefs.deleteBranch(this._prefName+"."+item.id); 153 } 154 catch(exc) 155 { 156 this._FBL.ERROR(exc); 157 } 158 // update disabled state of buttons 159 if (this._data.length == 0) 160 selection.clearSelection(); 161 }, 162 163 changeEditorHandler: function() 164 { 165 var selection = this._tree.view.selection; 166 if (selection.count != 1) 167 return; 168 var item = this._data[selection.currentIndex]; 169 var args = { 170 item: item, 171 FBL: this._FBL 172 }; 173 var result = {}; 174 175 openDialog("chrome://firebug/content/firefox/external-editors/changeeditor.xul", 176 "_blank", "modal,centerscreen", args, result); 177 178 if (result.saveChanges) 179 { 180 this._saveItem(item); 181 } 182 this._loadItem(item); 183 this._tree.view = this._treeView; 184 }, 185 186 moveUpEditorHandler: function() 187 { 188 var selection = this._tree.view.selection; 189 if (selection.count < 1) 190 return; 191 var item = this._data[selection.currentIndex]; 192 this._data.splice(selection.currentIndex, 1); 193 this._data.unshift(item); 194 this._tree.view = this._treeView; 195 try { 196 var editors = this._data.map(function(x) x.id); 197 prefs.setCharPref(this._prefName, editors.join(",")); 198 } 199 catch(exc) 200 { 201 this._FBL.ERROR(exc); 202 } 203 }, 204 205 206 _loadItem: function(item) 207 { 208 const prefName = this._prefName; 209 for( var i = 0; i < names.length; ++i ) 210 { 211 try { 212 item[names[i]] = prefs.getCharPref(prefName+"."+item.id+"."+names[i]); 213 } 214 catch(exc) 215 {} 216 } 217 if (!item.image) 218 item.image = this._FBL.getIconURLForFile(item.executable); 219 }, 220 221 _saveItem: function(item) 222 { 223 if ( item.image && item.image == this._FBL.getIconURLForFile(item.executable) ) 224 item.image = null; 225 226 const prefName = this._prefName; 227 for( var i = 0; i < names.length; ++i ) 228 { 229 try { 230 var value = item[names[i]]; 231 if ( value ) 232 prefs.setCharPref(prefName+"."+item.id+"."+names[i], value); 233 else 234 prefs.clearUserPref(prefName+"."+item.id+"."+names[i]); 235 } 236 catch(exc) 237 {} 238 } 239 }, 240 241 _load: function() 242 { 243 try { 244 var list = prefs.getCharPref(this._prefName).split(","); 245 for (var i = 0; i < list.length; ++i) 246 { 247 var editorId = list[i].replace(/\s/g, "_"); 248 if ( !editorId ) 249 continue; 250 var item = { id: editorId }; 251 this._data.push(item); 252 this._loadItem(item); 253 } 254 } 255 catch(exc) 256 { 257 this._FBL.ERROR(exc); 258 } 259 } 260 261 }; 262 263 // ************************************************************************************************ 264 // URLMappings 265 Cu.import("resource://firebug/loader.js") 266 267 var headerName = "X-Local-File-Path"; 268 var headerExplaination = "\ 269 // the following regexp is used by firebug to determine\n\ 270 // if it should send request to the server to get\n\ 271 // file path with " + headerName + " header\n\ 272 // defalt value is ^https?:\\/\\/(localhost)(\\/|:|$)"; 273 274 var listExplaination = "\ 275 // list of mappings in the form\n\ 276 // ^https?:\\/\\/my.domain.com/ => c:\\php/www\\ \n\ 277 // "; 278 var noMapping = "no mappings for tested url"; 279 var willQueryServer = "for this url Firebug will send query to server"; 280 281 var splitter = " => "; 282 var gUrlMappingManager = { 283 init: function() 284 { 285 var Firebug = opener.Firebug; 286 var extModule = Firebug.ExternalEditors; 287 this.checkHeaderRe = extModule.checkHeaderRe; 288 this.pathTransformations = extModule.pathTransformations; 289 290 var val = [ 291 headerExplaination, "\n", 292 headerName, splitter, extModule.checkHeaderRe.source, 293 "\n\n", 294 listExplaination, 295 "\n" 296 ]; 297 298 for (var i = 0; i < this.pathTransformations.length; i++) 299 { 300 var transform = this.pathTransformations[i]; 301 val.push(transform.regexp.source, splitter, transform.filePath, '\n'); 302 } 303 304 val.push(splitter, "\n") 305 306 document.getElementById("urlMappings").value = val.join(""); 307 document.getElementById("test").value = Firebug.Firefox.getCurrentBrowser().currentURI.spec; 308 309 this.onMainInput(); 310 }, 311 uninit: function() 312 { 313 this.save(); 314 opener.Firebug.ExternalEditors.saveUrlMappings(); 315 }, 316 save: function() 317 { 318 var checkHeaderRe = this.checkHeaderRe; 319 var pathTransformations = this.pathTransformations; 320 321 FirebugLoader.forEachWindow(function(win) 322 { 323 var extModule = win.Firebug.ExternalEditors; 324 delete extModule.pathTransformations; 325 delete extModule.checkHeaderRe; 326 extModule.checkHeaderRe = checkHeaderRe; 327 extModule.pathTransformations = pathTransformations; 328 }); 329 }, 330 parse: function(val) 331 { 332 var lines = val.split(/(?:\n\r|\n|\r)/); 333 var errors = this.errors = []; 334 function addRegexp(source, line) 335 { 336 if (!source) 337 return; 338 339 try 340 { 341 source = source.replace(/\\?\//g, '\\/'); 342 return RegExp(source, 'i'); 343 } 344 catch(e) 345 { 346 errors.push(line + ': ' + e); 347 return null; 348 } 349 } 350 351 this.pathTransformations = []; 352 this.checkHeaderRe = null; 353 for (var i in lines) 354 { 355 var line = lines[i].split('=>'); 356 357 if (!line[1] || !line[0]) 358 continue; 359 360 var start = line[0].trim() 361 var end = line[1].trim(); 362 363 if (start[0] == '/' && start[1] == '/') 364 continue; 365 366 if (start == headerName) 367 { 368 if (this.checkHeaderRe) 369 erors.push(i) 370 else 371 this.checkHeaderRe = addRegexp(end, i); 372 continue; 373 } 374 var t = { 375 regexp: addRegexp(start, i), 376 filePath: end 377 } 378 if (t.regexp && t.filePath) 379 this.pathTransformations.push(t) 380 } 381 382 if (!this.checkHeaderRe) 383 this.checkHeaderRe = /^$/ 384 }, 385 386 onTestInput: function() 387 { 388 var testBox = document.getElementById("test"); 389 var resultBox = document.getElementById("result"); 390 var href = testBox.value; 391 392 if (this.checkHeaderRe.test(href)) 393 { 394 resultBox.value = "firebug will send query to server"; 395 } 396 else 397 { 398 for (var i = 0; i < this.pathTransformations.length; i++) 399 { 400 var transform = this.pathTransformations[i]; 401 if (transform.regexp.test(href)) 402 { 403 var path = href.replace(transform.regexp, transform.filePath); 404 break; 405 } 406 } 407 408 if (path) 409 { 410 resultBox.style.cssText = "box-shadow: 0px 0px 1.5px 1px lime;"; 411 href = path; 412 } 413 414 resultBox.value = href.replace(/([^:\\\/])[\\\/]+/g, '$1/'); 415 } 416 }, 417 418 onMainInput: function() 419 { 420 this.parse(document.getElementById("urlMappings").value); 421 var resultBox = document.getElementById("result"); 422 if (this.errors.length) 423 { 424 resultBox.value = this.errors; 425 resultBox.style.cssText = "box-shadow: 0px 0px 1.5px 1px red;"; 426 } else 427 { 428 resultBox.style.cssText = ""; 429 this.onTestInput() 430 } 431 }, 432 schedule: function(funcName) 433 { 434 if (this._scheduled != "onMainInput") 435 this._scheduled = funcName 436 437 if (this.timeOut != null) 438 return; 439 this.timeOut = setTimeout(function(_this) 440 { 441 _this[_this._scheduled](); 442 _this._scheduled = _this.timeOut = null; 443 _this.save() 444 }, 80, this); 445 } 446 } 447 448 // ************************************************************************************************ 449 450