From d3371dbcd21adea48887fbc5c216ca51a54b6e66 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 13 Oct 2014 11:56:17 -0600 Subject: [PATCH 01/11] Start on StyleManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will take over raw stylesheet management from the theme manager now that it’s becoming more complex with the need to target specific host elements. Instead of actually adding nodes to the head of the document, it will instead simply manage a set of stylesheets we want to apply and leave actual DOM manipulation to custom elements that can render the set of active stylesheets in the appropriate locations. --- spec/style-manager-spec.coffee | 29 +++++++++++++++++++++++++++++ src/style-manager.coffee | 30 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 spec/style-manager-spec.coffee create mode 100644 src/style-manager.coffee diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee new file mode 100644 index 000000000..496e70419 --- /dev/null +++ b/spec/style-manager-spec.coffee @@ -0,0 +1,29 @@ +StyleManager = require '../src/style-manager' + +describe "StyleManager", -> + manager = null + + beforeEach -> + manager = new StyleManager + + describe "::addStyleSheet(source, params)", -> + it "adds a stylesheet based on the given source and returns a disposable allowing it to be removed", -> + addEvents = [] + removeEvents = [] + manager.onDidAddStyleSheet (event) -> addEvents.push(event) + manager.onDidRemoveStyleSheet (event) -> removeEvents.push(event) + + disposable = manager.addStyleSheet("a {color: red;}") + + expect(addEvents.length).toBe 1 + expect(addEvents[0].styleElement.textContent).toBe "a {color: red;}" + + styleElements = manager.getStyleElements() + expect(styleElements.length).toBe 1 + expect(styleElements[0].textContent).toBe "a {color: red;}" + + disposable.dispose() + + expect(removeEvents.length).toBe 1 + expect(removeEvents[0].styleElement.textContent).toBe "a {color: red;}" + expect(manager.getStyleElements().length).toBe 0 diff --git a/src/style-manager.coffee b/src/style-manager.coffee new file mode 100644 index 000000000..6d67bfce0 --- /dev/null +++ b/src/style-manager.coffee @@ -0,0 +1,30 @@ +{Emitter, Disposable} = require 'event-kit' + +module.exports = +class StyleManager + constructor: -> + @emitter = new Emitter + @styleElements = [] + + onDidAddStyleSheet: (callback) -> + @emitter.on 'did-add-style-sheet', callback + + onDidRemoveStyleSheet: (callback) -> + @emitter.on 'did-remove-style-sheet', callback + + getStyleElements: -> + @styleElements.slice() + + addStyleSheet: (source) -> + styleElement = document.createElement('style') + styleElement.textContent = source + @styleElements.push(styleElement) + @emitter.emit 'did-add-style-sheet', {styleElement} + + new Disposable => @removeStyleElement(styleElement) + + removeStyleElement: (styleElement) -> + index = @styleElements.indexOf(styleElement) + unless index is -1 + @styleElements.splice(index, 1) + @emitter.emit 'did-remove-style-sheet', {styleElement} From 1c97dcd195a3eeb1f2f741eee1c118ad5338aec0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 13 Oct 2014 12:09:33 -0600 Subject: [PATCH 02/11] Add sourcePath parameter to StyleManager::addStyleSheet --- spec/style-manager-spec.coffee | 31 ++++++++++++++++++++++++++++++- src/style-manager.coffee | 27 ++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee index 496e70419..d7f0b0e97 100644 --- a/spec/style-manager-spec.coffee +++ b/spec/style-manager-spec.coffee @@ -7,12 +7,18 @@ describe "StyleManager", -> manager = new StyleManager describe "::addStyleSheet(source, params)", -> - it "adds a stylesheet based on the given source and returns a disposable allowing it to be removed", -> + [addEvents, removeEvents, updateEvents] = [] + + beforeEach -> addEvents = [] removeEvents = [] + updateEvents = [] + manager.onDidAddStyleSheet (event) -> addEvents.push(event) manager.onDidRemoveStyleSheet (event) -> removeEvents.push(event) + manager.onDidUpdateStyleSheet (event) -> updateEvents.push(event) + it "adds a stylesheet based on the given source and returns a disposable allowing it to be removed", -> disposable = manager.addStyleSheet("a {color: red;}") expect(addEvents.length).toBe 1 @@ -27,3 +33,26 @@ describe "StyleManager", -> expect(removeEvents.length).toBe 1 expect(removeEvents[0].styleElement.textContent).toBe "a {color: red;}" expect(manager.getStyleElements().length).toBe 0 + + describe "when a sourcePath parameter is specified", -> + it "ensures a maximum of one style element for the given source path, updating a previous if it exists", -> + disposable1 = manager.addStyleSheet("a {color: red;}", sourcePath: '/foo/bar') + + expect(addEvents.length).toBe 1 + expect(addEvents[0].sourcePath).toBe '/foo/bar' + + disposable2 = manager.addStyleSheet("a {color: blue;}", sourcePath: '/foo/bar') + + expect(addEvents.length).toBe 1 + expect(updateEvents.length).toBe 1 + expect(updateEvents[0].sourcePath).toBe '/foo/bar' + expect(updateEvents[0].styleElement.textContent).toBe "a {color: blue;}" + + disposable2.dispose() + addEvents = [] + + manager.addStyleSheet("a {color: yellow;}", sourcePath: '/foo/bar') + + expect(addEvents.length).toBe 1 + expect(addEvents[0].sourcePath).toBe '/foo/bar' + expect(addEvents[0].styleElement.textContent).toBe "a {color: yellow;}" diff --git a/src/style-manager.coffee b/src/style-manager.coffee index 6d67bfce0..cd4604f8e 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -5,6 +5,7 @@ class StyleManager constructor: -> @emitter = new Emitter @styleElements = [] + @styleElementsBySourcePath = {} onDidAddStyleSheet: (callback) -> @emitter.on 'did-add-style-sheet', callback @@ -12,19 +13,35 @@ class StyleManager onDidRemoveStyleSheet: (callback) -> @emitter.on 'did-remove-style-sheet', callback + onDidUpdateStyleSheet: (callback) -> + @emitter.on 'did-update-style-sheet', callback + getStyleElements: -> @styleElements.slice() - addStyleSheet: (source) -> - styleElement = document.createElement('style') + addStyleSheet: (source, params) -> + sourcePath = params?.sourcePath + if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath] + updated = true + else + styleElement = document.createElement('style') + styleElement.textContent = source + @styleElements.push(styleElement) - @emitter.emit 'did-add-style-sheet', {styleElement} + @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath? - new Disposable => @removeStyleElement(styleElement) + if updated + @emitter.emit 'did-update-style-sheet', {styleElement, sourcePath} + else + @emitter.emit 'did-add-style-sheet', {styleElement, sourcePath} - removeStyleElement: (styleElement) -> + new Disposable => @removeStyleElement(styleElement, params) + + removeStyleElement: (styleElement, params) -> index = @styleElements.indexOf(styleElement) unless index is -1 @styleElements.splice(index, 1) + sourcePath = params?.sourcePath + delete @styleElementsBySourcePath[sourcePath] if sourcePath? @emitter.emit 'did-remove-style-sheet', {styleElement} From 1fe1147901e0268a28da9fb2e65fce19f9b2786f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 13 Oct 2014 12:43:36 -0600 Subject: [PATCH 03/11] Add group parameter to StyleManager::addStyleSheet This can be used to sequence style elements at the correct location in the cascade even if they are loaded later than elements in a subsequent group. --- spec/style-manager-spec.coffee | 12 ++++++++++++ src/style-manager.coffee | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee index d7f0b0e97..3b70e7ad4 100644 --- a/spec/style-manager-spec.coffee +++ b/spec/style-manager-spec.coffee @@ -56,3 +56,15 @@ describe "StyleManager", -> expect(addEvents.length).toBe 1 expect(addEvents[0].sourcePath).toBe '/foo/bar' expect(addEvents[0].styleElement.textContent).toBe "a {color: yellow;}" + + describe "when a group parameter is specified", -> + it "inserts the stylesheet at the end of any existing stylesheets for the same group", -> + manager.addStyleSheet("a {color: red}", group: 'a') + manager.addStyleSheet("a {color: blue}", group: 'b') + manager.addStyleSheet("a {color: green}", group: 'a') + + expect(manager.getStyleElements().map (elt) -> elt.textContent).toEqual [ + "a {color: red}" + "a {color: green}" + "a {color: blue}" + ] diff --git a/src/style-manager.coffee b/src/style-manager.coffee index cd4604f8e..cf3be51c9 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -21,20 +21,31 @@ class StyleManager addStyleSheet: (source, params) -> sourcePath = params?.sourcePath + group = params?.group + if sourcePath? and styleElement = @styleElementsBySourcePath[sourcePath] updated = true else styleElement = document.createElement('style') + styleElement.group = group if group? styleElement.textContent = source - @styleElements.push(styleElement) + if group? + for existingElement, index in @styleElements + if existingElement.group is group + insertIndex = index + 1 + else + break if insertIndex? + insertIndex ?= @styleElements.length + + @styleElements.splice(insertIndex, 0, styleElement) @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath? if updated - @emitter.emit 'did-update-style-sheet', {styleElement, sourcePath} + @emitter.emit 'did-update-style-sheet', {styleElement, sourcePath, group} else - @emitter.emit 'did-add-style-sheet', {styleElement, sourcePath} + @emitter.emit 'did-add-style-sheet', {styleElement, sourcePath, group} new Disposable => @removeStyleElement(styleElement, params) From 62a43c6fb9a7c12bdbf457927edb6b278a1811f6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 13 Oct 2014 17:29:55 -0600 Subject: [PATCH 04/11] Revise StyleManager API --- spec/style-manager-spec.coffee | 28 ++++++++++++---------------- src/style-manager.coffee | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee index 3b70e7ad4..131165a90 100644 --- a/spec/style-manager-spec.coffee +++ b/spec/style-manager-spec.coffee @@ -1,28 +1,24 @@ StyleManager = require '../src/style-manager' describe "StyleManager", -> - manager = null + [manager, addEvents, removeEvents, updateEvents] = [] beforeEach -> manager = new StyleManager + addEvents = [] + removeEvents = [] + updateEvents = [] + + manager.onDidAddStyleElement (event) -> addEvents.push(event) + manager.onDidRemoveStyleElement (event) -> removeEvents.push(event) + manager.onDidUpdateStyleElement (event) -> updateEvents.push(event) describe "::addStyleSheet(source, params)", -> - [addEvents, removeEvents, updateEvents] = [] - - beforeEach -> - addEvents = [] - removeEvents = [] - updateEvents = [] - - manager.onDidAddStyleSheet (event) -> addEvents.push(event) - manager.onDidRemoveStyleSheet (event) -> removeEvents.push(event) - manager.onDidUpdateStyleSheet (event) -> updateEvents.push(event) - it "adds a stylesheet based on the given source and returns a disposable allowing it to be removed", -> disposable = manager.addStyleSheet("a {color: red;}") expect(addEvents.length).toBe 1 - expect(addEvents[0].styleElement.textContent).toBe "a {color: red;}" + expect(addEvents[0].textContent).toBe "a {color: red;}" styleElements = manager.getStyleElements() expect(styleElements.length).toBe 1 @@ -31,7 +27,7 @@ describe "StyleManager", -> disposable.dispose() expect(removeEvents.length).toBe 1 - expect(removeEvents[0].styleElement.textContent).toBe "a {color: red;}" + expect(removeEvents[0].textContent).toBe "a {color: red;}" expect(manager.getStyleElements().length).toBe 0 describe "when a sourcePath parameter is specified", -> @@ -46,7 +42,7 @@ describe "StyleManager", -> expect(addEvents.length).toBe 1 expect(updateEvents.length).toBe 1 expect(updateEvents[0].sourcePath).toBe '/foo/bar' - expect(updateEvents[0].styleElement.textContent).toBe "a {color: blue;}" + expect(updateEvents[0].textContent).toBe "a {color: blue;}" disposable2.dispose() addEvents = [] @@ -55,7 +51,7 @@ describe "StyleManager", -> expect(addEvents.length).toBe 1 expect(addEvents[0].sourcePath).toBe '/foo/bar' - expect(addEvents[0].styleElement.textContent).toBe "a {color: yellow;}" + expect(addEvents[0].textContent).toBe "a {color: yellow;}" describe "when a group parameter is specified", -> it "inserts the stylesheet at the end of any existing stylesheets for the same group", -> diff --git a/src/style-manager.coffee b/src/style-manager.coffee index cf3be51c9..7099d9b2a 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -7,14 +7,18 @@ class StyleManager @styleElements = [] @styleElementsBySourcePath = {} - onDidAddStyleSheet: (callback) -> - @emitter.on 'did-add-style-sheet', callback + observeStyleElements: (callback) -> + callback(styleElement) for styleElement in @getStyleElements() + @onDidAddStyleElement(callback) - onDidRemoveStyleSheet: (callback) -> - @emitter.on 'did-remove-style-sheet', callback + onDidAddStyleElement: (callback) -> + @emitter.on 'did-add-style-element', callback - onDidUpdateStyleSheet: (callback) -> - @emitter.on 'did-update-style-sheet', callback + onDidRemoveStyleElement: (callback) -> + @emitter.on 'did-remove-style-element', callback + + onDidUpdateStyleElement: (callback) -> + @emitter.on 'did-update-style-element', callback getStyleElements: -> @styleElements.slice() @@ -27,6 +31,7 @@ class StyleManager updated = true else styleElement = document.createElement('style') + styleElement.sourcePath = sourcePath if sourcePath? styleElement.group = group if group? styleElement.textContent = source @@ -43,9 +48,9 @@ class StyleManager @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath? if updated - @emitter.emit 'did-update-style-sheet', {styleElement, sourcePath, group} + @emitter.emit 'did-update-style-element', styleElement else - @emitter.emit 'did-add-style-sheet', {styleElement, sourcePath, group} + @emitter.emit 'did-add-style-element', styleElement new Disposable => @removeStyleElement(styleElement, params) @@ -55,4 +60,4 @@ class StyleManager @styleElements.splice(index, 1) sourcePath = params?.sourcePath delete @styleElementsBySourcePath[sourcePath] if sourcePath? - @emitter.emit 'did-remove-style-sheet', {styleElement} + @emitter.emit 'did-remove-style-element', styleElement From 79598aaae9481da505458ac64eb5891652367db8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 13 Oct 2014 17:31:58 -0600 Subject: [PATCH 05/11] Add StyleElement This will be used to handle stylesheet rendering when we move management of loading stylesheets to the StyleManager instead of the theme manager. This sets us up for being able to render specific stylesheets in shadow roots in addition to just having global stylesheets. --- spec/spec-helper.coffee | 1 + spec/styles-element-spec.coffee | 35 +++++++++++++++++++++++++++++++++ src/atom.coffee | 2 ++ src/style-manager.coffee | 4 ++++ src/styles-element.coffee | 30 ++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 spec/styles-element-spec.coffee create mode 100644 src/styles-element.coffee diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 0396f4673..d428e9fe1 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -74,6 +74,7 @@ beforeEach -> atom.workspace = new Workspace() atom.keymaps.keyBindings = _.clone(keyBindingsToRestore) atom.commands.restoreSnapshot(commandsToRestore) + atom.styles.clear() window.resetTimeouts() atom.packages.packageStates = {} diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee new file mode 100644 index 000000000..eaed6ebd8 --- /dev/null +++ b/spec/styles-element-spec.coffee @@ -0,0 +1,35 @@ +StylesElement = require '../src/styles-element' +StyleManager = require '../src/style-manager' + +describe "StylesElement", -> + element = null + + beforeEach -> + element = new StylesElement + document.querySelector('#jasmine-content').appendChild(element) + + it "renders a style tag for all currently active stylesheets in the style manager", -> + expect(element.children.length).toBe 0 + + disposable1 = atom.styles.addStyleSheet("a {color: red;}") + expect(element.children.length).toBe 1 + expect(element.children[0].textContent).toBe "a {color: red;}" + + disposable2 = atom.styles.addStyleSheet("a {color: blue;}") + expect(element.children.length).toBe 2 + expect(element.children[1].textContent).toBe "a {color: blue;}" + + disposable1.dispose() + expect(element.children.length).toBe 1 + expect(element.children[0].textContent).toBe "a {color: blue;}" + + it "orders style elements by group", -> + expect(element.children.length).toBe 0 + + atom.styles.addStyleSheet("a {color: red}", group: 'a') + atom.styles.addStyleSheet("a {color: blue}", group: 'b') + atom.styles.addStyleSheet("a {color: green}", group: 'a') + + expect(element.children[0].textContent).toBe "a {color: red}" + expect(element.children[1].textContent).toBe "a {color: green}" + expect(element.children[2].textContent).toBe "a {color: blue}" diff --git a/src/atom.coffee b/src/atom.coffee index a18cafe4d..6db0e59de 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -182,6 +182,7 @@ class Atom extends Model Clipboard = require './clipboard' Syntax = require './syntax' ThemeManager = require './theme-manager' + StyleManager = require './style-manager' ContextMenuManager = require './context-menu-manager' MenuManager = require './menu-manager' {devMode, safeMode, resourcePath} = @getLoadSettings() @@ -202,6 +203,7 @@ class Atom extends Model @commands = new CommandRegistry @packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode}) @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode}) + @styles = new StyleManager({resourcePath}) @contextMenu = new ContextMenuManager({resourcePath, devMode}) @menu = new MenuManager({resourcePath}) @clipboard = new Clipboard() diff --git a/src/style-manager.coffee b/src/style-manager.coffee index 7099d9b2a..a451d72ed 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -61,3 +61,7 @@ class StyleManager sourcePath = params?.sourcePath delete @styleElementsBySourcePath[sourcePath] if sourcePath? @emitter.emit 'did-remove-style-element', styleElement + + clear: -> + @styleElements = [] + @styleElementsBySourcePath = {} diff --git a/src/styles-element.coffee b/src/styles-element.coffee new file mode 100644 index 000000000..522e6bfc0 --- /dev/null +++ b/src/styles-element.coffee @@ -0,0 +1,30 @@ +{CompositeDisposable} = require 'event-kit' + +class StylesElement extends HTMLElement + attachedCallback: -> + @subscriptions = new CompositeDisposable + @styleElementClonesByOriginalElement = new WeakMap + @subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this)) + @subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this)) + + styleElementAdded: (styleElement) -> + {group} = styleElement + styleElementClone = styleElement.cloneNode(true) + styleElementClone.group = group + @styleElementClonesByOriginalElement.set(styleElement, styleElementClone) + + if group? + for child in @children + if child.group is group and child.nextSibling?.group isnt group + insertBefore = child.nextSibling + break + + @insertBefore(styleElementClone, insertBefore) + + styleElementRemoved: (styleElement) -> + @styleElementClonesByOriginalElement.get(styleElement).remove() + + detachedCallback: -> + @subscriptions.dispose() + +module.exports = StylesElement = document.registerElement 'atom-styles', prototype: StylesElement.prototype From 65e077abd1b8909c1af4620c001d9731cc578afb Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 10:08:46 -0600 Subject: [PATCH 06/11] Use StyleManager and StylesElement to manage all stylesheet elements For now, loading remains in the theme manager, but all application of stylesheets is routed through atom.styles. --- spec/spec-helper.coffee | 3 +- spec/style-manager-spec.coffee | 6 +-- spec/styles-element-spec.coffee | 22 ++++----- src/atom.coffee | 5 +- src/style-manager.coffee | 45 +++++++++++------ src/styles-element.coffee | 5 +- src/theme-manager.coffee | 86 ++++++++++++++------------------- src/workspace-element.coffee | 24 ++++++--- 8 files changed, 106 insertions(+), 90 deletions(-) diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index d428e9fe1..e8fb048a7 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -29,6 +29,7 @@ atom.packages.packageDirPaths.unshift(fixturePackagesPath) atom.keymaps.loadBundledKeymaps() keyBindingsToRestore = atom.keymaps.getKeyBindings() commandsToRestore = atom.commands.getSnapshot() +styleElementsToRestore = atom.styles.getSnapshot() window.addEventListener 'core:close', -> window.close() window.addEventListener 'beforeunload', -> @@ -74,7 +75,7 @@ beforeEach -> atom.workspace = new Workspace() atom.keymaps.keyBindings = _.clone(keyBindingsToRestore) atom.commands.restoreSnapshot(commandsToRestore) - atom.styles.clear() + atom.styles.restoreSnapshot(styleElementsToRestore) window.resetTimeouts() atom.packages.packageStates = {} diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee index 131165a90..1bf4770b3 100644 --- a/spec/style-manager-spec.coffee +++ b/spec/style-manager-spec.coffee @@ -35,13 +35,13 @@ describe "StyleManager", -> disposable1 = manager.addStyleSheet("a {color: red;}", sourcePath: '/foo/bar') expect(addEvents.length).toBe 1 - expect(addEvents[0].sourcePath).toBe '/foo/bar' + expect(addEvents[0].getAttribute('source-path')).toBe '/foo/bar' disposable2 = manager.addStyleSheet("a {color: blue;}", sourcePath: '/foo/bar') expect(addEvents.length).toBe 1 expect(updateEvents.length).toBe 1 - expect(updateEvents[0].sourcePath).toBe '/foo/bar' + expect(updateEvents[0].getAttribute('source-path')).toBe '/foo/bar' expect(updateEvents[0].textContent).toBe "a {color: blue;}" disposable2.dispose() @@ -50,7 +50,7 @@ describe "StyleManager", -> manager.addStyleSheet("a {color: yellow;}", sourcePath: '/foo/bar') expect(addEvents.length).toBe 1 - expect(addEvents[0].sourcePath).toBe '/foo/bar' + expect(addEvents[0].getAttribute('source-path')).toBe '/foo/bar' expect(addEvents[0].textContent).toBe "a {color: yellow;}" describe "when a group parameter is specified", -> diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee index eaed6ebd8..bd68f7a8b 100644 --- a/spec/styles-element-spec.coffee +++ b/spec/styles-element-spec.coffee @@ -9,27 +9,27 @@ describe "StylesElement", -> document.querySelector('#jasmine-content').appendChild(element) it "renders a style tag for all currently active stylesheets in the style manager", -> - expect(element.children.length).toBe 0 + initialChildCount = element.children.length disposable1 = atom.styles.addStyleSheet("a {color: red;}") - expect(element.children.length).toBe 1 - expect(element.children[0].textContent).toBe "a {color: red;}" + expect(element.children.length).toBe initialChildCount + 1 + expect(element.children[initialChildCount].textContent).toBe "a {color: red;}" disposable2 = atom.styles.addStyleSheet("a {color: blue;}") - expect(element.children.length).toBe 2 - expect(element.children[1].textContent).toBe "a {color: blue;}" + expect(element.children.length).toBe initialChildCount + 2 + expect(element.children[initialChildCount + 1].textContent).toBe "a {color: blue;}" disposable1.dispose() - expect(element.children.length).toBe 1 - expect(element.children[0].textContent).toBe "a {color: blue;}" + expect(element.children.length).toBe initialChildCount + 1 + expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}" it "orders style elements by group", -> - expect(element.children.length).toBe 0 + initialChildCount = element.children.length atom.styles.addStyleSheet("a {color: red}", group: 'a') atom.styles.addStyleSheet("a {color: blue}", group: 'b') atom.styles.addStyleSheet("a {color: green}", group: 'a') - expect(element.children[0].textContent).toBe "a {color: red}" - expect(element.children[1].textContent).toBe "a {color: green}" - expect(element.children[2].textContent).toBe "a {color: blue}" + expect(element.children[initialChildCount].textContent).toBe "a {color: red}" + expect(element.children[initialChildCount + 1].textContent).toBe "a {color: green}" + expect(element.children[initialChildCount + 2].textContent).toBe "a {color: blue}" diff --git a/src/atom.coffee b/src/atom.coffee index 6db0e59de..c4676425e 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -14,6 +14,7 @@ fs = require 'fs-plus' {$} = require './space-pen-extensions' WindowEventHandler = require './window-event-handler' +StylesElement = require './styles-element' # Essential: Atom global for dealing with packages, themes, menus, and the window. # @@ -202,8 +203,8 @@ class Atom extends Model @keymap = @keymaps # Deprecated @commands = new CommandRegistry @packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode}) + @styles = new StyleManager @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode}) - @styles = new StyleManager({resourcePath}) @contextMenu = new ContextMenuManager({resourcePath, devMode}) @menu = new MenuManager({resourcePath}) @clipboard = new Clipboard() @@ -221,6 +222,8 @@ class Atom extends Model @windowEventHandler = new WindowEventHandler + document.head.appendChild(new StylesElement) + ### Section: Event Subscription ### diff --git a/src/style-manager.coffee b/src/style-manager.coffee index a451d72ed..4fbea2afc 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -31,14 +31,25 @@ class StyleManager updated = true else styleElement = document.createElement('style') - styleElement.sourcePath = sourcePath if sourcePath? - styleElement.group = group if group? + styleElement.setAttribute('source-path', sourcePath) if sourcePath? + styleElement.setAttribute('group', group) if group? styleElement.textContent = source + if updated + @emitter.emit 'did-update-style-element', styleElement + else + @addStyleElement(styleElement, params) + + new Disposable => @removeStyleElement(styleElement) + + addStyleElement: (styleElement, params) -> + sourcePath = params?.sourcePath + group = params?.group + if group? for existingElement, index in @styleElements - if existingElement.group is group + if existingElement.getAttribute('group') is group insertIndex = index + 1 else break if insertIndex? @@ -46,22 +57,26 @@ class StyleManager @styleElements.splice(insertIndex, 0, styleElement) @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath? - - if updated - @emitter.emit 'did-update-style-element', styleElement - else - @emitter.emit 'did-add-style-element', styleElement - - new Disposable => @removeStyleElement(styleElement, params) + @emitter.emit 'did-add-style-element', styleElement removeStyleElement: (styleElement, params) -> index = @styleElements.indexOf(styleElement) unless index is -1 @styleElements.splice(index, 1) - sourcePath = params?.sourcePath - delete @styleElementsBySourcePath[sourcePath] if sourcePath? + if sourcePath = styleElement.getAttribute('source-path') + delete @styleElementsBySourcePath[sourcePath] @emitter.emit 'did-remove-style-element', styleElement - clear: -> - @styleElements = [] - @styleElementsBySourcePath = {} + getSnapshot: -> + @styleElements.slice() + + restoreSnapshot: (styleElementsToRestore) -> + for styleElement in @getStyleElements() + @removeStyleElement(styleElement) unless styleElement in styleElementsToRestore + + existingStyleElements = @getStyleElements() + for styleElement in styleElementsToRestore + unless styleElement in existingStyleElements + sourcePath = styleElement.getAttribute('source-path') + group = styleElement.getAttribute('group') + @addStyleElement(styleElement, {sourcePath, group}) diff --git a/src/styles-element.coffee b/src/styles-element.coffee index 522e6bfc0..50a0b4a87 100644 --- a/src/styles-element.coffee +++ b/src/styles-element.coffee @@ -8,14 +8,13 @@ class StylesElement extends HTMLElement @subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this)) styleElementAdded: (styleElement) -> - {group} = styleElement styleElementClone = styleElement.cloneNode(true) - styleElementClone.group = group @styleElementClonesByOriginalElement.set(styleElement, styleElementClone) + group = styleElement.getAttribute('group') if group? for child in @children - if child.group is group and child.nextSibling?.group isnt group + if child.getAttribute('group') is group and child.nextSibling?.getAttribute('group') isnt group insertBefore = child.nextSibling break diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 3c05c3eb9..bd084e4c3 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -19,10 +19,42 @@ class ThemeManager constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode}) -> @emitter = new Emitter + @styleSheetDisposablesBySourcePath = {} @lessCache = null @initialLoadComplete = false @packageManager.registerPackageActivator(this, ['theme']) + atom.styles.onDidAddStyleElement @styleElementAdded.bind(this) + atom.styles.onDidRemoveStyleElement @styleElementRemoved.bind(this) + atom.styles.onDidUpdateStyleElement @styleElementUpdated.bind(this) + + styleElementAdded: (element) -> + sheet = @styleSheetForElement(element) + @emit 'stylesheet-added', sheet + @emitter.emit 'did-add-stylesheet', sheet + @emit 'stylesheets-changed' + @emitter.emit 'did-change-stylesheets' + + styleElementRemoved: (element) -> + sheet = @styleSheetForElement(element) + @emit 'stylesheet-removed', sheet + @emitter.emit 'did-remove-stylesheet', sheet + @emit 'stylesheets-changed' + @emitter.emit 'did-change-stylesheets' + + styleElementUpdated: (element) -> + sheet = @styleSheetForElement(element) + @emit 'stylesheet-removed', sheet + @emitter.emit 'did-remove-stylesheet', sheet + @emit 'stylesheet-added', sheet + @emitter.emit 'did-add-stylesheet', sheet + @emit 'stylesheets-changed' + @emitter.emit 'did-change-stylesheets' + + styleSheetForElement: (element) -> + @stylesElement ?= document.head.querySelector('atom-styles') + @stylesElement.styleElementClonesByOriginalElement.get(element)?.sheet + ### Section: Event Subscription ### @@ -188,7 +220,6 @@ class ThemeManager if fullPath = @resolveStylesheet(stylesheetPath) content = @loadStylesheet(fullPath) @applyStylesheet(fullPath, content, type) - new Disposable => @removeStylesheet(fullPath) else throw new Error("Could not find a file at path '#{stylesheetPath}'") @@ -204,8 +235,7 @@ class ThemeManager @userStylesheetPath = userStylesheetPath @userStylesheetFile = new File(userStylesheetPath) - @userStylesheetFile.on 'contents-changed moved removed', => - @loadUserStylesheet() + @userStylesheetFile.on 'contents-changed moved removed', => @loadUserStylesheet() userStylesheetContents = @loadStylesheet(userStylesheetPath, true) @applyStylesheet(userStylesheetPath, userStylesheetContents, 'userTheme') @@ -219,7 +249,7 @@ class ThemeManager @requireStylesheet(nativeStylesheetPath) stylesheetElementForId: (id) -> - document.head.querySelector("""style[id="#{id}"]""") + document.head.querySelector("atom-styles style[source-path=\"#{id}\"]") resolveStylesheet: (stylesheetPath) -> if path.extname(stylesheetPath).length > 0 @@ -256,40 +286,10 @@ class ThemeManager """ removeStylesheet: (stylesheetPath) -> - fullPath = @resolveStylesheet(stylesheetPath) ? stylesheetPath - element = @stylesheetElementForId(@stringToId(fullPath)) - if element? - {sheet} = element - element.remove() - @emit 'stylesheet-removed', sheet - @emitter.emit 'did-remove-stylesheet', sheet - @emit 'stylesheets-changed' - @emitter.emit 'did-change-stylesheets' + @styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose() applyStylesheet: (path, text, type='bundled') -> - styleId = @stringToId(path) - styleElement = @stylesheetElementForId(styleId) - - if styleElement? - @emit 'stylesheet-removed', styleElement.sheet - @emitter.emit 'did-remove-stylesheet', styleElement.sheet - styleElement.textContent = text - else - styleElement = document.createElement('style') - styleElement.setAttribute('class', type) - styleElement.setAttribute('id', styleId) - styleElement.textContent = text - - elementToInsertBefore = _.last(document.head.querySelectorAll("style.#{type}"))?.nextElementSibling - if elementToInsertBefore? - document.head.insertBefore(styleElement, elementToInsertBefore) - else - document.head.appendChild(styleElement) - - @emit 'stylesheet-added', styleElement.sheet - @emitter.emit 'did-add-stylesheet', styleElement.sheet - @emit 'stylesheets-changed' - @emitter.emit 'did-change-stylesheets' + @styleSheetDisposablesBySourcePath[path] = atom.styles.addStyleSheet(text, sourcePath: path, group: type) ### Section: Private @@ -358,17 +358,3 @@ class ThemeManager themePaths.push(path.join(themePath, Package.stylesheetsDir)) themePaths.filter (themePath) -> fs.isDirectorySync(themePath) - - updateGlobalEditorStyle: (property, value) -> - unless styleNode = @stylesheetElementForId('global-editor-styles') - @applyStylesheet('global-editor-styles', 'atom-text-editor {}') - styleNode = @stylesheetElementForId('global-editor-styles') - - {sheet} = styleNode - editorRule = sheet.cssRules[0] - editorRule.style[property] = value - - @emit 'stylesheet-updated', sheet - @emitter.emit 'did-update-stylesheet', sheet - @emit 'stylesheets-changed' - @emitter.emit 'did-change-stylesheets' diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 686674cde..e50ba8879 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -8,8 +8,11 @@ WorkspaceView = null module.exports = class WorkspaceElement extends HTMLElement + globalTextEditorStyleSheet: null + createdCallback: -> @subscriptions = new CompositeDisposable + @initializeGlobalTextEditorStyleSheet() @initializeContent() @observeScrollbarStyle() @observeTextEditorFontConfig() @@ -22,6 +25,10 @@ class WorkspaceElement extends HTMLElement detachedCallback: -> @model.destroy() + initializeGlobalTextEditorStyleSheet: -> + atom.styles.addStyleSheet('atom-text-editor {}', sourcePath: 'global-text-editor-styles') + @globalTextEditorStyleSheet = document.head.querySelector('style[source-path="global-text-editor-styles"]').sheet + initializeContent: -> @classList.add 'workspace' @setAttribute 'tabindex', -1 @@ -46,9 +53,9 @@ class WorkspaceElement extends HTMLElement @classList.add("scrollbars-visible-when-scrolling") observeTextEditorFontConfig: -> - @subscriptions.add atom.config.observe 'editor.fontSize', @setTextEditorFontSize - @subscriptions.add atom.config.observe 'editor.fontFamily', @setTextEditorFontFamily - @subscriptions.add atom.config.observe 'editor.lineHeight', @setTextEditorLineHeight + @subscriptions.add atom.config.observe 'editor.fontSize', @setTextEditorFontSize.bind(this) + @subscriptions.add atom.config.observe 'editor.fontFamily', @setTextEditorFontFamily.bind(this) + @subscriptions.add atom.config.observe 'editor.lineHeight', @setTextEditorLineHeight.bind(this) createSpacePenShim: -> WorkspaceView ?= require './workspace-view' @@ -68,13 +75,18 @@ class WorkspaceElement extends HTMLElement @__spacePenView.setModel(@model) setTextEditorFontSize: (fontSize) -> - atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px') + @updateGlobalEditorStyle('font-size', fontSize + 'px') setTextEditorFontFamily: (fontFamily) -> - atom.themes.updateGlobalEditorStyle('font-family', fontFamily) + @updateGlobalEditorStyle('font-family', fontFamily) setTextEditorLineHeight: (lineHeight) -> - atom.themes.updateGlobalEditorStyle('line-height', lineHeight) + @updateGlobalEditorStyle('line-height', lineHeight) + + updateGlobalEditorStyle: (property, value) -> + editorRule = @globalTextEditorStyleSheet.cssRules[0] + editorRule.style[property] = value + atom.themes.emitter.emit 'did-update-stylesheet', @globalTextEditorStyleSheet handleFocus: (event) -> @model.getActivePane().activate() From be51ccf786a29616b508b0b5120e7632cdb181df Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 12:47:38 -0600 Subject: [PATCH 07/11] Add StylesElement::onDidAdd/RemoveStyleElement --- spec/styles-element-spec.coffee | 9 ++++++++- src/styles-element.coffee | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee index bd68f7a8b..d21fbeb16 100644 --- a/spec/styles-element-spec.coffee +++ b/spec/styles-element-spec.coffee @@ -2,11 +2,15 @@ StylesElement = require '../src/styles-element' StyleManager = require '../src/style-manager' describe "StylesElement", -> - element = null + [element, addedStyleElements, removedStyleElements] = [] beforeEach -> element = new StylesElement document.querySelector('#jasmine-content').appendChild(element) + addedStyleElements = [] + removedStyleElements = [] + element.onDidAddStyleElement (element) -> addedStyleElements.push(element) + element.onDidRemoveStyleElement (element) -> removedStyleElements.push(element) it "renders a style tag for all currently active stylesheets in the style manager", -> initialChildCount = element.children.length @@ -14,14 +18,17 @@ describe "StylesElement", -> disposable1 = atom.styles.addStyleSheet("a {color: red;}") expect(element.children.length).toBe initialChildCount + 1 expect(element.children[initialChildCount].textContent).toBe "a {color: red;}" + expect(addedStyleElements).toEqual [element.children[initialChildCount]] disposable2 = atom.styles.addStyleSheet("a {color: blue;}") expect(element.children.length).toBe initialChildCount + 2 expect(element.children[initialChildCount + 1].textContent).toBe "a {color: blue;}" + expect(addedStyleElements).toEqual [element.children[initialChildCount], element.children[initialChildCount + 1]] disposable1.dispose() expect(element.children.length).toBe initialChildCount + 1 expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}" + expect(removedStyleElements).toEqual [addedStyleElements[0]] it "orders style elements by group", -> initialChildCount = element.children.length diff --git a/src/styles-element.coffee b/src/styles-element.coffee index 50a0b4a87..47931dea1 100644 --- a/src/styles-element.coffee +++ b/src/styles-element.coffee @@ -1,6 +1,15 @@ -{CompositeDisposable} = require 'event-kit' +{Emitter, CompositeDisposable} = require 'event-kit' class StylesElement extends HTMLElement + createdCallback: -> + @emitter = new Emitter + + onDidAddStyleElement: (callback) -> + @emitter.on 'did-add-style-element', callback + + onDidRemoveStyleElement: (callback) -> + @emitter.on 'did-remove-style-element', callback + attachedCallback: -> @subscriptions = new CompositeDisposable @styleElementClonesByOriginalElement = new WeakMap @@ -19,9 +28,12 @@ class StylesElement extends HTMLElement break @insertBefore(styleElementClone, insertBefore) + @emitter.emit 'did-add-style-element', styleElementClone styleElementRemoved: (styleElement) -> - @styleElementClonesByOriginalElement.get(styleElement).remove() + styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) + styleElementClone.remove() + @emitter.emit 'did-remove-style-element', styleElementClone detachedCallback: -> @subscriptions.dispose() From a8b9e1b790242404b1f9caf35be7ac9d29378a51 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 12:51:58 -0600 Subject: [PATCH 08/11] Handle style element updates in StylesElement --- spec/styles-element-spec.coffee | 14 +++++++++++++- src/styles-element.coffee | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee index d21fbeb16..0cfb1a185 100644 --- a/spec/styles-element-spec.coffee +++ b/spec/styles-element-spec.coffee @@ -2,15 +2,17 @@ StylesElement = require '../src/styles-element' StyleManager = require '../src/style-manager' describe "StylesElement", -> - [element, addedStyleElements, removedStyleElements] = [] + [element, addedStyleElements, removedStyleElements, updatedStyleElements] = [] beforeEach -> element = new StylesElement document.querySelector('#jasmine-content').appendChild(element) addedStyleElements = [] removedStyleElements = [] + updatedStyleElements = [] element.onDidAddStyleElement (element) -> addedStyleElements.push(element) element.onDidRemoveStyleElement (element) -> removedStyleElements.push(element) + element.onDidUpdateStyleElement (element) -> updatedStyleElements.push(element) it "renders a style tag for all currently active stylesheets in the style manager", -> initialChildCount = element.children.length @@ -40,3 +42,13 @@ describe "StylesElement", -> expect(element.children[initialChildCount].textContent).toBe "a {color: red}" expect(element.children[initialChildCount + 1].textContent).toBe "a {color: green}" expect(element.children[initialChildCount + 2].textContent).toBe "a {color: blue}" + + it "updates existing style nodes when style elements are updated", -> + initialChildCount = element.children.length + + atom.styles.addStyleSheet("a {color: red;}", sourcePath: '/foo/bar') + atom.styles.addStyleSheet("a {color: blue;}", sourcePath: '/foo/bar') + + expect(element.children.length).toBe initialChildCount + 1 + expect(element.children[initialChildCount].textContent).toBe "a {color: blue;}" + expect(updatedStyleElements).toEqual [element.children[initialChildCount]] diff --git a/src/styles-element.coffee b/src/styles-element.coffee index 47931dea1..069c3f8c7 100644 --- a/src/styles-element.coffee +++ b/src/styles-element.coffee @@ -10,11 +10,15 @@ class StylesElement extends HTMLElement onDidRemoveStyleElement: (callback) -> @emitter.on 'did-remove-style-element', callback + onDidUpdateStyleElement: (callback) -> + @emitter.on 'did-update-style-element', callback + attachedCallback: -> @subscriptions = new CompositeDisposable @styleElementClonesByOriginalElement = new WeakMap @subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this)) @subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this)) + @subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this)) styleElementAdded: (styleElement) -> styleElementClone = styleElement.cloneNode(true) @@ -35,6 +39,11 @@ class StylesElement extends HTMLElement styleElementClone.remove() @emitter.emit 'did-remove-style-element', styleElementClone + styleElementUpdated: (styleElement) -> + styleElementClone = @styleElementClonesByOriginalElement.get(styleElement) + styleElementClone.textContent = styleElement.textContent + @emitter.emit 'did-update-style-element', styleElementClone + detachedCallback: -> @subscriptions.dispose() From 506753a535547e142f87de6514cff5982d95c305 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 13:07:23 -0600 Subject: [PATCH 09/11] Subscribe to element to shim events in ThemeManager This is better than subscribing to atom.styles because the events it emits contain the *actual* style elements we insert into the DOM. --- src/atom.coffee | 3 +-- src/theme-manager.coffee | 20 +++++++------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index c4676425e..f60de7fab 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -204,6 +204,7 @@ class Atom extends Model @commands = new CommandRegistry @packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode}) @styles = new StyleManager + document.head.appendChild(new StylesElement) @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode}) @contextMenu = new ContextMenuManager({resourcePath, devMode}) @menu = new MenuManager({resourcePath}) @@ -222,8 +223,6 @@ class Atom extends Model @windowEventHandler = new WindowEventHandler - document.head.appendChild(new StylesElement) - ### Section: Event Subscription ### diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index bd084e4c3..d958e3846 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -24,26 +24,24 @@ class ThemeManager @initialLoadComplete = false @packageManager.registerPackageActivator(this, ['theme']) - atom.styles.onDidAddStyleElement @styleElementAdded.bind(this) - atom.styles.onDidRemoveStyleElement @styleElementRemoved.bind(this) - atom.styles.onDidUpdateStyleElement @styleElementUpdated.bind(this) + stylesElement = document.head.querySelector('atom-styles') + stylesElement.onDidAddStyleElement @styleElementAdded.bind(this) + stylesElement.onDidRemoveStyleElement @styleElementRemoved.bind(this) + stylesElement.onDidUpdateStyleElement @styleElementUpdated.bind(this) - styleElementAdded: (element) -> - sheet = @styleSheetForElement(element) + styleElementAdded: ({sheet}) -> @emit 'stylesheet-added', sheet @emitter.emit 'did-add-stylesheet', sheet @emit 'stylesheets-changed' @emitter.emit 'did-change-stylesheets' - styleElementRemoved: (element) -> - sheet = @styleSheetForElement(element) + styleElementRemoved: ({sheet}) -> @emit 'stylesheet-removed', sheet @emitter.emit 'did-remove-stylesheet', sheet @emit 'stylesheets-changed' @emitter.emit 'did-change-stylesheets' - styleElementUpdated: (element) -> - sheet = @styleSheetForElement(element) + styleElementUpdated: ({sheet}) -> @emit 'stylesheet-removed', sheet @emitter.emit 'did-remove-stylesheet', sheet @emit 'stylesheet-added', sheet @@ -51,10 +49,6 @@ class ThemeManager @emit 'stylesheets-changed' @emitter.emit 'did-change-stylesheets' - styleSheetForElement: (element) -> - @stylesElement ?= document.head.querySelector('atom-styles') - @stylesElement.styleElementClonesByOriginalElement.get(element)?.sheet - ### Section: Event Subscription ### From 04b3eef76885a5707d5c5d19cbb092db3eca4d17 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 13:21:19 -0600 Subject: [PATCH 10/11] Fix theme-manager-specs --- spec/theme-manager-spec.coffee | 26 +++++++++++++------------- src/theme-manager.coffee | 8 ++++++-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index c232f11ac..048d793ae 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -92,8 +92,8 @@ describe "ThemeManager", -> runs -> reloadHandler.reset() - expect($('style.theme')).toHaveLength 1 - expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/ + expect($('style[group=theme]')).toHaveLength 1 + expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/ atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax']) waitsFor -> @@ -101,9 +101,9 @@ describe "ThemeManager", -> runs -> reloadHandler.reset() - expect($('style.theme')).toHaveLength 2 - expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/ - expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/ + expect($('style[group=theme]')).toHaveLength 2 + expect($('style[group=theme]:eq(0)').attr('source-path')).toMatch /atom-dark-syntax/ + expect($('style[group=theme]:eq(1)').attr('source-path')).toMatch /atom-light-syntax/ atom.config.set('core.themes', []) waitsFor -> @@ -111,7 +111,7 @@ describe "ThemeManager", -> runs -> reloadHandler.reset() - expect($('style.theme')).toHaveLength 0 + expect($('style[group=theme]')).toHaveLength 0 # atom-dark-ui has an directory path, the syntax one doesn't atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui']) @@ -119,7 +119,7 @@ describe "ThemeManager", -> reloadHandler.callCount == 1 runs -> - expect($('style.theme')).toHaveLength 2 + expect($('style[group=theme]')).toHaveLength 2 importPaths = themeManager.getImportPaths() expect(importPaths.length).toBe 1 expect(importPaths[0]).toContain 'atom-dark-ui' @@ -142,8 +142,8 @@ describe "ThemeManager", -> expect(stylesheetAddedHandler).toHaveBeenCalled() expect(stylesheetsChangedHandler).toHaveBeenCalled() - element = $('head style[id*="css.css"]') - expect(element.attr('id')).toBe themeManager.stringToId(cssPath) + element = $('head style[source-path*="css.css"]') + expect(element.attr('source-path')).toBe themeManager.stringToId(cssPath) expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8') expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0] @@ -159,8 +159,8 @@ describe "ThemeManager", -> themeManager.requireStylesheet(lessPath) expect($('head style').length).toBe lengthBefore + 1 - element = $('head style[id*="sample.less"]') - expect(element.attr('id')).toBe themeManager.stringToId(lessPath) + element = $('head style[source-path*="sample.less"]') + expect(element.attr('source-path')).toBe themeManager.stringToId(lessPath) expect(element.text()).toBe """ #header { color: #4d926f; @@ -178,9 +178,9 @@ describe "ThemeManager", -> it "supports requiring css and less stylesheets without an explicit extension", -> themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css') - expect($('head style[id*="css.css"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('css.css')) + expect($('head style[source-path*="css.css"]').attr('source-path')).toBe themeManager.stringToId(atom.project.resolve('css.css')) themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample') - expect($('head style[id*="sample.less"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('sample.less')) + expect($('head style[source-path*="sample.less"]').attr('source-path')).toBe themeManager.stringToId(atom.project.resolve('sample.less')) $('head style[id*="css.css"]').remove() $('head style[id*="sample.less"]').remove() diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index d958e3846..edf371556 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -23,19 +23,23 @@ class ThemeManager @lessCache = null @initialLoadComplete = false @packageManager.registerPackageActivator(this, ['theme']) + @sheetsByStyleElement = new WeakMap stylesElement = document.head.querySelector('atom-styles') stylesElement.onDidAddStyleElement @styleElementAdded.bind(this) stylesElement.onDidRemoveStyleElement @styleElementRemoved.bind(this) stylesElement.onDidUpdateStyleElement @styleElementUpdated.bind(this) - styleElementAdded: ({sheet}) -> + styleElementAdded: (styleElement) -> + {sheet} = styleElement + @sheetsByStyleElement.set(styleElement, sheet) @emit 'stylesheet-added', sheet @emitter.emit 'did-add-stylesheet', sheet @emit 'stylesheets-changed' @emitter.emit 'did-change-stylesheets' - styleElementRemoved: ({sheet}) -> + styleElementRemoved: (styleElement) -> + sheet = @sheetsByStyleElement.get(styleElement) @emit 'stylesheet-removed', sheet @emitter.emit 'did-remove-stylesheet', sheet @emit 'stylesheets-changed' From 314c525d2d66187e3e934ed265df627bc3f5bf32 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Oct 2014 14:48:26 -0600 Subject: [PATCH 11/11] Assign properties in addition to attributes for convenience --- src/style-manager.coffee | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/style-manager.coffee b/src/style-manager.coffee index 4fbea2afc..9f6b912fd 100644 --- a/src/style-manager.coffee +++ b/src/style-manager.coffee @@ -31,25 +31,33 @@ class StyleManager updated = true else styleElement = document.createElement('style') - styleElement.setAttribute('source-path', sourcePath) if sourcePath? - styleElement.setAttribute('group', group) if group? + if sourcePath? + styleElement.sourcePath = sourcePath + styleElement.setAttribute('source-path', sourcePath) + + if context? + styleElement.context = context + styleElement.setAttribute('context', context) + + if group? + styleElement.group = group + styleElement.setAttribute('group', group) styleElement.textContent = source if updated @emitter.emit 'did-update-style-element', styleElement else - @addStyleElement(styleElement, params) + @addStyleElement(styleElement) new Disposable => @removeStyleElement(styleElement) - addStyleElement: (styleElement, params) -> - sourcePath = params?.sourcePath - group = params?.group + addStyleElement: (styleElement) -> + {sourcePath, group} = styleElement if group? for existingElement, index in @styleElements - if existingElement.getAttribute('group') is group + if existingElement.group is group insertIndex = index + 1 else break if insertIndex? @@ -59,12 +67,11 @@ class StyleManager @styleElementsBySourcePath[sourcePath] ?= styleElement if sourcePath? @emitter.emit 'did-add-style-element', styleElement - removeStyleElement: (styleElement, params) -> + removeStyleElement: (styleElement) -> index = @styleElements.indexOf(styleElement) unless index is -1 @styleElements.splice(index, 1) - if sourcePath = styleElement.getAttribute('source-path') - delete @styleElementsBySourcePath[sourcePath] + delete @styleElementsBySourcePath[styleElement.sourcePath] if styleElement.sourcePath? @emitter.emit 'did-remove-style-element', styleElement getSnapshot: -> @@ -76,7 +83,4 @@ class StyleManager existingStyleElements = @getStyleElements() for styleElement in styleElementsToRestore - unless styleElement in existingStyleElements - sourcePath = styleElement.getAttribute('source-path') - group = styleElement.getAttribute('group') - @addStyleElement(styleElement, {sourcePath, group}) + @addStyleElement(styleElement) unless styleElement in existingStyleElements