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