browser/devtools/styleeditor/StyleEditorUI.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* vim:set ts=2 sw=2 sts=2 et: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 "use strict";
michael@0 7
michael@0 8 this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
michael@0 9
michael@0 10 const Cc = Components.classes;
michael@0 11 const Ci = Components.interfaces;
michael@0 12 const Cu = Components.utils;
michael@0 13
michael@0 14 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 15 Cu.import("resource://gre/modules/Services.jsm");
michael@0 16 Cu.import("resource://gre/modules/NetUtil.jsm");
michael@0 17 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 18 Cu.import("resource://gre/modules/Task.jsm");
michael@0 19 Cu.import("resource://gre/modules/devtools/event-emitter.js");
michael@0 20 Cu.import("resource:///modules/devtools/gDevTools.jsm");
michael@0 21 Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
michael@0 22 Cu.import("resource:///modules/devtools/SplitView.jsm");
michael@0 23 Cu.import("resource:///modules/devtools/StyleSheetEditor.jsm");
michael@0 24 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
michael@0 25
michael@0 26 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
michael@0 27 "resource://gre/modules/PluralForm.jsm");
michael@0 28
michael@0 29 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
michael@0 30 const { PrefObserver, PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
michael@0 31
michael@0 32 const LOAD_ERROR = "error-load";
michael@0 33 const STYLE_EDITOR_TEMPLATE = "stylesheet";
michael@0 34
michael@0 35 /**
michael@0 36 * StyleEditorUI is controls and builds the UI of the Style Editor, including
michael@0 37 * maintaining a list of editors for each stylesheet on a debuggee.
michael@0 38 *
michael@0 39 * Emits events:
michael@0 40 * 'editor-added': A new editor was added to the UI
michael@0 41 * 'editor-selected': An editor was selected
michael@0 42 * 'error': An error occured
michael@0 43 *
michael@0 44 * @param {StyleEditorFront} debuggee
michael@0 45 * Client-side front for interacting with the page's stylesheets
michael@0 46 * @param {Target} target
michael@0 47 * Interface for the page we're debugging
michael@0 48 * @param {Document} panelDoc
michael@0 49 * Document of the toolbox panel to populate UI in.
michael@0 50 */
michael@0 51 function StyleEditorUI(debuggee, target, panelDoc) {
michael@0 52 EventEmitter.decorate(this);
michael@0 53
michael@0 54 this._debuggee = debuggee;
michael@0 55 this._target = target;
michael@0 56 this._panelDoc = panelDoc;
michael@0 57 this._window = this._panelDoc.defaultView;
michael@0 58 this._root = this._panelDoc.getElementById("style-editor-chrome");
michael@0 59
michael@0 60 this.editors = [];
michael@0 61 this.selectedEditor = null;
michael@0 62 this.savedLocations = {};
michael@0 63
michael@0 64 this._updateSourcesLabel = this._updateSourcesLabel.bind(this);
michael@0 65 this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
michael@0 66 this._onNewDocument = this._onNewDocument.bind(this);
michael@0 67 this._clear = this._clear.bind(this);
michael@0 68 this._onError = this._onError.bind(this);
michael@0 69
michael@0 70 this._prefObserver = new PrefObserver("devtools.styleeditor.");
michael@0 71 this._prefObserver.on(PREF_ORIG_SOURCES, this._onNewDocument);
michael@0 72 }
michael@0 73
michael@0 74 StyleEditorUI.prototype = {
michael@0 75 /**
michael@0 76 * Get whether any of the editors have unsaved changes.
michael@0 77 *
michael@0 78 * @return boolean
michael@0 79 */
michael@0 80 get isDirty() {
michael@0 81 if (this._markedDirty === true) {
michael@0 82 return true;
michael@0 83 }
michael@0 84 return this.editors.some((editor) => {
michael@0 85 return editor.sourceEditor && !editor.sourceEditor.isClean();
michael@0 86 });
michael@0 87 },
michael@0 88
michael@0 89 /*
michael@0 90 * Mark the style editor as having or not having unsaved changes.
michael@0 91 */
michael@0 92 set isDirty(value) {
michael@0 93 this._markedDirty = value;
michael@0 94 },
michael@0 95
michael@0 96 /*
michael@0 97 * Index of selected stylesheet in document.styleSheets
michael@0 98 */
michael@0 99 get selectedStyleSheetIndex() {
michael@0 100 return this.selectedEditor ?
michael@0 101 this.selectedEditor.styleSheet.styleSheetIndex : -1;
michael@0 102 },
michael@0 103
michael@0 104 /**
michael@0 105 * Initiates the style editor ui creation and the inspector front to get
michael@0 106 * reference to the walker.
michael@0 107 */
michael@0 108 initialize: function() {
michael@0 109 let toolbox = gDevTools.getToolbox(this._target);
michael@0 110 return toolbox.initInspector().then(() => {
michael@0 111 this._walker = toolbox.walker;
michael@0 112 }).then(() => {
michael@0 113 this.createUI();
michael@0 114 this._debuggee.getStyleSheets().then((styleSheets) => {
michael@0 115 this._resetStyleSheetList(styleSheets);
michael@0 116
michael@0 117 this._target.on("will-navigate", this._clear);
michael@0 118 this._target.on("navigate", this._onNewDocument);
michael@0 119 });
michael@0 120 });
michael@0 121 },
michael@0 122
michael@0 123 /**
michael@0 124 * Build the initial UI and wire buttons with event handlers.
michael@0 125 */
michael@0 126 createUI: function() {
michael@0 127 let viewRoot = this._root.parentNode.querySelector(".splitview-root");
michael@0 128
michael@0 129 this._view = new SplitView(viewRoot);
michael@0 130
michael@0 131 wire(this._view.rootElement, ".style-editor-newButton", function onNew() {
michael@0 132 this._debuggee.addStyleSheet(null).then(this._onStyleSheetCreated);
michael@0 133 }.bind(this));
michael@0 134
michael@0 135 wire(this._view.rootElement, ".style-editor-importButton", function onImport() {
michael@0 136 this._importFromFile(this._mockImportFile || null, this._window);
michael@0 137 }.bind(this));
michael@0 138
michael@0 139 this._contextMenu = this._panelDoc.getElementById("sidebar-context");
michael@0 140 this._contextMenu.addEventListener("popupshowing",
michael@0 141 this._updateSourcesLabel);
michael@0 142
michael@0 143 this._sourcesItem = this._panelDoc.getElementById("context-origsources");
michael@0 144 this._sourcesItem.addEventListener("command",
michael@0 145 this._toggleOrigSources);
michael@0 146 },
michael@0 147
michael@0 148 /**
michael@0 149 * Update text of context menu option to reflect whether we're showing
michael@0 150 * original sources (e.g. Sass files) or not.
michael@0 151 */
michael@0 152 _updateSourcesLabel: function() {
michael@0 153 let string = "showOriginalSources";
michael@0 154 if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
michael@0 155 string = "showCSSSources";
michael@0 156 }
michael@0 157 this._sourcesItem.setAttribute("label", _(string + ".label"));
michael@0 158 this._sourcesItem.setAttribute("accesskey", _(string + ".accesskey"));
michael@0 159 },
michael@0 160
michael@0 161 /**
michael@0 162 * Refresh editors to reflect the stylesheets in the document.
michael@0 163 *
michael@0 164 * @param {string} event
michael@0 165 * Event name
michael@0 166 * @param {StyleSheet} styleSheet
michael@0 167 * StyleSheet object for new sheet
michael@0 168 */
michael@0 169 _onNewDocument: function() {
michael@0 170 this._debuggee.getStyleSheets().then((styleSheets) => {
michael@0 171 this._resetStyleSheetList(styleSheets);
michael@0 172 })
michael@0 173 },
michael@0 174
michael@0 175 /**
michael@0 176 * Add editors for all the given stylesheets to the UI.
michael@0 177 *
michael@0 178 * @param {array} styleSheets
michael@0 179 * Array of StyleSheetFront
michael@0 180 */
michael@0 181 _resetStyleSheetList: function(styleSheets) {
michael@0 182 this._clear();
michael@0 183
michael@0 184 for (let sheet of styleSheets) {
michael@0 185 this._addStyleSheet(sheet);
michael@0 186 }
michael@0 187
michael@0 188 this._root.classList.remove("loading");
michael@0 189
michael@0 190 this.emit("stylesheets-reset");
michael@0 191 },
michael@0 192
michael@0 193 /**
michael@0 194 * Remove all editors and add loading indicator.
michael@0 195 */
michael@0 196 _clear: function() {
michael@0 197 // remember selected sheet and line number for next load
michael@0 198 if (this.selectedEditor && this.selectedEditor.sourceEditor) {
michael@0 199 let href = this.selectedEditor.styleSheet.href;
michael@0 200 let {line, ch} = this.selectedEditor.sourceEditor.getCursor();
michael@0 201
michael@0 202 this._styleSheetToSelect = {
michael@0 203 href: href,
michael@0 204 line: line,
michael@0 205 col: ch
michael@0 206 };
michael@0 207 }
michael@0 208
michael@0 209 // remember saved file locations
michael@0 210 for (let editor of this.editors) {
michael@0 211 if (editor.savedFile) {
michael@0 212 let identifier = this.getStyleSheetIdentifier(editor.styleSheet);
michael@0 213 this.savedLocations[identifier] = editor.savedFile;
michael@0 214 }
michael@0 215 }
michael@0 216
michael@0 217 this._clearStyleSheetEditors();
michael@0 218 this._view.removeAll();
michael@0 219
michael@0 220 this.selectedEditor = null;
michael@0 221
michael@0 222 this._root.classList.add("loading");
michael@0 223 },
michael@0 224
michael@0 225 /**
michael@0 226 * Add an editor for this stylesheet. Add editors for its original sources
michael@0 227 * instead (e.g. Sass sources), if applicable.
michael@0 228 *
michael@0 229 * @param {StyleSheetFront} styleSheet
michael@0 230 * Style sheet to add to style editor
michael@0 231 */
michael@0 232 _addStyleSheet: function(styleSheet) {
michael@0 233 let editor = this._addStyleSheetEditor(styleSheet);
michael@0 234
michael@0 235 if (!Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
michael@0 236 return;
michael@0 237 }
michael@0 238
michael@0 239 styleSheet.getOriginalSources().then((sources) => {
michael@0 240 if (sources && sources.length) {
michael@0 241 this._removeStyleSheetEditor(editor);
michael@0 242 sources.forEach((source) => {
michael@0 243 // set so the first sheet will be selected, even if it's a source
michael@0 244 source.styleSheetIndex = styleSheet.styleSheetIndex;
michael@0 245 source.relatedStyleSheet = styleSheet;
michael@0 246
michael@0 247 this._addStyleSheetEditor(source);
michael@0 248 });
michael@0 249 }
michael@0 250 });
michael@0 251 },
michael@0 252
michael@0 253 /**
michael@0 254 * Add a new editor to the UI for a source.
michael@0 255 *
michael@0 256 * @param {StyleSheet} styleSheet
michael@0 257 * Object representing stylesheet
michael@0 258 * @param {nsIfile} file
michael@0 259 * Optional file object that sheet was imported from
michael@0 260 * @param {Boolean} isNew
michael@0 261 * Optional if stylesheet is a new sheet created by user
michael@0 262 */
michael@0 263 _addStyleSheetEditor: function(styleSheet, file, isNew) {
michael@0 264 // recall location of saved file for this sheet after page reload
michael@0 265 let identifier = this.getStyleSheetIdentifier(styleSheet);
michael@0 266 let savedFile = this.savedLocations[identifier];
michael@0 267 if (savedFile && !file) {
michael@0 268 file = savedFile;
michael@0 269 }
michael@0 270
michael@0 271 let editor =
michael@0 272 new StyleSheetEditor(styleSheet, this._window, file, isNew, this._walker);
michael@0 273
michael@0 274 editor.on("property-change", this._summaryChange.bind(this, editor));
michael@0 275 editor.on("linked-css-file", this._summaryChange.bind(this, editor));
michael@0 276 editor.on("linked-css-file-error", this._summaryChange.bind(this, editor));
michael@0 277 editor.on("error", this._onError);
michael@0 278
michael@0 279 this.editors.push(editor);
michael@0 280
michael@0 281 editor.fetchSource(this._sourceLoaded.bind(this, editor));
michael@0 282 return editor;
michael@0 283 },
michael@0 284
michael@0 285 /**
michael@0 286 * Import a style sheet from file and asynchronously create a
michael@0 287 * new stylesheet on the debuggee for it.
michael@0 288 *
michael@0 289 * @param {mixed} file
michael@0 290 * Optional nsIFile or filename string.
michael@0 291 * If not set a file picker will be shown.
michael@0 292 * @param {nsIWindow} parentWindow
michael@0 293 * Optional parent window for the file picker.
michael@0 294 */
michael@0 295 _importFromFile: function(file, parentWindow) {
michael@0 296 let onFileSelected = function(file) {
michael@0 297 if (!file) {
michael@0 298 // nothing selected
michael@0 299 return;
michael@0 300 }
michael@0 301 NetUtil.asyncFetch(file, (stream, status) => {
michael@0 302 if (!Components.isSuccessCode(status)) {
michael@0 303 this.emit("error", LOAD_ERROR);
michael@0 304 return;
michael@0 305 }
michael@0 306 let source = NetUtil.readInputStreamToString(stream, stream.available());
michael@0 307 stream.close();
michael@0 308
michael@0 309 this._debuggee.addStyleSheet(source).then((styleSheet) => {
michael@0 310 this._onStyleSheetCreated(styleSheet, file);
michael@0 311 });
michael@0 312 });
michael@0 313
michael@0 314 }.bind(this);
michael@0 315
michael@0 316 showFilePicker(file, false, parentWindow, onFileSelected);
michael@0 317 },
michael@0 318
michael@0 319
michael@0 320 /**
michael@0 321 * When a new or imported stylesheet has been added to the document.
michael@0 322 * Add an editor for it.
michael@0 323 */
michael@0 324 _onStyleSheetCreated: function(styleSheet, file) {
michael@0 325 this._addStyleSheetEditor(styleSheet, file, true);
michael@0 326 },
michael@0 327
michael@0 328 /**
michael@0 329 * Forward any error from a stylesheet.
michael@0 330 *
michael@0 331 * @param {string} event
michael@0 332 * Event name
michael@0 333 * @param {string} errorCode
michael@0 334 * Code represeting type of error
michael@0 335 * @param {string} message
michael@0 336 * The full error message
michael@0 337 */
michael@0 338 _onError: function(event, errorCode, message) {
michael@0 339 this.emit("error", errorCode, message);
michael@0 340 },
michael@0 341
michael@0 342 /**
michael@0 343 * Toggle the original sources pref.
michael@0 344 */
michael@0 345 _toggleOrigSources: function() {
michael@0 346 let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
michael@0 347 Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
michael@0 348 },
michael@0 349
michael@0 350 /**
michael@0 351 * Remove a particular stylesheet editor from the UI
michael@0 352 *
michael@0 353 * @param {StyleSheetEditor} editor
michael@0 354 * The editor to remove.
michael@0 355 */
michael@0 356 _removeStyleSheetEditor: function(editor) {
michael@0 357 if (editor.summary) {
michael@0 358 this._view.removeItem(editor.summary);
michael@0 359 }
michael@0 360 else {
michael@0 361 let self = this;
michael@0 362 this.on("editor-added", function onAdd(event, added) {
michael@0 363 if (editor == added) {
michael@0 364 self.off("editor-added", onAdd);
michael@0 365 self._view.removeItem(editor.summary);
michael@0 366 }
michael@0 367 })
michael@0 368 }
michael@0 369
michael@0 370 editor.destroy();
michael@0 371 this.editors.splice(this.editors.indexOf(editor), 1);
michael@0 372 },
michael@0 373
michael@0 374 /**
michael@0 375 * Clear all the editors from the UI.
michael@0 376 */
michael@0 377 _clearStyleSheetEditors: function() {
michael@0 378 for (let editor of this.editors) {
michael@0 379 editor.destroy();
michael@0 380 }
michael@0 381 this.editors = [];
michael@0 382 },
michael@0 383
michael@0 384 /**
michael@0 385 * Called when a StyleSheetEditor's source has been fetched. Create a
michael@0 386 * summary UI for the editor.
michael@0 387 *
michael@0 388 * @param {StyleSheetEditor} editor
michael@0 389 * Editor to create UI for.
michael@0 390 */
michael@0 391 _sourceLoaded: function(editor) {
michael@0 392 // add new sidebar item and editor to the UI
michael@0 393 this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
michael@0 394 data: {
michael@0 395 editor: editor
michael@0 396 },
michael@0 397 disableAnimations: this._alwaysDisableAnimations,
michael@0 398 ordinal: editor.styleSheet.styleSheetIndex,
michael@0 399 onCreate: function(summary, details, data) {
michael@0 400 let editor = data.editor;
michael@0 401 editor.summary = summary;
michael@0 402
michael@0 403 wire(summary, ".stylesheet-enabled", function onToggleDisabled(event) {
michael@0 404 event.stopPropagation();
michael@0 405 event.target.blur();
michael@0 406
michael@0 407 editor.toggleDisabled();
michael@0 408 });
michael@0 409
michael@0 410 wire(summary, ".stylesheet-name", {
michael@0 411 events: {
michael@0 412 "keypress": function onStylesheetNameActivate(aEvent) {
michael@0 413 if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
michael@0 414 this._view.activeSummary = summary;
michael@0 415 }
michael@0 416 }.bind(this)
michael@0 417 }
michael@0 418 });
michael@0 419
michael@0 420 wire(summary, ".stylesheet-saveButton", function onSaveButton(event) {
michael@0 421 event.stopPropagation();
michael@0 422 event.target.blur();
michael@0 423
michael@0 424 editor.saveToFile(editor.savedFile);
michael@0 425 });
michael@0 426
michael@0 427 this._updateSummaryForEditor(editor, summary);
michael@0 428
michael@0 429 summary.addEventListener("focus", function onSummaryFocus(event) {
michael@0 430 if (event.target == summary) {
michael@0 431 // autofocus the stylesheet name
michael@0 432 summary.querySelector(".stylesheet-name").focus();
michael@0 433 }
michael@0 434 }, false);
michael@0 435
michael@0 436 Task.spawn(function* () {
michael@0 437 // autofocus if it's a new user-created stylesheet
michael@0 438 if (editor.isNew) {
michael@0 439 yield this._selectEditor(editor);
michael@0 440 }
michael@0 441
michael@0 442 if (this._styleSheetToSelect
michael@0 443 && this._styleSheetToSelect.href == editor.styleSheet.href) {
michael@0 444 yield this.switchToSelectedSheet();
michael@0 445 }
michael@0 446
michael@0 447 // If this is the first stylesheet and there is no pending request to
michael@0 448 // select a particular style sheet, select this sheet.
michael@0 449 if (!this.selectedEditor && !this._styleSheetBoundToSelect
michael@0 450 && editor.styleSheet.styleSheetIndex == 0) {
michael@0 451 yield this._selectEditor(editor);
michael@0 452 }
michael@0 453
michael@0 454 this.emit("editor-added", editor);
michael@0 455 }.bind(this)).then(null, Cu.reportError);
michael@0 456 }.bind(this),
michael@0 457
michael@0 458 onShow: function(summary, details, data) {
michael@0 459 let editor = data.editor;
michael@0 460 this.selectedEditor = editor;
michael@0 461
michael@0 462 Task.spawn(function* () {
michael@0 463 if (!editor.sourceEditor) {
michael@0 464 // only initialize source editor when we switch to this view
michael@0 465 let inputElement = details.querySelector(".stylesheet-editor-input");
michael@0 466 yield editor.load(inputElement);
michael@0 467 }
michael@0 468
michael@0 469 editor.onShow();
michael@0 470
michael@0 471 this.emit("editor-selected", editor);
michael@0 472 }.bind(this)).then(null, Cu.reportError);
michael@0 473 }.bind(this)
michael@0 474 });
michael@0 475 },
michael@0 476
michael@0 477 /**
michael@0 478 * Switch to the editor that has been marked to be selected.
michael@0 479 *
michael@0 480 * @return {Promise}
michael@0 481 * Promise that will resolve when the editor is selected.
michael@0 482 */
michael@0 483 switchToSelectedSheet: function() {
michael@0 484 let sheet = this._styleSheetToSelect;
michael@0 485
michael@0 486 for (let editor of this.editors) {
michael@0 487 if (editor.styleSheet.href == sheet.href) {
michael@0 488 // The _styleSheetBoundToSelect will always hold the latest pending
michael@0 489 // requested style sheet (with line and column) which is not yet
michael@0 490 // selected by the source editor. Only after we select that particular
michael@0 491 // editor and go the required line and column, it will become null.
michael@0 492 this._styleSheetBoundToSelect = this._styleSheetToSelect;
michael@0 493 this._styleSheetToSelect = null;
michael@0 494 return this._selectEditor(editor, sheet.line, sheet.col);
michael@0 495 }
michael@0 496 }
michael@0 497
michael@0 498 return promise.resolve();
michael@0 499 },
michael@0 500
michael@0 501 /**
michael@0 502 * Select an editor in the UI.
michael@0 503 *
michael@0 504 * @param {StyleSheetEditor} editor
michael@0 505 * Editor to switch to.
michael@0 506 * @param {number} line
michael@0 507 * Line number to jump to
michael@0 508 * @param {number} col
michael@0 509 * Column number to jump to
michael@0 510 * @return {Promise}
michael@0 511 * Promise that will resolve when the editor is selected.
michael@0 512 */
michael@0 513 _selectEditor: function(editor, line, col) {
michael@0 514 line = line || 0;
michael@0 515 col = col || 0;
michael@0 516
michael@0 517 let editorPromise = editor.getSourceEditor().then(() => {
michael@0 518 editor.sourceEditor.setCursor({line: line, ch: col});
michael@0 519 this._styleSheetBoundToSelect = null;
michael@0 520 });
michael@0 521
michael@0 522 let summaryPromise = this.getEditorSummary(editor).then((summary) => {
michael@0 523 this._view.activeSummary = summary;
michael@0 524 });
michael@0 525
michael@0 526 return promise.all([editorPromise, summaryPromise]);
michael@0 527 },
michael@0 528
michael@0 529 getEditorSummary: function(editor) {
michael@0 530 if (editor.summary) {
michael@0 531 return promise.resolve(editor.summary);
michael@0 532 }
michael@0 533
michael@0 534 let deferred = promise.defer();
michael@0 535 let self = this;
michael@0 536
michael@0 537 this.on("editor-added", function onAdd(e, selected) {
michael@0 538 if (selected == editor) {
michael@0 539 self.off("editor-added", onAdd);
michael@0 540 deferred.resolve(editor.summary);
michael@0 541 }
michael@0 542 });
michael@0 543
michael@0 544 return deferred.promise;
michael@0 545 },
michael@0 546
michael@0 547 /**
michael@0 548 * Returns an identifier for the given style sheet.
michael@0 549 *
michael@0 550 * @param {StyleSheet} aStyleSheet
michael@0 551 * The style sheet to be identified.
michael@0 552 */
michael@0 553 getStyleSheetIdentifier: function (aStyleSheet) {
michael@0 554 // Identify inline style sheets by their host page URI and index at the page.
michael@0 555 return aStyleSheet.href ? aStyleSheet.href :
michael@0 556 "inline-" + aStyleSheet.styleSheetIndex + "-at-" + aStyleSheet.nodeHref;
michael@0 557 },
michael@0 558
michael@0 559 /**
michael@0 560 * selects a stylesheet and optionally moves the cursor to a selected line
michael@0 561 *
michael@0 562 * @param {string} [href]
michael@0 563 * Href of stylesheet that should be selected. If a stylesheet is not passed
michael@0 564 * and the editor is not initialized we focus the first stylesheet. If
michael@0 565 * a stylesheet is not passed and the editor is initialized we ignore
michael@0 566 * the call.
michael@0 567 * @param {Number} [line]
michael@0 568 * Line to which the caret should be moved (zero-indexed).
michael@0 569 * @param {Number} [col]
michael@0 570 * Column to which the caret should be moved (zero-indexed).
michael@0 571 */
michael@0 572 selectStyleSheet: function(href, line, col) {
michael@0 573 this._styleSheetToSelect = {
michael@0 574 href: href,
michael@0 575 line: line,
michael@0 576 col: col,
michael@0 577 };
michael@0 578
michael@0 579 /* Switch to the editor for this sheet, if it exists yet.
michael@0 580 Otherwise each editor will be checked when it's created. */
michael@0 581 this.switchToSelectedSheet();
michael@0 582 },
michael@0 583
michael@0 584
michael@0 585 /**
michael@0 586 * Handler for an editor's 'property-changed' event.
michael@0 587 * Update the summary in the UI.
michael@0 588 *
michael@0 589 * @param {StyleSheetEditor} editor
michael@0 590 * Editor for which a property has changed
michael@0 591 */
michael@0 592 _summaryChange: function(editor) {
michael@0 593 this._updateSummaryForEditor(editor);
michael@0 594 },
michael@0 595
michael@0 596 /**
michael@0 597 * Update split view summary of given StyleEditor instance.
michael@0 598 *
michael@0 599 * @param {StyleSheetEditor} editor
michael@0 600 * @param {DOMElement} summary
michael@0 601 * Optional item's summary element to update. If none, item corresponding
michael@0 602 * to passed editor is used.
michael@0 603 */
michael@0 604 _updateSummaryForEditor: function(editor, summary) {
michael@0 605 summary = summary || editor.summary;
michael@0 606 if (!summary) {
michael@0 607 return;
michael@0 608 }
michael@0 609
michael@0 610 let ruleCount = editor.styleSheet.ruleCount;
michael@0 611 if (editor.styleSheet.relatedStyleSheet && editor.linkedCSSFile) {
michael@0 612 ruleCount = editor.styleSheet.relatedStyleSheet.ruleCount;
michael@0 613 }
michael@0 614 if (ruleCount === undefined) {
michael@0 615 ruleCount = "-";
michael@0 616 }
michael@0 617
michael@0 618 var flags = [];
michael@0 619 if (editor.styleSheet.disabled) {
michael@0 620 flags.push("disabled");
michael@0 621 }
michael@0 622 if (editor.unsaved) {
michael@0 623 flags.push("unsaved");
michael@0 624 }
michael@0 625 if (editor.linkedCSSFileError) {
michael@0 626 flags.push("linked-file-error");
michael@0 627 }
michael@0 628 this._view.setItemClassName(summary, flags.join(" "));
michael@0 629
michael@0 630 let label = summary.querySelector(".stylesheet-name > label");
michael@0 631 label.setAttribute("value", editor.friendlyName);
michael@0 632 if (editor.styleSheet.href) {
michael@0 633 label.setAttribute("tooltiptext", editor.styleSheet.href);
michael@0 634 }
michael@0 635
michael@0 636 let linkedCSSFile = "";
michael@0 637 if (editor.linkedCSSFile) {
michael@0 638 linkedCSSFile = OS.Path.basename(editor.linkedCSSFile);
michael@0 639 }
michael@0 640 text(summary, ".stylesheet-linked-file", linkedCSSFile);
michael@0 641 text(summary, ".stylesheet-title", editor.styleSheet.title || "");
michael@0 642 text(summary, ".stylesheet-rule-count",
michael@0 643 PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
michael@0 644 },
michael@0 645
michael@0 646 destroy: function() {
michael@0 647 this._clearStyleSheetEditors();
michael@0 648
michael@0 649 this._prefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
michael@0 650 this._prefObserver.destroy();
michael@0 651 }
michael@0 652 }

mercurial